SynapSynapDocs

API keys

Create, scope, grant and revoke the keys that apps, scripts and agents use to call your pod.

For BuildersOpen-source podSynap CloudReviewed 2026-10-20

Bearer tokens for apps, sites, scripts and AI agents that call your pod.

A key answers two questions:

  • Which doors can it knock on? That is its scopes, a coarse gate per API surface.
  • What can it touch once inside? That is its grant, fine-grained: kinds, actions, workspaces, projects, or single objects.

A key can never do more than the person it acts for. Effective access is always grant ∩ what you can see and do.


Creating a key

  1. Open your pod's admin console (for example https://pod-admin.example.com) and go to Keys. A key for a single workspace is created from that workspace's API keys tab instead.
  2. Click New key and name it after what will use it (for example "Portfolio site", "n8n").
  3. Pick its scopes and its grant.

The key is shown once, so copy it immediately. Only a hash is stored.

For an AI agent, do not create a key by hand: synap connect <agent> (from @synap-core/cli) gives each agent its own key and identity. See Connect an agent.

To create one programmatically, call the tRPC procedure apiKeys.create. keyName, scope (at least one) and grant are required:

{
  "keyName": "Portfolio site",
  "scope": ["hub-protocol.read"],
  "expiresInDays": 90,
  "grant": {
    "permissions": ["entity.portfolio.read", "document.read"],
    "workspaceIds": ["<workspace id>"],
    "label": "Portfolio site"
  }
}

Grants: what a key may touch

A grant is a list of permissions plus optional resource sets. Permissions use the same keys as Synap's governance events, with an optional kind in the middle:

<subject>[.<kind>].<action>
PermissionAllows
*everything you can do (an explicit full-access key)
entityevery action on every entity
entity.knowledgeevery action on knowledge entities
entity.knowledge.readreading knowledge entities only
entity.*.read or entity.readreading every kind
entity.*.createcreating any kind
document.readreading documents
view.read, project.read, session.readreading views, projects, sessions

Matching is segment by segment. * stands for one segment, and a shorter permission covers everything under it: entity.knowledge covers entity.knowledge.read and entity.knowledge.update. There is no partial-word wildcard.

Resource sets narrow further. Leave a set out for no narrowing on that axis.

FieldNarrows to
workspaceIdsthose workspaces
projectIdsobjects filed in those projects
entityIdsthose exact objects

How it is enforced:

  • Reads are filtered in the pod's access layer, so a key with entity.portfolio.read simply never sees other kinds.
  • Writes are checked at the governance gate before anything else. A write outside the grant is refused, never queued for approval.
  • A grant permits, it never auto-approves. Agent writes still go through proposals as usual.
  • A write that creates related objects (links, files) needs those subjects in the grant too.
  • A key with no grant keeps the older behaviour: its scopes plus everything you can see.

Doors a scoped key can read through

A key whose grant is narrower than * can only read through doors whose reads the grant actually bounds. Every other read door answers 403 GRANT_DOOR_UNSUPPORTED; doors are added as they are migrated.

