API reference

Operator control plane

Deployment-wide, for whoever runs the infrastructure. As a customer you should never need these.

Credential: HMAC-signed · scheme HMAC signature

12 operations · 11 captured against the live API.

Every example below is a real exchange recorded against a throwaway tenant, then redacted — not a shape transcribed from a schema. Ids and timestamps are stable fixtures so the same request always reads the same way. Back to the full index →

Rooms, members, tokens #

POST /internal/v1/rooms #

Create a room

Registers tenant_id (1–128 characters) as a tenant if it is new. With external_ref the call is idempotent per tenant: repeating it returns the existing room unchanged and ignores the other fields. Without external_ref, every call creates a new room. The room has no members. e2ee and e2ee_scheme (mls or megolm, which requires e2ee: true) are set at creation and cannot change. expires_at is informational only: nothing closes or deletes the room when it passes.

createRoom captured live

Request

curl -X POST https://api.ollacore.com/internal/v1/rooms \
  -H "X-Chatbox-Key-Id: $KEY_ID" \
  -H "X-Chatbox-Timestamp: $TS" \
  -H "X-Chatbox-Nonce: $NONCE" \
  -H "X-Chatbox-Signature: $SIG" \
  -H "Content-Type: application/json" \
  -d '{"tenant_id":"acct_fac91b282191cc02dd29912c8db43f1c","created_by":"alice","external_ref":"ops-room-1"}'
{
  "tenant_id": "acct_fac91b282191cc02dd29912c8db43f1c",
  "created_by": "alice",
  "external_ref": "ops-room-1"
}
ParameterTypeDescription
X-Request-Id header string Your own id for this request. It is returned in the response's X-Request-Id header and attached to every log line kept for the request, so quote it when you contact support. Use printable ASCII. Without it, the server assigns a UUID.

Request fields

FieldTypeDescription
tenant_id required string The tenant to create the room in, 1–128 characters.
created_by required string The principal recorded as the room's creator.
external_ref string Your own reference for the room. If one with this external_ref exists in the tenant, it is returned instead.
expires_at string (date-time) Optional informational expiry. Nothing closes or deletes the room when it passes.

Response · 200

{
  "id": "00919f0a-cfeb-438d-bdb7-65af28b4f678",
  "tenant_id": "acct_fac91b282191cc02dd29912c8db43f1c",
  "external_ref": "ops-room-1",
  "created_by": "alice",
  "status": "active",
  "e2ee": false,
  "created_at": "2026-01-15T09:30:02Z"
}
Response fields (9)
FieldTypeDescription
id required string (uuid) The room's id, assigned by the server.
tenant_id required string The tenant that owns the room.
external_ref string Your own reference for the room, if you gave one. Creating a room with an external_ref that already exists in the tenant returns that room.
created_by required string The principal recorded as the room's creator.
status required one of: active, closed active, or closed (no new tokens are minted for it).
e2ee required boolean End-to-end encrypted room. When true, the server accepts only ciphertext message kinds, rejects search and reactions, and pushes are content-free — the client must encrypt before sending.
e2ee_scheme one of: mls, megolm Which client E2EE scheme the room's members run, when the creator declared it: mls (the primary scheme) or megolm (the matrix-sdk-crypto transport). The two cannot share a room. Absent when undeclared and on plaintext rooms.
created_at required string (date-time) When the room was created.
expires_at string (date-time) Informational expiry time, if one was set. Nothing closes or deletes the room when it passes.
Errors · 400 · 401 · 429
StatusWhen
400tenant_id outside 1–128 characters, an invalid e2ee_scheme, or a malformed body.
401Missing, expired or invalid credentials
429Rate limit exceeded
GET /internal/v1/rooms #

Resolve by external_ref

Both tenant_id and external_ref are required, and external_ref must match exactly within that tenant. The room is returned whatever its status.

lookupRoom captured live

Request

