> ## Documentation Index
> Fetch the complete documentation index at: https://docs.social-api.ai/llms.txt
> Use this file to discover all available pages before exploring further.

> ## Agent Instructions
> Base URL: https://api.social-api.ai/v1. Send your API key in the Authorization header: Authorization: Bearer <your API key>.
> Every error response from /v1 is JSON: { "error": { "code", "message", "meta" (optional) }, "request_id" }. Branch on error.code; use error.message only for logs.
> A 429 response carries a Retry-After header, in seconds, when the limit has a known wait.
> Paginated list endpoints return next_cursor, at the top level or inside pagination (see /guides/pagination). Pass it as the cursor query parameter to get the next page. An absent or empty next_cursor means you have the last page.
> Treat every ID as an opaque string: do not parse it or assume a format.
> Support differs by platform and by operation. Check /platforms/overview before assuming an operation works on a platform. An unsupported operation returns 501 with error.code resource.not_supported.

# OAuth for MCP clients

> Authenticate an MCP client or your own app against SocialAPI with OAuth 2.1, PKCE, and dynamic client registration, then call the API for a user.

SocialAPI runs an OAuth 2.1 authorization server so that an MCP client or your own app can act for a SocialAPI user without handling an API key. The user approves the app on SocialAPI's consent screen, and the app receives an access token it sends as `Authorization: Bearer <token>` to the MCP server at `https://api.social-api.ai/mcp` and to every public `/v1` endpoint.

Claude, ChatGPT, Cursor, and other MCP clients that support OAuth run this flow on their own once you add the server URL: see [the MCP server](/integrations/overview). Build the flow yourself only for a client you write. The server follows RFC 8414 (metadata), RFC 7591 (dynamic client registration), RFC 7636 (PKCE, required), RFC 7009 (revocation), and RFC 9728 (protected resource metadata).

## Endpoints

| Method | Path | Description |
| - | - | - |
| `GET` | `/.well-known/oauth-protected-resource` | Protected resource metadata for the MCP server. Also served at `/.well-known/oauth-protected-resource/mcp` |
| `GET` | `/.well-known/oauth-authorization-server` | Authorization server metadata |
| `POST` | `/oauth/register` | Register a client |
| `GET` | `/oauth/authorize` | Send the user to the consent screen |
| `POST` | `/oauth/token` | Exchange a code for tokens, or refresh them |
| `POST` | `/oauth/revoke` | Revoke a refresh token |
| `GET` | `/.well-known/mcp/server-card.json` | The MCP server card, for directories that list MCP servers |

Every path is on `https://api.social-api.ai`, with no `/v1` prefix and no API key.

## Discover the server

The protected resource metadata names the MCP server and its authorization server:

```bash theme={null}
curl https://api.social-api.ai/.well-known/oauth-protected-resource
```

Response (HTTP 200):

```json theme={null}
{
  "resource": "https://api.social-api.ai/mcp",
  "authorization_servers": ["https://api.social-api.ai"],
  "bearer_methods_supported": ["header"],
  "scopes_supported": ["social:all"]
}
```

The authorization server metadata lists every endpoint:

```bash theme={null}
curl https://api.social-api.ai/.well-known/oauth-authorization-server
```

Response (HTTP 200):

```json theme={null}
{
  "issuer": "https://api.social-api.ai",
  "authorization_endpoint": "https://api.social-api.ai/oauth/authorize",
  "token_endpoint": "https://api.social-api.ai/oauth/token",
  "registration_endpoint": "https://api.social-api.ai/oauth/register",
  "revocation_endpoint": "https://api.social-api.ai/oauth/revoke",
  "response_types_supported": ["code"],
  "grant_types_supported": ["authorization_code", "refresh_token"],
  "token_endpoint_auth_methods_supported": ["client_secret_post"],
  "code_challenge_methods_supported": ["S256"],
  "scopes_supported": ["social:all"]
}
```

## Run the flow

**1. Register your client**

Registration is open: any app can register, with no API key.

```bash theme={null}
curl -X POST https://api.social-api.ai/oauth/register \
  -H "Content-Type: application/json" \
  -d '{
    "client_name": "Acme Assistant",
    "redirect_uris": ["https://app.example.com/oauth/socialapi/callback"]
  }'
```

