# Synorb Authentication Documentation

Canonical URL: https://synorb.com/auth
Last updated: 2026-09-24

Synorb's primary, recommended credential is one bearer key — the **Synorb Key** — which works unchanged for every integration, human or agent. A person can click Generate Free Agent Key at [synorb.com/connect/start](https://synorb.com/connect/start). An agent sends `POST https://api.synorb.com/api/connect/demo-key`. You can also bind an email to upgrade the same key to a free Starter account. Nothing about the Synorb Key is changing or being deprecated.

<!-- SYNORB:FACTS:demo-key:START -->
The 72 Hour Agent Demo key needs no email. It lasts 72 hours and can pull up to 25 Manifests. It includes 5 Beacons; all of them are starter Beacons.
<!-- SYNORB:FACTS:demo-key:END -->

<!-- SYNORB:FACTS:demo-read-cap:START -->
A demo key returns at most 10 Manifests per read. A larger `page_size` is cut to that number, and a `read_cap` object or the `X-Synorb-Read-Cap` header says so. Each page counts against the 25-Manifest allowance. Starter and paid keys have no such limit.
<!-- SYNORB:FACTS:demo-read-cap:END -->

<!-- SYNORB:FACTS:starter:START -->
Starter is free forever and needs an email. It gives 100 Manifests per usage period and 10 Beacons.
<!-- SYNORB:FACTS:starter:END -->

