API reference

Directory (end-user) API

The turnkey identity plane. Your client calls these directly.

Credential: du_ directory session — or none for OTP · scheme du_ directory session

36 operations · 35 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 →

Sign-in & profile #

POST /v1/directory/otp/request #

Request a one-time code (unauthenticated)

Sends a 6-digit code that is valid for 5 minutes. Only its hash is stored. phone must be E.164 (+ and 8 to 15 digits), or the call is a 400. Four limits apply, each a 429 with Retry-After whose message names the limit: 300 requests a minute per app (otp_app), 5 a minute per phone number (otp_phone), 30 a minute per client IP (otp_ip), and at most 3 codes per number in 60 seconds (otp_resend). The code is delivered before the response: in webhook mode it is POSTed to your webhook with one retry after 500 ms, and in provider mode your SMS vendor is called. A delivery failure answers 500 internal. A retry issues a new code. The response is the same whether or not the number is already registered.

otpRequest captured live

Request

curl -X POST https://api.ollacore.com/v1/directory/otp/request \
  -H "Content-Type: application/json" \
  -d '{"app_id":"da_3a0a2cfaabd34b7dbfb4ecd5033b0c5f","phone":"+15550001111"}'
{
  "app_id": "da_3a0a2cfaabd34b7dbfb4ecd5033b0c5f",
  "phone": "+15550001111"
}
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
app_id required string Your directory app's public id.
phone required string E.164

Response · 200

{
  "dispatched": true,
  "expires_at": "2026-01-15T09:35:42Z"
}
Response fields (2)
FieldTypeDescription
dispatched boolean True when a code was handed to your webhook or SMS vendor. The answer is the same whether or not the number is registered.
expires_at string (date-time) When the code stops working: 5 minutes after it was issued.
Errors · 400 · 404 · 429
StatusWhen
400phone is not E.164 (+ and 8–15 digits).
404unknown app
429A limit was reached; message names it: otp_app (300 a minute per app), otp_phone (5 a minute per number), otp_ip (30 a minute per client address) or otp_resend (3 codes per number in 60 s).
The code is never returned here — it is delivered to your configured webhook or SMS provider.
POST /v1/directory/otp/verify #

Verify and receive a du_ session

Only the newest unexpired code for the number can be verified, so requesting a new code supersedes older ones. Each code allows 5 attempts, right or wrong, and works once. After that, or after it expires, every attempt is a 401 until the user requests a new code. On success the user is created if the number is new to the tenant, and a new du_ session valid for 30 days is returned. Users belong to the tenant, so the same number reaches the same user through any of the tenant's apps. An unknown app_id is a 404, and a malformed phone is a 400.

otpVerify captured live

Request

curl -X POST https://api.ollacore.com/v1/directory/otp/verify \
  -H "Content-Type: application/json" \
  -d '{"app_id":"da_3a0a2cfaabd34b7dbfb4ecd5033b0c5f","phone":"+15550001111","code":"481920"}'
{
  "app_id": "da_3a0a2cfaabd34b7dbfb4ecd5033b0c5f",
  "phone": "+15550001111",
  "code": "481920"
}
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
app_id required string Your directory app's public id.
phone required string The same E.164 number the code was sent to.
code required string The 6-digit code. At most 5 attempts per code; each code works once.

Response · 200

{
  "session_token": "as_EXAMPLE_SESSION",
  "user_id": "4a98e767-a426-47e9-89a0-c499b54d2904",
  "display_name": "Alice Nakamura"
}
Response fields (3)
FieldTypeDescription
session_token string The du_… directory session for this user, valid 30 days. Store it securely on the device.
user_id string (uuid) The directory user's id. It is also their chat principal_id.
display_name string, nullable The user's display name, or null if not set yet.
Errors · 400 · 401 · 404
StatusWhen
400phone is not E.164.
401No unexpired code for this number, a wrong code, a code already used, or one past its 5 attempts. Request a new code.
404Unknown app_id.
POST /v1/directory/logout #

Revoke this session (or every device)

Signs the end-user out. du_ sessions otherwise live for 30 days with no way to withdraw them, so this is what makes a leaked token recoverable. Pass all_devices to revoke every session the user holds.

directoryLogout captured live

Request

curl -X POST https://api.ollacore.com/v1/directory/logout \
  -H "Authorization: Bearer du_…" \
  -H "Content-Type: application/json" \
  -d '{"all_devices":false}'
{
  "all_devices": false
}
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
all_devices boolean default false Revoke every session for this user, not just the one presented.

Response · 200

{
  "logged_out": true,
  "sessions_revoked": 1
}
Response fields (2)
FieldTypeDescription
logged_out boolean Always true: the session (or every session, with all_devices) no longer works.
sessions_revoked integer How many sessions were revoked.
Errors · 401
StatusWhen
401Missing, expired or invalid credentials
GET /v1/directory/me #

Current user: id, phone, display_name, about, avatar_version

Returns the profile of the user the du_ session belongs to. id is also the user's chat principal_id. avatar_version is the value to cache their picture by when fetching GET /v1/directory/users/{user_id}/avatar. Change the name or about line with PATCH /v1/directory/me.

meGet captured live

Request

curl -X GET https://api.ollacore.com/v1/directory/me \
  -H "Authorization: Bearer du_…"
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.

Response · 200

{
  "id": "4a98e767-a426-47e9-89a0-c499b54d2904",
  "phone": "+15550001111",
  "display_name": "Alice Nakamura",
  "about": "Ships things. Slow to reply at weekends.",
  "avatar_version": null
}
Response fields (5)
FieldTypeDescription
id required string (uuid) The user's id, which is also their chat principal_id.
phone required string The user's verified phone number, E.164.
display_name required string, nullable The user's display name, or null if not set.
about required string, nullable max length 256 The profile line, up to 256 characters; null when not set.
avatar_version required string (date-time), nullable When the current profile photo was set; null when there is none. The value to cache the picture by: fetch GET /v1/directory/users/{user_id}/avatar once per version (its ETag is this value).
Errors · 401
StatusWhen
401Missing, expired or invalid credentials
PATCH /v1/directory/me #

Update display name and/or about line

A field left out is untouched. An empty about clears it. Both are checked before either is written. Answers the whole profile.

mePatch captured live

Request

curl -X PATCH https://api.ollacore.com/v1/directory/me \
  -H "Authorization: Bearer du_…" \
  -H "Content-Type: application/json" \
  -d '{"display_name":"Alice Nakamura","about":"Ships things. Slow to reply at weekends."}'
{
  "display_name": "Alice Nakamura",
  "about": "Ships things. Slow to reply at weekends."
}
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
display_name string max length 128 New display name, 1–128 characters after trimming.
about string max length 256 New profile line, up to 256 characters after trimming; an empty string clears it.

Response · 200

{
  "id": "4a98e767-a426-47e9-89a0-c499b54d2904",
  "phone": "+15550001111",
  "display_name": "Alice Nakamura",
  "about": "Ships things. Slow to reply at weekends.",
  "avatar_version": null
}
Response fields (5)
FieldTypeDescription
id required string (uuid) The user's id, which is also their chat principal_id.
phone required string The user's verified phone number, E.164.
display_name required string, nullable The user's display name, or null if not set.
about required string, nullable max length 256 The profile line, up to 256 characters; null when not set.
avatar_version required string (date-time), nullable When the current profile photo was set; null when there is none. The value to cache the picture by: fetch GET /v1/directory/users/{user_id}/avatar once per version (its ETag is this value).
Errors · 400 · 401
StatusWhen
400Nothing to update, a display name outside 1–128 characters, or an about line over 256.
401Missing, expired or invalid credentials
Either field or both; a field left out is untouched and an empty about clears it. Answers the whole profile.
DELETE /v1/directory/me #

Delete my account: leave every chat (groups pass to the next member), erase my data, sign out everywhere

What an app store requires an app to offer. You leave every conversation as leave would — a group whose only owner you were passes to its longest-standing member, a conversation left empty closes, your live sockets close and each room hears member.removed — then every session you hold is revoked, on every device, and the same erasure as POST /v1/server/principals/{principal_id}/erase is queued: your messages, uploads, profile photo, phone number, push devices, blocks and invite links are deleted within moments. Irreversible; ask the user to confirm first. Signing in again with the same phone creates a new, empty account.

meDelete from schema placeholder credentials

Request

curl -X DELETE https://api.ollacore.com/v1/directory/me \
  -H "Authorization: Bearer du_…"
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.

Response · 200

{
  "deleted": true
}
Response fields (1)
FieldTypeDescription
deleted required boolean Always true.
Errors · 401
StatusWhen
401Missing, expired or invalid credentials
Synthetic wire-shape illustration, not a captured result: a capture run against production cannot delete its fixture user. Irreversible. The session used here, and every other session of the user, stops authenticating at once; the erasure completes in the background.
POST /v1/directory/me/avatar/init #

Start a profile-photo upload (JPEG/PNG/WebP/GIF, ≤ 2 MiB)

Answers a presigned PUT for the bytes. PUT them, then call complete. Only image types (JPEG, PNG, WebP, GIF), at most 2 MiB; the bytes are sniffed at complete, so the declared type is a hint, not a promise. An upload never completed is deleted after an hour. Ten starts a minute per user.

meAvatarInit captured live

Request

curl -X POST https://api.ollacore.com/v1/directory/me/avatar/init \
  -H "Authorization: Bearer du_…" \
  -H "Content-Type: application/json" \
  -d '{"declared_mime":"image/jpeg","byte_size":160}'
{
  "declared_mime": "image/jpeg",
  "byte_size": 160
}
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
declared_mime required one of: image/jpeg, image/png, image/webp, image/gif The image type you will PUT.
byte_size required integer (int64) min 1 · max 2097152 Exact size of the bytes, at most 2 MiB.