curl -X GET https://api.ollacore.com/internal/v1/rooms?tenant_id=acct_fac91b282191cc02dd29912c8db43f1c&external_ref=ops-room-1 \
  -H "X-Chatbox-Key-Id: $KEY_ID" \
  -H "X-Chatbox-Timestamp: $TS" \
  -H "X-Chatbox-Nonce: $NONCE" \
  -H "X-Chatbox-Signature: $SIG"
ParameterTypeDescription
tenant_id query required string The tenant to look in.
external_ref query required string unique per tenant; the same ref always resolves to the same room, which is what makes room creation idempotent
X-Request-Id header string Your own id for this request. It is returned in the response's X-Request-Id header and attached to every log line kept for the request, so quote it when you contact support. Use printable ASCII. Without it, the server assigns a UUID.

Response · 200

{
  "id": "00919f0a-cfeb-438d-bdb7-65af28b4f678",
  "tenant_id": "acct_fac91b282191cc02dd29912c8db43f1c",
  "external_ref": "ops-room-1",
  "created_by": "alice",
  "status": "active",
  "e2ee": false,
  "created_at": "2026-01-15T09:30:02Z"
}
Response fields (9)
FieldTypeDescription
id required string (uuid) The room's id, assigned by the server.
tenant_id required string The tenant that owns the room.
external_ref string Your own reference for the room, if you gave one. Creating a room with an external_ref that already exists in the tenant returns that room.
created_by required string The principal recorded as the room's creator.
status required one of: active, closed active, or closed (no new tokens are minted for it).
e2ee required boolean End-to-end encrypted room. When true, the server accepts only ciphertext message kinds, rejects search and reactions, and pushes are content-free — the client must encrypt before sending.
e2ee_scheme one of: mls, megolm Which client E2EE scheme the room's members run, when the creator declared it: mls (the primary scheme) or megolm (the matrix-sdk-crypto transport). The two cannot share a room. Absent when undeclared and on plaintext rooms.
created_at required string (date-time) When the room was created.
expires_at string (date-time) Informational expiry time, if one was set. Nothing closes or deletes the room when it passes.
Errors · 401 · 404 · 429
StatusWhen
401Missing, expired or invalid credentials
404no room with that external_ref in the tenant
429Rate limit exceeded
PATCH /internal/v1/rooms/{room_id} #

Patch status / expiry

Leave a field out to keep its current value. expires_at cannot be cleared, because null also keeps the current value. status must be active or closed (400 otherwise). Closing a room only stops new session tokens from being minted for it (409 room is closed). Members keep their tokens, open sockets and history, and can still send until their tokens expire. Setting active lifts this. No event is published. expires_at remains informational only.

patchRoom captured live

Request

curl -X PATCH https://api.ollacore.com/internal/v1/rooms/00919f0a-cfeb-438d-bdb7-65af28b4f678 \
  -H "X-Chatbox-Key-Id: $KEY_ID" \
  -H "X-Chatbox-Timestamp: $TS" \
  -H "X-Chatbox-Nonce: $NONCE" \
  -H "X-Chatbox-Signature: $SIG" \
  -H "Content-Type: application/json" \
  -d '{"expires_at":"2026-01-16T09:30:02Z"}'
{
  "expires_at": "2026-01-16T09:30:02Z"
}
ParameterTypeDescription
room_id path required string (uuid) The room's id.
X-Request-Id header string Your own id for this request. It is returned in the response's X-Request-Id header and attached to every log line kept for the request, so quote it when you contact support. Use printable ASCII. Without it, the server assigns a UUID.

Request fields

FieldTypeDescription
status one of: active, closed closed stops new session tokens being minted for the room (409); members keep access with the tokens they hold. active reopens it.
expires_at string (date-time) Informational expiry time, stored and returned. Nothing closes or deletes the room when it passes.

Response · 200

