Secret keys
Every request carries a secret key in the Authorization header:
GET /v1/me HTTP/1.1
Host: api.totemstudio.ai
Authorization: Bearer tsk_live_…A secret key starts with tsk_live_, followed by a lookup id and the secret itself. Totem shows it once, when it is created, and keeps only a one-way digest, so a lost key cannot be recovered: roll it instead. Settings → Developers shows the last four characters of each key so you can tell them apart. A key does not expire on its own: it works until you roll it or revoke it.
Keys that start with tsk_test_ are reserved for future test environments. Today the API refuses them with 401 invalid_credentials.
Create a key
A team administrator opens Settings → Developers, chooses Create API key, gives the integration a name and confirms read-only access to the current team. Advanced options hold the optional description, IP restrictions and, for a family administrator, more teams of the organization (Teams and the Totem-Team header explains who may manage what). If the administrator has not signed in recently, Totem asks them to sign in again first. Totem then shows the integration's secret key once. Copy it straight into your server's secret store.
The API is in Beta and available without an invitation. Create API key works when:
- the person creating the key has the administrator role on the team and a verified email address;
- the organization is on Pro or a higher plan, or has API access agreed with Totem;
- the team holds data (a billing-only account does not) and is neither disabled nor archived;
- the team has completed its legal setup: a team administrator saves its privacy policy, processing purposes and business sector in Privacy & agreements;
- the team is not, for now, in a sensitive business sector, such as health care.
If Create API key does not appear, check the role, the email address and that the team is not a billing-only account; when the plan is the reason, Settings says so. The button is disabled once the organization holds 10 active integrations. If Totem refuses a team, Settings names it and says why. Otherwise, contact support.
Where a key may live
A key reads administrator-level data, so keep it on a server you control:
- in a secret manager, or an environment variable that only the process calling the API can read;
- never in a browser, a mobile app, client-side JavaScript or a public repository;
- never in a URL, a log line, a command you type in a shell or a support request.
The API sends no CORS headers, and it answers 400 secret_key_in_browser to a request that carries both a secret key and an Origin header. If a key has ever been in a web page, a mobile app or a browser extension, treat it as leaked: roll it and tick This key may have been compromised, as Roll and revoke explains.
What a key can read
Every v1.0a key is read-only and carries the same fixed set of read permissions: there is nothing to choose. GET /v1/me lists them in scopes: teams:read, crm:read, calls:read, messages:read and calendar:read. messages:read covers conversation metadata only; no endpoint returns the text of a message.
A key reads everything an administrator of its teams can read, including channels and records marked Private. The administrator who creates the integration confirms this before the key is issued. Give an integration only the teams it needs.
Teams and the Totem-Team header
An integration covers one team, or a list of teams of the same organization (a billing account and its sub-teams; in Settings → Developers, the access scope Selected family teams). The list is explicit: a new team is never added on its own. An administrator adds it, and the administrators who manage the integration, and those of every team it covers, are told.
Who may manage an integration depends on what it covers:
- A team administrator creates and manages an integration for their own team.
- Only a family administrator can create or change an integration that covers several teams: an administrator of the billing account who is also listed on it as a family administrator. The administrators of each team it covers see it in Settings, read-only.
- An organization, or a team outside any organization, can hold up to 10 active integrations, counting all of its teams. Revoke one you no longer use to free a place.
GET /v1/teams lists the teams a key can read, each with an id that starts with team_.
GET /v1/meandGET /v1/teamsdescribe the key and never take the header.- Requests about one record, such as
GET /v1/leads/{id}, need no header: the record's id already names its team. - Requests that read one team's collection (lists such as
GET /v1/leads,GET /v1/boardsorGET /v1/members,POST /v1/leads/searchandGET /v1/sync/changes) use the key's only team. When the key covers several teams, name one in theTotem-Teamheader:
curl "https://api.totemstudio.ai/v1/leads?limit=50" \
-H "Authorization: Bearer $TOTEM_API_KEY" \
-H "Totem-Team: team_r9uYrIW4gSFJJ6hZQ9ip1UIpPaAD1CF7NR"The samples in the API reference send Totem-Team: $TOTEM_TEAM_ID on these requests. With a one-team key, leave TOTEM_TEAM_ID unset and they leave the header out: cURL drops a header whose value is empty, and the Node.js and Python samples check the variable.
A key with one team may send the header too, as long as it names that team. What each answer means for your copy of the team:
- Without the header, a key with several teams gets
400 team_required. A header that is not a team id, or that appears twice, gets400 invalid_request. - A team the key does not cover, or that no longer exists, gets
404 team_not_accessible. ReadGET /v1/teams: if the team is no longer listed, delete its data and its cursor. - A one-team key covers its team only while the team stays where it was when the key was created: in the same organization, or outside any organization. If the team joins an organization, leaves it or moves to another one, the key gets
404 team_not_accessibleandGET /v1/teamsno longer lists the team. Revoke the key in Settings → Developers and create a new one. - A team that is disabled or archived, or whose API access Totem has paused, gets
403 team_state_restricted. Keep the team's data and its cursor, skip it for this run and try again later. A disabled team or paused access can come back; an archived team stays restricted unless Totem restores it. While a team is restricted, the changes feed delivers nothing for it, removals included, so if the restriction lasts longer than your retention policy allows, delete your copy. - For now, Totem does not export teams in a sensitive business sector, such as health care. Such a team cannot be added to an integration, and a key that already covers one gets
403 sector_restrictedfor it. Skip the team: waiting does not change it. The changes feed delivers nothing for it, removals included, so keep what you already have only as long as your retention policy allows, then delete your copy.
IP allowlist
An integration can be limited to up to 20 IP addresses or ranges in CIDR notation. A request from any other address gets 403 ip_not_allowed. Set the list in Settings → Developers, under Advanced options when you create the key or with Edit integration later; changing it asks for a recent sign-in.
- The API is reachable over IPv4 only, so list the public IPv4 addresses your requests leave from. An IPv6 entry never matches today.
- Write one address or range per line. A range must start at its network address, such as
203.0.113.0/24, and/0is refused. An empty list allows any address. - If your platform has no fixed outgoing address, as with many serverless hosts, send its traffic through a static IP first.
- After you save the list, call
GET /v1/mefrom the deployed server to check it.
Totem checks the address only after the key, so a wrong key gets 401 invalid_credentials wherever it comes from.
Roll and revoke
Roll a key with Roll key to replace it. Totem issues a new key and keeps the old one working for the time you choose in the dialog: 1 hour, 24 hours (the default) or 7 days. Deploy the new key within that time and nothing stops. When that time is over, the old key answers 401 key_expired. Rolling again revokes any old key still in its grace period, and the dialog asks you to confirm it.
To stop the old key at once, tick This key may have been compromised when you roll, or choose End old key now later. The old key then answers 401 invalid_credentials.
If a key may have leaked, roll it and tick This key may have been compromised: the old key is revoked at once, with no grace period. Then tell us, as Support explains.
Revoke an integration with Revoke access to stop all of its keys. A revoked key stops working within 5 seconds, and revoking cannot be undone. The data you already copied stays in your systems and receives no more removals from Totem: delete it, or keep it only as long as the customer's own rules allow. Security and personal data explains what to do with it.
Creating or rolling a key, adding a team and changing the IP allowlist ask the administrator to sign in again if they have not done so recently. Every change appears in the integration's Recent activity, and the administrators who manage the integration, and those of every team it covers, are told about each one, in Totem and by email. Revoking and End old key now never ask for a sign-in: anyone allowed to manage the integration can stop it at once.
Failed attempts
Totem counts failed attempts: requests with a key that is missing, malformed, unknown or wrong. After as few as 60 failures from one IP address in a minute, or 20 with one key, Totem can lock that address or that key for 600 seconds. While it is locked, requests get 429 rate_limited with Retry-After set to the full lock length; the lock may end sooner.
A revoked or expired key, an integration that is no longer active and a request from outside the allowlist are not counted. While a key is locked, requests with its real secret keep working: only guesses are refused. While an address is locked, every request from it is refused. If you are locked out, fix the key first, then wait for Retry-After before you try again.
Many requests without a well-formed key from one address can also make the firewall in front of the API answer 429 itself, with no Retry-After and no JSON body, for up to 10 minutes. Stop the loop, fix the header, then retry with exponential backoff, as Errors explains.
Authentication errors
Every code links to its full entry on the Errors page.
| Status | code | What to do |
|---|---|---|
| 400 | secret_key_in_browser | The request came from a browser. Call the API from your server, and if the key was ever in a web page or an app, roll it as compromised. |
| 400 | team_required | The key covers several teams. Send Totem-Team. |
| 401 | invalid_credentials | The key is missing, malformed, unknown, wrong or revoked. Check the header and the key. |
| 401 | key_expired | The key was rolled and its grace period is over. Use the new key. |
| 403 | ip_not_allowed | The request came from an address outside the integration's allowlist. The API is reachable over IPv4 only. |
| 403 | plan_not_entitled | The organization's plan does not include the API. Keep your data and talk to the customer; the feed delivers no removals until access returns, so if that lasts longer than your retention policy allows, delete your copy. |
| 403 | insufficient_scope | The key lacks a permission this endpoint needs. Every v1.0a key has the same permissions, so tell us, with the request id. |
| 403 | team_state_restricted | The team is disabled, archived or paused. Keep its data and its cursor, skip it for this run and try again later; if the restriction lasts longer than your retention policy allows, delete your copy. |
| 403 | sector_restricted | For now, Totem does not export teams in a sensitive sector, such as health care. Skip the team: waiting does not change it. The feed delivers no removals for it, so keep what you have only as long as your retention policy allows, then delete your copy. |
| 404 | team_not_accessible | The key does not cover this team. If GET /v1/teams no longer lists it, delete its data and its cursor. |
| 429 | rate_limited | Too many failed attempts, or too many requests. Wait for the seconds in Retry-After. A 429 without a JSON body comes from the firewall: back off exponentially. |