/v1 endpoints requires a Bearer token in the Authorization header. The API accepts three token types.
Token types
| Token type | Who uses it | Format | Validation method |
|---|---|---|---|
| API key | External developers, API clients | sapi_key_<hex> prefix | SHA-256 lookup hash, then bcrypt verify against api_keys table |
| Auth service JWT | Dashboard frontend (Next.js) | EdDSA/Ed25519 signed JWT | Signature verified via JWKS endpoint on the auth service (cached 15 min) |
| OAuth 2.1 access token | MCP clients (Claude, ChatGPT, Cursor) | EdDSA/Ed25519 signed JWT | Signature verified via the API’s own OAuth public key |
sapi_key_ are treated as API keys. All other tokens are validated as JWTs: first against the auth service public key, then (if that fails) against the OAuth 2.1 public key.
API keys
API keys are the primary authentication method for external integrations.Format
Passing the key
Include the key in theAuthorization header:
Creating keys
Keys are created in the dashboard under Keys > New Key, or via the API:raw_key. Store it immediately. Subsequent reads only show a truncated preview like sapi_key_a1b2c....
Revoking keys
Listing keys
preview (suffix only), is_active, and last_used_at.
Auth service JWTs (dashboard)
The SocialAPI dashboard authenticates users through a standalone auth service that issues EdDSA/Ed25519 signed JWTs. When a dashboard user makes an API call, their JWT is sent as a Bearer token. The API validates the JWT signature by fetching the auth service’s public key from its JWKS endpoint (AUTH_SERVICE_URL/internal/.well-known/jwks.json). The public key is cached for 15 minutes.
The JWT’s sub (subject) claim contains the auth service user UUID, which is mapped to a user record in the API database via POST /v1/users/provision.
Before a dashboard user can call any endpoint, they must be provisioned via
POST /v1/users/provision. This creates the user record that JWT authentication depends on. The dashboard calls this automatically after every login. If this record is missing, JWT-authenticated requests return 401 user not provisioned.OAuth 2.1 access tokens (MCP clients)
MCP clients (Claude, ChatGPT, Cursor) authenticate via the API’s built-in OAuth 2.1 authorization server. After completing the OAuth flow (see OAuth guide), the client receives an EdDSA/Ed25519 signed JWT access token. These tokens look identical to auth service JWTs at the wire level, but they are signed with the API’s own OAuth private key (configured viaOAUTH_JWT_PRIVATE_KEY) and contain a type: "oauth_access" claim.
Access tokens expire after 1 hour. Use the refresh token to obtain a new access token without re-authorization.
How the middleware resolves tokens
- If the token starts with
sapi_key_, it is validated as an API key (SHA-256 lookup hash, bcrypt verify). - Otherwise, the token is validated as an auth service JWT (EdDSA signature check against the JWKS endpoint).
- If auth service JWT validation fails, the token is validated as an OAuth 2.1 access token (EdDSA signature check against the OAuth public key).
- If all checks fail, the request is rejected with
401.
APIKey record attached to the request context with the user’s ID and plan tier.
Security best practices
- Never expose keys in frontend code. Keys must only be used server-side.
- Use environment variables. Do not hardcode keys in source code.
- Create one key per service. Revoke individual keys if compromised without disrupting others.
- Monitor
last_used_at. Unused keys should be revoked. - Rotate OAuth refresh tokens. The API enforces refresh token rotation. Each refresh grants a new token pair.