{
  "id": "00919f0a-cfeb-438d-bdb7-65af28b4f678",
  "tenant_id": "acct_fac91b282191cc02dd29912c8db43f1c",
  "external_ref": "ops-room-1",
  "created_by": "alice",
  "status": "active",
  "e2ee": false,
  "created_at": "2026-01-15T09:30:02Z",
  "expires_at": "2026-01-16T09:30:02Z"
}
Response fields (9)
FieldTypeDescription
id required string (uuid) The room's id, assigned by the server.
tenant_id required string The tenant that owns the room.
external_ref string Your own reference for the room, if you gave one. Creating a room with an external_ref that already exists in the tenant returns that room.
created_by required string The principal recorded as the room's creator.
status required one of: active, closed active, or closed (no new tokens are minted for it).
e2ee required boolean End-to-end encrypted room. When true, the server accepts only ciphertext message kinds, rejects search and reactions, and pushes are content-free — the client must encrypt before sending.
e2ee_scheme one of: mls, megolm Which client E2EE scheme the room's members run, when the creator declared it: mls (the primary scheme) or megolm (the matrix-sdk-crypto transport). The two cannot share a room. Absent when undeclared and on plaintext rooms.
created_at required string (date-time) When the room was created.
expires_at string (date-time) Informational expiry time, if one was set. Nothing closes or deletes the room when it passes.
Errors · 400 · 401 · 404 · 429
StatusWhen
400status other than active or closed.
401Missing, expired or invalid credentials
404No such room, or the room is being erased: it cannot be reactivated.
429Rate limit exceeded
GET /internal/v1/rooms/{room_id}/status #

Members, presence, live call roster

member_count counts active members. online_participants lists the members who currently have chat presence. rtc_participants lists the principals in the room's call on the SFU at request time, and is empty when nobody is in the call. If presence cannot be read, online_participants comes back empty and the request still succeeds.

roomStatus captured live

Request

curl -X GET https://api.ollacore.com/internal/v1/rooms/25964f12-9ae9-4356-80e8-69bc5f749bd1/status \
  -H "X-Chatbox-Key-Id: $KEY_ID" \
  -H "X-Chatbox-Timestamp: $TS" \
  -H "X-Chatbox-Nonce: $NONCE" \
  -H "X-Chatbox-Signature: $SIG"
ParameterTypeDescription
room_id path required string (uuid) The room's id.
X-Request-Id header string Your own id for this request. It is returned in the response's X-Request-Id header and attached to every log line kept for the request, so quote it when you contact support. Use printable ASCII. Without it, the server assigns a UUID.

Response · 200

{
  "room": {
    "id": "25964f12-9ae9-4356-80e8-69bc5f749bd1",
    "tenant_id": "acct_fac91b282191cc02dd29912c8db43f1c",
    "external_ref": "order-7781",
    "created_by": "alice",
    "status": "active",
    "e2ee": false,
    "created_at": "2026-01-15T09:30:03Z"
  },
  "member_count": 4,
  "online_participants": [],
  "rtc_participants": []
}
Response fields (13)
FieldTypeDescription
room required Room The room.
room.id required string (uuid) The room's id, assigned by the server.
room.tenant_id required string The tenant that owns the room.
room.external_ref string Your own reference for the room, if you gave one. Creating a room with an external_ref that already exists in the tenant returns that room.
room.created_by required string The principal recorded as the room's creator.
room.status required one of: active, closed active, or closed (no new tokens are minted for it).
room.e2ee required boolean End-to-end encrypted room. When true, the server accepts only ciphertext message kinds, rejects search and reactions, and pushes are content-free — the client must encrypt before sending.
room.e2ee_scheme one of: mls, megolm Which client E2EE scheme the room's members run, when the creator declared it: mls (the primary scheme) or megolm (the matrix-sdk-crypto transport). The two cannot share a room. Absent when undeclared and on plaintext rooms.
room.created_at required string (date-time) When the room was created.
room.expires_at string (date-time) Informational expiry time, if one was set. Nothing closes or deletes the room when it passes.
member_count required integer (int64) How many active members the room has.
online_participants required array of string principals with a live chat WebSocket
rtc_participants required array of string principals currently published to the SFU
Errors · 401 · 404 · 429
StatusWhen
401Missing, expired or invalid credentials
404No such room, message, member or attachment
429Rate limit exceeded
POST /internal/v1/rooms/{room_id}/members #