Response · 200

{
  "expires_at": "2026-01-15T09:35:43Z",
  "max_bytes": 2097152,
  "upload_id": "3a7d0ead-8ffa-4194-b38e-2e5c0ce04738",
  "upload_url": "https://files.example.com/objects/…?X-Amz-Signature=…"
}
Response fields (4)
FieldTypeDescription
upload_id required string (uuid) Hand this to complete after the PUT.
upload_url required string (uri) Presigned PUT for the photo bytes. Send the raw bytes with the declared Content-Type.
expires_at required string (date-time) When the upload URL stops working.
max_bytes required integer (int64) The size cap, 2 MiB. Downscale on the device first.
Errors · 400 · 401 · 415 · 429
StatusWhen
400byte_size outside 1..=2 MiB.
401Missing, expired or invalid credentials
415declared_mime is not an image type a profile photo may be.
429Rate limit exceeded
Image types only (JPEG, PNG, WebP, GIF), at most 2 MiB; downscale on the device first. PUT the raw bytes to upload_url, then call complete.
POST /v1/directory/me/avatar/complete #

Make the uploaded bytes your photo — verified before it answers

Synchronous: the object is read back, sniffed, decoded and thumbnailed before this answers, so the answer is final. On success the profile comes back with its new avatar_version, and the previous photo is deleted. Bytes that are not a decodable image are refused with 415 and the object is removed.

meAvatarComplete captured live

Request

curl -X POST https://api.ollacore.com/v1/directory/me/avatar/complete \
  -H "Authorization: Bearer du_…" \
  -H "Content-Type: application/json" \
  -d '{"upload_id":"3a7d0ead-8ffa-4194-b38e-2e5c0ce04738"}'
{
  "upload_id": "3a7d0ead-8ffa-4194-b38e-2e5c0ce04738"
}
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
upload_id required string (uuid) From init.

Response · 200

{
  "id": "4a98e767-a426-47e9-89a0-c499b54d2904",
  "phone": "+15550001111",
  "display_name": "Alice Nakamura",
  "about": "Ships things. Slow to reply at weekends.",
  "avatar_version": "2026-01-15T09:30:44Z"
}
Response fields (5)
FieldTypeDescription
id required string (uuid) The user's id, which is also their chat principal_id.
phone required string The user's verified phone number, E.164.
display_name required string, nullable The user's display name, or null if not set.
about required string, nullable max length 256 The profile line, up to 256 characters; null when not set.
avatar_version required string (date-time), nullable When the current profile photo was set; null when there is none. The value to cache the picture by: fetch GET /v1/directory/users/{user_id}/avatar once per version (its ETag is this value).
Errors · 400 · 401 · 409 · 415
StatusWhen
400The uploaded object is larger than 2 MiB (it is removed).
401Missing, expired or invalid credentials
409Nothing has been uploaded for this upload_id yet.
415The bytes are not an image, or could not be decoded (the object is removed).
Synchronous: the bytes are read back, sniffed, decoded and thumbnailed before this answers (415 if they are not an image). avatar_version is the value every list carries and the value to cache the picture by.
DELETE /v1/directory/me/avatar #

Remove your profile photo

The photo and its thumbnail are deleted from storage and avatar_version becomes null. Idempotent.

meAvatarDelete captured live

Request

curl -X DELETE https://api.ollacore.com/v1/directory/me/avatar \
  -H "Authorization: Bearer du_…"
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.

Response · 200

{
  "id": "4a98e767-a426-47e9-89a0-c499b54d2904",
  "phone": "+15550001111",
  "display_name": "Alice Nakamura",
  "about": "Ships things. Slow to reply at weekends.",
  "avatar_version": null
}
Response fields (5)
FieldTypeDescription
id required string (uuid) The user's id, which is also their chat principal_id.
phone required string The user's verified phone number, E.164.
display_name required string, nullable The user's display name, or null if not set.
about required string, nullable max length 256 The profile line, up to 256 characters; null when not set.
avatar_version required string (date-time), nullable When the current profile photo was set; null when there is none. The value to cache the picture by: fetch GET /v1/directory/users/{user_id}/avatar once per version (its ETag is this value).
Errors · 401
StatusWhen
401Missing, expired or invalid credentials
Idempotent: the photo and its thumbnail are deleted and avatar_version becomes null.
GET /v1/directory/users/{user_id}/avatar #

A user's photo: 302 to the bytes, ETag = avatar_version

A redirect to a short-lived presigned URL for the picture. ETag is the photo's version, exactly the avatar_version the lists show, so a client that caches the bytes by version sends it in If-None-Match and gets 304 instead of a redirect. 404 when the user has no photo, is not one of your tenant's, or when either of you has blocked the other. The redirect itself must not be cached (Cache-Control: private, no-store); cache the bytes by version.

userAvatar captured live

Request

curl -X GET https://api.ollacore.com/v1/directory/users/4a98e767-a426-47e9-89a0-c499b54d2904/avatar?size=thumb \
  -H "Authorization: Bearer du_…"
ParameterTypeDescription
user_id path required string (uuid) A user of your tenant (yourself included).
size query one of: thumb, full default "thumb" thumb: a JPEG no larger than 256 px on its longest side. full: the bytes as uploaded.
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 · 302

Empty body.

Errors · 400 · 401 · 404
StatusWhen
400size is neither thumb nor full.
401Missing, expired or invalid credentials
404No such room, message, member or attachment
Follow Location for the bytes — a short-lived presigned URL, not shown here. ETag is the avatar_version the lists carry; send it as If-None-Match to get 304 instead of a redirect. Cache the bytes by version, never the redirect.

Contacts & conversations #

POST /v1/directory/contacts/lookup #

Which phone numbers are registered (≤500)

Send up to 500 numbers per call. More is a 400. Matching is exact on the E.164 form (+ and 8 to 15 digits), and anything else is silently dropped, so normalize numbers on the device first. Only users of the caller's own tenant are matched. Each match comes back with user_id, phone, display_name, about and avatar_version, in no particular order. Each user may call this 20 times a minute. Past that it answers 429 rate_limited with Retry-After.

contactsLookup captured live

Request

curl -X POST https://api.ollacore.com/v1/directory/contacts/lookup \
  -H "Authorization: Bearer du_…" \
  -H "Content-Type: application/json" \
  -d '{"phones":["+15550002222","+15550009999"]}'
{
  "phones": [
    "+15550002222",
    "+15550009999"
  ]
}
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
phones required array of string max 500 items Up to 500 phone numbers, E.164. Invalid numbers are dropped rather than failing the batch.

Response · 200

{
  "contacts": [
    {
      "about": null,
      "avatar_version": null,
      "display_name": "malavika rajesh",
      "phone": "+15550002222",
      "user_id": "0b925a2d-944a-4f1d-a761-56d24db90341"
    }
  ]
}
Response fields (6)
FieldTypeDescription
contacts array of object The numbers that belong to registered users in your tenant. Other tenants' users are never disclosed.
contacts[].user_id string (uuid) The user's id, which is also their chat principal_id.
contacts[].phone string The matched number, E.164.
contacts[].display_name string, nullable The user's display name, or null if not set.
contacts[].about string, nullable max length 256 The profile line, up to 256 characters; null when not set.
contacts[].avatar_version string (date-time), nullable When their current profile photo was set; null when there is none. The value to cache the picture by: fetch GET /v1/directory/users/{user_id}/avatar once per version (its ETag is this value).
Errors · 400 · 401 · 429
StatusWhen
400More than 500 numbers.
401Missing, expired or invalid credentials
429Over 20 lookups a minute by this user.
POST /v1/directory/conversations/direct #

Open a 1:1 (idempotent; blocked across a block)

Refused with blocked (403) when either of you has blocked the other.

conversationDirect captured live

Request

curl -X POST https://api.ollacore.com/v1/directory/conversations/direct \
  -H "Authorization: Bearer du_…" \
  -H "Content-Type: application/json" \
  -d '{"peer_user_id":"0b925a2d-944a-4f1d-a761-56d24db90341"}'
{
  "peer_user_id": "0b925a2d-944a-4f1d-a761-56d24db90341"
}
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
peer_user_id required string (uuid) The other person's directory user id, in your tenant.

Response · 200

{
  "room_id": "eb0a0da1-5344-483f-8b18-b95bfc4d1e36",
  "kind": "direct"
}
Response fields (2)
FieldTypeDescription
room_id string (uuid) The 1:1 room. The same pair always gets the same room.
kind string Always direct.
Errors · 400 · 401 · 403 · 404
StatusWhen
400Invalid (e.g. self)
401Missing, expired or invalid credentials
403The direct conversation is frozen by a block (code blocked): one of the two people has blocked the other. Neither can send, react, type or start a call in it, and neither can open a new direct conversation with the other, until the block is lifted. History stays readable. Both sides get this answer; it does not say who blocked whom.
404No such room, message, member or attachment
POST /v1/directory/conversations/group #

Create a group

Anyone you named who has blocked you is left out and listed in not_added; the group is created with the rest. If everyone you named has blocked you, nothing is created: blocked (403).

conversationGroup captured live

Request

curl -X POST https://api.ollacore.com/v1/directory/conversations/group \
  -H "Authorization: Bearer du_…" \
  -H "Content-Type: application/json" \
  -d '{"member_user_ids":["0b925a2d-944a-4f1d-a761-56d24db90341"],"name":"Weekend plans"}'
{
  "member_user_ids": [
    "0b925a2d-944a-4f1d-a761-56d24db90341"
  ],
  "name": "Weekend plans"
}
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
member_user_ids required array of string (uuid) max 4999 items The other members' directory user ids, all in your tenant. You are added as owner. A group holds at most 5,000 members including you; anything larger is 400 before any lookup.
name string, nullable max length 128 Optional group name, up to 128 characters.

