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.
Sign-in & profile #
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.
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"
} | Parameter | Type | Description |
|---|---|---|
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
| Field | Type | Description |
|---|---|---|
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)
| Field | Type | Description |
|---|---|---|
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
| Status | When |
|---|---|
400 | phone is not E.164 (+ and 8–15 digits). |
404 | unknown app |
429 | A 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). |
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.
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"
} | Parameter | Type | Description |
|---|---|---|
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
| Field | Type | Description |
|---|---|---|
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)
| Field | Type | Description |
|---|---|---|
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
| Status | When |
|---|---|
400 | phone is not E.164. |
401 | No unexpired code for this number, a wrong code, a code already used, or one past its 5 attempts. Request a new code. |
404 | Unknown app_id. |
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.
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
} | Parameter | Type | Description |
|---|---|---|
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
| Field | Type | Description |
|---|---|---|
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)
| Field | Type | Description |
|---|---|---|
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
| Status | When |
|---|---|
401 | Missing, expired or invalid credentials |
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.
Request
curl -X GET https://api.ollacore.com/v1/directory/me \
-H "Authorization: Bearer du_…" | Parameter | Type | Description |
|---|---|---|
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)
| Field | Type | Description |
|---|---|---|
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
| Status | When |
|---|---|
401 | Missing, expired or invalid credentials |
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.
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."
} | Parameter | Type | Description |
|---|---|---|
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
| Field | Type | Description |
|---|---|---|
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)
| Field | Type | Description |
|---|---|---|
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
| Status | When |
|---|---|
400 | Nothing to update, a display name outside 1–128 characters, or an about line over 256. |
401 | Missing, expired or invalid credentials |
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.
Request
curl -X DELETE https://api.ollacore.com/v1/directory/me \
-H "Authorization: Bearer du_…" | Parameter | Type | Description |
|---|---|---|
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)
| Field | Type | Description |
|---|---|---|
deleted required | boolean | Always true. |
Errors · 401
| Status | When |
|---|---|
401 | Missing, expired or invalid credentials |
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.
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
} | Parameter | Type | Description |
|---|---|---|
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
| Field | Type | Description |
|---|---|---|
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)
| Field | Type | Description |
|---|---|---|
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
| Status | When |
|---|---|
400 | byte_size outside 1..=2 MiB. |
401 | Missing, expired or invalid credentials |
415 | declared_mime is not an image type a profile photo may be. |
429 | Rate limit exceeded |
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.
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"
} | Parameter | Type | Description |
|---|---|---|
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
| Field | Type | Description |
|---|---|---|
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)
| Field | Type | Description |
|---|---|---|
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
| Status | When |
|---|---|
400 | The uploaded object is larger than 2 MiB (it is removed). |
401 | Missing, expired or invalid credentials |
409 | Nothing has been uploaded for this upload_id yet. |
415 | The bytes are not an image, or could not be decoded (the object is removed). |
Remove your profile photo
The photo and its thumbnail are deleted from storage and avatar_version becomes null. Idempotent.
Request
curl -X DELETE https://api.ollacore.com/v1/directory/me/avatar \
-H "Authorization: Bearer du_…" | Parameter | Type | Description |
|---|---|---|
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)
| Field | Type | Description |
|---|---|---|
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
| Status | When |
|---|---|
401 | Missing, expired or invalid credentials |
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.
Request
curl -X GET https://api.ollacore.com/v1/directory/users/4a98e767-a426-47e9-89a0-c499b54d2904/avatar?size=thumb \
-H "Authorization: Bearer du_…" | Parameter | Type | Description |
|---|---|---|
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
| Status | When |
|---|---|
400 | size is neither thumb nor full. |
401 | Missing, expired or invalid credentials |
404 | No such room, message, member or attachment |
Contacts & conversations #
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.
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"
]
} | Parameter | Type | Description |
|---|---|---|
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
| Field | Type | Description |
|---|---|---|
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)
| Field | Type | Description |
|---|---|---|
contacts | array of object | The numbers that belong to registered users in your tenant. Other tenants' users are never disclosed. |
contacts[]. | string (uuid) | The user's id, which is also their chat principal_id. |
contacts[]. | string | The matched number, E.164. |
contacts[]. | string, nullable | The user's display name, or null if not set. |
contacts[]. | string, nullable max length 256 | The profile line, up to 256 characters; null when not set. |
contacts[]. | 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
| Status | When |
|---|---|
400 | More than 500 numbers. |
401 | Missing, expired or invalid credentials |
429 | Over 20 lookups a minute by this user. |
Open a 1:1 (idempotent; blocked across a block)
Refused with blocked (403) when either of you has blocked the other.
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"
} | Parameter | Type | Description |
|---|---|---|
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
| Field | Type | Description |
|---|---|---|
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)
| Field | Type | Description |
|---|---|---|
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
| Status | When |
|---|---|
400 | Invalid (e.g. self) |
401 | Missing, expired or invalid credentials |
403 | The 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. |
404 | No such room, message, member or attachment |
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).
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"
} | Parameter | Type | Description |
|---|---|---|
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
| Field | Type | Description |
|---|---|---|
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)
| Field | Type | Description |
|---|---|---|
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[]. required if its parent is sent | string (uuid) | Someone you named who was not added. |
not_added[]. required if its parent is sent | one of: blocked | blocked: they have blocked you. |
Errors · 401 · 403 · 404
| Status | When |
|---|---|
401 | Missing, expired or invalid credentials |
403 | The 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. |
404 | No such room, message, member or attachment |
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.
Request
curl -X GET https://api.ollacore.com/v1/directory/conversations \
-H "Authorization: Bearer du_…" | Parameter | Type | Description |
|---|---|---|
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)
| Field | Type | Description |
|---|---|---|
conversations | array of object | The caller's directory conversations. |
conversations[]. | string (uuid) | The conversation's room. Mint a token for it to chat or call. |
conversations[]. | one of: direct, group | direct (two people) or group. |
conversations[]. | string, nullable | The group's name; null for a direct conversation. |
conversations[]. | string, nullable max length 1024 | The group's description, up to 1024 characters; null when there is none. |
conversations[]. | 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[]. | string (date-time) | When the conversation was created. |
Errors · 401
| Status | When |
|---|---|
401 | Missing, expired or invalid credentials |
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.
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_…" | Parameter | Type | Description |
|---|---|---|
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)
| Field | Type | Description |
|---|---|---|
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[]. required | array of string | The server URLs. They never carry a ?transport= query, which Safari refuses. |
ice_servers[]. | string | expiry_unix:principal — ephemeral, 600 s |
ice_servers[]. | string | base64(HMAC-SHA1(turn_secret, username)) |
Errors · 401 · 403 · 404 · 409
| Status | When |
|---|---|
401 | Missing, expired or invalid credentials |
403 | Authenticated, but the token is not scoped to this room or principal |
404 | No such room, message, member or attachment |
409 | The room is closed: no new tokens are minted for it. |
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.
Request
curl -X GET https://api.ollacore.com/v1/directory/inbox?limit=30 \
-H "Authorization: Bearer du_…" | Parameter | Type | Description |
|---|---|---|
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)
| Field | Type | Description |
|---|---|---|
conversations | array of object | The caller's conversations as a chat list, most recent activity first. |
conversations[]. | string (uuid) | The conversation's room. |
conversations[]. | one of: direct, group | direct or group. |
conversations[]. | string, nullable | The group's name; null for a direct conversation (use peer). |
conversations[]. | string, nullable max length 1024 | The group's description, up to 1024 characters; null when there is none. |
conversations[]. | 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[]. | object, nullable | For a direct conversation, the other person; null for a group. |
conversations[]. | string (uuid) | The other person's user id. |
conversations[]. | string, nullable | Their display name, or null. |
conversations[]. | string | Their phone number, E.164. |
conversations[]. | 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[]. | integer | Messages after the caller's read cursor. |
conversations[]. | 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[]. | object, nullable | The newest message, for the preview line; null if the conversation has none. |
conversations[]. | integer | Its position in the room. |
conversations[]. | string, nullable | Who sent it. |
conversations[]. | string | Its kind. |
conversations[]. | 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[]. | string (date-time), nullable | When it was sent. |
Errors · 401
| Status | When |
|---|---|
401 | Missing, expired or invalid credentials |
Group management #
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.
Request
curl -X GET https://api.ollacore.com/v1/directory/conversations/60f99e3b-2084-4bb5-9adb-527ee9907673 \
-H "Authorization: Bearer du_…" | Parameter | Type | Description |
|---|---|---|
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)
| Field | Type | Description |
|---|---|---|
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
| Status | When |
|---|---|
401 | Missing, expired or invalid credentials |
403 | Authenticated, but the token is not scoped to this room or principal |
404 | No such room, message, member or attachment |
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.
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."
} | Parameter | Type | Description |
|---|---|---|
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
| Field | Type | Description |
|---|---|---|
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)
| Field | Type | Description |
|---|---|---|
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
| Status | When |
|---|---|
400 | Nothing 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. |
401 | Missing, expired or invalid credentials |
403 | Authenticated, but the token is not scoped to this room or principal |
404 | No such room, message, member or attachment |
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.
Request
curl -X GET https://api.ollacore.com/v1/directory/conversations/60f99e3b-2084-4bb5-9adb-527ee9907673/members \
-H "Authorization: Bearer du_…" | Parameter | Type | Description |
|---|---|---|
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)
| Field | Type | Description |
|---|---|---|
members | array of DirectoryGroupMember | |
members[]. required if its parent is sent | string (uuid) | The member's directory user id — also their principal id in the room. |
members[]. | string, nullable | As set on their profile, or null. |
members[]. required if its parent is sent | string | E.164. |
members[]. | string, nullable max length 256 | The profile line, up to 256 characters; null when not set. |
members[]. | 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[]. required if its parent is sent | one of: owner, member, viewer | owner manages the group; see the group-management operations. |
members[]. | string (date-time), nullable | When they first joined; a restored member keeps the original time. |
Errors · 401 · 403 · 404
| Status | When |
|---|---|
401 | Missing, expired or invalid credentials |
403 | Authenticated, but the token is not scoped to this room or principal |
404 | No such room, message, member or attachment |
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.
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"
]
} | Parameter | Type | Description |
|---|---|---|
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
| Field | Type | Description |
|---|---|---|
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)
| Field | Type | Description |
|---|---|---|
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[]. required if its parent is sent | string (uuid) | Someone you named who was not added. |
not_added[]. required if its parent is sent | one of: blocked | blocked: they have blocked you. |
Errors · 401 · 403 · 404 · 409
| Status | When |
|---|---|
401 | Missing, expired or invalid credentials |
403 | Authenticated, but the token is not scoped to this room or principal |
404 | No such room, message, member or attachment |
409 | Conflicts with current state |
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.
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"
} | Parameter | Type | Description |
|---|---|---|
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
| Field | Type | Description |
|---|---|---|
role required | one of: owner, member | The role to give. |
Response · 200
{
"role": "owner",
"user_id": "0b925a2d-944a-4f1d-a761-56d24db90341"
} Response fields (2)
| Field | Type | Description |
|---|---|---|
user_id required | string (uuid) | |
role required | one of: owner, member |
Errors · 400 · 401 · 403 · 404 · 409
| Status | When |
|---|---|
400 | A role other than owner or member, or a direct conversation. |
401 | Missing, expired or invalid credentials |
403 | Authenticated, but the token is not scoped to this room or principal |
404 | No such room, message, member or attachment |
409 | The member is the only owner. |
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.
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_…" | Parameter | Type | Description |
|---|---|---|
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)
| Field | Type | Description |
|---|---|---|
removed | boolean |
Errors · 401 · 403 · 404
| Status | When |
|---|---|
401 | Missing, expired or invalid credentials |
403 | Authenticated, but the token is not scoped to this room or principal |
404 | No such room, message, member or attachment |
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.
Request
curl -X POST https://api.ollacore.com/v1/directory/conversations/60f99e3b-2084-4bb5-9adb-527ee9907673/leave \
-H "Authorization: Bearer du_…" | Parameter | Type | Description |
|---|---|---|
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)
| Field | Type | Description |
|---|---|---|
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
| Status | When |
|---|---|
401 | Missing, expired or invalid credentials |
403 | Authenticated, but the token is not scoped to this room or principal |
404 | No such room, message, member or attachment |
Invite links #
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.
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
} | Parameter | Type | Description |
|---|---|---|
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
| Field | Type | Description |
|---|---|---|
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)
| Field | Type | Description |
|---|---|---|
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
| Status | When |
|---|---|
400 | expires_in_seconds outside 1..=2592000 or max_uses below 1. |
401 | Missing, expired or invalid credentials |
403 | Authenticated, but the token is not scoped to this room or principal |
404 | No such room, message, member or attachment |
409 | The group already holds 20 live links. |
429 | Rate limit exceeded |
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.
Request
curl -X GET https://api.ollacore.com/v1/directory/conversations/60f99e3b-2084-4bb5-9adb-527ee9907673/invites \
-H "Authorization: Bearer du_…" | Parameter | Type | Description |
|---|---|---|
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)
| Field | Type | Description |
|---|---|---|
invites required | array of GroupInvite | |
invites[]. required | string (uuid) | The link, for revoking it. |
invites[]. required | string (uuid) | The group it admits to. |
invites[]. required | string (uuid) | The owner who created it. |
invites[]. required | string (date-time) | |
invites[]. required | string (date-time), nullable | When it stops working; null for a link that does not expire. |
invites[]. required | integer, nullable | How many people may join through it; null for any number. |
invites[]. required | integer | How many have. A member joining again is not a use. |
Errors · 401 · 403 · 404
| Status | When |
|---|---|
401 | Missing, expired or invalid credentials |
403 | Authenticated, but the token is not scoped to this room or principal |
404 | No such room, message, member or attachment |
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.
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_…" | Parameter | Type | Description |
|---|---|---|
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)
| Field | Type | Description |
|---|---|---|
revoked required | boolean |
Errors · 401 · 403 · 404
| Status | When |
|---|---|
401 | Missing, expired or invalid credentials |
403 | Authenticated, but the token is not scoped to this room or principal |
404 | No such room, message, member or attachment |
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.
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"
} | Parameter | Type | Description |
|---|---|---|
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
| Field | Type | Description |
|---|---|---|
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)
| Field | Type | Description |
|---|---|---|
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
| Status | When |
|---|---|
401 | Missing, expired or invalid credentials |
404 | No such room, message, member or attachment |
429 | Rate limit exceeded |
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.
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"
} | Parameter | Type | Description |
|---|---|---|
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
| Field | Type | Description |
|---|---|---|
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)
| Field | Type | Description |
|---|---|---|
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
| Status | When |
|---|---|
401 | Missing, expired or invalid credentials |
404 | No such room, message, member or attachment |
409 | The group is full (5,000 members). |
429 | Rate limit exceeded |
Mute & block #
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.
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"
} | Parameter | Type | Description |
|---|---|---|
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
| Field | Type | Description |
|---|---|---|
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)
| Field | Type | Description |
|---|---|---|
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
| Status | When |
|---|---|
401 | Missing, expired or invalid credentials |
403 | Authenticated, but the token is not scoped to this room or principal |
404 | No such room, message, member or attachment |
422 | A duration outside 8h|1w|always, or an unknown field. |
Unmute
Push notifications again. Unmuting a conversation that was not muted answers the same 200.
Request
curl -X DELETE https://api.ollacore.com/v1/directory/conversations/eb0a0da1-5344-483f-8b18-b95bfc4d1e36/mute \
-H "Authorization: Bearer du_…" | Parameter | Type | Description |
|---|---|---|
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)
| Field | Type | Description |
|---|---|---|
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
| Status | When |
|---|---|
401 | Missing, expired or invalid credentials |
403 | Authenticated, but the token is not scoped to this room or principal |
404 | No such room, message, member or attachment |
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.
Request
curl -X GET https://api.ollacore.com/v1/directory/blocks \
-H "Authorization: Bearer du_…" | Parameter | Type | Description |
|---|---|---|
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)
| Field | Type | Description |
|---|---|---|
blocks required | array of BlockEntry | |
blocks[]. required | string (uuid) | The blocked user. |
blocks[]. required | string, nullable | Their display name, or null. |
blocks[]. required | string | Their phone number, E.164. |
blocks[]. required | string (date-time) | When you blocked them. |
Errors · 401
| Status | When |
|---|---|
401 | Missing, expired or invalid credentials |
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.
Request
curl -X PUT https://api.ollacore.com/v1/directory/blocks/0b925a2d-944a-4f1d-a761-56d24db90341 \
-H "Authorization: Bearer du_…" | Parameter | Type | Description |
|---|---|---|
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)
| Field | Type | Description |
|---|---|---|
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
| Status | When |
|---|---|
400 | You cannot block yourself. |
401 | Missing, expired or invalid credentials |
404 | No such room, message, member or attachment |
409 | Your block list is full (1,000 users). |
429 | Rate limit exceeded |
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.
Request
curl -X DELETE https://api.ollacore.com/v1/directory/blocks/0b925a2d-944a-4f1d-a761-56d24db90341 \
-H "Authorization: Bearer du_…" | Parameter | Type | Description |
|---|---|---|
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)
| Field | Type | Description |
|---|---|---|
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
| Status | When |
|---|---|
401 | Missing, expired or invalid credentials |
429 | Rate limit exceeded |
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.
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"
} | Parameter | Type | Description |
|---|---|---|
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
| Field | Type | Description |
|---|---|---|
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)
| Field | Type | Description |
|---|---|---|
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
| Status | When |
|---|---|
400 | platform is not fcm, apns or web, or push_token is empty or over 4096 characters after trimming. |
401 | Missing, expired or invalid credentials |
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.
Request
curl -X GET https://api.ollacore.com/v1/directory/devices \
-H "Authorization: Bearer du_…" | Parameter | Type | Description |
|---|---|---|
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)
| Field | Type | Description |
|---|---|---|
devices | array of object | The caller's registered push devices. |
devices[]. | string (uuid) | The device registration's id. |
devices[]. | string | fcm, apns or web. |
devices[]. | string | The registered push token. |
devices[]. | 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[]. | string (date-time) | When the token was last registered or refreshed. |
Errors · 401
| Status | When |
|---|---|
401 | Missing, expired or invalid credentials |
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.
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"
} | Parameter | Type | Description |
|---|---|---|
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
| Field | Type | Description |
|---|---|---|
push_token required | string | The token to unregister. Call this on sign-out. |
Response · 200
{
"deleted": true
} Response fields (1)
| Field | Type | Description |
|---|---|---|
deleted | boolean | True if the token was registered to the caller and is now removed. |
Errors · 401 · 404
| Status | When |
|---|---|
401 | Missing, expired or invalid credentials |
404 | The 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.