docs
API

Authentication

The credentials the nima API accepts (personal API keys, Slack-issued headless keys, OAuth access tokens), how each is created and expires, and the rules no credential breaks.

Every call goes to https://api.getnima.io and carries one credential in one header: Authorization: Bearer <credential>. nima accepts three kinds of credential that travel this way: a personal API key, a headless key minted from Slack, and an OAuth access token issued to an MCP client. The first two are the same nima_ key format. All three are checked against the same scopes, the same permission rules and the same errors.

On for every workspace. Any member can create a personal API key. Only a workspace owner or admin can mint a headless key from Slack.

How nima routes a bearer token

nima decides what kind of credential a bearer is from its shape, before it looks anything up.

Bearer shapeTreated as
Contains a .A browser session token from the nima admin panel. It is never checked as an API key or OAuth token, and it is not a public API credential.
Starts with nima_A personal or headless API key.
Anything elseAn OAuth access token.
No Authorization headerPassed through. Endpoints that need a credential answer 401.

A webhook secret sent as a bearer therefore falls into the last row and comes back 401. It belongs in X-Nima-Webhook-Secret or as the Basic auth password. See Webhooks.

The check is closed by default. A route that declares no scope refuses every API key and every OAuth token with 403 This endpoint does not accept OAuth access tokens. A route that declares scopes accepts a credential holding any one of them.

Personal API keys

A personal API key is a nima_ credential you create for a script, a CI job or an agent. It acts as you, in the workspace you created it in.

API key format

nima_4f9c2b7e1a6d8305fe72cb41a90d5e638b2470ac01tpedv
PartValue
Prefixnima_
Secret40 lowercase hex characters (160 random bits)
Checksum7 characters, the secret's CRC32 in base36
Total length52 characters

A key with a bad shape or checksum is rejected before any lookup and returns the same 401 as a key that does not exist. nima stores only a SHA-256 hash. The full key is shown once, at creation, and cannot be recovered. Lists show the display prefix, nima_ plus the first eight characters of the secret, which identifies a key without being usable.

Create an API key

Create a key in the admin panel under Settings → API keys, or with POST /v1/api-keys using a signed-in session token. The route declares no scope, so a key or OAuth token cannot call it.

FieldRules
name1 to 100 characters.
scopesAt least one of the 13 scopes listed in Scopes and permissions. The Settings form ticks knowledge:read and signals:read by default.
expiresInDays30, 90 or 365. The Settings form defaults to 90. There is no key that never expires.
allowedCidrsRequired. Send [] for any address, or up to 20 entries of at most 64 characters each: single IPv4 or IPv6 addresses or CIDR ranges. Leaving the field out answers 400.

A scope your own role cannot back is dropped from the key without an error. If none of the scopes you asked for survive, creation fails with 400 None of the requested scopes can be granted by this user, with details.requestedScopes. Read back the scopes on the key you just created rather than assuming you got what you asked for.

Only the 13 consumer scopes can be requested this way. The four scopes operations:write, integrations:write, actions:approve and identities:write are rejected here. See Headless keys from Slack.

Restrict an API key to allowed addresses

An API key with allowedCidrs set works only from those addresses. A live key used from any other address gets 403 API key not allowed from this address, so you can tell "wrong key" from "right key, wrong network".

The address checked is the one nima's load balancer stamps into the X-Nima-Real-IP header. It replaces anything a caller sends under that name, and X-Forwarded-For is never read. If your traffic leaves through a NAT, a proxy or a CI provider's shared egress, allow that egress range.

API key lifecycle

EventWhat happens
CreatedThe full key is returned once.
Cap10 active keys per person per workspace. An eleventh returns 409 with details.limit. Headless keys count toward the same cap.
RotatedPOST /v1/api-keys/{id}/rotate issues a new secret with the same name, scopes, allowed addresses and expiry date, and adds graceUntil to the response. The old secret keeps working for 10 more minutes. The old key leaves the list at once, so swap the secret inside that window.
RevokedDELETE /v1/api-keys/{id} answers 204 and the key stops working. Revoking a key that was just rotated also ends the grace window of the secret it replaced.
Expiry warningIn Settings → API keys a key's expiry date turns red when it is 7 days or less away. No Slack message or email is sent, so track expiry dates yourself or rotate on a schedule.
ExpiredThe key stops authenticating at the expiry date fixed at creation.
Last usedThe admin panel shows when and from which address a key was last used. It is recorded at most once every 5 minutes per key.

