Authentication

Integration requests authenticate with an organization API key (sk-org-…) in the Authorization: Bearer header. The key opens two doors, each limited by the key's scopes:

DoorScope
OpenAI-compatible API: POST /v1/chat/completions, GET /v1/modelschat:write
MCP server at /mcp/: readstools:read
MCP server at /mcp/: write calls and repl_executetools:write

The key goes only in Authorization: Bearer; an x-api-key header is not read, and a request carrying only that header gets 401 AUTH_HEADER_MISSING.

The native /api/v1 is the internal API of the Samreshuuu apps: it takes a signed-in user's token, answers a key with 401 AUTH_TOKEN_INVALID and is not an integration contract. The /v1 endpoints are the other way round: they take a key only and answer a user token with the same code.

Create a key and send your first request

  1. Sign in to Samreshuuu and select the organization you want to use.
  2. Open Settings → API keys and choose Create key. Organization owners and administrators can create keys.
  3. Enter a name, such as My integration. Under Access, keep Full access: it is selected by default and includes chat:write. AI apps only and View only open MCP but not the chat endpoint.
  4. Choose an expiration period and create the key.
  5. Copy and save the sk-org-… key. The full secret is shown only once.

If the create button is absent, contact your organization owner or administrator.

Store the key in an environment variable on your server or computer:

bash
export SAMRESHUUU_API_KEY="sk-org-your_api_key"

First check authentication by listing the agents you can talk to, without starting one:

bash
curl --fail-with-body https://samreshuuu.ru/v1/models \ -H "Authorization: Bearer $SAMRESHUUU_API_KEY"

Successful authentication returns a JSON list of agents. Then send a message to one of them by its handle; this request uses your available quota:

bash
curl --fail-with-body https://samreshuuu.ru/v1/chat/completions \ -H "Authorization: Bearer $SAMRESHUUU_API_KEY" \ -H "Content-Type: application/json" \ -d '{"model":"samreshu","messages":[{"role":"user","content":"Reply with one word: hello"}]}'

Read the answer from choices[0].message.content. A 401 means the key was not accepted; a 403 means the key lacks chat:write; a 404 means model names no agent you can reach. For Python and Node.js examples, see OpenAI-compatible API.

Organization Context

A key is bound to the organization that issued it and to the member who created it, and it is an organization credential, not a personal one. It reaches the agents that member can reach there and the organization's shared connections open to that member. It never reaches the creator's memory, the creator's personal connections or the creator's devices: an agent answering a key starts without memory, and a connection only the creator can see is not connected for the key.

A key works only while its creator is an owner or administrator of a live organization. This is checked on every request: when the creator leaves or is removed, loses the role, or the organization is deleted, the next request with the key gets 401 AUTH_INVALID_CREDENTIALS.

Keep keys on your server; do not embed them in browser or mobile bundles. Key management happens in Settings → API keys or with the signed-in user's token ($ADMIN_TOKEN below): owners and administrators create and revoke keys, any member can list them.

Token Format

API keys are prefixed with sk-org-. The full secret is shown only once, at creation: store it securely. List and revoke endpoints only ever return the key prefix, never the secret. The sk-org- prefix lets a secret scanner find a leaked key: add the pattern to your pre-commit hook and log search.

Scopes

A key carries a list of scopes, one at least: tools:read, tools:write, chat:write, sessions:read, sessions:write. Each is resource:action. A request that needs a scope the key lacks fails with 403 AUTH_PERMISSION_DENIED. A key created through the API without scopes gets tools:write, chat:write, sessions:write; an empty list and any other word (write:tools, chat, read:tools, admin included) are rejected with 422.

ScopeGrants
chat:writePOST /v1/chat/completions and GET /v1/models
tools:readReads over MCP: list_services, describe_service, read calls of connector_execute
tools:writeWrite calls of connector_execute and every repl_execute over MCP; includes tools:read
sessions:readReserved: no operation asks it yet
sessions:writeReserved: no operation asks it yet; includes sessions:read

The access levels in Settings map to scope sets: Full access is tools:write, chat:write, sessions:write, the same set a key created through the API without scopes gets; AI apps only is tools:write; View only is tools:read. Only Full access opens the chat endpoint.

Managing API Keys

Keys are issued, listed, and revoked per organization. Each key expires after expires_in_days: 90 by default, from 1 to 365; an explicit null creates a non-expiring key.

bash
# Create a key — the full secret is returned once curl -X POST https://samreshuuu.ru/api/v1/organizations/$ORG_ID/api-keys \ -H "Authorization: Bearer $ADMIN_TOKEN" \ -H "Content-Type: application/json" \ -d '{"name": "production", "scopes": ["chat:write", "tools:write"], "expires_in_days": 90}' # List keys without the secret: prefix, scopes, expires_at, last_used_at curl https://samreshuuu.ru/api/v1/organizations/$ORG_ID/api-keys \ -H "Authorization: Bearer $ADMIN_TOKEN" # Revoke a key (204) curl -X DELETE https://samreshuuu.ru/api/v1/organizations/$ORG_ID/api-keys/$KEY_ID \ -H "Authorization: Bearer $ADMIN_TOKEN"