Add a member

A room holds at most 5,000 active members, on every plane; once it is full the add is refused with 409 and nothing changes. Restoring a removed member or changing an existing member's role never needs a seat.

addMember captured live

Request

curl -X POST https://api.ollacore.com/internal/v1/rooms/00919f0a-cfeb-438d-bdb7-65af28b4f678/members \
  -H "X-Chatbox-Key-Id: $KEY_ID" \
  -H "X-Chatbox-Timestamp: $TS" \
  -H "X-Chatbox-Nonce: $NONCE" \
  -H "X-Chatbox-Signature: $SIG" \
  -H "Content-Type: application/json" \
  -d '{"principal_id":"alice","role":"owner"}'
{
  "principal_id": "alice",
  "role": "owner"
}
ParameterTypeDescription
room_id path required string (uuid) The room's id.
X-Request-Id header string Your own id for this request. It is returned in the response's X-Request-Id header and attached to every log line kept for the request, so quote it when you contact support. Use printable ASCII. Without it, the server assigns a UUID.

Request fields

FieldTypeDescription
principal_id required string Who to add, 1–128 characters. A removed member is restored.
role one of: owner, member, viewer default "member" owner, member or viewer. Defaults to member; anything else is 400.

Response · 200

{
  "tenant_id": "acct_fac91b282191cc02dd29912c8db43f1c",
  "room_id": "00919f0a-cfeb-438d-bdb7-65af28b4f678",
  "principal_id": "alice",
  "role": "owner",
  "joined_at": "2026-01-15T09:30:02Z"
}
Response fields (7)
FieldTypeDescription
tenant_id required string The tenant that owns the room.
room_id required string (uuid) The room.
principal_id required string The member's principal id.
role required string owner, member or viewer.
joined_at string (date-time) When the principal first joined the room. Restoring a removed member keeps the original time.
left_at string (date-time) set when the member was removed; the row is retained so history stays auditable
muted_until string (date-time) Until when this member has push notifications from the room silenced; absent when not muted. 9999-12-31T00:00:00Z stands for "until unmuted". Live delivery and call rings are unaffected.
Errors · 401 · 404 · 409 · 429
StatusWhen
401Missing, expired or invalid credentials
404No such room, message, member or attachment
409Conflicts with current state
429Rate limit exceeded
DELETE /internal/v1/rooms/{room_id}/members/{principal_id} #

Remove a member

Sets the membership's left_at; history is kept, and adding the principal again restores the membership. The removal takes effect at once: the member's live chat and RTC sockets close with 4403, and their HTTP requests and new session tokens are refused. A durable member.removed event goes to the room's connected clients and to webhooks subscribed to it. E2EE clients use this event as the cue to rotate keys. 404 if the room does not exist or the principal is not an active member.

removeMember captured live

Request

curl -X DELETE https://api.ollacore.com/internal/v1/rooms/00919f0a-cfeb-438d-bdb7-65af28b4f678/members/carol2 \
  -H "X-Chatbox-Key-Id: $KEY_ID" \
  -H "X-Chatbox-Timestamp: $TS" \
  -H "X-Chatbox-Nonce: $NONCE" \
  -H "X-Chatbox-Signature: $SIG"
ParameterTypeDescription
room_id path required string (uuid) The room's id.
principal_id path required string The member to remove.
X-Request-Id header string Your own id for this request. It is returned in the response's X-Request-Id header and attached to every log line kept for the request, so quote it when you contact support. Use printable ASCII. Without it, the server assigns a UUID.

Response · 200

{
  "removed": true
}
Response fields (1)
FieldTypeDescription
removed required boolean Always true: removing a principal who is not an active member answers 404.
Errors · 401 · 404 · 429
StatusWhen
401Missing, expired or invalid credentials
404No such room, message, member or attachment
429Rate limit exceeded
POST /internal/v1/session-tokens #

Mint a token

role defaults to the member's stored role and may only lower it: a token above the member's role (viewer → owner) is refused with 403, a role outside owner|member|viewer with 400.

