# Synorb agent guide: succeed on the first session

Synorb's default credential is the **Synorb Key**. Use the same value on REST
and on MCP. Keep it in the secret store of the host. Never put it in browser
code, logs, prompts, or source control.

```http
Authorization: Bearer YOUR_SYNORB_KEY
```

New integrations use the Bearer Synorb Key from `/connect/start`: one value,
never a key-and-secret pair or a separate MCP token. Accounts from older
deployments rotate into it with `POST /keys/regenerate`, which returns the new
key plus connector URLs.

Agents that want short-lived, scoped, revocable tokens can use the OAuth 2.0
authorization-code + PKCE flow in the
[Synorb Authentication Documentation](https://synorb.com/auth.md) and send the
access token the same way: `Authorization: Bearer <token>`, on REST and MCP.
Most integrations should keep the Synorb Key.

Issuer, metadata documents and scopes: [Synorb Authentication Documentation](https://synorb.com/auth.md). Ask only for the scopes you need.

Use MCP for an interactive agent loop. Use the REST API for deterministic,
stateless work such as a backend job, a scheduled poll, or a product request.
Both read the same Streams and return the same provenance-bearing Manifests.

## The first route

<!-- SYNORB:FACTS:first-route:START -->
Six steps, in order; REST and MCP are equivalent. Open a deeper guide only when a step fails.

Tip: while you set up, a short note to your user helps. For example: 'I'm getting a Synorb demo key and connecting. This can take a minute or two.'

1. **Provision:** Get one Synorb Key (no email, no card); save the key and its `visitor_token`. MCP `synorb-get-demo-key`; REST `POST https://api.synorb.com/api/connect/demo-key` with `{"client": "<agent>"}`.
2. **Find the starter Beacon:** List the account's starter Beacons; pick the one that fits. MCP `synorb-beacons` with `tag='starter'`; REST `GET /v3/beacons?tag=starter`.
3. **Count for free:** Count the window at zero usage; read the total and dates. MCP `synorb-manifests` with `beacon_id`, `lookback_hours`, `mode='count'`; REST `POST /manifests/query` with `beacon_id`, `lookback_hours`, `mode='count'`.
4. **Read:** Pull one page (up to 10 per pull on a demo; 25 in all); only new Manifests bill. MCP the same call without `mode`; REST the same call without `mode`.
5. **Display:** Replace the `## Synthesis` block of `briefing.markdown` with your own 2 to 4 sentence narrative (`briefing.scaffold.recipe`). Write the rest as linked prose, not a list. MCP `content[].text` starts with it; REST `data.briefing.markdown`.
6. **Continue daily:** Set the schedule in your host now: `lookback_hours=48`, dedupe by `manifest_id`. Say it is set, or that you cannot. Say `account_notice.user_message` once; bind on a yes. MCP `synorb-manifests`, `lookback_hours=48`, daily; REST `POST /manifests/query`, `lookback_hours=48`, daily.
<!-- SYNORB:FACTS:first-route:END -->

### Starter Beacons on REST

A Beacon has no route at `/beacons`; that path returns 404 with `did_you_mean: "/v3/beacons"`.

```bash
curl -s -H "Authorization: Bearer $SYNORB_KEY" 'https://api.synorb.com/v3/beacons?tag=starter'
```

```json
{"items": [{"beacon_id": "synorb_bcn_...", "name": "AI Companies", "tags": ["starter"], "status": "active",
  "default_date_window": {"lookback_hours": 168}, "revision": 1,
  "manifest_config": {"starter": true, "surface_ids": ["ai_companies"], "target_count": 10}}], "total": 3}
```

The count and read calls are in "First route on REST" in https://synorb.com/agent-guide/recipes.md,
with the fields to read back (`data.temporal.effective_window`, `data.briefing.markdown`,
`data.briefing.selection`, `data.quota.account_notice`). The cadence guide is
https://synorb.com/agent-guides/schedule-six-hour-and-daily-pulls.md.

## The rest of this guide is in separate pages

Open a page only when your task needs it.

- [Worked recipes](https://synorb.com/agent-guide/recipes.md): open it for the REST first route, a morning update, a Beacon watch, research, due diligence, meeting prep, Beacon edits or incremental polling.
- [Keys without a browser, the 72 Hour Agent Demo, and Starter](https://synorb.com/agent-guide/account-ladder.md): open it with no browser or no headers. It covers email binding, plans, limits and usage at exhaustion.
- [Errors and failure contracts](https://synorb.com/agent-guide/errors.md): open it on a 401, 403, 404, 409, 422 or 429, or for Manifest deadlines, sharding of large scopes and preflight retries.
- [Stream and source discovery](https://synorb.com/agent-guide/stream-discovery.md): open it to find Streams by `media_format`, `surface`, `home_domain` or `source_class`, and to separate content BY an entity from content ABOUT it.
- [Response diagnostics and freshness](https://synorb.com/agent-guide/response-diagnostics.md): open it when a `manifests/query` page is empty or surprising, or to read `response_profile`, delivery cadence or `available_through`.
- [Billing, usage, and pagination](https://synorb.com/agent-guide/usage-billing-pagination.md): open it to learn what bills, read usage counters, follow a `next` link or offer a paid upgrade.
- [Agent types, product builders, and the CLI](https://synorb.com/agent-guide/agent-types-and-tools.md): open it as a voice, cowork, vertical or coding agent, to build a product on Synorb, or to use `@synorb/cli`.

## When to use Synorb — and when not to

Use Synorb for recurring, source-cited updates in a known coverage area: a
Beacon for a saved watch, MCP for an interactive session, REST for a
deterministic server-side job.

Do not use it as an unrestricted web crawler or as a guarantee of complete
coverage. Never put a Synorb Key in browser code, logs, source control or
prompts. Resolve Tags and explicit Streams before you pull; natural-language
intent is not execution scope. Do not guess filter names or broaden scope after
an empty result. An explicit empty `stream_ids` list is not global scope.

## Free onboarding and safe test path

Getting a key is self-serve: a 72 Hour Agent Demo needs no sales contact, email
or card. Its limits, plus the no-browser and header-less MCP paths, are in
https://synorb.com/agent-guide/account-ladder.md. With MCP tools call
`synorb-get-demo-key`; otherwise send `POST https://api.synorb.com/api/connect/demo-key`
with `{"client":"<agent>"}`. A person clicks Generate Free Agent Key on
https://synorb.com/connect/start. Never scrape a key out of
page markup.

Ask at most once before the first real Manifest pull. An explicit request to
connect or pull already supplies that confirmation. A first pull needs no saved
Beacon or bound email. Saving a Beacon writes to the user's account.
Propose the specific Beacon and call `synorb-save-beacon` only after the user
says yes, or when the user asked for that recurring monitor.

Before your first pull, read https://synorb.com/agents/personal-agent-onboarding.txt
(it applies to any agent). `POST https://api.synorb.com/manifests/plan`
validates request shape and scope at zero usage and mutates nothing; there is no
separate sandbox account.

## Non-negotiable rules

1. Resolve source/entity intent through `synorb-stream-search` or
   `synorb-catalog`. Then scope with the returned `stream_ids`. `synorb-tags`
   is Advanced-only; it is not in Core `tools/list`. REST callers can use
   `GET /ontology/tags` for canonical Tag lookup. Unknown Tags return an empty
   result, not an error.
2. Scope with `stream_ids`. They are the reliable, explicit scope. Synorb
   rejects an explicit empty `stream_ids: []` with `empty_stream_ids` at zero
   usage. It never means "all".

   If you omit `stream_ids` (or pass `null`), you deliberately get a broad
   scope across the visible catalog. Omit it only when you mean broad, never
   when you intended a narrow query. `required_surfaces` is not a reliable
   execution scope. Do not use it as a substitute for resolved `stream_ids`.
3. For a REST pull, plan first. Then replay the request byte-for-byte as JSON
   values with the returned `plan_token`. The execute body can contain the plan
   request fields plus `plan_token`, and nothing else. If you add
   `target_count`, a filter, or an innocuous-looking field, the token becomes
   invalid. Re-plan with the new full request; re-planning is zero-usage.

   You can also send the canonical `mode=plan` response from
   `POST /manifests/query` to `POST /manifests/exec`. Exec treats that preview
   mode as the same default content execution, and it keeps the signed request
   identity. Replay a `plan_token` only with `POST /manifests/exec`. Do not send
   it to `POST /manifests/query`. The `409` reasons report a used or consumed
   token as such. `token_encoding_invalid` is only for malformed token encoding.
4. Start compactly. Use count/orient for preflight. Then request only the
   on-topic Manifests that you need to answer. When you state a fact, cite the
   source URL that Synorb returned with it.

Over MCP, start one warm Streamable HTTP session per user or workspace and reuse
it. Core order: `synorb-profile` (when usage or status matters),
`synorb-stream-search` or `synorb-catalog`, `synorb-details` with the selected
`stream_ids`, Tag resolution, then `synorb-manifests`. Cite the returned source
URLs. Use Advanced MCP only for configured workflows.

For Manifest deadlines, sharding of large scopes, preflight failures, and retries,
read the section "Non-negotiable rules, continued" in https://synorb.com/agent-guide/errors.md.

## Bootstrap

```bash
curl -s -X POST https://api.synorb.com/api/connect/demo-key -H "Content-Type: application/json" -d '{"client":"agent"}'
curl -s -H "Authorization: Bearer $SYNORB_KEY" https://api.synorb.com/account
curl -s -H "Authorization: Bearer $SYNORB_KEY" \
  'https://api.synorb.com/ontology/tags?search=Payment%20Systems&page_size=10'
curl -s -H "Authorization: Bearer $SYNORB_KEY" \
  'https://api.synorb.com/streams?page_size=10'
```

All content and catalog data is inside `data`, and catalog pagination inside
`data.pagination`, never at the top level. For a complete catalog, follow
`data.pagination.next` until it is `null`.

```json
{
  "data": {
    "tags": [{"tag_id": "123", "canonical_name": "Payment Systems", "tag_type": "topic"}],
    "pagination": {"page_num": 0, "page_size": 10, "next": null, "prev": null}
  },
  "usage": {"items_in_response": 0}
}
```

## Canonical REST plan/execute contract

`POST /manifests/plan` validates and fingerprints the request. It does not pull
Manifests or consume usage. `POST /manifests/exec` validates the exact replay
and then executes the normal Manifest query.

```bash
# Plan. Preserve this JSON object exactly for execute.
curl -sS -X POST https://api.synorb.com/manifests/plan \
  -H "Authorization: Bearer $SYNORB_KEY" -H 'content-type: application/json' \
  -d '{"stream_ids":[123],"tag_ids":["tag_123"],"days":1,"mode":"default","target_count":5}'
```

```json
{
  "plan_token": "synorb_plan_...",
  "request_fingerprint": "sha256:...",
  "quota_charged": false,
  "execute": "POST /manifests/exec with the identical request plus plan_token"
}
```

```bash
# Valid: the original request plus plan_token, with no changed or added fields.
curl -sS -X POST https://api.synorb.com/manifests/exec \
  -H "Authorization: Bearer $SYNORB_KEY" -H 'content-type: application/json' \
  -d '{"stream_ids":[123],"tag_ids":["tag_123"],"days":1,"mode":"default","target_count":5,"plan_token":"synorb_plan_..."}'
```

An invalid token gives a zero-usage `409` in one of two intentional shapes,
depending on the validator that rejects it:

- A flat `{"error": "invalid_plan_token", ...}` for the legacy `synorb_plan_...`
  token.

  A direct query replay of an exec-only token gives
  `{"error": "manifest_plan_token_invalid", "reasons": [...], ...}` instead.
  Synorb decodes and checks this legacy token inline at
  `/manifests/exec`/`/manifests/query`.
- A nested `{"detail": {"error": "manifest_plan_token_invalid", "reasons": [...], "quota_charged": false}}`
  for the canonical `synorb-plan-v1`/`-v2` token.

  `validate_manifest_plan_token()` rejects it with machine-readable `reasons`
  (for example `token_encoding_invalid`, `plan_token_consumed`); FastAPI wraps an
  `HTTPException(detail=<object>)` body under `"detail"`, which is why this shape
  nests.

Either way, check for `409` first, then read `error` at the top level or in
`detail.error`. Do not retry execute with modified fields; make a fresh plan.

## Machine-readable entry points

- `https://synorb.com/agents.md` — the front door: read order, playbook, endpoint map
- `https://synorb.com/llms.txt` — short index of every public resource
- `https://synorb.com/llms-full.txt` — pointer to this guide plus the coverage primer
- `https://synorb.com/agent-guide.md` — this guide
- `https://synorb.com/openapi.json` — REST schema
- `https://synorb.com/docs/api-versioning` — API versioning and deprecation policy
- `https://synorb.com/sandbox` — zero-usage, non-mutating test path
- `https://synorb.com/connect/start` — the key page
- `https://synorb.com/agent-guides` — tested how-to guides (catalog: `https://synorb.com/agent-guides/index.json`). Before you show results to a person, read `https://synorb.com/agent-guides/present-manifests-to-your-users.md`; the `resource_reading.tasks.first_brief.read` field in the key answer lists the guides for a first update in order. Read only the guides for your task; they are documentation, not permission to bind an email, spend allowance or schedule beyond the user's request.