Response (HTTP 201):

```json theme={null}
{
  "client_id": "9f2c4e6a8b1d3f5a7c9e1b3d5f7a9c2e",
  "client_secret": "c572b7d490c47214440a3f049e0582a4d03e012b0a48b9ef8d2aeaa7c5e36f8c",
  "client_id_issued_at": 1791997200,
  "client_secret_expires_at": 0,
  "client_name": "Acme Assistant",
  "redirect_uris": ["https://app.example.com/oauth/socialapi/callback"],
  "grant_types": ["authorization_code", "refresh_token"],
  "response_types": ["code"],
  "token_endpoint_auth_method": "client_secret_post"
}
```

`client_name` and at least one entry in `redirect_uris` are required, or the request returns `400 validation.field_required`. Each redirect URI must use `http` or `https` and must not contain a user name, a password, or a `#fragment`; otherwise the request returns `400 validation.field_invalid`. `client_secret` is shown once and never expires (`client_secret_expires_at` is `0`).

**2. Send the user to the consent screen**

Generate a PKCE code verifier, derive its `S256` challenge, and redirect the user's browser to:

```text theme={null}
https://api.social-api.ai/oauth/authorize?client_id=9f2c4e6a8b1d3f5a7c9e1b3d5f7a9c2e&redirect_uri=https%3A%2F%2Fapp.example.com%2Foauth%2Fsocialapi%2Fcallback&response_type=code&scope=social%3Aall&state=Zx81Kq&code_challenge=E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM&code_challenge_method=S256
```

SocialAPI checks the request, then sends the user to the SocialAPI dashboard. The user signs in to SocialAPI, sees your `client_name`, and approves or denies. The consent screen accepts the request for 1 hour.

| Parameter | Required | Description |
| - | - | - |
| `client_id` | Yes | From registration |
| `redirect_uri` | Yes | Must equal one of the client's registered URIs, character for character |
| `code_challenge` | Yes | The PKCE challenge |
| `code_challenge_method` | Yes | Must be `S256` |
| `response_type` | No | `code` |
| `scope` | No | `social:all`, the default and the only scope |
| `state` | No | Your value, returned unchanged with the code |

A missing `client_id`, `redirect_uri`, or `code_challenge` returns `400 validation.field_required`. A `code_challenge_method` other than `S256` returns `400 validation.field_invalid`. An unknown `client_id` returns `400 auth.oauth.invalid_client`, and an unregistered `redirect_uri` returns `400 auth.oauth.invalid_redirect`. These errors are JSON responses to the browser, not redirects.

**3. Receive the code**

After approval, the browser lands on your redirect URI:

```text theme={null}
https://app.example.com/oauth/socialapi/callback?code=f1f59823f4e9ff94e434e2c1a633d9c5830e62cdbd43eb8ef97ccabe15fbcf41&state=Zx81Kq
```

When the user denies, the redirect carries `error=access_denied` and your `state` instead. The code expires 10 minutes after it is issued and works once.

**4. Exchange the code for tokens**

```bash theme={null}
curl -X POST https://api.social-api.ai/oauth/token \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "grant_type=authorization_code" \
  -d "code=f1f59823f4e9ff94e434e2c1a633d9c5830e62cdbd43eb8ef97ccabe15fbcf41" \
  -d "client_id=9f2c4e6a8b1d3f5a7c9e1b3d5f7a9c2e" \
  -d "client_secret=c572b7d490c47214440a3f049e0582a4d03e012b0a48b9ef8d2aeaa7c5e36f8c" \
  -d "code_verifier=dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk" \
  -d "redirect_uri=https://app.example.com/oauth/socialapi/callback"
```

Response (HTTP 200):

```json theme={null}
{
  "access_token": "eyJhbGciOiJFZERTQSIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI1NTBlODQwMCJ9.c2lnbmF0dXJl",
  "token_type": "Bearer",
  "expires_in": 3600,
  "refresh_token": "d3161495b7d3f064afd3b5aa7876503f0ff9273896ea7e9257bef82a809defcf",
  "scope": "social:all"
}
```

