Call history

Every call and room leaves a record: who started it, who was rung, what became of each person, how it ended and how long it was connected. This page is for reading that history. To place and answer calls, see Calls.

A record is written when the call starts, with status: "live", and completed when it ends. While it is live, answered_at, ended_at, end_reason and duration_ms are null, and everyone rung shows as invited. The final outcome of each person is written when the call ends.

The same moments fire the call.started, call.answered and call.finished webhooks.

The call record

{
  "id": "4a6922a7-0c3e-4b1d-9f5a-8e2c6d0b7a14",
  "conversation_id": "c8a1f4e2-6b3d-4f7a-9c2e-1d5b8a0f3e61",
  "kind": "call",
  "media": "video",
  "status": "ended",
  "started_at": "2026-10-26T07:33:20.000000Z",
  "answered_at": "2026-10-26T07:33:25.000000Z",
  "ended_at": "2026-10-26T07:34:25.000000Z",
  "end_reason": "finished",
  "duration_ms": 60000,
  "started_by": { "id": "11111111-2222-3333-4444-555555555555", "external_id": "acme-alice" },
  "participants": [
    { "id": "22222222-3333-4444-5555-666666666666", "external_id": "acme-bob", "status": "joined" },
    { "id": "33333333-4444-5555-6666-777777777777", "external_id": "acme-carol", "status": "declined" }
  ]
}
FieldTypeDescription
iduuidThe call's id, the same one the Calls endpoints use.
conversation_iduuid
kind"call" | "room"A call rings the other members. A room rings nobody: people drop in.
media"audio" | "video"
status"live" | "ended"
started_atstringISO 8601.
answered_atstring | nullWhen the first person rung picked up. null for rooms, unanswered calls, and while live.
ended_atstring | null
end_reasonstring | nullSee below. null while live.
duration_msinteger | nullConnected time. For a call, from the first answer to the end (null when nobody answered). For a room, from start to end. null while live.
started_byobject | null{ id, external_id } of whoever placed the call or opened the room.
participantsobject[]{ id, external_id, status } for everyone else rung or present. The starter is not included.

end_reason values:

ValueMeaning
finishedThe call or room ran and everyone left.
cancelledThe caller hung up before anyone answered.
no_answerThe ring ran out with nobody answering.
declined_allEveryone rung declined.
busy_allEveryone rung was busy on another call.

Participant status values: joined, declined, busy (their phone was on another call), missed (rung, never answered), and while the call is live, invited.

List the tenant's calls

GET/v1/calls
API key

For your backend: every call and room in the tenant, newest first. Any valid API key works; no scope is needed.

Query parameters

NameTypeDefaultDescription
conversation_iduuidnoneOnly this conversation's calls.
external_idstringnoneOnly calls this user started or was part of. An unknown external_id returns an empty list.
kindcall | roomnone
statuslive | endednone
sinceISO 8601 timenoneStarted at or after this time. Include a UTC offset, e.g. 2026-10-01T00:00:00Z.
untilISO 8601 timenoneStarted before this time.
limitinteger501–200. Values outside the range are clamped.
cursorstringnoneThe previous page's next_cursor.

Response

200:

{ "data": [ CallRecord, … ], "next_cursor": string | null }.

next_cursor is null on the last page. Treat it as an opaque string and pass it back unchanged as cursor.

curl "https://api.poolse.dev/v1/calls?external_id=acme-bob&status=ended&limit=100" \
  -H "Authorization: Bearer $POOLSE_API_KEY"

Errors

CodeStatusWhen
invalid_parameter400conversation_id is not a UUID, or since / until is not an ISO 8601 time.
invalid_cursor400cursor is not one the server issued.

Get a call

GET/v1/calls/:id
API key

200: { "data": CallRecord }. Note the data wrapper.

CodeStatusWhen
not_found404No such call in the tenant.

A user's own calls

GET/v1/me/calls
End-user JWT

For your app, with the signed-in user's token: every call they started or were part of, newest first. This is what a calls tab lists. See Building a calls tab.

Query parameters: kind, status, since, until, limit, cursor, as above. conversation_id and external_id are not accepted here.

200: { "data": [ CallRecord, … ], "next_cursor": … }.

Errors: invalid_parameter and invalid_cursor, as above.

A conversation's calls

GET/v1/conversations/:conversation_id/calls
End-user JWT

Every call and room on one conversation, newest first. Only for members of the conversation.

Query parameters: kind, status, since, until, limit, cursor, as above.

200: { "data": [ CallRecord, … ], "next_cursor": … }.

CodeStatusWhen
not_found404No such conversation, or the caller is not a member.
invalid_parameter400since / until is not an ISO 8601 time.
invalid_cursor400Bad cursor.

In the conversation itself

When a call or room ends, poolse also posts one message of type: "system" into the conversation. Its body is a JSON string with kind: "call", v: 2, call_kind (call or room), call_id, media, caller_user_id, started_at, ended_at, end_reason, duration_ms, joined_user_ids and participants ({ user_id, status }). Clients render it as a call bubble. See Messages.

Call history · API · poolse docs