Get startedAuthentication

Authentication

Two things authenticate against SAGARIS: a person, with a session, and a program, with a workspace API key. This page is about the second, plus the part of the first that decides who is allowed to create one.

Who can mint a key

Creating an API key is an admin action. A key carries long-lived write scopes, so a lower-privilege member, or a compromised one, must not be able to mint one; the gate runs before the request body is even parsed. Four workspace roles pass it: super_admin, org_admin, owner and admin. The same rule applies to registering a webhook subscription, and to revoking a key, for the same reason.

A key needs a name and at least one valid scope. An unrecognized scope string is dropped rather than stored, so a typo cannot smuggle in a permission, and a request whose scopes all get dropped is refused rather than creating a key that grants nothing.

The key itself

A key is a single token beginning ros_live_, followed by 256 bits of random data in URL-safe base64. The prefix is deliberate: a leaked key is greppable and recognizable as ours.

The plaintext is returned exactly once, at creation, and is never recoverable. What is stored is the SHA-256 hash of the key plus a short non-secret display prefix, which is what you see in the settings list. Nothing logs the plaintext, and no endpoint echoes it back. If you lose it, revoke the key and mint another.

Scopes

Seven scopes exist and each operation asserts exactly one. Deny by default: a key that holds no scopes satisfies nothing.

contacts.read
List contacts.
contacts.write
Create a contact, update a contact's fields, add a tag.
sequences.read
List sequences.
sequences.write
Enroll a contact into a sequence.
deals.write
Create a deal.
activities.write
Log an activity on a contact.
suppression.write
Add an address to the suppression list.

Presenting a key

Send it as a bearer token. The scheme name is matched case-insensitively; nothing else is accepted.

GET /api/v1/contacts
curl https://www.sagaris.ai/api/v1/contacts \  -H "Authorization: Bearer $SAGARIS_API_KEY"

The workspace is resolved from the key, and the request is pinned to it. There is no workspace parameter on any endpoint, and no request-supplied workspace id is read, so a key can only ever reach the tenancy it was created in.

Why a request is rejected

Every rejection carries a stable machine-readable code alongside its message, so a client can branch without parsing prose. A 401 also carries a WWW-Authenticate header advertising the scheme.

401 missing_key
No Authorization header, or a token that is not shaped like one of our keys.
401 invalid_key
The token does not match any key.
401 revoked_key
The key was revoked.
401 expired_key
The key carried an expiry and it has passed.
403 insufficient_scope
The key is valid but does not hold the scope this operation asserts. The message names the missing scope.
503 db_unavailable
The service cannot reach the key store. This is not an authentication verdict and should be retried, not treated as a bad key.

Failures are recorded as auth events with the reason and the workspace, and a scope denial is recorded distinctly from a failed authentication. Successful use stamps the key's last-used time, which is what the settings list shows; that stamp is best-effort and never blocks an authorized call.

Revoking

Revoking is an admin action too, on the same four roles that can mint. Revocation is a soft flag rather than a delete, so the key id stays referenceable for idempotency records and audit rows, and the next request presenting that key is refused with revoked_key. The update is constrained to the caller's own workspace, so a key belonging to another workspace is reported as not found rather than revoked. Both creating and revoking a key are written to the workspace configuration audit.

What a key can never do

  • Reach another workspace. To integrate two workspaces, mint two keys.
  • Force a gated pipeline promotion. An API key holds no workspace role, so it cannot take the manager override that a person can: asking for it is a 403 rather than a silent refusal.
  • Gain a scope by calling a different surface. The MCP endpoint authenticates the key without asserting any single scope, because the scope depends on which tool is called; every tool dispatch then enforces its own. A key with an empty scope list authenticates there and is offered zero tools.

Behaviour on this page is read from

  • src/lib/public-api-key.ts
  • src/lib/public-api-auth.ts
  • src/app/api/api-keys/route.ts
  • src/app/api/api-keys/[id]/route.ts
  • src/lib/admin-access.ts
  • src/app/api/v1/contacts/route.ts
  • src/app/api/v1/contacts/[id]/route.ts

Was this page helpful?

Book a demo