Revocation takes effect immediately, over HTTP and MCP. A member's keys are revoked automatically when they leave the organization or lose the owner or administrator role, and all keys of an organization are revoked when it is deleted. An unknown, revoked or expired key, and a key whose creator no longer holds a key (see Organization Context), gets 401 AUTH_INVALID_CREDENTIALS.

Rotation without downtime. Create a new key, deploy it, confirm through last_used_at that the old key is no longer used, then revoke the old one. Requests to /v1/* and to MCP both set the stamp, at most once every 10 minutes: the key has gone quiet when its last_used_at is older than that window and stops moving. Revoke a key that reached a repository, log or chat right away, even with no sign of use. Keep separate keys for separate environments (development, production).

Rate Limits and Browser Calls

Request rate is limited per key, per minute, by the organization's plan:

PlanRequests per minute, per key
Pro180
Max360

Exceeding it returns 429 RATE_LIMIT_EXCEEDED with a Retry-After header in seconds. Model spend is a separate weekly limit, see Errors & Rate Limits.

Calls from a browser page on another origin are cut by CORS: the API does not answer cross-origin requests with a key. Call it from your own server, which also keeps the key out of the page.

Authentication Errors

A /v1 refusal arrives in the public error object: branch on error.type and error.code, not on the text. Every 401 carries the header WWW-Authenticate: Bearer realm="samreshuuu". Status, code and the retry flag below come from the server's error registry; the text of a key refusal is the key copy shown after the table.

HTTPCodeMeaningRetry
401AUTH_HEADER_MISSINGMissing authorization header.no
401AUTH_TOKEN_INVALIDInvalid authorization token.no
401AUTH_TOKEN_EXPIREDToken has expired.no
401AUTH_INVALID_CREDENTIALSWe couldn't verify the sign-in. Try again.no
403AUTH_PERMISSION_DENIEDInsufficient permissions for this operation.no
429RATE_LIMIT_EXCEEDEDToo many requests. Please wait a moment.yes
  • AUTH_HEADER_MISSING: no Authorization header. Add Bearer.
  • AUTH_TOKEN_INVALID: not Bearer, a malformed token, or an organization key on the native /api/v1. Call /v1 or MCP instead.
  • AUTH_TOKEN_EXPIRED: a user token has expired. Keys do not expire this way: an expired key is AUTH_INVALID_CREDENTIALS.
  • AUTH_INVALID_CREDENTIALS: the key is unknown, revoked or expired, or its creator is no longer an owner or administrator of the organization. The message reads "Invalid, revoked or expired API key. Create a new key in the organization settings."
  • AUTH_PERMISSION_DENIED: the key lacks the scope the operation asks; the message names it. Do not retry: create a key with the scope.
  • RATE_LIMIT_EXCEEDED: the key's rate limit. Wait Retry-After seconds.
json
{ "type": "error", "error": { "type": "permission_error", "code": "AUTH_PERMISSION_DENIED", "message": "This API key lacks the chat:write scope. Create a key with this scope.", "param": null }, "request_id": "76a9e461-7848-4159-b90f-5c0d704ac97f" }

MCP answers an authentication failure before any MCP message is read, with HTTP 401, the same WWW-Authenticate header and a {"detail": {…}} body (code, message, message_en); see MCP. The native /api/v1 (key management) answers in the same detail body.

Frequently asked questions

How do I authenticate a request?

Send an organization API key as Authorization: Bearer sk-org-... It opens the OpenAI-compatible /v1 endpoints and the MCP server, limited by the key's scopes.

Which organization does a request run in?

The one that issued the key. A key is an organization credential created by one member: it reaches the organization's shared connections, never the creator's memory or personal connections, and it works only while the creator is still an owner or administrator there. To work in another organization, create a key there.

What happens if my key lacks a required scope?

The request fails with 403 AUTH_PERMISSION_DENIED, and the error message names the missing scope. chat:write opens /v1/chat/completions and /v1/models, tools:read opens reads over MCP, tools:write opens write calls and repl_execute over MCP and includes tools:read. sessions:read and sessions:write are reserved: no operation asks them yet. Every key holds at least one scope; a key created without scopes gets tools:write, chat:write and sessions:write.

Can I retrieve a key's secret after creating it?

No. The full secret is shown only once, at creation. List and revoke endpoints return only the key prefix, never the secret. A key expires after expires_in_days, see Managing API keys; an explicit null creates a non-expiring key.

Was this page helpful?