Notation
GET /v1/leads/{id}is a method and a path underhttps://api.totemstudio.ai. A word in braces is a placeholder.…stands for a value left out of an example.$TOTEM_API_KEYis an environment variable that holds your secret key.- Field names appear as they are in the JSON. A dot reaches into an object:
contact_permissions.do_not_contact.
Requests and responses
- HTTPS only. Request and response bodies are JSON in UTF-8, and field names are
snake_case. - Every resource, such as a lead, a call or a team, has an
objectfield that names its type, and alivemodefield.livemodeistrueon everything the API returns; only the example data of these docs haslivemode: false. - A collection comes in a list envelope with
object: "list",data,has_moreandnext_cursor. Lists and pagination explains it. - A few bodies have shapes of their own. The items of the changes feed carry
kind, and the record sits inside them underobject, or a removal underremoved.GET /v1/healthanswers{"object": "health", "status": "ok"}. Errors follow the format described under Errors.
Ids
Ids are strings with a prefix that names the type of record:
| Prefix | Record |
|---|---|
team_ | Team |
mem_ | Team member |
svc_ | Bookable service |
brd_ | Board |
stg_ | Stage |
lead_ | Lead, and its lead_attribution and lead_response facets |
act_ | Lead activity |
call_ | Call |
conv_ | Conversation |
bkg_ | Booking |
prn_ | Integration |
Treat an id as opaque: store it whole, compare it exactly and never parse it. An id stays the same while its record exists. A record that is removed and later created again can come back with a new id.
Member ids belong to one team: the same person has a different mem_ id in each team.
Timestamps
Timestamps are ISO 8601 strings in UTC, such as 2026-10-15T09:30:00Z, and may carry fractional seconds. Each team's time zone comes with GET /v1/teams/{id}; use it to display times, never to store them.
Empty values
In API objects, a field with no value is null, never an empty string. Some fields say why they are empty:
- a lead's
phoneisnullwithphone_statusset tomissingorinvalid; - a lead's pipeline fields are
nullwithpipeline_cleared: truewhen someone converted the lead and cleared its pipeline data.
Phones and amounts
- Phones are E.164 strings, such as
+12025550123. - A lead's
estimated_valueandvalue_at_conversionare amounts as Totem stores them: in major units, such as euros or dollars rather than cents, possibly with decimals.estimated_value_currencyis alwaysnull, because Totem stores no currency with these values. - No field of version 1.0a carries an amount in minor units.
Enums can grow
Fields with a fixed set of values, such as status or archive_reason, can gain values in any version, and so can error codes. When you meet a value you do not know, keep the record, treat the value as "other", and carry on with the sync. New kinds of record can also appear in the changes feed: store or skip an item whose kind you do not know, and keep syncing.
The OpenAPI document lists the values known today. If you generate a client from it, make sure the client accepts a value it does not know instead of rejecting the whole object.
Record versions
Every record the changes feed delivers carries version, an integer that grows each time the record changes, and updated_at, the time of that version. That covers leads and their two facets, activities, calls, conversations, bookings and boards. Apply a record only when its version is greater than the one you hold. The feed can deliver a record more than once; its version tells you which copy is newer.
Teams, members and bookable services have no version. When you read them again, replace your copy as a whole.
API versions and stability
/v1 is the current API version. New fields, enum values, endpoints and error codes can appear, so ignore what you do not use.
Every operation states its stability in this reference and, in the OpenAPI document, as x-totem-stability. In version 1.0a every operation is beta. Read the changelog for updates.
What Beta means
While the API is in Beta, Totem can change or remove fields, values and behavior, with no fixed notice period. The changelog lists changes under the date they shipped, and an entry that asks you to change your integration or to run a full resync starts with Action required. Additive changes, such as new fields, enum values, endpoints or error codes, can appear at any time, so build your integration to ignore what it does not know.
Request ids
Every response from the API carries a Totem-Request-Id header, such as req_0f6b2c1e-8d4a-4b7e-9c3f-5a2d1e7b9c40, and an error body repeats it as request_id. Log it, and quote it when you ask Support for help.
Errors
Errors follow RFC 9457 and come as application/problem+json:
{
"type": "https://totemstudio.ai/developers/errors#team_required",
"title": "Team required",
"status": 400,
"code": "team_required",
"request_id": "req_0f6b2c1e-8d4a-4b7e-9c3f-5a2d1e7b9c40",
"param": "Totem-Team",
"doc_url": "https://totemstudio.ai/developers/errors#team_required"
}codesays what went wrong. Branch oncode, never ontitle, which can change.typeanddoc_urlboth link to the code's entry on the Errors page, which says when it happens and what to do.param, when present, names the header, parameter or field at fault. It never repeats the value you sent.- New codes can appear. Handle a code you do not know by its HTTP status.
- A
401also carries the headerWWW-Authenticate: Bearer realm="Totem API". - A
429carriesRetry-After, a whole number of seconds: wait at least that long. Two codes use it,rate_limitedandquota_exceeded; Rate limits explains both. Retry a5xxwith exponential backoff.
During Beta, the firewall in front of the API answers some requests itself with a plain 403, 404 or 429 that is not in this format: no JSON error body, no Retry-After and no Totem-Request-Id. It answers 404 to any path outside /v1/ and to any method other than GET and POST, HEAD included. Handle its answer by its status; Errors lists when each one happens.
Personal data in URLs
Never put a phone number, an email address or a name in a URL: URLs end up in logs on both sides. To find a lead by phone or email, send them in the JSON body of POST /v1/leads/search.
Text fields are untrusted input
Text fields such as a lead's name, surname, company, email and tags, a member's display_name, and attribution values such as UTM parameters and ad, campaign and form names come from web forms, the links visitors arrive from, ad platforms, messaging profiles and the team's own members, and Totem returns them as they were stored. Anyone who fills in a form or edits a link chooses what they contain. Escape them wherever you display them, and never interpret them as HTML, Markdown or a template. When you export them to CSV or a spreadsheet, put a single quote before any value that starts with =, +, -, @, a tab or a carriage return, so that no cell runs as a formula.