mintSessionToken captured live

Request

curl -X POST https://api.ollacore.com/internal/v1/session-tokens \
  -H "X-Chatbox-Key-Id: $KEY_ID" \
  -H "X-Chatbox-Timestamp: $TS" \
  -H "X-Chatbox-Nonce: $NONCE" \
  -H "X-Chatbox-Signature: $SIG" \
  -H "Content-Type: application/json" \
  -d '{"tenant_id":"acct_fac91b282191cc02dd29912c8db43f1c","room_id":"00919f0a-cfeb-438d-bdb7-65af28b4f678","principal_id":"alice","role":"owner","ttl_seconds":3600}'
{
  "tenant_id": "acct_fac91b282191cc02dd29912c8db43f1c",
  "room_id": "00919f0a-cfeb-438d-bdb7-65af28b4f678",
  "principal_id": "alice",
  "role": "owner",
  "ttl_seconds": 3600
}
ParameterTypeDescription
X-Request-Id header string Your own id for this request. It is returned in the response's X-Request-Id header and attached to every log line kept for the request, so quote it when you contact support. Use printable ASCII. Without it, the server assigns a UUID.

Request fields

FieldTypeDescription
tenant_id required string The tenant the room belongs to.
room_id required string (uuid) The room the token is for. A token opens exactly one room.
principal_id required string Who the token is for. Must be an active member of the room.
role one of: owner, member, viewer Optional: mint with fewer rights than the member has. At or below the member's stored role (a higher one is 403); anything but owner|member|viewer is 400. Omit to use the member's own role.
ttl_seconds integer default 600 · min 60 · max 3600 Token lifetime in seconds. Defaults to 600 and is clamped to 60–3600.

Response · 200

{
  "access_token": "eyJhbGciOiJFZERTQSIsInR5cCI6IkpXVCIsImtpZCI6ImEwM2I1NjQ4MjM3YjlmZWIifQ.eyJpc3MiOiJjaGF0Ym94LWJhY2tlbmQiLCJhdWQiOiJjaGF0Ym94LXJlYWx0aW1lIiwic3ViIjoiYWxpY2UiLCJ0aWQiOiJhY2N0X2ZhYzkxYjI4MjE5MWNjMDJkZDI5OTEyYzhkYjQzZjFjIiwicmlkIjoiMDA5MTlmMGEtY2ZlYi00MzhkLWJkYjctNjVhZjI4YjRmNjc4Iiwicm9sZSI6Im93bmVyIiwicGVybWlzc2lvbnMiOlsiY2hhdDpzZW5kIiwiY2hhdDpoaXN0b3J5IiwiYXR0YWNobWVudDp1cGxvYWQiLCJhdHRhY2htZW50OmRvd25sb2FkIiwicnRjOnB1Ymxpc2giLCJydGM6c3Vic2NyaWJlIl0sImp0aSI6ImJmZThlYzg2LTVkZmUtNDM1YS1iNTk3LTkwZjBkY2RhM2FiZSIsImlhdCI6MTc5MjMyNDY2NSwibmJmIjoxNzkyMzI0NjY1LCJleHAiOjE3OTIzMjgyNjV9.SIGNATURE_REMOVED",
  "expires_at": "2026-01-15T10:30:02Z",
  "rtc_websocket_url": "wss://api.ollacore.com/v1/rtc/ws",
  "chat_websocket_url": "wss://api.ollacore.com/v1/chat/ws",
  "ice_servers": [
    {
      "urls": [
        "stun:api.ollacore.com:3478"
      ]
    },
    {
      "urls": [
        "turn:api.ollacore.com:3478",
        "turns:api.ollacore.com:5349"
      ],
      "username": "1789064267:alice",
      "credential": "EXAMPLE_TURN_CREDENTIAL="
    }
  ]
}
Response fields (8)
FieldTypeDescription
access_token required string The room-scoped session token (an Ed25519-signed JWT). Present it as Authorization: Bearer … over HTTP and as the WebSocket subprotocol bearer.<token>, never in a URL.
expires_at required string (date-time) When the token expires. Mint a new one before then; a socket closes with 4401 at expiry.
rtc_websocket_url required string (uri) The call-signalling WebSocket URL.
chat_websocket_url required string (uri) The chat WebSocket URL.
ice_servers required array of object STUN and TURN servers for WebRTC, with short-lived TURN credentials. Pass them to RTCPeerConnection unchanged.
ice_servers[].urls required array of string The server URLs. They never carry a ?transport= query, which Safari refuses.
ice_servers[].username string expiry_unix:principal — ephemeral, 600 s
ice_servers[].credential string base64(HMAC-SHA1(turn_secret, username))
Errors · 400 · 401 · 403 · 404 · 429
StatusWhen
400role is not one of owner | member | viewer.
401Missing, expired or invalid credentials
403The principal is not an active member of the room, or role is above the member's stored role (a viewer cannot be minted an owner token).
404Room not found in tenant
429Rate limit exceeded
GET /internal/v1/principals/{principal_id}/unread #