Response · 200

{
  "room_id": "60f99e3b-2084-4bb5-9adb-527ee9907673",
  "kind": "group",
  "name": "Weekend plans"
}
Response fields (6)
FieldTypeDescription
room_id string (uuid) The new group's room. Each call creates a new group.
kind string Always group.
name string, nullable The group's name, or null.
not_added array of NotAdded People you named who were not added, with the reason — only blocked today: they have blocked you. Absent or empty when everyone was added.
not_added[].user_id required if its parent is sent string (uuid) Someone you named who was not added.
not_added[].reason required if its parent is sent one of: blocked blocked: they have blocked you.
Errors · 401 · 403 · 404
StatusWhen
401Missing, expired or invalid credentials
403The direct conversation is frozen by a block (code blocked): one of the two people has blocked the other. Neither can send, react, type or start a call in it, and neither can open a new direct conversation with the other, until the block is lifted. History stays readable. Both sides get this answer; it does not say who blocked whom.
404No such room, message, member or attachment
GET /v1/directory/conversations #

List conversations

Lists the direct and group conversations the caller is an active member of, newest-created first, in one unpaginated response. Rooms your server created with an ak_ key do not appear here, even if the user is a member. For a chat list with the last message and unread counts, use GET /v1/directory/inbox.

conversationsList captured live

Request

curl -X GET https://api.ollacore.com/v1/directory/conversations \
  -H "Authorization: Bearer du_…"
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.

Response · 200

{
  "conversations": [
    {
      "created_at": "2026-01-15T09:30:44Z",
      "description": null,
      "icon_attachment_id": null,
      "kind": "group",
      "name": "Weekend plans",
      "room_id": "60f99e3b-2084-4bb5-9adb-527ee9907673"
    },
    {
      "created_at": "2026-01-15T08:20:10Z",
      "description": "Saturday hike, then dinner at eight.",
      "icon_attachment_id": null,
      "kind": "group",
      "name": "Weekend plans",
      "room_id": "1cac5f45-0101-442b-a6cf-bdc9136a4c55"
    },
    {
      "created_at": "2026-01-15T05:11:50Z",
      "description": "Saturday hike, then dinner at eight.",
      "icon_attachment_id": null,
      "kind": "group",
      "name": "Weekend plans",
      "room_id": "8ef5c24c-eca5-4c18-b1c3-bb4e86b24632"
    },
    {
      "created_at": "2026-01-14T05:48:54Z",
      "description": "Saturday hike, then dinner at eight.",
      "icon_attachment_id": null,
      "kind": "group",
      "name": "Weekend plans",
      "room_id": "94a7993d-f741-47dc-b524-dc63a8986899"
    },
    {
      "created_at": "2026-01-14T05:44:03Z",
      "description": "Saturday hike, then dinner at eight.",
      "icon_attachment_id": null,
      "kind": "group",
      "name": "Weekend plans",
      "room_id": "b7740fb5-02e4-4095-980c-abd1f098ae37"
    },
    {
      "created_at": "2026-01-07T02:12:15Z",
      "description": "Saturday hike, then dinner at eight.",
      "icon_attachment_id": null,
      "kind": "group",
      "name": "Weekend plans",
      "room_id": "aca2ceb1-e310-4e14-8882-d8e966b7d712"
    },
    {
      "created_at": "2026-01-07T02:10:38Z",
      "description": "Saturday hike, then dinner at eight.",
      "icon_attachment_id": null,
      "kind": "group",
      "name": "Weekend plans",
      "room_id": "b5e03d35-ea1d-403e-9a87-02e614aee58a"
    },
    {
      "created_at": "2026-01-04T06:25:11Z",
      "description": null,
      "icon_attachment_id": null,
      "kind": "group",
      "name": "testing me",
      "room_id": "61f13be9-cf5b-4e1c-9d33-e39872d872c7"
    },
    {
      "created_at": "2026-01-02T08:34:58Z",
      "description": null,
      "icon_attachment_id": null,
      "kind": "group",
      "name": "test group",
      "room_id": "6eb71e13-fe76-4e5a-a9c2-6c197c5f1400"
    },
    {
      "created_at": "2026-01-02T06:08:08Z",
      "description": null,
      "icon_attachment_id": null,
      "kind": "group",
      "name": "Live Verify Group 1791189343212",
      "room_id": "9e052209-79c3-487b-9a5a-ad43bbceb481"
    },
    {
      "created_at": "2025-12-26T17:24:52Z",
      "description": null,
      "icon_attachment_id": null,
      "kind": "direct",
      "name": null,
      "room_id": "c8366e3b-a43d-4e20-91ef-567e1287e110"
    },
    {
      "created_at": "2025-12-26T06:22:28Z",
      "description": null,
      "icon_attachment_id": null,
      "kind": "group",
      "name": "Backend Test Grp",
      "room_id": "36bf8624-46f1-40a0-9ead-1b6ad5a1163f"
    },
    {
      "created_at": "2025-12-26T05:45:00Z",
      "description": null,
      "icon_attachment_id": null,
      "kind": "group",
      "name": "sdfghjklllkjhgfdcvbhjklkmnbv",
      "room_id": "d4ac2357-7773-4840-b9ac-f4ebc7978267"
    },
    {
      "created_at": "2025-12-23T08:09:04Z",
      "description": null,
      "icon_attachment_id": null,
      "kind": "group",
      "name": "Self Media Group",
      "room_id": "fadcd831-d379-493d-be50-222ecbf697e9"
    },
    {
      "created_at": "2025-12-21T08:10:13Z",
      "description": null,
      "icon_attachment_id": null,
      "kind": "group",
      "name": "Group call",
      "room_id": "e23fb102-5878-4b14-a637-a6878a202935"
    },
    {
      "created_at": "2025-12-21T08:02:17Z",
      "description": null,
      "icon_attachment_id": null,
      "kind": "group",
      "name": "TestGroup-1790159400517",
      "room_id": "4d55f4fa-516c-4ddc-8e08-18719784882d"
    },
    {
      "created_at": "2025-12-09T06:34:28Z",
      "description": null,
      "icon_attachment_id": null,
      "kind": "group",
      "name": "Weekend plans",
      "room_id": "dc18e5c2-e264-4ead-be79-1f78faf15a54"
    },
    {
      "created_at": "2025-12-09T04:29:48Z",
      "description": null,
      "icon_attachment_id": null,
      "kind": "group",
      "name": "QA Test Group",
      "room_id": "5e38f7cd-e6f7-428e-b3ec-1d57d6fc4025"
    },
    {
      "created_at": "2025-12-09T04:28:31Z",
      "description": null,
      "icon_attachment_id": null,
      "kind": "group",
      "name": "QA Test Group",
      "room_id": "d7f66e06-79d0-4cd8-9f13-6795b4ac692f"
    },
    {
      "created_at": "2025-12-08T15:56:18Z",
      "description": null,
      "icon_attachment_id": null,
      "kind": "group",
      "name": "Weekend plans",
      "room_id": "6c0db0aa-7c88-410c-bcf1-e0f17cbe0cdf"
    },
    {
      "created_at": "2025-12-08T15:53:16Z",
      "description": null,
      "icon_attachment_id": null,
      "kind": "group",
      "name": "Weekend plans",
      "room_id": "ee286c5c-7607-453f-8165-8a40f3e19214"
    },
    {
      "created_at": "2025-12-08T15:43:20Z",
      "description": null,
      "icon_attachment_id": null,
      "kind": "group",
      "name": "Weekend plans",
      "room_id": "a970b461-8d3c-4c2e-88c2-77beee06a75f"
    },
    {
      "created_at": "2025-12-08T15:40:48Z",
      "description": null,
      "icon_attachment_id": null,
      "kind": "group",
      "name": "Weekend plans",
      "room_id": "07191895-7185-4474-b1fd-e804fc432f1a"
    },
    {
      "created_at": "2025-12-08T15:38:48Z",
      "description": null,
      "icon_attachment_id": null,
      "kind": "group",
      "name": "Weekend plans",
      "room_id": "f9f4c84c-1028-46de-905b-214423a81304"
    },
    {
      "created_at": "2025-12-08T15:38:48Z",
      "description": null,
      "icon_attachment_id": null,
      "kind": "direct",
      "name": null,
      "room_id": "eb0a0da1-5344-483f-8b18-b95bfc4d1e36"
    }
  ]
}
Response fields (7)
FieldTypeDescription
conversations array of object The caller's directory conversations.
conversations[].room_id string (uuid) The conversation's room. Mint a token for it to chat or call.
conversations[].kind one of: direct, group direct (two people) or group.
conversations[].name string, nullable The group's name; null for a direct conversation.
conversations[].description string, nullable max length 1024 The group's description, up to 1024 characters; null when there is none.
conversations[].icon_attachment_id string (uuid), nullable The group's icon: an image attachment of the group's own room, or null. Fetch it like any picture posted there — GET /v1/rooms/{room_id}/attachments/{attachment_id}/download with a room token — and cache it by id; a changed icon is a new id.
conversations[].created_at string (date-time) When the conversation was created.
Errors · 401
StatusWhen
401Missing, expired or invalid credentials
GET /v1/directory/conversations/{room_id}/token #

Mint a room token

Works for any room in the caller's tenant where the caller is an active member. The token carries the caller's own role in that room. It lasts 10 minutes, so mint a new one before expires_at. Pass device_id to stamp it into the token's did claim. A closed room answers 409 conflict. A room the caller is not an active member of answers 403, and a room outside the tenant answers 404.

conversationToken captured live

Request

