Authentication
Every request to the Sursa API carries an API key, or an OAuth access token of an application, in the Authorization
header:
Authorization: Bearer kb_3f9c…
The key identifies the organization and decides what the request can see and change: its role, its scopes, its data profile and its collections. There are no sessions or cookies on the API address. Each request is authenticated on its own, so you can send requests in parallel and from several servers with the same key.
curl https://api.example.com/v1/me \
-H "Authorization: Bearer $SURSA_API_KEY"Earlier integrations may send the key in an X-API-Key header instead. It is still accepted, but new code should
use Authorization: Bearer.
Kinds of credentials
| Credential | Looks like | Where it works |
|---|---|---|
| Organization API key | kb_… | Every endpoint of the API, within its role, scopes, data profile and collections. |
| Custom source key | kbsrc_… | Only the push endpoints of its own custom source (/v1/push/sources/{source_id}/…). |
| OAuth access token | kbat_… | The endpoints of the API reference, within the scopes of the token. A token from POST /oauth/token with an authorization code acts on behalf of the employee who gave consent; a client_credentials token acts for the organization, like an API key. It is valid for 1 hour. |
All of them are sent the same way. A custom source key cannot read, search or change anything outside its source; use it where the key is stored by a third party, such as an integration platform.
Authentication errors
| Status and code | What it means | What to do |
|---|---|---|
401 auth.not_authenticated | No key, a mistyped key, or a revoked key. | Check the header and the key value. |
401 api_keys.expired | The key has passed its expiry date. | Create a new key and replace the old one. |
401 sources.push_key_invalid | A custom source key is wrong, revoked or belongs to another source. | Check the key and the source ID in the path. |
401 oauth.token_expired | The OAuth access token has expired. | Get a new one with the refresh token. |
403 api_keys.scope | The key has no scope for this operation. | Add the scope, or use a key that has it. |
403 api_keys.feature_disabled | The organization's plan does not include API keys. | Contact the organization's administrator. |
403 api_keys.ip_not_allowed | The request comes from an address outside the allowed addresses of the key. | Call from an allowed network, or change the key's addresses. |
403 api_keys.mcp_only | A personal MCP key was used outside the /mcp endpoint. | Use an organization API key for the API. |
403 oauth.operation_not_allowed | The operation is not part of the public API, so an OAuth token cannot call it. | Use an operation from the API reference. |
403 org.suspended | The organization is suspended. Its data is kept. | Contact the organization's administrator. |
403 means the request was authenticated but is not allowed. An object the key is not allowed to see answers 404,
the same as an object that does not exist, so a key cannot learn what lies outside its access.
Acting as a data profile
A key with the admin role can check what another group of people would see: the X-View-As header runs a request
as a data profile and, optionally, a role.
curl "https://api.example.com/v1/cards?limit=20" \
-H "Authorization: Bearer $SURSA_ADMIN_KEY" \
-H "X-View-As: employee:viewer"Requests in this mode only read: any change returns 403 with the code
view_as.read_only. An OAuth token, or a key without the admin role, cannot use
X-View-As: it answers 403 with the code view_as.admin_key_required. A
header that names no existing profile answers 400 with the code
view_as.invalid_header.
Browser applications
The API allows requests from any web origin (Access-Control-Allow-Origin: *) and never uses cookies, so a page can
call it with fetch. The key then lives in the page and is visible to whoever uses it. Call the API from the browser
only with a key you are willing to show to those users, such as a viewer key limited to public collections, or put
your own server between the browser and Sursa.
What only an employee can do
An organization API key acts for the organization, not for a person. Two parts of the API belong to people, so they work only with a token that acts on behalf of an employee: a session in Sursa or an OAuth access token issued with the employee's consent.
Conversations belong to a person. An organization API key sees in
GET /v1/conversations only the conversations that it started itself.
The public API has no operation that starts a conversation, so for a key the list is normally empty, and the
conversations of employees are never shown to it. Questions without a conversation,
with POST /v1/ask or POST /v1/chat, work with any key that
has the ask scope. To list the conversations of an employee and read the history of one with
GET /v1/conversations/{conv_id}, call the API with an OAuth token on
behalf of that employee. The token sees only that employee's own conversations; any other conversation answers 404
with the code conversations.not_found.
Only an employee verifies a card. Verification confirms that a person checked the content, so
POST /v1/cards/{card_id}/verification/verify and
POST /v1/cards/{card_id}/verify need a session or an OAuth token on behalf of an
employee who may verify the card. With an organization API key, or a client_credentials token, they answer 400
with the code cards.verify_user_account_required. The usual checks
come first: a card the key cannot see answers 404, and a key that is not allowed to verify it answers 403.