Per-room unread for a principal

tenant_id is required. Lists every room where the principal is an active member, including rooms with no unread messages. A room's count is the messages after the principal's read cursor, excluding their own and deleted messages, and total is the sum. A principal with no memberships gets total: 0 and an empty list.

principalUnread captured live

Request

curl -X GET https://api.ollacore.com/internal/v1/principals/bob/unread?tenant_id=acct_fac91b282191cc02dd29912c8db43f1c \
  -H "X-Chatbox-Key-Id: $KEY_ID" \
  -H "X-Chatbox-Timestamp: $TS" \
  -H "X-Chatbox-Nonce: $NONCE" \
  -H "X-Chatbox-Signature: $SIG"
ParameterTypeDescription
principal_id path required string The principal whose unread counts to return.
tenant_id query required string The tenant the principal belongs to.
X-Request-Id header string Your own id for this request. It is returned in the response's X-Request-Id header and attached to every log line kept for the request, so quote it when you contact support. Use printable ASCII. Without it, the server assigns a UUID.

Response · 200

{
  "rooms": [
    {
      "room_id": "25964f12-9ae9-4356-80e8-69bc5f749bd1",
      "unread_count": 0
    }
  ],
  "total": 0
}
Response fields (4)
FieldTypeDescription
total required integer (int64) Unread messages across all the principal's rooms, for an app-icon badge.
rooms required array of object Per-room unread counts.
rooms[].room_id required string (uuid) The room.
rooms[].unread_count required integer (int64) Messages after the principal's read cursor in that room.
Errors · 401 · 429
StatusWhen
401Missing, expired or invalid credentials
429Rate limit exceeded

Provisioning & lifecycle #

POST /internal/v1/api-keys #

Issue a customer key

environment is live or test. A test key works in a separate {tenant_id}::test sandbox with its own rooms, messages and usage, so tenant_id may not contain ::. Each scope must be one of rooms:read, rooms:write, members:write, tokens:mint, messages:write, webhooks:manage, usage:read, rooms:delete or data:erase. plan defaults to default and must already exist. Any invalid value is 400. The tenant, and for a test key its sandbox, is registered if new. api_key (ak_<env>_<id>.<secret>) is returned only once, because the server stores only a SHA-256 digest of the secret. After expires_at the key is refused with 401.

createApiKey captured live

Request

curl -X POST https://api.ollacore.com/internal/v1/api-keys \
  -H "X-Chatbox-Key-Id: $KEY_ID" \
  -H "X-Chatbox-Timestamp: $TS" \
  -H "X-Chatbox-Nonce: $NONCE" \
  -H "X-Chatbox-Signature: $SIG" \
  -H "Content-Type: application/json" \
  -d '{"tenant_id":"acct_fac91b282191cc02dd29912c8db43f1c","name":"sandbox-key","environment":"test","scopes":["rooms:read","rooms:write"]}'
{
  "tenant_id": "acct_fac91b282191cc02dd29912c8db43f1c",
  "name": "sandbox-key",
  "environment": "test",
  "scopes": [
    "rooms:read",
    "rooms:write"
  ]
}
ParameterTypeDescription
X-Request-Id header string Your own id for this request. It is returned in the response's X-Request-Id header and attached to every log line kept for the request, so quote it when you contact support. Use printable ASCII. Without it, the server assigns a UUID.