curl -X GET https://api.ollacore.com/v1/directory/conversations/eb0a0da1-5344-483f-8b18-b95bfc4d1e36/token?device_id=dev-alice-phone \
  -H "Authorization: Bearer du_…"
ParameterTypeDescription
room_id path required string (uuid) The conversation's room. The caller must be a member (403 otherwise); a room in another tenant is 404.
device_id query string Identifies this device. Needed for the E2EE endpoints, and to join a call from two devices at once.
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

{
  "access_token": "eyJhbGciOiJFZERTQSIsInR5cCI6IkpXVCIsImtpZCI6ImEwM2I1NjQ4MjM3YjlmZWIifQ.eyJpc3MiOiJjaGF0Ym94LWJhY2tlbmQiLCJhdWQiOiJjaGF0Ym94LXJlYWx0aW1lIiwic3ViIjoiNGE5OGU3NjctYTQyNi00N2U5LTg5YTAtYzQ5OWI1NGQyOTA0IiwidGlkIjoiYWNjdF9lZmFmN2YwMWEzNzM0NTNiYjEwMDQ2YzcyYzNiYjdiYiIsInJpZCI6ImViMGEwZGExLTUzNDQtNDgzZi04YjE4LWI5NWJmYzRkMWUzNiIsInJvbGUiOiJtZW1iZXIiLCJwZXJtaXNzaW9ucyI6WyJjaGF0OnNlbmQiLCJjaGF0Omhpc3RvcnkiLCJhdHRhY2htZW50OnVwbG9hZCIsImF0dGFjaG1lbnQ6ZG93bmxvYWQiLCJydGM6cHVibGlzaCIsInJ0YzpzdWJzY3JpYmUiXSwiZGlkIjoiZGV2LWFsaWNlLXBob25lIiwianRpIjoiY2MxYjhmMDEtNmVkMS00OWZmLWIyNGItZjFkMzA3NjAyYzc1IiwiaWF0IjoxNzkyMzI0NzA4LCJuYmYiOjE3OTIzMjQ3MDgsImV4cCI6MTc5MjMyNTMwOH0.SIGNATURE_REMOVED",
  "expires_at": "2026-01-15T09:40:45Z",
  "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 · 401 · 403 · 404 · 409
StatusWhen
401Missing, expired or invalid credentials
403Authenticated, but the token is not scoped to this room or principal
404No such room, message, member or attachment
409The room is closed: no new tokens are minted for it.
GET /v1/directory/inbox #

Chat list: peer, last message, unread, muted_until

Conversations are ordered newest first by the time of their latest message, or by their creation time if they have no messages. limit is clamped to 1–200 rather than rejected. There is no cursor: the response holds only the first limit conversations. unread_count counts messages after the caller's read cursor, excluding the caller's own messages and deleted ones. last_message is null for a conversation with no messages. A deleted latest message shows as kind deleted, a text preview is cut at 120 characters, and media messages get a fixed label. Direct conversations carry the other member as peer. muted_until is null unless the caller's mute is still in force.

listInbox captured live

Request

curl -X GET https://api.ollacore.com/v1/directory/inbox?limit=30 \
  -H "Authorization: Bearer du_…"
ParameterTypeDescription
limit query integer default 50 · min 1 · max 200 Maximum conversations, 1–200. Defaults to 50.
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

{
  "conversations": [
    {
      "description": "Saturday hike, then dinner at eight.",
      "icon_attachment_id": null,
      "kind": "group",
      "last_message": {
        "created_at": "2026-01-15T09:30:45Z",
        "event_seq": 5208,
        "kind": "system",
        "preview": "system",
        "sender_id": "4a98e767-a426-47e9-89a0-c499b54d2904"
      },
      "muted_until": null,
      "name": "Weekend plans",
      "peer": null,
      "room_id": "60f99e3b-2084-4bb5-9adb-527ee9907673",
      "unread_count": 0
    },
    {
      "description": "Saturday hike, then dinner at eight.",
      "icon_attachment_id": null,
      "kind": "group",
      "last_message": {
        "created_at": "2026-01-15T08:20:11Z",
        "event_seq": 5196,
        "kind": "system",
        "preview": "system",
        "sender_id": "4a98e767-a426-47e9-89a0-c499b54d2904"
      },
      "muted_until": null,
      "name": "Weekend plans",
      "peer": null,
      "room_id": "1cac5f45-0101-442b-a6cf-bdc9136a4c55",
      "unread_count": 0
    },
    {
      "description": "Saturday hike, then dinner at eight.",
      "icon_attachment_id": null,
      "kind": "group",
      "last_message": {
        "created_at": "2026-01-15T05:11:51Z",
        "event_seq": 5184,
        "kind": "system",
        "preview": "system",
        "sender_id": "4a98e767-a426-47e9-89a0-c499b54d2904"
      },
      "muted_until": null,
      "name": "Weekend plans",
      "peer": null,
      "room_id": "8ef5c24c-eca5-4c18-b1c3-bb4e86b24632",
      "unread_count": 0
    },
    {
      "description": "Saturday hike, then dinner at eight.",
      "icon_attachment_id": null,
      "kind": "group",
      "last_message": {
        "created_at": "2026-01-14T05:48:55Z",
        "event_seq": 5131,
        "kind": "system",
        "preview": "system",
        "sender_id": "4a98e767-a426-47e9-89a0-c499b54d2904"
      },
      "muted_until": null,
      "name": "Weekend plans",
      "peer": null,
      "room_id": "94a7993d-f741-47dc-b524-dc63a8986899",
      "unread_count": 0
    },
    {
      "description": "Saturday hike, then dinner at eight.",
      "icon_attachment_id": null,
      "kind": "group",
      "last_message": {
        "created_at": "2026-01-14T05:44:04Z",
        "event_seq": 5122,
        "kind": "system",
        "preview": "system",
        "sender_id": "4a98e767-a426-47e9-89a0-c499b54d2904"
      },
      "muted_until": null,
      "name": "Weekend plans",
      "peer": null,
      "room_id": "b7740fb5-02e4-4095-980c-abd1f098ae37",
      "unread_count": 0
    },
    {
      "description": null,
      "icon_attachment_id": null,
      "kind": "direct",
      "last_message": {
        "created_at": "2026-01-09T06:55:18Z",
        "event_seq": 4842,
        "kind": "text",
        "preview": "",
        "sender_id": "4a98e767-a426-47e9-89a0-c499b54d2904"
      },
      "muted_until": "2026-01-22T09:30:47Z",
      "name": null,
      "peer": {
        "avatar_version": null,
        "display_name": "malavika rajesh",
        "phone": "+15550002222",
        "user_id": "0b925a2d-944a-4f1d-a761-56d24db90341"
      },
      "room_id": "eb0a0da1-5344-483f-8b18-b95bfc4d1e36",
      "unread_count": 5
    },
    {
      "description": null,
      "icon_attachment_id": null,
      "kind": "group",
      "last_message": {
        "created_at": "2026-01-08T11:56:21Z",
        "event_seq": 4733,
        "kind": "text",
        "preview": "cna you see this",
        "sender_id": "0b925a2d-944a-4f1d-a761-56d24db90341"
      },
      "muted_until": null,
      "name": "test group",
      "peer": null,
      "room_id": "6eb71e13-fe76-4e5a-a9c2-6c197c5f1400",
      "unread_count": 0
    },
    {
      "description": "Saturday hike, then dinner at eight.",
      "icon_attachment_id": null,
      "kind": "group",
      "last_message": {
        "created_at": "2026-01-07T02:12:16Z",
        "event_seq": 4689,
        "kind": "system",
        "preview": "system",
        "sender_id": "4a98e767-a426-47e9-89a0-c499b54d2904"
      },
      "muted_until": null,
      "name": "Weekend plans",
      "peer": null,
      "room_id": "aca2ceb1-e310-4e14-8882-d8e966b7d712",
      "unread_count": 0
    },
    {
      "description": "Saturday hike, then dinner at eight.",
      "icon_attachment_id": null,
      "kind": "group",
      "last_message": {
        "created_at": "2026-01-07T02:10:39Z",
        "event_seq": 4680,
        "kind": "system",
        "preview": "system",
        "sender_id": "4a98e767-a426-47e9-89a0-c499b54d2904"
      },
      "muted_until": null,
      "name": "Weekend plans",
      "peer": null,
      "room_id": "b5e03d35-ea1d-403e-9a87-02e614aee58a",
      "unread_count": 0
    },
    {
      "description": null,
      "icon_attachment_id": null,
      "kind": "group",
      "last_message": {
        "created_at": "2026-01-04T08:56:33Z",
        "event_seq": 4373,
        "kind": "file",
        "preview": "📎 File",
        "sender_id": "0b925a2d-944a-4f1d-a761-56d24db90341"
      },
      "muted_until": null,
      "name": "testing me",
      "peer": null,
      "room_id": "61f13be9-cf5b-4e1c-9d33-e39872d872c7",
      "unread_count": 0
    },
    {
      "description": null,
      "icon_attachment_id": null,
      "kind": "group",
      "last_message": {
        "created_at": "2026-01-03T07:42:03Z",
        "event_seq": 4270,
        "kind": "text",
        "preview": "QA VERIFY 4",
        "sender_id": "4a98e767-a426-47e9-89a0-c499b54d2904"
      },
      "muted_until": null,
      "name": "Self Media Group",
      "peer": null,
      "room_id": "fadcd831-d379-493d-be50-222ecbf697e9",
      "unread_count": 0
    },
    {
      "description": null,
      "icon_attachment_id": null,
      "kind": "group",
      "last_message": {
        "created_at": "2026-01-02T08:38:19Z",
        "event_seq": 4072,
        "kind": "text",
        "preview": "hyy",
        "sender_id": "4a98e767-a426-47e9-89a0-c499b54d2904"
      },
      "muted_until": null,
      "name": "Live Verify Group 1791189343212",
      "peer": null,
      "room_id": "9e052209-79c3-487b-9a5a-ad43bbceb481",
      "unread_count": 0
    },
    {
      "description": null,
      "icon_attachment_id": null,
      "kind": "direct",
      "last_message": {
        "created_at": "2025-12-26T17:24:59Z",
        "event_seq": 3670,
        "kind": "text",
        "preview": "hi",
        "sender_id": "4a98e767-a426-47e9-89a0-c499b54d2904"
      },
      "muted_until": null,
      "name": null,
      "peer": {
        "avatar_version": null,
        "display_name": null,
        "phone": "+15550003333",
        "user_id": "9b2f6052-edf8-424e-9c1a-ecd90a1607e4"
      },
      "room_id": "c8366e3b-a43d-4e20-91ef-567e1287e110",
      "unread_count": 0
    },
    {
      "description": null,
      "icon_attachment_id": null,
      "kind": "group",
      "last_message": {
        "created_at": "2025-12-26T15:02:18Z",
        "event_seq": 3657,
        "kind": "deleted",
        "preview": "Message deleted",
        "sender_id": "4a98e767-a426-47e9-89a0-c499b54d2904"
      },
      "muted_until": null,
      "name": "sdfghjklllkjhgfdcvbhjklkmnbv",
      "peer": null,
      "room_id": "d4ac2357-7773-4840-b9ac-f4ebc7978267",
      "unread_count": 0
    },
    {
      "description": null,
      "icon_attachment_id": null,
      "kind": "group",
      "last_message": null,
      "muted_until": null,
      "name": "Backend Test Grp",
      "peer": null,
      "room_id": "36bf8624-46f1-40a0-9ead-1b6ad5a1163f",
      "unread_count": 0
    },
    {
      "description": null,
      "icon_attachment_id": null,
      "kind": "group",
      "last_message": {
        "created_at": "2025-12-21T09:19:52Z",
        "event_seq": 3293,
        "kind": "text",
        "preview": "ClearWatermarkProbe",
        "sender_id": "0b925a2d-944a-4f1d-a761-56d24db90341"
      },
      "muted_until": null,
      "name": "Group call",
      "peer": null,
      "room_id": "e23fb102-5878-4b14-a637-a6878a202935",
      "unread_count": 0
    },
    {
      "description": null,
      "icon_attachment_id": null,
      "kind": "group",
      "last_message": null,
      "muted_until": null,
      "name": "TestGroup-1790159400517",
      "peer": null,
      "room_id": "4d55f4fa-516c-4ddc-8e08-18719784882d",
      "unread_count": 0
    },
    {
      "description": null,
      "icon_attachment_id": null,
      "kind": "group",
      "last_message": null,
      "muted_until": null,
      "name": "Weekend plans",
      "peer": null,
      "room_id": "dc18e5c2-e264-4ead-be79-1f78faf15a54",
      "unread_count": 0
    },
    {
      "description": null,
      "icon_attachment_id": null,
      "kind": "group",
      "last_message": null,
      "muted_until": null,
      "name": "QA Test Group",
      "peer": null,
      "room_id": "5e38f7cd-e6f7-428e-b3ec-1d57d6fc4025",
      "unread_count": 0
    },
    {
      "description": null,
      "icon_attachment_id": null,
      "kind": "group",
      "last_message": null,
      "muted_until": null,
      "name": "QA Test Group",
      "peer": null,
      "room_id": "d7f66e06-79d0-4cd8-9f13-6795b4ac692f",
      "unread_count": 0
    },
    {
      "description": null,
      "icon_attachment_id": null,
      "kind": "group",
      "last_message": null,
      "muted_until": null,
      "name": "Weekend plans",
      "peer": null,
      "room_id": "6c0db0aa-7c88-410c-bcf1-e0f17cbe0cdf",
      "unread_count": 0
    },
    {
      "description": null,
      "icon_attachment_id": null,
      "kind": "group",
      "last_message": null,
      "muted_until": null,
      "name": "Weekend plans",
      "peer": null,
      "room_id": "ee286c5c-7607-453f-8165-8a40f3e19214",
      "unread_count": 0
    },
    {
      "description": null,
      "icon_attachment_id": null,
      "kind": "group",
      "last_message": null,
      "muted_until": null,
      "name": "Weekend plans",
      "peer": null,
      "room_id": "a970b461-8d3c-4c2e-88c2-77beee06a75f",
      "unread_count": 0
    },
    {
      "description": null,
      "icon_attachment_id": null,
      "kind": "group",
      "last_message": null,
      "muted_until": null,
      "name": "Weekend plans",
      "peer": null,
      "room_id": "07191895-7185-4474-b1fd-e804fc432f1a",
      "unread_count": 0
    },
    {
      "description": null,
      "icon_attachment_id": null,
      "kind": "group",
      "last_message": null,
      "muted_until": null,
      "name": "Weekend plans",
      "peer": null,
      "room_id": "f9f4c84c-1028-46de-905b-214423a81304",
      "unread_count": 0
    }
  ]
}
Response fields (19)
FieldTypeDescription
conversations array of object The caller's conversations as a chat list, most recent activity first.
conversations[].room_id string (uuid) The conversation's room.
conversations[].kind one of: direct, group direct or group.
conversations[].name string, nullable The group's name; null for a direct conversation (use peer).
conversations[].description string, nullable max length 1024 The group's description, up to 1024 characters; null when there is none.
conversations[].icon_attachment_id string (uuid), nullable The group's icon: an image attachment of the group's own room, or null. Fetch it like any picture posted there — GET /v1/rooms/{room_id}/attachments/{attachment_id}/download with a room token — and cache it by id; a changed icon is a new id.
conversations[].peer object, nullable For a direct conversation, the other person; null for a group.
conversations[].peer.user_id string (uuid) The other person's user id.
conversations[].peer.display_name string, nullable Their display name, or null.
conversations[].peer.phone string Their phone number, E.164.
conversations[].peer.avatar_version string (date-time), nullable When the other person's current profile photo was set; null when there is none. The value to cache the picture by: fetch GET /v1/directory/users/{user_id}/avatar once per version (its ETag is this value).
conversations[].unread_count integer Messages after the caller's read cursor.
conversations[].muted_until string (date-time), nullable Until when you have muted this conversation; null when not muted (a lapsed mute reads as null). 9999-12-31T00:00:00Z stands for "until unmuted". Private: only your own mute is ever shown.
conversations[].last_message object, nullable The newest message, for the preview line; null if the conversation has none.
conversations[].last_message.event_seq integer Its position in the room.
conversations[].last_message.sender_id string, nullable Who sent it.
conversations[].last_message.kind string Its kind.
conversations[].last_message.preview string A short label for the chat list: text up to 120 characters, or a label such as "📷 Photo" for media and "Message deleted" for a deleted message.
conversations[].last_message.created_at string (date-time), nullable When it was sent.
Errors · 401
StatusWhen
401Missing, expired or invalid credentials

Group management #

GET /v1/directory/conversations/{room_id} #

A conversation as a member sees it: name, description, icon, size

Any active member may ask, of a group or a direct conversation: the group's name, description and icon, how many are in it, and when it began.

conversationGet captured live

Request

curl -X GET https://api.ollacore.com/v1/directory/conversations/60f99e3b-2084-4bb5-9adb-527ee9907673 \
  -H "Authorization: Bearer du_…"
ParameterTypeDescription
room_id path required string (uuid) The conversation. 404 if it is not a directory conversation of your tenant; 403 if you are not a member.
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": "60f99e3b-2084-4bb5-9adb-527ee9907673",
  "kind": "group",
  "name": "Weekend plans",
  "description": null,
  "icon_attachment_id": null,
  "member_count": 2,
  "created_at": "2026-01-15T09:30:44Z"
}
Response fields (7)
FieldTypeDescription
room_id required string (uuid) The conversation's room.
kind required one of: direct, group direct or group.
name required string, nullable The group's name; null for a direct conversation or an unnamed group.
description required string, nullable max length 1024 The group's description, up to 1024 characters; null when there is none.
icon_attachment_id required string (uuid), nullable The group's icon: an image attachment of the group's own room, or null. Fetch it like any picture posted there — GET /v1/rooms/{room_id}/attachments/{attachment_id}/download with a room token — and cache it by id; a changed icon is a new id.
member_count required integer Active members.
created_at required string (date-time) When the conversation was created.
Errors · 401 · 403 · 404
StatusWhen
401Missing, expired or invalid credentials
403Authenticated, but the token is not scoped to this room or principal
404No such room, message, member or attachment
PATCH /v1/directory/conversations/{room_id} #

