Docs navigation

Reference

REST API

Everything the MCP server does is also available over plain HTTP, so n8n workflows, cron jobs, and custom code get the same surface. Base URL below; all routes are under /v1.

Authentication

Create an API key in the dashboard at https://app.affiliateos.dev/developers and pass it as a Bearer token. Keys look like aff_live_..., are shown once, and are stored only as SHA-256 hashes.

shell
curl https://api.affiliateos.dev/v1/accounts \
  -H "Authorization: Bearer aff_live_..."

Keys carry scopes; the defaults are read and links:create. Two things are deliberately out of reach of any key: key management itself (create, list, revoke), so a leaked key can’t mint more keys, and connecting or editing a network account, so a leaked key can’t redirect your commissions. Both need a dashboard session.

Conventions

TopicBehavior
Rate limit120 requests per minute per key. Exceeding it returns 429 with a Retry-After header.
ErrorsEvery error is { error: { code, message } } with a matching HTTP status.
PaginationList endpoints take ?cursor=&limit= (default 25, max 100) and return a cursor for the next page.
Content typeJSON in, JSON out.

Networks & accounts

GET /v1/networks

Supported networks with their credential field specs and help text.

GET /v1/accounts

Your connected accounts with status and last sync. Each row also carries missing_credentials — credential fields the network expects that the account does not hold. A field can become required after you connect (CJ’s Website ID did), and the account keeps working for everything except the operation that needs it, so check this before minting rather than after the mint fails.

POST /v1/accounts

Body { network, label, credentials }. Credentials are verified against the network before being sealed and stored; invalid ones are rejected with the connector’s reason.

PATCH /v1/accounts/:id

Body { label?, credentials? }. Credentials are merged over what is stored, so you can add or rotate one field without re-typing a secret you can no longer read back. The merged set is re-verified before anything is written — a rejected edit leaves the working credentials in place — and a successful one clears auth_error and queues a fresh sync.

DELETE /v1/accounts/:id

POST /v1/accounts/:id/sync

Force a sync, rate-limited to once per five minutes per account.

POST, PATCH and DELETE on accounts need a dashboard session — an API key or MCP token gets a 403. Credentials are merged on PATCH, so a write here doesn’t need to know a secret to do damage: swapping CJ’s Website ID or Amazon’s Partner Tag re-points your commissions while the account still reads as active. Reads and /sync stay open to keys and agents. To connect a network from an agent, use the connect handoff below.

Connect handoff

How an agent gets a network connected without ever holding the credential. The agent calls start_network_connection over MCP and receives a URL; the human opens it, sees which client asked, and types the credential into a browser form that posts it straight into the vault. These are the routes that page talks to — all three take a dashboard session only, so the token in the URL names a pending request rather than authenticating one.

GET /v1/connect/:token

What the page renders: network, suggested label, the client_name that asked, and the credential_fields to collect.

POST /v1/connect/:token

Body { label, credentials }. Verifies against the network, seals the credential, connects the account and queues the first sync — the same path as the dashboard’s own form. A failed verify leaves the link usable, so a typo doesn’t cost a new one.

POST /v1/connect/:token/cancel

Decline, so the agent polling get_connection_status hears an answer instead of a timeout.

Links last 15 minutes, work once, and resolve only for the user who owns the session — everything else (unknown token, someone else’s, expired, already spent) is the same 404, so the endpoint can’t be used to probe which tokens exist.

Programs

GET /v1/programs

Hybrid search. Query params: query, network, vertical, status, min_commission_pct. Uses semantic search when an embeddings key is configured and falls back to keyword matching otherwise.

GET /v1/programs/:id

Full detail including commission summary and cookie window.

Transactions, earnings & payments

GET /v1/transactions

Query params: from, to, status, network, attribution_tag, program_id. Defaults to the last 30 days.

GET /v1/earnings/summary

Query params: from, to, group_by=network|program|tag|day, currency=USD. Amounts are converted with daily exchange rates and split by status: pending, approved, reversed, paid.

GET /v1/payments

Payout records, filterable by date range.

Events & webhooks

GET /v1/events

Pull-based event feed. Query params: after (an event id or ISO timestamp high-water mark) and types.

POST /v1/webhooks

Body { url, event_types[] }. Returns the signing secret once. See the webhooks page for verification.

GET /v1/webhooks

DELETE /v1/webhooks/:id

API keys

GET /v1/keys

POST /v1/keys

DELETE /v1/keys/:id

These three routes only accept a dashboard session (Supabase JWT), never an API key. That’s deliberate: a leaked key must not be able to create more keys.