Request fields

FieldTypeDescription
tenant_id required string The tenant the key acts on, 1–128 characters. Must not contain ::, which is reserved for the test namespace.
name string Optional label for the key.
environment required one of: live, test live or test. A test key works in the {tenant_id}::test namespace.
scopes required array of one of: rooms:read, rooms:write, members:write, tokens:mint, messages:write, webhooks:manage, usage:read, rooms:delete, data:erase The scopes to grant. Each must be a known scope.
expires_at string (date-time) Optional expiry. After it the key is refused as if revoked.
plan string Rate-tier plan name (see the plans table); defaults to 'default'.

Response · 200

{
  "key_id": "key-id-sandbox",
  "api_key": "ak_live_EXAMPLE.NOT_A_REAL_KEY",
  "tenant_id": "acct_fac91b282191cc02dd29912c8db43f1c",
  "environment": "test",
  "scopes": [
    "rooms:read",
    "rooms:write"
  ]
}
Response fields (5)
FieldTypeDescription
key_id required string The key's public id (ak_live_… or ak_test_…). Use it to revoke the key; it is not a credential.
api_key required string Full ak_<env>_<id>.<secret>, shown once.
tenant_id required string The tenant the key acts on. A test key's data lives in its own namespace, {tenant_id}::test, and never mixes with live data.
environment required one of: live, test live or test. A test key sees only test data.
scopes required array of one of: rooms:read, rooms:write, members:write, tokens:mint, messages:write, webhooks:manage, usage:read, rooms:delete, data:erase What the key may do. A call outside these scopes is refused with 403 when it is made, not when the key is created.
Errors · 400 · 401 · 429
StatusWhen
400A bad environment, scope, plan or tenant_id.
401Missing, expired or invalid credentials
429Rate limit exceeded
DELETE /internal/v1/api-keys/{key_id} #

Revoke a key

Takes effect on the key's next request, which is refused with 401. 404 if the id is unknown or the key is already revoked. Session tokens the key already minted stay valid until they expire, at most one hour. After revocation the key's plan no longer counts toward the tenant's quotas or retention window.

revokeApiKey captured live

Request

curl -X DELETE https://api.ollacore.com/internal/v1/api-keys/key-id-sandbox \
  -H "X-Chatbox-Key-Id: $KEY_ID" \
  -H "X-Chatbox-Timestamp: $TS" \
  -H "X-Chatbox-Nonce: $NONCE" \
  -H "X-Chatbox-Signature: $SIG"
ParameterTypeDescription
key_id path required string The API key's public id (ak_live_… or ak_test_…).
X-Request-Id header string Your own id for this request. It is returned in the response's X-Request-Id header and attached to every log line kept for the request, so quote it when you contact support. Use printable ASCII. Without it, the server assigns a UUID.

Response · 200

{
  "revoked": true
}
Response fields (1)
FieldTypeDescription
revoked boolean True once the key no longer works.
Errors · 401 · 404 · 429
StatusWhen
401Missing, expired or invalid credentials
404No such room, message, member or attachment
429Rate limit exceeded
PUT /internal/v1/plans/{name} #

Create/update a rate + quota plan

Replaces the whole plan. Optional fields left out become null: unlimited quota, no plan retention, no description. name is 1–64 characters, rate_limit and rate_window_seconds must be positive, and quotas and retention_days must be at least 0 or null; anything else is 400. Plans are global. The rate limit applies per key, from that key's next request. For each quota and for retention, a tenant gets the most generous plan among its live keys, and null counts as unlimited. A tenant's own retention setting overrides the plan's. Monthly quotas count message.sent and attachment.bytes per UTC calendar month.

putPlan captured on a test deployment

Request