Owner: rename a group, set its description or icon

Owner only. A field left out is untouched; null (or an empty string) clears it. The icon must be a verified, ready image attachment uploaded to this very group (400 otherwise). Each change is posted into the room as a system message — {"type": "group.renamed", "name": …, "by": <user id>}, {"type": "group.description_changed", "description": …, "by": …}, {"type": "group.icon_changed", "icon_attachment_id": …, "by": …} — so every member sees it live and it stays in the history. A direct conversation answers 400.

conversationRename captured live

Request

curl -X PATCH https://api.ollacore.com/v1/directory/conversations/60f99e3b-2084-4bb5-9adb-527ee9907673 \
  -H "Authorization: Bearer du_…" \
  -H "Content-Type: application/json" \
  -d '{"name":"Weekend plans","description":"Saturday hike, then dinner at eight."}'
{
  "name": "Weekend plans",
  "description": "Saturday hike, then dinner at eight."
}
ParameterTypeDescription
room_id path required string (uuid) The group. 404 if it is not a directory conversation of your tenant; 403 if you are not a member; 403 if you are not its owner.
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
name string, nullable max length 128 The new name, up to 128 characters; null or empty clears it.
description string, nullable max length 1024 The new description, up to 1024 characters; null or empty clears it.
icon_attachment_id string (uuid), nullable A ready image attachment of this group's room, or null to remove the icon.

