# auth.md

You are an agent that wants to call UnifAPI on a user's behalf. This file explains the credential paths UnifAPI supports today and how to handle those credentials safely.

Two hosts are relevant:

- **Resource server** - `https://api.unifapi.com` - direct HTTP public-data APIs.
- **MCP server** - `https://mcp.unifapi.com` - the hosted connector for agent Skills, protected by OAuth.

## Current state

UnifAPI does not issue credentials from WorkOS-style `POST /agent/auth`, `/agent/auth/claim`, or `/agent/auth/revoke` endpoints today. `GET /agent/auth` is a discovery hint only: it returns `401` with `WWW-Authenticate` so agents can find OAuth protected-resource metadata.

UnifAPI supports agent access through Skills and MCP OAuth, and supports direct HTTP access through workspace API keys supplied out of band. OAuth and API keys spend the same workspace credits and do not grant access to private source accounts.

## Use existing tooling first

Before doing anything credential-shaped, check whether the user has already wired UnifAPI into your environment.

1. **UnifAPI Skills** - task prompts and operating guidance live at `https://unifapi.com/skills.md`. Load the relevant Skill before calling tools or the HTTP API directly.
2. **UnifAPI MCP server** - if you are an MCP client, use `https://mcp.unifapi.com`. A compatible client discovers OAuth from `https://mcp.unifapi.com/.well-known/oauth-protected-resource` and stores the token outside the conversation.
3. **Packaged installs** - if the user's AI client offers a UnifAPI listing, use that install path because it may configure Skills and MCP together.
4. **OpenAPI and docs** - inspect `https://api.unifapi.com/api/openapi.json`, `https://unifapi.com/openapi.json`, or `https://docs.unifapi.com/llms.txt` for exact paths and schemas.

If MCP OAuth is already configured and the task can be done through MCP tools, use it and stop. Do not ask the user for an API key you do not need.

## Supported login option: MCP OAuth

Use this when you are an MCP-capable agent or client.

Discovery:

```http
GET /.well-known/oauth-protected-resource HTTP/1.1
Host: mcp.unifapi.com
```

Canonical metadata:

```json
{
  "resource": "https://mcp.unifapi.com/",
  "resource_name": "UnifAPI MCP",
  "resource_logo_uri": "https://unifapi.com/unif.png",
  "resource_documentation": "https://docs.unifapi.com/mcp",
  "authorization_servers": [
    "https://api.unifapi.com/api/auth"
  ],
  "scopes_supported": [
    "unifapi:mcp"
  ],
  "bearer_methods_supported": [
    "header"
  ]
}
```

Agent behavior:

- Register the MCP server URL: `https://mcp.unifapi.com`.
- Let the MCP client complete the browser OAuth flow when it asks.
- Do not ask the user to paste OAuth tokens into chat.
- Use MCP tools such as `list_operations`, `get_operation`, and `call_api`.
- Treat a `401` from a previously-working OAuth token as revocation or expiry: drop it and let the MCP client re-authenticate.

## Supported login option: API key, supplied out of band

Use this when you are calling the HTTP API directly or when an MCP client cannot complete OAuth. UnifAPI API keys are workspace-scoped and use the `unif_` prefix.

### How to pick the key up

Look for a key in this order. Stop at the first one that exists:

1. A bearer header already configured in the MCP client or HTTP client.
2. `UNIFAPI_API_KEY` in your process environment.
3. `UNIFAPI_KEY` in your process environment.
4. A project `.env` file the user has explicitly told you to read.
5. A secret manager entry the user has configured for this runtime.

If none of the above is set and you genuinely need a key, do not ask the user to paste it into the conversation. Tell them to:

- Sign in at `https://api.unifapi.com/sign-in`.
- Create a workspace API key for this UnifAPI workspace.
- Put it in `UNIFAPI_API_KEY`, the MCP client config, a local `.env`, or their secret manager.
- Resume the task once it is set.

### How to use the key

Present it as a bearer token:

```http
GET /x/tweets/search/recent HTTP/1.1
Host: api.unifapi.com
Authorization: Bearer $UNIFAPI_API_KEY
```

Read the key from the environment at the moment of the call. Do not echo it, log it, put it in command history, commit it, include it in screenshots, or paste it into PR descriptions.

The key does not expire on its own. Treat a `401` on a previously-working key as revocation: drop the cached value and re-read from the same secure source.

## 401 discovery

When UnifAPI rejects a missing or invalid credential, API responses include a discovery hint for OAuth-capable agents:

```http
HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer resource_metadata="https://api.unifapi.com/.well-known/oauth-protected-resource"
```

The canonical MCP protected resource metadata is `https://mcp.unifapi.com/.well-known/oauth-protected-resource`. Treat protected resource metadata as the runtime source of truth if it differs from this file.

## Errors

Canonical public error codes:

`unauthorized`, `validation_error`, `invalid_id`, `not_found`, `rate_limited`, `insufficient_credits`, `upstream_error`, `upstream_unavailable`, `response_shape_error`, `internal_error`.

Agents should inspect `error.code` before retrying or changing credentials.

```json
{
  "error": {
    "code": "unauthorized",
    "message": "Missing or invalid bearer token."
  }
}
```

| `error.code` | Retry behavior | Credential behavior |
| --- | --- | --- |
| `unauthorized` | Do not retry the same request blindly. | Re-read the configured credential, restart MCP OAuth, or ask the user to reconnect the workspace. |
| `validation_error` | Do not retry until parameters are fixed. | Keep the credential; inspect the operation schema and correct the request. |
| `invalid_id` | Do not retry until the source identifier is corrected. | Keep the credential; ask for or discover a valid public source id. |
| `not_found` | Do not retry unless the user changes the source id, handle, or URL. | Keep the credential; report that the public resource was not found or is unavailable. |
| `rate_limited` | Retry only after backoff. | Keep the credential; honor `Retry-After` if present. |
| `insufficient_credits` | Do not retry until credits are added. | Keep the credential; ask the user to top up or switch workspace. |
| `upstream_error` | Retry only if the operation is safe and the task still needs it. | Keep the credential; explain that the source provider failed. |
| `upstream_unavailable` | Retry with backoff or defer the task. | Keep the credential; check status guidance before repeated retries. |
| `response_shape_error` | Do not transform the broken response silently. | Keep the credential; report the mismatch and include the request id if available. |
| `internal_error` | Retry with backoff once, then stop. | Keep the credential; contact support with the request id if the failure repeats. |

| Status | Meaning | What to do |
| --- | --- | --- |
| `401` on first use | Credential is missing, malformed, revoked, disabled, expired, or for the wrong resource. | Re-read the credential from the configured secure source, or use MCP OAuth discovery. |
| `401` on a previously-working credential | Credential was revoked, rotated, expired, or no longer has the right audience. | Drop the cached value and restart OAuth or refresh the secret source. |
| `402` | Workspace does not have enough credits. | Ask the user to top up or switch workspace. |
| `429` | Rate limited. | Back off and retry. Honor `Retry-After` if present. |

## Revocation

Users revoke API keys in the UnifAPI dashboard at `https://api.unifapi.com/sign-in`. MCP OAuth tokens are reissued by the OAuth client flow. Agents discover revocation as a `401` on a previously-working credential and should restart from discovery.

## Integration contact

For auth discovery issues, contact `support@unifapi.com`.