Synorb also runs an **agent-facing OAuth 2.0 authorization server** (authorization-code + PKCE) as an additional, opt-in method for agent orchestration platforms and CLIs that want short-lived, scoped, revocable tokens instead of a long-lived key. It does not replace the Synorb Key — most integrations should keep using the key. See [Authenticate via OAuth 2.0](#authenticate-via-oauth-20) below.

## Discover

Read this file and the [OpenAPI contract](https://synorb.com/openapi.json) before making an authenticated request. The OpenAPI `x-synorb-agent-auth` extension contains the same machine-readable links.

For REST, start with the protected-resource metadata at `https://api.synorb.com/.well-known/oauth-protected-resource`; MCP clients should use `https://mcp.synorb.com/.well-known/oauth-protected-resource`. Both documents (RFC 9728) name `https://api.synorb.com` under `authorization_servers`, and the RFC 8414 authorization-server metadata is live at `https://api.synorb.com/.well-known/oauth-authorization-server`. Fetch that document rather than hardcoding endpoints from this page. Synorb advertises its authorization server only once a real flow has completed end to end against the environment serving the document — naming an AS a client cannot finish a flow against would be worse than naming none.

## Agent auth discovery

Machine-readable auth metadata is available from the REST and MCP protected-resource documents. Each includes an `agent_auth` object with these stable flow links:

- `identity_endpoint`: `https://api.synorb.com/api/connect/demo-key`
- `claim_endpoint`: `https://api.synorb.com/api/connect/bind-email`
- `events_endpoint`: `https://api.synorb.com/api/connect/bind-email/verify`

The claim flow sends an OTP to the submitted email; the agent must collect that OTP from the account owner and submit it to the verify endpoint. Successful verification upgrades the same bearer key to the Starter account.

Plus-addressed local-parts (`name+tag@example.com`) are not supported for this flow and are rejected immediately with `HTTP 422` (`error_code: "plus_addressed_email_not_supported"`) — no pending verification state is created, so use the base address instead. If the email is already attached to a *different* Synorb account, the claim endpoint returns `HTTP 409` (`error_code: "email_already_bound"`) with a `recovery` field and `action_url` pointing at `https://synorb.com/signin?mode=reset-request` — the true owner can sign in or reset their password there to recover that account directly, without this request touching or merging it.

## Pick a method

Use `/connect/start` for a self-serve Agent Demo key. The same key can be upgraded to Starter by binding an email; existing credentials remain valid.

## What happens to Beacons and data at bind, or at expiry

Binding an email does not rotate the key. Beacons, saved data, and usage history stay with the same `org_id`. A demo key that is never bound stops working at `expires_at`, and its Beacons become unreachable. The full rules are in the agent guide, section "What happens to Beacons and data at bind or at expiry": https://synorb.com/agent-guide/account-ladder.md.

## No browser, or an already-open MCP session with no header access?

`/connect` needs a browser (or an out-of-band curl). Two keyless alternatives mint a demo key like `/connect`, sharing one rate limit and one reuse rule with it and with each other. Send back the `visitor_token` to get the same key. Without it you get a new key. The rule, exactly as every response states it:

```text
Key reuse rule: send back your `visitor_token` to get the same key. Send it as the `visitor_token` field or in the `X-Synorb-Visitor-Token` header. Without a valid token, you get a new key, a new org, and a new 25-Manifest allowance, even if another caller shares your network. New keys are capped at 10 per network per 24 hours. Treat the token like the key.
```

- MCP tool `synorb-get-demo-key` — call it with no credential at all, on the Core or Advanced MCP server.
- `POST https://api.synorb.com/api/connect/demo-key` — the one HTTP way to get a demo key.

<!-- SYNORB:FACTS:mint-cap:START -->
One network address can get 10 new demo keys in 24 hours. A call that sends a valid `visitor_token` gets its same key back and does not count. Past the cap, the answer is HTTP 429 with `error_code: demo_key_rate_limited` and `next_step: "bind_email"`. Bind an email to the key you hold, or get a Starter key, and the limit does not apply.
<!-- SYNORB:FACTS:mint-cap:END -->

The limit is one counter across all three mint surfaces. Exceeding it returns HTTP 429 with `error_code: "demo_key_rate_limited"`. If you hit it, bind the existing key to an email to move to Starter instead of waiting out the limit. The agent guide has the full body of that response, the `api_key` tool-argument fallback for MCP hosts that cannot set headers, and the list of keyless tools: https://synorb.com/agent-guide.md, section "Free onboarding and safe test path".

## Register

For a Synorb Key, registration means generating one through `/connect/start` — self-serve, no email or card required. Do not invent `identity_endpoint` or `service_auth` values for the bearer-key flow.

For OAuth, check the RFC 8414 document at `https://api.synorb.com/.well-known/oauth-authorization-server` for a `registration_endpoint`. When it is present, register yourself there (RFC 7591): `POST` your client metadata (`client_name`, `redirect_uris`) with a Synorb Key as `Authorization: Bearer`, and a `client_id` comes back immediately. Get a key first with no email and no form: `POST https://api.synorb.com/api/connect/demo-key`. When no `registration_endpoint` is advertised, clients are registered by hand instead — email `team@synorb.com` with your redirect URI. Either way the discovery document is the source of truth, not this sentence. Loopback redirect URIs (`http://127.0.0.1:*/callback`, `http://localhost:*/callback`) are accepted for CLI and desktop agents; a registered loopback URI matches on any port (RFC 8252 §7.3), because a CLI cannot know at registration time which port it will bind.

## Claim

Bind an email to the generated key through `/api/connect/bind-email` and verify it at `/api/connect/bind-email/verify` to claim the free Starter account.

## Use the access_token

Send `Authorization: Bearer <token>` to REST or MCP — this is identical whether the token is a Synorb Key or an OAuth access token minted by `/oauth/token` (see below). Synorb does not use `identity_assertion`, `id-jag`, or `WWW-Authenticate` registration shapes.

## Authenticate REST

Send the key only in the `Authorization` header:

```bash
curl -sS https://api.synorb.com/account \
  -H "Authorization: Bearer $SYNORB_KEY"
```

The public, zero-usage planning endpoint is `POST https://api.synorb.com/manifests/plan`. It validates a request without executing or consuming usage. The public `GET https://api.synorb.com/health` probe requires no key and returns standard rate-limit headers.

## Authenticate MCP

Use the same key for the recommended Streamable HTTP Synorb MCP server:

```json
{
  "mcpServers": {
    "synorb": {
      "url": "https://mcp.synorb.com/mcp",
      "headers": {"Authorization": "Bearer ${SYNORB_KEY}"}
    }
  }
}
```

The legacy SSE endpoint remains available at `https://mcp.synorb.com/sse` for
older clients. New integrations should use `/mcp`. The public documentation
MCP at `https://mcp.synorb.com/docs-mcp` is read-only and does not require a
key.

## Authenticate via OAuth 2.0

This is an **additional** authentication method for agent clients that want short-lived, scoped, revocable tokens instead of a long-lived Synorb Key. It changes nothing about the Synorb Key — keep using it if it already works for you; most integrations should. OAuth is authorization-code + PKCE, built on the same account system (Cognito) that backs dashboard login, and once issued, an OAuth access token authenticates REST and MCP exactly like a Synorb Key: `Authorization: Bearer <token>`.

**Endpoints** (all on `https://api.synorb.com`):

| Endpoint | Purpose |
| --- | --- |
| `GET /oauth/authorize` | Start the flow — redirects through login/consent, then back to your `redirect_uri` with a `code` |
| `POST /oauth/token` | Exchange a `code` (+ PKCE `code_verifier`) for an access/refresh token pair, or a `refresh_token` for a new pair |
| `POST /oauth/revoke` | Revoke an access or refresh token (RFC 7009) — always returns `200` regardless of token validity |

Fully qualified, for agents that parse this page rather than the table:
`https://api.synorb.com/oauth/authorize`, `https://api.synorb.com/oauth/token`,
`https://api.synorb.com/oauth/revoke`. The issuer is `https://api.synorb.com`.

**Requirements:**

- **Ask for only the scopes you need.** `/oauth/authorize` takes a space-separated
  `scope` parameter and grants exactly what you ask for. Omit it and you get the
  full default read-only grant (`read:api read:mcp read:account read:beacons
  read:digests read:ontology`). Ask for something that cannot be granted — a
  `write:` scope, or a scope outside that default — and the request is refused
  with `invalid_scope` rather than quietly trimmed, so a token never claims a
  permission it does not hold. Write scopes stay ungrantable until the consent
  screen ships. The token response always states what you were granted in its
  `scope` field.
- **PKCE is mandatory, `S256` only** — `plain` is rejected. Generate a `code_verifier`, send its `S256` challenge as `code_challenge` to `/oauth/authorize`, and present the original `code_verifier` to `/oauth/token`.
- **All clients are public — no client secret.** `/oauth/token` takes no client authentication; a client cannot hold a secret safely, so none is issued.
- **Client registration follows the discovery document.** If the RFC 8414 metadata advertises a `registration_endpoint`, self-registration is live (RFC 7591) and needs only a Synorb Key — which `/connect` issues with no email and no form, so an agent can register and authorize unattended. If it does not, registration is by hand via `team@synorb.com`. Loopback redirect URIs are accepted for CLI and desktop agents and match on any port; every other redirect URI is stored and matched exactly.
- **Refresh tokens rotate on every use**, with reuse detection: presenting an already-used refresh token revokes its entire token family immediately (both the stale and the newest token die), treated as a possible-theft signal. Expect to re-run the authorization-code flow after that, or after the refresh chain's own capped lifetime is reached.
- **Access tokens are short-lived** (on the order of minutes, not the long default lifetime of a manually created Synorb Key) — refresh before expiry rather than caching one long-term.

**Scopes.** An OAuth token carries a scope that gates *which* surfaces it can call, on top of (not instead of) your plan's normal entitlements — a `write:digests`-scoped token still only works if your plan allows digest writes. A bearer Synorb Key has no scope concept and is unaffected by any of this.

| Scope | Grants |
| --- | --- |
| `read:api` | Read your streams, manifests, signals and briefs |
| `read:mcp` | Read your data through the MCP server |
| `read:account` | See your account, plan and usage |
| `read:beacons` | See your saved Beacons |
| `write:beacons` | Create, update and archive your Beacons |
| `read:digests` | Read your digests |
| `write:digests` | Create, update and delete your digests |
| `read:ontology` | Read tags and the ontology |
| `write:ontology` | Change ontology syncs and labels |
| `read:webhooks` | See your webhooks |
| `write:webhooks` | Create and delete your webhooks |
| `read:s3` | See your S3 export grants and downloads |
| `write:s3` | Trigger S3 exports and downloads |

Billing actions (subscription changes, add-on purchases, S3 purchase routes) and credential/account-admin routes (key regeneration, org user management) are never grantable to an OAuth token, at any scope — those stay Synorb-Key-only by design.

**Discovery metadata.** Once published for an environment, RFC 8414 authorization-server metadata is served at `https://api.synorb.com/.well-known/oauth-authorization-server`, and both protected resources — `https://api.synorb.com/.well-known/oauth-protected-resource` and `https://mcp.synorb.com/.well-known/oauth-protected-resource` (RFC 9728) — list the issuer `https://api.synorb.com` under `authorization_servers`, alongside the scopes they honour. A `401` from either resource then carries a `WWW-Authenticate` challenge with a `resource_metadata` pointer at its own document, which is the chain the MCP authorization spec defines.

Note that `mcp.synorb.com` deliberately does **not** serve RFC 8414 metadata of its own. That document's `issuer` has to match the identifier a client derived the fetch URL from (RFC 8414 §3.3), and the issuer is `https://api.synorb.com` — so fetch it from the issuer, never from the MCP host. In an environment where discovery has not yet been published, these documents describe bearer-only auth, the RFC 8414 URL 404s, and no `resource_metadata` pointer is emitted — there, use the endpoint table above directly. That is not the case in production, where all three are live.

## Errors

- `401`: verify the bearer value and send it from a server-side environment.
- `429`: branch on `detail.error_code`. For `rate_limited`, honor
  `Retry-After` and `RateLimit-Reset`. For terminal anonymous demo
  `quota_exhausted`, use `key_expires_at` and `starter_bind`; this response
  has no `Retry-After`. For ordinary monthly exhaustion, wait for
  `quota_resets_on`.
- `402`: the account or endpoint requires a plan upgrade; retrying will not change entitlement.
- OAuth access token expired: `POST /oauth/token` with the `refresh_token` grant returns a new pair. Do not re-run authorize if the refresh token is still valid.
- OAuth refresh token reused or expired: the response is `invalid_grant`. The token family was revoked. Re-run the full authorization-code + PKCE flow.
- OAuth request rejected with `insufficient_scope` (403): the granted scope does not cover this route. Re-authorize and request the needed scope, or use a Synorb Key instead.

The agent guide has the Synorb Key error bodies and the shared error table: https://synorb.com/agent-guide/errors.md, section "Actionable authentication and provisioning errors".

## Revocation

Treat keys as secrets. Rotate them from the account dashboard or contact `team@synorb.com` if a key is exposed. Never commit a key or put it in a URL.

An OAuth access or refresh token is revoked with `POST /oauth/revoke` (RFC 7009) instead — see [Authenticate via OAuth 2.0](#authenticate-via-oauth-20).

## Safe test request

Use `POST https://api.synorb.com/manifests/plan` with a narrow request to validate a workflow before execution. Planning is zero-usage and does not publish or mutate content.