Response · 200

{
  "room_id": "60f99e3b-2084-4bb5-9adb-527ee9907673",
  "kind": "group",
  "name": "Weekend plans",
  "description": "Saturday hike, then dinner at eight.",
  "icon_attachment_id": null,
  "member_count": 2,
  "created_at": "2026-01-15T09:30:44Z"
}
Response fields (7)
FieldTypeDescription
room_id required string (uuid) The conversation's room.
kind required one of: direct, group direct or group.
name required string, nullable The group's name; null for a direct conversation or an unnamed group.
description required string, nullable max length 1024 The group's description, up to 1024 characters; null when there is none.
icon_attachment_id required string (uuid), nullable The group's icon: an image attachment of the group's own room, or null. Fetch it like any picture posted there — GET /v1/rooms/{room_id}/attachments/{attachment_id}/download with a room token — and cache it by id; a changed icon is a new id.
member_count required integer Active members.
created_at required string (date-time) When the conversation was created.
Errors · 400 · 401 · 403 · 404
StatusWhen
400Nothing to update, a name over 128 or a description over 1024 characters, an icon_attachment_id that is not a ready image of this group, or a direct conversation.
401Missing, expired or invalid credentials
403Authenticated, but the token is not scoped to this room or principal
404No such room, message, member or attachment
Owner only. Each change is posted into the room as a system message (group.renamed, group.description_changed, group.icon_changed). The icon is a ready image attachment uploaded to this group first and named here as icon_attachment_id.
GET /v1/directory/conversations/{room_id}/members #

Members, owners first

Any active member may ask, of a group or a direct conversation. Owners first, then by seniority. A group holds at most 5,000 members, so the list is never paged.

conversationMembers captured live

Request

curl -X GET https://api.ollacore.com/v1/directory/conversations/60f99e3b-2084-4bb5-9adb-527ee9907673/members \
  -H "Authorization: Bearer du_…"
ParameterTypeDescription
room_id path required string (uuid) The conversation. 404 if it is not a directory conversation of your tenant; 403 if you are not a member.
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

{
  "members": [
    {
      "about": "Ships things. Slow to reply at weekends.",
      "avatar_version": "2026-01-15T09:30:44Z",
      "display_name": "Alice Nakamura",
      "joined_at": "2026-01-15T09:30:44Z",
      "phone": "+15550001111",
      "role": "owner",
      "user_id": "4a98e767-a426-47e9-89a0-c499b54d2904"
    },
    {
      "about": null,
      "avatar_version": null,
      "display_name": "malavika rajesh",
      "joined_at": "2026-01-15T09:30:44Z",
      "phone": "+15550002222",
      "role": "member",
      "user_id": "0b925a2d-944a-4f1d-a761-56d24db90341"
    }
  ]
}
Response fields (8)
FieldTypeDescription
members array of DirectoryGroupMember
members[].user_id required if its parent is sent string (uuid) The member's directory user id — also their principal id in the room.
members[].display_name string, nullable As set on their profile, or null.
members[].phone required if its parent is sent string E.164.
members[].about string, nullable max length 256 The profile line, up to 256 characters; null when not set.
members[].avatar_version string (date-time), nullable When their current profile photo was set; null when there is none. The value to cache the picture by: fetch GET /v1/directory/users/{user_id}/avatar once per version (its ETag is this value).
members[].role required if its parent is sent one of: owner, member, viewer owner manages the group; see the group-management operations.
members[].joined_at string (date-time), nullable When they first joined; a restored member keeps the original time.
Errors · 401 · 403 · 404
StatusWhen
401Missing, expired or invalid credentials
403Authenticated, but the token is not scoped to this room or principal
404No such room, message, member or attachment
POST /v1/directory/conversations/{room_id}/members #

Owner: add up to 256 members (409 once the room holds 5,000)

Owner only. Up to 256 ids per call, every one a directory user of your tenant (404 otherwise, and nothing is added). Your own id and duplicates are ignored. A previously removed member is restored. Each addition reaches connected clients as a durable member.added event. 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. Anyone you named who has blocked you is left out and listed in not_added; the rest are added, and a request that adds nobody for that reason is still a 200.

conversationAddMembers captured live

Request

curl -X POST https://api.ollacore.com/v1/directory/conversations/60f99e3b-2084-4bb5-9adb-527ee9907673/members \
  -H "Authorization: Bearer du_…" \
  -H "Content-Type: application/json" \
  -d '{"member_user_ids":["0b925a2d-944a-4f1d-a761-56d24db90341"]}'
{
  "member_user_ids": [
    "0b925a2d-944a-4f1d-a761-56d24db90341"
  ]
}
ParameterTypeDescription
room_id path required string (uuid) The group. 404 if it is not a directory conversation of your tenant; 403 if you are not its owner; 400 if it is a direct conversation.
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
member_user_ids required array of string (uuid) max 256 items Who to add, 1–256 directory user ids.

Response · 200

{
  "added": [
    "0b925a2d-944a-4f1d-a761-56d24db90341"
  ],
  "member_count": 2,
  "not_added": []
}
Response fields (5)
FieldTypeDescription
added array of string (uuid) The ids admitted by this call, in request order, without duplicates.
member_count integer Active members after the call.
not_added array of NotAdded People you named who were not added, with the reason — only blocked today: they have blocked you. Absent or empty when everyone was added.
not_added[].user_id required if its parent is sent string (uuid) Someone you named who was not added.
not_added[].reason required if its parent is sent one of: blocked blocked: they have blocked you.
Errors · 401 · 403 · 404 · 409
StatusWhen
401Missing, expired or invalid credentials
403Authenticated, but the token is not scoped to this room or principal
404No such room, message, member or attachment
409Conflicts with current state
Up to 256 ids per call; a previously removed member is restored. Anyone who has blocked you is left out and listed in not_added. 409 once the room holds 5,000.
PUT /v1/directory/conversations/{room_id}/members/{user_id} #

Owner: make a member an owner, or an owner a member

Owner only. A group may have any number of owners (there is no separate admin role: owners are the admins), each able to do everything an owner can. The last owner cannot be demoted — make someone else an owner first — which is the same rule leave keeps. Announced to the room as a durable member.updated event { "principal_id": <user id>, "role": … }, which webhooks may subscribe to.

conversationSetRole captured live

Request

curl -X PUT https://api.ollacore.com/v1/directory/conversations/60f99e3b-2084-4bb5-9adb-527ee9907673/members/0b925a2d-944a-4f1d-a761-56d24db90341 \
  -H "Authorization: Bearer du_…" \
  -H "Content-Type: application/json" \
  -d '{"role":"owner"}'
{
  "role": "owner"
}
ParameterTypeDescription
room_id path required string (uuid) The group. 404 if it is not a directory conversation of your tenant; 403 if you are not a member; 403 if you are not an owner.
user_id path required string (uuid) An active member of the group (404 otherwise); yourself included.
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
role required one of: owner, member The role to give.

Response · 200

{
  "role": "owner",
  "user_id": "0b925a2d-944a-4f1d-a761-56d24db90341"
}
Response fields (2)
FieldTypeDescription
user_id required string (uuid)
role required one of: owner, member
Errors · 400 · 401 · 403 · 404 · 409
StatusWhen
400A role other than owner or member, or a direct conversation.
401Missing, expired or invalid credentials
403Authenticated, but the token is not scoped to this room or principal
404No such room, message, member or attachment
409The member is the only owner.
Owners are the admins and a group may have any number; the last one cannot be demoted. Announced to the room as member.updated.
DELETE /v1/directory/conversations/{room_id}/members/{user_id} #

Owner: remove a member

Owner only. Removing yourself is leave, which also settles ownership; asking to remove yourself here is 400. The removal reaches connected clients as a durable member.removed event.

conversationRemoveMember captured live

Request

curl -X DELETE https://api.ollacore.com/v1/directory/conversations/60f99e3b-2084-4bb5-9adb-527ee9907673/members/0b925a2d-944a-4f1d-a761-56d24db90341 \
  -H "Authorization: Bearer du_…"
ParameterTypeDescription
room_id path required string (uuid) The group. 404 if it is not a directory conversation of your tenant; 403 if you are not its owner.
user_id path required string (uuid) The member to remove. 404 if they are not an active member.
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 boolean
Errors · 401 · 403 · 404
StatusWhen
401Missing, expired or invalid credentials
403Authenticated, but the token is not scoped to this room or principal
404No such room, message, member or attachment
POST /v1/directory/conversations/{room_id}/leave #

Leave a group; a lone owner's seat passes to the longest-standing member

Any active member. If you were the only owner, the longest-standing remaining member becomes owner — announced as member.added with role owner. When nobody remains the room is closed (history stays readable to nobody; it is no longer a conversation). A direct conversation cannot be left: 400.

conversationLeave captured live

Request

curl -X POST https://api.ollacore.com/v1/directory/conversations/60f99e3b-2084-4bb5-9adb-527ee9907673/leave \
  -H "Authorization: Bearer du_…"
ParameterTypeDescription
room_id path required string (uuid) The group. 404 if it is not a directory conversation of your tenant; 403 if you are not a member.
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

{
  "closed": false,
  "left": true,
  "new_owner": null
}
Response fields (3)
FieldTypeDescription
left boolean
new_owner string, nullable The member who inherited ownership, or null.
closed boolean True when you were the last member and the room closed.
Errors · 401 · 403 · 404
StatusWhen
401Missing, expired or invalid credentials
403Authenticated, but the token is not scoped to this room or principal
404No such room, message, member or attachment
Any active member. A lone owner's seat passes to the longest-standing remaining member (announced as member.added with role owner); when nobody remains the room is closed.
POST /v1/directory/conversations/{room_id}/invites #