SurfaceRead doors open to scoped keys
Hub REST (/api/hub)GET /users/me, /auth/status, /entities (listing; a full-text q search is refused), /users/{id}/entities, /entities/{id}, /entities/{id}/connections, /entities/{id}/facets, /documents/{id}, /documents/{id}/raw, /relations, /projects, /projects/{id}, /projects/{id}/digest, /projects/{id}/outputs, /tracks, /tracks/{id}, /views, /proposals, /channels, /threads, /threads/{id}/messages, /threads/{id}/branches, /messaging/channels, /messaging/linked-unread, /focus-sessions, /artifacts, /commands, /commands/{id}, /brand/kit, and /webhooks (the key's own subscriptions)
MCPsynap_get_entities, synap_get_document, synap_get_relations, synap_list_tracks, synap_resolve_identity, synap_get_project, synap_list_projects, synap_list_views
tRPC bridge (/api/hub/trpc)entities.getEntities, documents.getDocument, relations.listRelations, relations.listRelationsPodWide, automations.listRuns, automations.getRun, commands.listCommands, commands.getCommand, views.listViews, proposals.listProposals

GET /entities without q and GET /webhooks are open to scoped keys on releases after v1.1.3.

Doors that combine several kinds of record (POST /knowledge/search, /knowledge/ask, /knowledge/answer, /entities/retrieve and /memory/search, and MCP read tools such as synap_ask, synap_orient and synap_find) cannot filter row by row yet. A scoped key passes one only when its grant reads every kind of record the door reads, with no workspace, project or object limit. Every other read door (for example the REST graph, events and runs) needs a key with the full-access * grant.

Writes go through to the governance gate, which applies the grant. On releases after v1.1.3, two kinds of write need a key with unrestricted * (no workspace, project or object limit): doors that act on everything the person owns and that no grant can narrow (for example the calendar feed URL, connector credentials and disconnection, and deleting a vault secret), and, on the tRPC bridge, mutations the gate cannot bound yet.

Access roles: reusable grants

Save a grant under a name in the Browser app (Settings → AI → Access roles), or with Save as role in any access editor. The next key, app or agent can start from it.

  • A role is a template. A key made from a role keeps what the role said at that moment. Editing the role later never changes an existing key.
  • The key remembers which role it came from. Pass grant.roleId when minting through the API; it must be one of your own roles.
  • Only you can create or change your roles. Your agents can list them but never edit them.

Limiting an agent

An agent's capabilities use the same permission keys. In the agent's panel, the same editor limits what it may change, down to one kind (entity.knowledge.update). An agent can always read what you can read, so the read column is shown on and locked. Leaving every box unticked makes the agent read-only. It never reverts to full access.


Lifetime

expiresInDays sets how long a key lives:

ValueLifetime
omitted90 days (the default)
a number (1–36500)that many days
nullnever expires. Choose this deliberately.

Rotating a CLI key keeps its remaining lifetime.


Key formats

The prefix tells you what kind of key it is:

PrefixKey
synap_user_A personal key, created for you or for one workspace
synap_hub_live_A service key (system-wide, or registered for a hub) on a production pod
synap_hub_test_The same kind of service key on a pod not running in production mode

Scopes: which surfaces a key can call

ScopeGate
hub-protocol.read / hub-protocol.writethe Hub REST API (/api/hub/*), including its tRPC bridge (/api/hub/trpc)
hub-protocol.admintreated as hub-protocol.write by every door; it only marks the key as privileged when it is created
mcp.read / mcp.writethe MCP endpoint (/mcp)
data.read / data.writealiases of hub-protocol.*, honoured on /mcp only
skills.invoke/api/external/skills
chat.stream/api/external/chat and the OpenAI-compatible endpoint
read:entities / write:entities / ai:analyzethe n8n integration routes
realtime:observeread-only realtime subscriptions
providers.writeAI provider configuration (operator tools only)
setup.agentPOST /api/hub/setup/agent, to provision agents on the pod
probemarks writes as test probes, hidden from listings

Older keys may still carry retired scopes (preferences, notes, tasks, …). No door ever checked them, and new keys cannot be minted with them.


Workspace-bound keys

A key created for one workspace (from that workspace's settings, or with a workspaceIds grant) is confined to it. A request for another workspace is refused with 403.


Using a key

Pass it as a Bearer token:

curl https://pod.example.com/api/hub/entities \
  -H "Authorization: Bearer synap_user_..."

A key works on the Hub REST API, MCP and the other doors its scopes name. It does not work on /trpc, the endpoint the SDK calls: that endpoint accepts only a sign-in session. See SDK first query.


Seeing and revoking keys

My connections (in the pod admin console's account menu) lists the keys you hold and the keys your agents and apps hold for you, and you can revoke either kind there. Each key shows what it may touch, e.g. "Read Portfolios, Documents · 1 Space". Revoking takes effect immediately: in-flight requests with that key get 401.


Security notes

  • Keys are hashed at rest. The pod never stores the plaintext.
  • An AI agent's key cannot mint keys for other agents, widen its own approval rules, or grant itself vault access. Those requests are refused or become proposals you approve.
  • Keep keys in environment variables, one key per integration, with the smallest grant that works.

Next steps

On this page