`code`, `client_id`, and `code_verifier` are required. If you send `client_secret`, SocialAPI verifies it. If you omit it, PKCE alone protects the exchange. `redirect_uri` is checked when you send it, and must equal the one used in step 2. An exchange that passes the `client_id` and `client_secret` check still uses up the code when it fails because the code was issued to another client, `redirect_uri` does not match, or PKCE verification fails: send the user through step 2 again.

**5. Call the API**

Send the access token as the Bearer token:

```bash theme={null}
curl https://api.social-api.ai/v1/accounts \
  -H "Authorization: Bearer eyJhbGciOiJFZERTQSIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI1NTBlODQwMCJ9.c2lnbmF0dXJl"
```

## Tokens

| Token | Lifetime | Notes |
| - | - | - |
| Access token | 1 hour (`expires_in: 3600`) | Full access to the SocialAPI account of the user who approved. Not limited by scopes or brands |
| Refresh token | 30 days | Works once. Each refresh returns a new refresh token and ends the old one |
| Authorization code | 10 minutes | Works once |

An expired or invalid access token returns `401 auth.invalid_token` on `/v1` endpoints. An access token whose user no longer exists returns `401 auth.user_not_found`. For a key with narrower access, use a [scoped API key](/guides/scoped-keys) instead.

## Refresh the access token

```bash theme={null}
curl -X POST https://api.social-api.ai/oauth/token \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "grant_type=refresh_token" \
  -d "refresh_token=d3161495b7d3f064afd3b5aa7876503f0ff9273896ea7e9257bef82a809defcf" \
  -d "client_id=9f2c4e6a8b1d3f5a7c9e1b3d5f7a9c2e" \
  -d "client_secret=c572b7d490c47214440a3f049e0582a4d03e012b0a48b9ef8d2aeaa7c5e36f8c"
```

The response (HTTP 200) has the same shape as the code exchange, with a new access token and a new refresh token. Store the new refresh token: the one you sent stops working. `refresh_token` and `client_id` are required. If you send `client_secret`, SocialAPI verifies it. If you omit it, the refresh token alone authorizes the refresh (PKCE does not apply to a refresh).

## Client authentication

The token endpoint reads `client_id` and `client_secret` from the form body. It also accepts them with HTTP Basic authentication (`Authorization: Basic base64(client_id:client_secret)`); when both are present, the body wins. It accepts a JSON body when the request has no form-encoded `grant_type`.

A client that cannot keep a secret, such as a desktop or browser app, can leave `client_secret` out: PKCE protects the code exchange.

## Revoke a refresh token

```bash theme={null}
curl -X POST https://api.social-api.ai/oauth/revoke \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "token=d3161495b7d3f064afd3b5aa7876503f0ff9273896ea7e9257bef82a809defcf" \
  -d "client_id=9f2c4e6a8b1d3f5a7c9e1b3d5f7a9c2e"
```

Response: HTTP 200 with no body, whether or not the token existed (RFC 7009). The refresh token stops working when it belongs to that `client_id`. Revocation applies to refresh tokens only: an access token already issued keeps working until it expires, at most 1 hour later.

## Token endpoint errors

`/oauth/token` answers errors in the OAuth format (RFC 6749), not in the SocialAPI error envelope:

```json theme={null}
{
  "error": "invalid_grant",
  "error_description": "invalid or expired authorization code"
}
```

* `400 invalid_request`: `grant_type` is missing, or a required parameter of the grant is missing.
* `400 unsupported_grant_type`: `grant_type` is not `authorization_code` or `refresh_token`.
* `400 invalid_grant`: the code or refresh token is unknown, expired, already used, or issued to another client, or the PKCE verifier or `redirect_uri` does not match. On a refresh, an unknown `client_id` also returns `invalid_grant`.
* `401 invalid_client`: on a code exchange, the `client_id` is unknown. On either grant, the `client_secret` you sent is wrong.

## Related

* [SocialAPI MCP server](/integrations/overview): connect Claude, ChatGPT, Cursor, and other clients
* [Custom MCP clients](/integrations/custom): connect your own client with an API key
* [Authentication](/guides/authentication): API keys and the two token types
* [Scoped API keys](/guides/scoped-keys): access narrower than a full-access token
* [Errors](/guides/errors): the `auth.oauth.*` codes


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.