Owner: create a link — the token is shown once

Owner only. Whoever in your tenant presents the token joins the group as a member, under the room cap, until the link is revoked, expired or used up. The token is in this answer and nowhere else; only its hash is kept. A group holds at most 20 live links; an expiry is at most 30 days away; sixty creations a minute per user. A link dies with the owner who created it, should they be erased.

inviteCreate captured live

Request

curl -X POST https://api.ollacore.com/v1/directory/conversations/60f99e3b-2084-4bb5-9adb-527ee9907673/invites \
  -H "Authorization: Bearer du_…" \
  -H "Content-Type: application/json" \
  -d '{"expires_in_seconds":86400,"max_uses":5}'
{
  "expires_in_seconds": 86400,
  "max_uses": 5
}
ParameterTypeDescription
room_id path required string (uuid) The group. 404 if it is not a directory conversation of your tenant; 403 if you are not a member; 403 if you are not an owner.
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
expires_in_seconds integer (int64) min 1 · max 2592000 Seconds until the link stops working; omitted means it does not expire.
max_uses integer min 1 How many people may join through it; omitted means any number.

Response · 200

{
  "created_at": "2026-01-15T09:30:46Z",
  "created_by": "4a98e767-a426-47e9-89a0-c499b54d2904",
  "expires_at": "2026-01-16T09:30:46Z",
  "invite_id": "e7a02742-30b8-40ec-aa18-85744bbac5d3",
  "max_uses": 5,
  "room_id": "60f99e3b-2084-4bb5-9adb-527ee9907673",
  "token": "EXAMPLE_INVITE_TOKEN",
  "uses": 0
}
Response fields (8)
FieldTypeDescription
invite_id required string (uuid) The link, for revoking it.
room_id required string (uuid) The group it admits to.
created_by required string (uuid) The owner who created it.
created_at required string (date-time)
expires_at required string (date-time), nullable When it stops working; null for a link that does not expire.
max_uses required integer, nullable How many people may join through it; null for any number.
uses required integer How many have. A member joining again is not a use.
token required string The token to share (inv_ and 43 URL-safe characters). Shown only here.
Errors · 400 · 401 · 403 · 404 · 409 · 429
StatusWhen
400expires_in_seconds outside 1..=2592000 or max_uses below 1.
401Missing, expired or invalid credentials
403Authenticated, but the token is not scoped to this room or principal
404No such room, message, member or attachment
409The group already holds 20 live links.
429Rate limit exceeded
Owner only. The token appears in this answer and nowhere else; only its hash is stored. At most 20 live links per group, an expiry at most 30 days out.
GET /v1/directory/conversations/{room_id}/invites #

Owner: live links, without tokens

Owner only. The links that have not been revoked, newest first, without their tokens. An expired or used-up link stays listed, with the fields that say so, until it is revoked.

invitesList captured live

Request

curl -X GET https://api.ollacore.com/v1/directory/conversations/60f99e3b-2084-4bb5-9adb-527ee9907673/invites \
  -H "Authorization: Bearer du_…"
ParameterTypeDescription
room_id path required string (uuid) The group. 404 if it is not a directory conversation of your tenant; 403 if you are not a member; 403 if you are not an owner.
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

{
  "invites": [
    {
      "created_at": "2026-01-15T09:30:46Z",
      "created_by": "4a98e767-a426-47e9-89a0-c499b54d2904",
      "expires_at": "2026-01-16T09:30:46Z",
      "invite_id": "e7a02742-30b8-40ec-aa18-85744bbac5d3",
      "max_uses": 5,
      "room_id": "60f99e3b-2084-4bb5-9adb-527ee9907673",
      "uses": 0
    }
  ]
}
Response fields (8)
FieldTypeDescription
invites required array of GroupInvite
invites[].invite_id required string (uuid) The link, for revoking it.
invites[].room_id required string (uuid) The group it admits to.
invites[].created_by required string (uuid) The owner who created it.
invites[].created_at required string (date-time)
invites[].expires_at required string (date-time), nullable When it stops working; null for a link that does not expire.
invites[].max_uses required integer, nullable How many people may join through it; null for any number.
invites[].uses required integer How many have. A member joining again is not a use.
Errors · 401 · 403 · 404
StatusWhen
401Missing, expired or invalid credentials
403Authenticated, but the token is not scoped to this room or principal
404No such room, message, member or attachment
DELETE /v1/directory/conversations/{room_id}/invites/{invite_id} #

Owner: revoke a link

Owner only. Whoever holds the token can no longer preview or join. 404 when the group has no such live link.

inviteRevoke captured live

Request

curl -X DELETE https://api.ollacore.com/v1/directory/conversations/60f99e3b-2084-4bb5-9adb-527ee9907673/invites/e7a02742-30b8-40ec-aa18-85744bbac5d3 \
  -H "Authorization: Bearer du_…"
ParameterTypeDescription
room_id path required string (uuid) The group. 404 if it is not a directory conversation of your tenant; 403 if you are not a member; 403 if you are not an owner.
invite_id path required string (uuid) From inviteCreate or the list.
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 required boolean
Errors · 401 · 403 · 404
StatusWhen
401Missing, expired or invalid credentials
403Authenticated, but the token is not scoped to this room or principal
404No such room, message, member or attachment
POST /v1/directory/invites/preview #

What a link leads to, before joining (token in the body)

Before joining: the group's name, description, icon and size, and whether you are already in it. Any user of the tenant may ask. One 404 for every way a link can be no good — unknown, another tenant's, revoked, expired, used up, or its group closed — so a token tells a stranger nothing. The token travels in the body, never the URL. Sixty a minute per user.

invitePreview captured live

Request

curl -X POST https://api.ollacore.com/v1/directory/invites/preview \
  -H "Authorization: Bearer du_…" \
  -H "Content-Type: application/json" \
  -d '{"token":"EXAMPLE_INVITE_TOKEN"}'
{
  "token": "EXAMPLE_INVITE_TOKEN"
}
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
token required string The link token, exactly as the owner received it (inv_…).

Response · 200

{
  "description": "Saturday hike, then dinner at eight.",
  "expires_at": "2026-01-16T09:30:46Z",
  "icon_attachment_id": null,
  "member": false,
  "member_count": 1,
  "name": "Weekend plans",
  "room_id": "60f99e3b-2084-4bb5-9adb-527ee9907673"
}
Response fields (7)
FieldTypeDescription
room_id required string (uuid) The group the link admits to.
name required string, nullable
description required string, nullable max length 1024 The group's description, up to 1024 characters; null when there is none.
icon_attachment_id required string (uuid), nullable The group's icon, if any. A non-member cannot fetch it until they join; show it from the id once in.
member_count required integer Active members.
member required boolean Whether you are already in the group.
expires_at required string (date-time), nullable When the link stops working; null for one that does not expire.
Errors · 401 · 404 · 429
StatusWhen
401Missing, expired or invalid credentials
404No such room, message, member or attachment
429Rate limit exceeded
The token travels in the body, never in a URL. Every way a link can be no good — unknown, revoked, expired, used up, another tenant's, group closed — is one 404.
POST /v1/directory/invites/join #

Join a group through a link

You join as a member. Someone already in the group gets joined: false and the link is not spent. The admission is announced as member.added, exactly as an owner adding you is, and the room cap applies (409 when the group is full). 404 for a link that is no good (see invitePreview). Sixty a minute per user.

inviteJoin captured live

Request

curl -X POST https://api.ollacore.com/v1/directory/invites/join \
  -H "Authorization: Bearer du_…" \
  -H "Content-Type: application/json" \
  -d '{"token":"EXAMPLE_INVITE_TOKEN"}'
{
  "token": "EXAMPLE_INVITE_TOKEN"
}
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
token required string The link token, exactly as the owner received it (inv_…).

Response · 200

{
  "joined": true,
  "member_count": 2,
  "room_id": "60f99e3b-2084-4bb5-9adb-527ee9907673"
}
Response fields (3)
FieldTypeDescription
room_id required string (uuid) The group. Mint a token for it to chat or call.
joined required boolean False when you were already in.
member_count required integer Active members after the call.
Errors · 401 · 404 · 409 · 429
StatusWhen
401Missing, expired or invalid credentials
404No such room, message, member or attachment
409The group is full (5,000 members).
429Rate limit exceeded
Admits you as a member and the room sees member.added. Someone already in the group gets joined: false and the link is not spent.

Mute & block #

PUT /v1/directory/conversations/{room_id}/mute #

Silence this conversation's push for yourself — 8h, 1w or always

Silence push notifications from this conversation for yourself: eight hours, one week, or until you unmute. Live delivery over the WebSocket is unaffected and a call still rings. Private: no event is emitted and nobody else can see your mute. GET /v1/directory/inbox shows muted_until on each conversation, so a client needs no further request to draw the icon.

conversationMute captured live

Request

curl -X PUT https://api.ollacore.com/v1/directory/conversations/eb0a0da1-5344-483f-8b18-b95bfc4d1e36/mute \
  -H "Authorization: Bearer du_…" \
  -H "Content-Type: application/json" \
  -d '{"duration":"1w"}'
{
  "duration": "1w"
}
ParameterTypeDescription
room_id path required string (uuid) The conversation, direct or group. 404 if it is not a directory conversation of your tenant; 403 if you are not a member.
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
duration required one of: 8h, 1w, always How long to silence push notifications: eight hours, one week, or until unmuted. 9999-12-31T00:00:00Z stands for "until unmuted".

Response · 200