GET /v1/api-keys (or ?scope=own) lists your own keys. GET /v1/api-keys?scope=org lists every key in the workspace and needs an owner or admin session, and owners and admins can rotate or revoke any of them. Any other scope value is 400.

Headless keys from Slack for operations and integrations

A headless key is a 30-day nima_ key that a workspace owner or admin mints from Slack. It is the only way to hold operations:write or integrations:write, the scopes that start agent operations and administer integrations over MCP.

Run the command as the /nima slash command, or as a mention or direct message to nima whose first word is mcp-key.

CommandWhat it does
mcp-key operationsMints a key with operations:write, documents:read and knowledge:read.
mcp-key integrationsMints a key with integrations:write only.
mcp-key listLists your own unrevoked keys with id, name, scopes and expiry.
mcp-key revoke <key-id>Revokes one of your own keys by its id.

Rules for headless keys:

  • The workspace must be the one you currently have selected in the admin panel.
  • Only a linked nima owner or admin can run the command. Anyone else gets an ephemeral refusal. A Slack user who is not yet linked is matched to a nima account by email on demand.
  • Each command that creates or revokes a key answers with a confirmation card. Press Confirm, or say confirm <code> to nima in the same channel, within 5 minutes. The confirmation works once and only for the person and channel that asked.
  • The key is sent in an ephemeral message, shown once, with its id and the revoke command. If delivery fails the key is revoked immediately.
  • The lifetime is 30 days. There is no way to choose another one and no address allowlist.
  • The key is named Slack MCP operations or Slack MCP integrations, belongs to the person who confirmed, and counts toward their cap of 10.
  • Revoke it with mcp-key revoke <key-id>, or from Settings → API keys.

The operations key does not enable hosted compute or GitHub publishing. Those are separate workspace opt-ins.

Two more scopes, actions:approve and identities:write, cannot be minted by anyone today, so a key or OAuth client cannot call the tools behind them. Approvals happen on the Slack card. See Scopes and permissions.

OAuth access tokens for MCP clients

An OAuth access token is what an MCP client holds after you approve its consent screen. You do not mint one by hand. See Connect nima to Claude for the connect steps and OAuth for the endpoints.

PropertyValue
Access token lifetime1 hour. The client refreshes it with the refresh token issued alongside.
Refresh token lifetime30 days, rolling. The clock restarts on every refresh, so a client that refreshes at least monthly stays connected.
Refresh rotationEach refresh issues a new refresh token and revokes the old one. Presenting an already-used refresh token revokes the whole token family and answers 401 TOKEN_REPLAY.
ScopesThe 8 consent-screen scopes: knowledge:read, signals:read, sources:read, documents:read, signals:write, subscriptions:read, subscriptions:write, inbox:read. The approver's role can narrow them.
WorkspaceThe one the approver had open when they consented.
RevokingCall POST /oauth/revoke with the refresh or access token. Disconnecting in the client revokes it only if the client calls that endpoint; otherwise the refresh token lapses 30 days after its last use.

Every refresh re-checks that the owner is a member of the workspace and that it is the workspace they currently have selected in the admin panel. If not, the refresh token is revoked and the refresh answers 401 TOKEN_INVALID; the client has to reconnect.

An OAuth client can never hold an administrative scope. A tool that needs one answers with text explaining that it requires an explicit headless grant from a workspace admin, obtained with /nima mcp-key in Slack.

Credential rules: keys never manage keys, scopes AND live role, one workspace

Three rules hold for every credential, whatever its kind.

A key never manages keys

Creating, listing, rotating and revoking keys need a signed-in session in the admin panel. The same is true of members, invitations, workspace settings and workspace deletion. Those routes declare no scope, so every API key and OAuth token is refused. A leaked key cannot mint a second key, widen its scopes or extend its expiry. The one exception is Slack, where an owner or admin can mint or revoke headless keys with /nima mcp-key.

Effective permissions are the scopes AND the owner's live role