curl -X PUT https://api.ollacore.com/internal/v1/plans/docs-example \
  -H "X-Chatbox-Key-Id: $KEY_ID" \
  -H "X-Chatbox-Timestamp: $TS" \
  -H "X-Chatbox-Nonce: $NONCE" \
  -H "X-Chatbox-Signature: $SIG" \
  -H "Content-Type: application/json" \
  -d '{"rate_limit":120,"rate_window_seconds":10,"monthly_message_quota":5000000,"monthly_byte_quota":549755813888,"retention_days":365,"description":"Example tier for documentation"}'
{
  "rate_limit": 120,
  "rate_window_seconds": 10,
  "monthly_message_quota": 5000000,
  "monthly_byte_quota": 549755813888,
  "retention_days": 365,
  "description": "Example tier for documentation"
}
ParameterTypeDescription
name path required string The plan's name, 1–64 characters. The call creates the plan or replaces it.
X-Request-Id header string Your own id for this request. It is returned in the response's X-Request-Id header and attached to every log line kept for the request, so quote it when you contact support. Use printable ASCII. Without it, the server assigns a UUID.

Request fields

FieldTypeDescription
rate_limit required integer (int64) Requests allowed per window, per API key on this plan. Must be positive.
rate_window_seconds required integer (int64) The rate-limit window in seconds. Must be positive.
monthly_message_quota integer (int64), nullable Monthly message cap; omit or null for unlimited.
monthly_byte_quota integer (int64), nullable Monthly attachment-byte cap; omit or null for unlimited.
retention_days integer (int64), nullable Auto-expire messages older than this many days; omit or null to keep forever.
description string Optional human description of the plan.

Response · 200

{
  "plan": "docs-example"
}
Response fields (1)
FieldTypeDescription
plan string The plan's name.
Errors · 400 · 401 · 429
StatusWhen
400A name outside 1–64 characters, a rate limit or window that is not positive, or a negative quota or retention.
401Missing, expired or invalid credentials
429Rate limit exceeded
Captured against a test deployment: `plans` is deployment-wide, so writing it on production would re-tier every existing tenant.
DELETE /internal/v1/tenants/{tenant_id} #

Offboard a tenant — revokes the account and its logins, keys and directory at once, then erases rooms, blobs, webhooks and usage

Async, in two steps. At once, every way in is revoked: the tenant's account (and its sessions), API keys, directory apps, pending OTP challenges and directory sessions. About 45 seconds later — long enough for any request already in flight to finish — every room is purged (blobs, rows and every stored copy of its events), then the rest of the tenant's data: usage ledger, webhooks and their delivery log, push subscriptions, identities, the directory, and E2EE key material. Most destructive; HMAC-only.

purgeTenant captured live

Request

curl -X DELETE https://api.ollacore.com/internal/v1/tenants/acct_fac91b282191cc02dd29912c8db43f1c \
  -H "X-Chatbox-Key-Id: $KEY_ID" \
  -H "X-Chatbox-Timestamp: $TS" \
  -H "X-Chatbox-Nonce: $NONCE" \
  -H "X-Chatbox-Signature: $SIG"
ParameterTypeDescription
tenant_id path required string The tenant to erase.
X-Request-Id header string Your own id for this request. It is returned in the response's X-Request-Id header and attached to every log line kept for the request, so quote it when you contact support. Use printable ASCII. Without it, the server assigns a UUID.

Response · 200

{
  "purging": true
}
Response fields (1)
FieldTypeDescription
purging required boolean True: every room and all of the tenant's data are being erased in the background. Irreversible.
Errors · 401 · 429
StatusWhen
401Missing, expired or invalid credentials
429Rate limit exceeded

Errors on this plane #

Every failure uses the same body — code and message. These were triggered deliberately against the live API.

401 · signature does not match the canonical string

{
  "code": "unauthorized",
  "message": "unauthorized: signature mismatch"
}

401 · timestamp outside the 120-second window

{
  "code": "unauthorized",
  "message": "unauthorized: timestamp outside allowed window"
}

The full code list and retry guidance is on Errors & rate limits.