{
  "muted_until": "2026-01-22T09:30:47Z",
  "room_id": "eb0a0da1-5344-483f-8b18-b95bfc4d1e36"
}
Response fields (2)
FieldTypeDescription
room_id required string (uuid) The conversation.
muted_until required string (date-time), nullable Until when push notifications from this conversation are silenced for you; null when not muted. 9999-12-31T00:00:00Z stands for "until unmuted".
Errors · 401 · 403 · 404 · 422
StatusWhen
401Missing, expired or invalid credentials
403Authenticated, but the token is not scoped to this room or principal
404No such room, message, member or attachment
422A duration outside 8h|1w|always, or an unknown field.
Push only: the WebSocket still delivers and a call still rings. Private — no event is emitted. Durations are 8h, 1w or always; for always, muted_until is a date in the year 9999.
DELETE /v1/directory/conversations/{room_id}/mute #

Unmute

Push notifications again. Unmuting a conversation that was not muted answers the same 200.

conversationUnmute captured live

Request

curl -X DELETE https://api.ollacore.com/v1/directory/conversations/eb0a0da1-5344-483f-8b18-b95bfc4d1e36/mute \
  -H "Authorization: Bearer du_…"
ParameterTypeDescription
room_id path required string (uuid) The conversation, direct or group. 404 if it is not a directory conversation of your tenant; 403 if you are not a member.
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

{
  "muted_until": null,
  "room_id": "eb0a0da1-5344-483f-8b18-b95bfc4d1e36"
}
Response fields (2)
FieldTypeDescription
room_id required string (uuid) The conversation.
muted_until required string (date-time), nullable Until when push notifications from this conversation are silenced for you; null when not muted. 9999-12-31T00:00:00Z stands for "until unmuted".
Errors · 401 · 403 · 404
StatusWhen
401Missing, expired or invalid credentials
403Authenticated, but the token is not scoped to this room or principal
404No such room, message, member or attachment
GET /v1/directory/blocks #

Your block list

Who you have blocked, newest first, as directory users. Private: nobody else can see your list, and nobody is told they are on it. A block freezes the direct conversation between you (blocked on sends, reactions, typing, calls and on opening a new one), and keeps the blocked person from adding you to a group. Shared groups are unaffected.

blocksList captured live

Request

curl -X GET https://api.ollacore.com/v1/directory/blocks \
  -H "Authorization: Bearer du_…"
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.

Response · 200

{
  "blocks": [
    {
      "display_name": "malavika rajesh",
      "phone": "+15550002222",
      "since": "2026-01-15T09:30:47Z",
      "user_id": "0b925a2d-944a-4f1d-a761-56d24db90341"
    }
  ]
}
Response fields (5)
FieldTypeDescription
blocks required array of BlockEntry
blocks[].user_id required string (uuid) The blocked user.
blocks[].display_name required string, nullable Their display name, or null.
blocks[].phone required string Their phone number, E.164.
blocks[].since required string (date-time) When you blocked them.
Errors · 401
StatusWhen
401Missing, expired or invalid credentials
PUT /v1/directory/blocks/{user_id} #

Block a user — the direct chat freezes for both

Idempotent: blocking someone already blocked answers the same, with the original since. 400 for yourself, 404 for an id that is not a user of your tenant, 409 when your list holds 1,000 users. Rate limited to 60 changes a minute.

blockUser captured live

Request

curl -X PUT https://api.ollacore.com/v1/directory/blocks/0b925a2d-944a-4f1d-a761-56d24db90341 \
  -H "Authorization: Bearer du_…"
ParameterTypeDescription
user_id path required string (uuid) A directory user of your tenant.
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

{
  "blocked": true,
  "since": "2026-01-15T09:30:47Z",
  "user_id": "0b925a2d-944a-4f1d-a761-56d24db90341"
}
Response fields (3)
FieldTypeDescription
user_id required string (uuid)
blocked required boolean
since string (date-time) When the block was placed; present when blocked is true. Blocking again keeps the original time.
Errors · 400 · 401 · 404 · 409 · 429
StatusWhen
400You cannot block yourself.
401Missing, expired or invalid credentials
404No such room, message, member or attachment
409Your block list is full (1,000 users).
429Rate limit exceeded
The direct conversation between the two is frozen for both — sends, reactions, typing and the RTC join answer blocked (403) — and a new one is refused; the blocked user cannot add the blocker to a group. Shared groups are unaffected; the blocked person is not told. Idempotent.
DELETE /v1/directory/blocks/{user_id} #

Unblock

Idempotent: lifting a block that is not there answers the same, and says nothing about the id. Rate limited to 60 changes a minute.

unblockUser captured live

Request

curl -X DELETE https://api.ollacore.com/v1/directory/blocks/0b925a2d-944a-4f1d-a761-56d24db90341 \
  -H "Authorization: Bearer du_…"
ParameterTypeDescription
user_id path required string (uuid) A directory user of your tenant.
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

{
  "blocked": false,
  "user_id": "0b925a2d-944a-4f1d-a761-56d24db90341"
}
Response fields (3)
FieldTypeDescription
user_id required string (uuid)
blocked required boolean
since string (date-time) When the block was placed; present when blocked is true. Blocking again keeps the original time.
Errors · 401 · 429
StatusWhen
401Missing, expired or invalid credentials
429Rate limit exceeded

Devices #

POST /v1/directory/devices #

Register a push token

Idempotent per token: registering a token the caller already has updates its platform and app_id and refreshes updated_at. It does not add a second row. The token is tied to the directory app that issued the caller's session. That app's credentials, set with PUT /v1/account/directory-apps/{app_id}/push, sign its pushes, and nothing is sent until the app has credentials for the platform. Native pushes go only to fcm and apns tokens. platform must be fcm, apns or web, and push_token must be 1 to 4096 characters after trimming. Otherwise the call is a 400.

registerDevice captured live

Request

curl -X POST https://api.ollacore.com/v1/directory/devices \
  -H "Authorization: Bearer du_…" \
  -H "Content-Type: application/json" \
  -d '{"platform":"fcm","push_token":"EXAMPLE_DEVICE_PUSH_TOKEN"}'
{
  "platform": "fcm",
  "push_token": "EXAMPLE_DEVICE_PUSH_TOKEN"
}
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
platform required one of: fcm, apns, web fcm (Android), apns (iOS) or web. The backend sends native pushes to fcm and apns tokens.
push_token required string max length 4096 The device's push token, up to 4096 characters. Registering the same token again refreshes it.

Response · 200

{
  "id": "fa01a2a6-4fd1-48e7-a18c-9bf7719f01b7",
  "platform": "fcm",
  "push_token": "EXAMPLE_DEVICE_PUSH_TOKEN",
  "app_id": "da_3a0a2cfaabd34b7dbfb4ecd5033b0c5f",
  "updated_at": "2026-01-15T09:30:44Z"
}
Response fields (5)
FieldTypeDescription
id string (uuid) The device registration's id.
platform string The platform as stored.
push_token string The token as stored.
app_id string, nullable The directory app this token was registered under — selects the FCM/APNs credentials used to reach it.
updated_at string (date-time) When it was registered or refreshed.
Errors · 400 · 401
StatusWhen
400platform is not fcm, apns or web, or push_token is empty or over 4096 characters after trimming.
401Missing, expired or invalid credentials
GET /v1/directory/devices #

List devices

Returns the caller's push tokens on every platform (fcm, apns and web), most recently registered or refreshed first. Each carries the app_id it was registered under. Tokens that FCM or APNs report as no longer valid are removed automatically, so they drop out of this list.

listDevices captured live

Request

curl -X GET https://api.ollacore.com/v1/directory/devices \
  -H "Authorization: Bearer du_…"
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.

Response · 200

{
  "devices": [
    {
      "app_id": "da_3a0a2cfaabd34b7dbfb4ecd5033b0c5f",
      "id": "fa01a2a6-4fd1-48e7-a18c-9bf7719f01b7",
      "platform": "fcm",
      "push_token": "EXAMPLE_DEVICE_PUSH_TOKEN",
      "updated_at": "2026-01-15T09:30:44Z"
    }
  ]
}
Response fields (6)
FieldTypeDescription
devices array of object The caller's registered push devices.
devices[].id string (uuid) The device registration's id.
devices[].platform string fcm, apns or web.
devices[].push_token string The registered push token.
devices[].app_id string, nullable The directory app this token was registered under — selects the FCM/APNs credentials used to reach it. Null for tokens registered before per-app credentials existed.
devices[].updated_at string (date-time) When the token was last registered or refreshed.
Errors · 401
StatusWhen
401Missing, expired or invalid credentials
DELETE /v1/directory/devices #

Unregister a push token

Removes the token only from the caller's own registrations. The token must match exactly as registered. A token the caller does not have answers 404, so a repeated delete can be treated as done. Signing out with POST /v1/directory/logout does not remove device tokens. Delete the token first, while the session still works, or the device keeps receiving that user's pushes.

deleteDevice captured live

Request

curl -X DELETE https://api.ollacore.com/v1/directory/devices \
  -H "Authorization: Bearer du_…" \
  -H "Content-Type: application/json" \
  -d '{"push_token":"EXAMPLE_DEVICE_PUSH_TOKEN"}'
{
  "push_token": "EXAMPLE_DEVICE_PUSH_TOKEN"
}
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
push_token required string The token to unregister. Call this on sign-out.

Response · 200

{
  "deleted": true
}
Response fields (1)
FieldTypeDescription
deleted boolean True if the token was registered to the caller and is now removed.
Errors · 401 · 404
StatusWhen
401Missing, expired or invalid credentials
404The caller has no such push token registered.

Errors on this plane #

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

401 · a du_ directory session is required here

{
  "code": "unauthorized",
  "message": "unauthorized: not a directory session token"
}

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