A credential's scopes never grant anything its owner does not already have. Every call reads the owner's current role in the workspace, and the permissions in force are the intersection of that role and the credential's scopes. Scopes can only narrow.

So a key holding documents:read and documents:write, minted while its owner was an admin, keeps both scopes after the owner is demoted to member. Deletes start answering 403, because members lack the delete permission, while reads, creates and edits keep working. The key did not change. Its owner did. Roles are described in Members and roles.

A credential never carries super-user access, whatever its owner is.

A key acts in exactly one workspace

The workspace is fixed when the key is created: the one its creator had open at that moment. No request can change it, and no API parameter selects a workspace. A person in two workspaces needs two keys.

The role behind the key is read from the owner's membership in that workspace, and only while that membership is the active one. If the owner switches workspace in the admin panel, keys minted in the other workspace still authenticate but resolve to no role, and permission-checked operations answer 403 until they switch back.

When a key owner leaves or switches workspace

Removing a person from a workspace does not revoke their keys. Each key keeps authenticating and resolves to no role, so its effective permissions are empty. GET /oauth/userinfo still answers 200 with "orgRole": null, and every operation that needs a permission answers 403. The key is inert, not gone. Revoke it as well from Settings → API keys, where an owner or admin can revoke any key in the workspace.

The same happens to an API key when the owner switches the admin panel to another workspace: the key's own workspace is no longer their active membership, so it resolves to no role until they switch back.

OAuth tokens are stricter. Once the owner is removed or switches the admin panel to another workspace, the client's next refresh revokes the connection for good, and switching back does not restore it. Reconnect the client.

GET /oauth/userinfo

GET /oauth/userinfo returns the identity a credential acts as. It accepts a credential holding any of the 17 grantable scopes, so every key and every OAuth token can call it. It is the cheapest way to prove a credential works and to see which workspace it lands in. The path has no /v1 segment.

export NIMA_API_KEY="nima_4f9c2b7e1a6d8305fe72cb41a90d5e638b2470ac01tpedv"

curl -s https://api.getnima.io/oauth/userinfo \
  -H "Authorization: Bearer $NIMA_API_KEY"
{
  "sub": "8f14e45f-ceea-4b7c-9c3b-2a1d0f5a9e21",
  "email": "ci-bot@northwind.example",
  "isOnboarded": true,
  "organizationId": "b3d9a0c2-5f41-4a8e-9d77-6c2e1b84f503",
  "orgRole": "member"
}
FieldTypeMeaning
substringThe id of the person the credential acts as.
emailstringThat person's email.
isOnboardedbooleanWhether they finished onboarding in the admin panel.
organizationIdstring or nullThe workspace the credential acts in.
orgRoleowner, admin, member or nullThe owner's role in that workspace right now. null means the credential resolves to no role and every permission-checked operation answers 403.

The response has no scope list.

Authentication errors

Authentication failures use the standard error envelope described in Errors. details appears only on 4xx responses.

StatusMessageWhen
401Invalid or expired access tokenThe key or token is unknown, malformed, revoked, expired, or past its 10-minute rotation grace.
401TOKEN_REPLAY codeA refresh token or authorization code was used twice.
401TOKEN_EXPIRED code, Refresh token has expiredA refresh token past its 30-day window was presented.
403This endpoint does not accept OAuth access tokensThe route declares no scope, such as key management, members, workspace settings or session routes.
403OAuth token scopes do not cover this endpointThe credential is live but holds none of the scopes the route accepts. details.requiredScopes lists them.
403API key not allowed from this addressThe key has an allowlist and the caller's address is not on it.
403Missing required permissions (AND): <names> (or (OR))The scope was enough but the owner's current role was not. details.requiredPermissions lists the permissions and details.mode is and or or.
403This tool requires an explicit headless grant. Ask a workspace admin to use /nima mcp-key operations or /nima mcp-key integrations in Slack, then connect using that key. Existing OAuth consent does not grant administration.An MCP tool that needs a scope outside the 8 consent scopes (operations:write, integrations:write, actions:approve, identities:write) was called with a credential that lacks it. This arrives as tool result text, not an HTTP error.
409At most 10 active API keys per userCreating an eleventh active key. details.limit is 10.
429RATE_LIMITEDOver the rate limit. See Rate limits.