Get started
Authentication
An API key for your code and for MCP clients without a sign-in button; Apogee's OAuth sign-in for AI assistants. Both act as the Apogee account that created them, within the scopes it chose.
Two kinds of credentials
| Credential | API key | OAuth access token |
|---|---|---|
| Looks like | apk_… (47 characters) | apo_at_… |
| Works with | The REST API and the MCP server | The MCP server only |
| Made by | You, in Settings → Connections → API | Your AI assistant, when you click Authorize on Apogee's page |
| Lasts | 30 days, 90 days, 1 year or never, as you choose | 1 hour, renewed with a refresh token valid 60 days |
| Revoked in | Settings → Connections → API | Settings → Connections → MCP (Connected assistants) |
Both go in the Authorization header. Unknown, revoked and expired credentials get the same 401: Apogee never says whether a credential exists.
Authorization: Bearer $APOGEE_API_KEY API keys
- Create one in the Apogee app: Settings → Connections → API → Create key. Give it a name (up to characters), its scopes, and an expiry: 30 days, 90 days, 1 year, or never.
- It is shown once. Apogee stores only its SHA-256 hash and a hint (its first and last characters) to tell your keys apart. Lost: revoke it and create another.
- 10 active keys at most per account (20 created an hour at most). Revoking one stops it at once.
- One key per program or assistant, with only the scopes it needs: you can revoke one without touching the others, and Settings → Usage shows each key's calls and credits.
- Never in a browser or a repository. The REST API sends no CORS headers (except on its public OpenAPI document): call it from a server, a script or CI, with the key in an environment variable or a secret store.
Creating keys and revoking them is done in the app only, logged in to Apogee: a key can never make another key.
Scopes
Each key, and each assistant you authorize, gets one or more of three scopes. A tool outside them is not even listed to the assistant; a REST route outside them answers 403 insufficient_scope with the required_scope.
| Scope | What it allows |
|---|---|
read | Read ad accounts, campaigns, ad sets, ads, insights, and follow imports and launches. |
launch | Import and upload creatives, prepare and launch ads, create a campaign. |
actions | Pause, activate, change budgets and targeting, duplicate, create ad sets and ads from existing posts. |
Running a prepared action checks the action's own scope: a new campaign needs launch, every other change actions.
OAuth for the MCP server
Assistants with a "connect" button (Claude, ChatGPT, Perplexity, VS Code…) sign in with OAuth 2.1, authorization code with PKCE, as the MCP authorization specification describes it. You never handle a token: you log in to Apogee on its own page, check what the assistant asks for, untick scopes if you want (one at least), and click Authorize.
- The client calls
https://app.apogee.ad/mcpwithout a token and gets401withWWW-Authenticate: Bearer resource_metadata="https://app.apogee.ad/.well-known/oauth-protected-resource/mcp". - It reads the metadata documents below, then registers itself (Dynamic Client Registration) or presents a client metadata document (a
client_idthat is an https URL on the assistant's own domain). - It opens Apogee's consent page with a PKCE challenge (
S256only); after your Authorize, it receives a code (valid 10 minutes) and exchanges it for tokens. - It calls the server with
Authorization: Bearer apo_at_…and refreshes the token every 1 hour.
| Endpoint | URL |
|---|---|
| Protected resource metadata (RFC 9728) | https://app.apogee.ad/.well-known/oauth-protected-resource/mcp |
| Authorization server metadata (RFC 8414) | https://app.apogee.ad/.well-known/oauth-authorization-server |
| Client registration (RFC 7591) | https://app.apogee.ad/oauth/register |
| Authorization (the consent page) | https://app.apogee.ad/oauth/authorize |
| Token | https://app.apogee.ad/oauth/token |
| Revocation (RFC 7009) | https://app.apogee.ad/oauth/revoke |
- Grant types:
authorization_codeandrefresh_token. Scopes:read launch actions, plusoffline_access, accepted for the clients that ask for it (every grant gets a refresh token anyway). - Where the code may go back: only to these assistants' callbacks:
claude.ai,claude.com,chatgpt.com,www.perplexity.ai,enterprise.perplexity.ai,chat.mistral.ai,console.mistral.ai,vscode.dev; and to your own machine (http://localhost,127.0.0.1or[::1], any port) for Claude Code, Gemini CLI or VS Code. Any other client connects with an API key. - Disconnect an assistant in Settings → Connections → MCP: its access stops at once. Ads it launched are not touched.
The plan check
The MCP server and the REST API are part of the Pro and Scale plans (plans). Every request checks the account's plan, so a key survives a change of plan: on a plan without the feature, it gets 403 upgrade_required with feature, required_plan and current_plan, and works again as soon as the plan includes the feature. On the MCP server, the refusal comes back as a JSON-RPC error with the same words.
Authentication errors
| Answer | Means |
|---|---|
401 invalid_api_key | No key, or an unknown, revoked or expired one (the REST API). The MCP server answers a bare 401 with its WWW-Authenticate header. |
403 insufficient_scope | The key lacks the route's scope. |
403 upgrade_required | The account's plan does not include the MCP server, the REST API or the feature. |
429 rate_limited | Too many requests with a missing or invalid credential from one address (300 per 15 minutes). A credential that worked in the last few minutes is never held back. |
An OAuth token on the REST API is refused: use an API key there (https://api.apogee.ad/api/v1).