[{"data":1,"prerenderedAt":581},["ShallowReactive",2],{"developers:\u002Fdevelopers\u002Fpagination":3},{"doc":4,"lang":407,"nav":418,"alternatePath":522,"policy":523,"specVersion":567,"reference":7,"layout":568,"toc":569,"prev":578,"next":579,"operations":580,"overview":7,"heroExample":7},{"id":5,"title":6,"apiTag":7,"body":8,"description":404,"draft":405,"extension":406,"lang":407,"meta":408,"navTitle":7,"navigation":409,"order":410,"path":411,"rawbody":412,"section":413,"seo":414,"stem":415,"translationKey":416,"version":7,"__hash__":417},"developers_en\u002Fdevelopers\u002Fpagination.md","Lists and pagination",null,{"type":9,"value":10,"toc":391},"minimark",[11,31,36,39,49,113,135,140,164,173,182,191,199,203,209,213,220,250,256,264,276,280,287,316,333,337,360,364,378,381],[12,13,14,15,23,24,30],"p",{},"List endpoints, such as ",[16,17,19],"a",{"href":18},"\u002Fdevelopers\u002Freference-leads#leads.list",[20,21,22],"code",{},"GET \u002Fv1\u002Fleads"," or ",[16,25,27],{"href":26},"\u002Fdevelopers\u002Freference-calls#calls.list",[20,28,29],{},"GET \u002Fv1\u002Fcalls",", return a team's records one page at a time. This page explains how to read every page, how a list is ordered and filtered, and which endpoints are not paged at all.",[32,33,35],"h2",{"id":34},"pages-and-cursors","Pages and cursors",[12,37,38],{},"Every page comes in the same list envelope:",[40,41,47],"pre",{"className":42,"code":44,"language":45,"meta":46},[43],"language-json","{\n  \"object\": \"list\",\n  \"data\": [\n    {\n      \"id\": \"call_m2kUbFcayHlZ5Lgs9xZNmDS3c3Jvk\",\n      \"object\": \"call\",\n      \"livemode\": false,\n      \"team\": \"team_r9uYrIW4gSFJJ6hZQ9ip1UIpPaAD1CF7NR\",\n      \"lead\": \"lead_r72EfbRNUzuqORFJR0O8HxAwEXd93jGUTYMaMMXKSF0QbLS8CiUKJpEP34LFQG\",\n      \"direction\": \"inbound\",\n      \"started_at\": \"2026-09-28T10:17:00.000Z\",\n      \"duration_seconds\": 184,\n      \"outcome\": \"answered_ai\",\n      \"outcome_basis\": \"inbound_verdict\",\n      \"version\": 2,\n      \"updated_at\": \"2026-09-28T10:21:04.010Z\"\n    }\n  ],\n  \"has_more\": true,\n  \"next_cursor\": \"eyJ2IjoxLCJrIjoiY2FsbCIsImEiOiJjYWxsX20ya1ViRmNheUhsWjVMZ3M5eFpObURTM2MzSnZrIiwicyI6ImM0OTk4YTdlNDJkYiJ9357H5Y\"\n}\n","json","",[20,48,44],{"__ignoreMap":46},[50,51,52,68,99],"ul",{},[53,54,55,58,59,63,64,67],"li",{},[20,56,57],{},"limit"," sets the page size, from 1 to ",[60,61],"api-const",{"name":62},"PAGE_LIMIT.max",". The default is ",[60,65],{"name":66},"PAGE_LIMIT.default",". You can change it from one page to the next.",[53,69,70,71,74,75,78,79,82,83,86,87,74,89,92,93,74,95,98],{},"While ",[20,72,73],{},"has_more"," is ",[20,76,77],{},"true",", send ",[20,80,81],{},"next_cursor"," back as ",[20,84,85],{},"cursor"," to get the next page. On the last page, ",[20,88,73],{},[20,90,91],{},"false"," and ",[20,94,81],{},[20,96,97],{},"null",".",[53,100,101,102,104,105,108,109,74,111,98],{},"A page can hold fewer items than ",[20,103,57],{},", or none at all, and still say ",[20,106,107],{},"has_more: true",". Totem skips records it has not prepared yet, and ends a page early rather than make you wait. Keep going until ",[20,110,73],{},[20,112,91],{},[12,114,115,116,119,120,123,124,126,127,129,130,123,132,134],{},"A cursor is opaque: store it exactly as you received it, and never build or edit one. It works only with the endpoint, the filters and the team it came from. Any other use gets ",[20,117,118],{},"400 invalid_request",", with ",[20,121,122],{},"param"," set to ",[20,125,85],{},". A conversations cursor also stops working if the conversation it points at is erased: if a cursor you did not change gets ",[20,128,118],{}," with ",[20,131,122],{},[20,133,85],{},", start that list again from the first page.",[136,137,139],"h3",{"id":138},"read-every-page","Read every page",[12,141,142,143,146,147,74,149,151,152,155,156,159,160,163],{},"To read a whole list, keep the same filters and the same ",[20,144,145],{},"Totem-Team"," on every request, and stop only when ",[20,148,73],{},[20,150,91],{},". These loops list a team's calls with cURL and ",[20,153,154],{},"jq",", with TypeScript on Node.js 22.18 or later (an ES module, so ",[20,157,158],{},"await"," works at the top level), and with Python and ",[20,161,162],{},"requests",":",[40,165,171],{"className":166,"code":168,"filename":169,"language":170,"meta":46},[167],"language-bash","cursor=\"\"\nwhile :; do\n  page=$(curl -sS --fail-with-body \\\n    \"https:\u002F\u002Fapi.totemstudio.ai\u002Fv1\u002Fcalls?limit=100${cursor:+&cursor=$cursor}\" \\\n    -H \"Authorization: Bearer $TOTEM_API_KEY\" \\\n    -H \"Totem-Team: $TOTEM_TEAM_ID\") || { printf '%s\\n' \"$page\" >&2; break; }\n  printf '%s\\n' \"$page\" | jq -r '.data[].id'\n  [ \"$(printf '%s' \"$page\" | jq -r '.has_more')\" = true ] || break\n  next=$(printf '%s' \"$page\" | jq -er '.next_cursor') || { echo 'The page has no next_cursor' >&2; break; }\n  [ \"$next\" != \"$cursor\" ] || { echo 'The list repeated a cursor' >&2; break; }\n  cursor=$next\ndone\n","list-calls.sh","bash",[20,172,168],{"__ignoreMap":46},[40,174,180],{"className":175,"code":177,"filename":178,"language":179,"meta":46},[176],"language-ts","interface CallPage {\n  data: Array\u003C{ id: string }>\n  has_more: boolean\n  next_cursor: string | null\n}\n\nconst apiKey = process.env.TOTEM_API_KEY\nif (!apiKey) throw new Error('Set TOTEM_API_KEY on your server')\nconst headers: Record\u003Cstring, string> = { Authorization: `Bearer ${apiKey}` }\nif (process.env.TOTEM_TEAM_ID) headers['Totem-Team'] = process.env.TOTEM_TEAM_ID\n\nlet cursor: string | null = null\nwhile (true) {\n  const url = new URL('https:\u002F\u002Fapi.totemstudio.ai\u002Fv1\u002Fcalls')\n  url.searchParams.set('limit', '100')\n  if (cursor) url.searchParams.set('cursor', cursor)\n  const response = await fetch(url, { headers, signal: AbortSignal.timeout(30_000) })\n  if (!response.ok) {\n    const problem = (await response.json().catch(() => ({}))) as { code?: string }\n    throw new Error(`Totem API ${response.status} ${problem.code ?? response.statusText}, request ${response.headers.get('Totem-Request-Id') ?? 'n\u002Fa'}`)\n  }\n  const page = (await response.json()) as CallPage\n  \u002F\u002F Your code goes here. Print ids, never whole records: they hold personal data.\n  for (const call of page.data) console.log(call.id)\n  if (!page.has_more) break\n  if (!page.next_cursor || page.next_cursor === cursor) throw new Error('The list did not move forward')\n  cursor = page.next_cursor\n}\n","list-calls.ts","ts",[20,181,177],{"__ignoreMap":46},[40,183,189],{"className":184,"code":186,"filename":187,"language":188,"meta":46},[185],"language-python","# pip install requests\nimport os\n\nimport requests\n\nheaders = {\"Authorization\": f\"Bearer {os.environ['TOTEM_API_KEY']}\"}\nif os.environ.get(\"TOTEM_TEAM_ID\"):\n    headers[\"Totem-Team\"] = os.environ[\"TOTEM_TEAM_ID\"]\n\nparams = {\"limit\": 100}\nwhile True:\n    response = requests.get(\"https:\u002F\u002Fapi.totemstudio.ai\u002Fv1\u002Fcalls\", headers=headers, params=params, timeout=30)\n    if not response.ok:\n        try:\n            code = response.json().get(\"code\")\n        except ValueError:\n            code = None\n        raise RuntimeError(\n            f\"Totem API {response.status_code} {code or response.reason}, \"\n            f\"request {response.headers.get('Totem-Request-Id', 'n\u002Fa')}\"\n        )\n    page = response.json()\n    # Your code goes here. Print ids, never whole records: they hold personal data.\n    for call in page[\"data\"]:\n        print(call[\"id\"])\n    if not page[\"has_more\"]:\n        break\n    if not page[\"next_cursor\"] or page[\"next_cursor\"] == params.get(\"cursor\"):\n        raise RuntimeError(\"The list did not move forward\")\n    params[\"cursor\"] = page[\"next_cursor\"]\n","list_calls.py","python",[20,190,186],{"__ignoreMap":46},[12,192,193,194,198],{},"Each loop also stops when the API answers an error, or when a page repeats the cursor it was asked for, rather than asking for the same page forever. To keep a copy of a team, use the changes feed instead, as ",[16,195,197],{"href":196},"#keeping-a-copy","Keeping a copy"," explains.",[32,200,202],{"id":201},"order","Order",[12,204,205,206,98],{},"A list comes in a stable order that means nothing: it is not by date, and not by name. If you need an order, sort on your side, for example by ",[20,207,208],{},"created_at",[32,210,212],{"id":211},"filters","Filters",[12,214,215,219],{},[16,216,217],{"href":18},[20,218,22],{}," is the only list with filters. It takes at most one of them, with one value:",[50,221,222,232,241],{},[53,223,224,227,228,231],{},[20,225,226],{},"board",": the leads on one board (",[20,229,230],{},"brd_…",");",[53,233,234,237,238,231],{},[20,235,236],{},"stage",": the leads in one stage (",[20,239,240],{},"stg_…",[53,242,243,246,247,98],{},[20,244,245],{},"status",": the leads with one status, such as ",[20,248,249],{},"qualified",[40,251,254],{"className":252,"code":253,"language":170,"meta":46},[167],"curl \"https:\u002F\u002Fapi.totemstudio.ai\u002Fv1\u002Fleads?status=qualified\" \\\n  -H \"Authorization: Bearer $TOTEM_API_KEY\" \\\n  -H \"Totem-Team: $TOTEM_TEAM_ID\"\n",[20,255,253],{"__ignoreMap":46},[12,257,258,259,119,261,263],{},"Two filters in one request get ",[20,260,118],{},[20,262,122],{}," naming one of them. A board or stage the team does not have returns an empty page, not an error.",[12,265,266,267,270,271,275],{},"There is no ",[20,268,269],{},"updated_since"," filter. To find out what changed, use the changes feed: ",[16,272,274],{"href":273},"\u002Fdevelopers\u002Fsync","Keep a copy in sync"," explains it.",[32,277,279],{"id":278},"lists-that-come-whole","Lists that come whole",[12,281,282,283,23,285,163],{},"Three lists are short, so they come whole, in one response, and take no ",[20,284,57],{},[20,286,85],{},[50,288,289,298,307],{},[53,290,291,297],{},[16,292,294],{"href":293},"\u002Fdevelopers\u002Freference-account#teams.list",[20,295,296],{},"GET \u002Fv1\u002Fteams",", the teams your key reads;",[53,299,300,306],{},[16,301,303],{"href":302},"\u002Fdevelopers\u002Freference-account#members.list",[20,304,305],{},"GET \u002Fv1\u002Fmembers",", a team's members;",[53,308,309,315],{},[16,310,312],{"href":311},"\u002Fdevelopers\u002Freference-bookings#booking_services.list",[20,313,314],{},"GET \u002Fv1\u002Fbooking_services",", a team's bookable services.",[12,317,318,319,92,322,325,326,23,328,330,331,98],{},"They use the same envelope, with ",[20,320,321],{},"has_more: false",[20,323,324],{},"next_cursor: null",". Sending ",[20,327,57],{},[20,329,85],{}," to one of them gets ",[20,332,118],{},[32,334,336],{"id":335},"search-is-not-a-list","Search is not a list",[12,338,339,345,346,349,350,352,353,92,355,352,357,359],{},[16,340,342],{"href":341},"\u002Fdevelopers\u002Freference-leads#leads.search",[20,343,344],{},"POST \u002Fv1\u002Fleads\u002Fsearch"," finds leads by exact phone or email, sent in the JSON body. It returns at most ",[60,347],{"name":348},"SEARCH_MAX_RESULTS"," leads, in one response that never pages: ",[20,351,73],{}," is always ",[20,354,91],{},[20,356,81],{},[20,358,97],{},". When many records share a phone number or an email address, search may not return them all.",[32,361,363],{"id":362},"records-that-are-not-ready-yet","Records that are not ready yet",[12,365,366,367,373,374,377],{},"Totem prepares each record for the API shortly after it changes. Until then, lists and search skip a new record, and a request for it by id, such as ",[16,368,370],{"href":369},"\u002Fdevelopers\u002Freference-leads#leads.get",[20,371,372],{},"GET \u002Fv1\u002Fleads\u002F{id}",", answers ",[20,375,376],{},"404 resource_missing",". Do not retry in a loop: the changes feed delivers the record as soon as it is ready.",[32,379,197],{"id":380},"keeping-a-copy",[12,382,383,384,388,389,198],{},"A list shows the records as they are now. It is the right tool to look something up, not to keep a copy: re-reading every list to find changes costs far more of your ",[16,385,387],{"href":386},"\u002Fdevelopers\u002Frate-limits","daily quota"," and still misses removals. To keep a copy of a team, follow the changes feed, as ",[16,390,274],{"href":273},{"title":46,"searchDepth":392,"depth":392,"links":393},3,[394,398,399,400,401,402,403],{"id":34,"depth":395,"text":35,"children":396},2,[397],{"id":138,"depth":392,"text":139},{"id":201,"depth":395,"text":202},{"id":211,"depth":395,"text":212},{"id":278,"depth":395,"text":279},{"id":335,"depth":395,"text":336},{"id":362,"depth":395,"text":363},{"id":380,"depth":395,"text":197},"How list endpoints return records a page at a time, how to read every page, and what order and filters a list supports.",false,"md","en",{},true,"20","\u002Fdevelopers\u002Fpagination","---\ntitle: Lists and pagination\ndescription: How list endpoints return records a page at a time, how to read every page, and what order and filters a list supports.\nsection: guides\norder: 20\ntranslationKey: pagination\nlang: en\ndraft: false\n---\n\nList endpoints, such as [`GET \u002Fv1\u002Fleads`](\u002Fdevelopers\u002Freference-leads#leads.list) or [`GET \u002Fv1\u002Fcalls`](\u002Fdevelopers\u002Freference-calls#calls.list), return a team's records one page at a time. This page explains how to read every page, how a list is ordered and filtered, and which endpoints are not paged at all.\n\n## Pages and cursors\n\nEvery page comes in the same list envelope:\n\n```json\n{\n  \"object\": \"list\",\n  \"data\": [\n    {\n      \"id\": \"call_m2kUbFcayHlZ5Lgs9xZNmDS3c3Jvk\",\n      \"object\": \"call\",\n      \"livemode\": false,\n      \"team\": \"team_r9uYrIW4gSFJJ6hZQ9ip1UIpPaAD1CF7NR\",\n      \"lead\": \"lead_r72EfbRNUzuqORFJR0O8HxAwEXd93jGUTYMaMMXKSF0QbLS8CiUKJpEP34LFQG\",\n      \"direction\": \"inbound\",\n      \"started_at\": \"2026-09-28T10:17:00.000Z\",\n      \"duration_seconds\": 184,\n      \"outcome\": \"answered_ai\",\n      \"outcome_basis\": \"inbound_verdict\",\n      \"version\": 2,\n      \"updated_at\": \"2026-09-28T10:21:04.010Z\"\n    }\n  ],\n  \"has_more\": true,\n  \"next_cursor\": \"eyJ2IjoxLCJrIjoiY2FsbCIsImEiOiJjYWxsX20ya1ViRmNheUhsWjVMZ3M5eFpObURTM2MzSnZrIiwicyI6ImM0OTk4YTdlNDJkYiJ9357H5Y\"\n}\n```\n\n- `limit` sets the page size, from 1 to :api-const{name=\"PAGE_LIMIT.max\"}. The default is :api-const{name=\"PAGE_LIMIT.default\"}. You can change it from one page to the next.\n- While `has_more` is `true`, send `next_cursor` back as `cursor` to get the next page. On the last page, `has_more` is `false` and `next_cursor` is `null`.\n- A page can hold fewer items than `limit`, or none at all, and still say `has_more: true`. Totem skips records it has not prepared yet, and ends a page early rather than make you wait. Keep going until `has_more` is `false`.\n\nA cursor is opaque: store it exactly as you received it, and never build or edit one. It works only with the endpoint, the filters and the team it came from. Any other use gets `400 invalid_request`, with `param` set to `cursor`. A conversations cursor also stops working if the conversation it points at is erased: if a cursor you did not change gets `400 invalid_request` with `param` set to `cursor`, start that list again from the first page.\n\n### Read every page\n\nTo read a whole list, keep the same filters and the same `Totem-Team` on every request, and stop only when `has_more` is `false`. These loops list a team's calls with cURL and `jq`, with TypeScript on Node.js 22.18 or later (an ES module, so `await` works at the top level), and with Python and `requests`:\n\n```bash [list-calls.sh]\ncursor=\"\"\nwhile :; do\n  page=$(curl -sS --fail-with-body \\\n    \"https:\u002F\u002Fapi.totemstudio.ai\u002Fv1\u002Fcalls?limit=100${cursor:+&cursor=$cursor}\" \\\n    -H \"Authorization: Bearer $TOTEM_API_KEY\" \\\n    -H \"Totem-Team: $TOTEM_TEAM_ID\") || { printf '%s\\n' \"$page\" >&2; break; }\n  printf '%s\\n' \"$page\" | jq -r '.data[].id'\n  [ \"$(printf '%s' \"$page\" | jq -r '.has_more')\" = true ] || break\n  next=$(printf '%s' \"$page\" | jq -er '.next_cursor') || { echo 'The page has no next_cursor' >&2; break; }\n  [ \"$next\" != \"$cursor\" ] || { echo 'The list repeated a cursor' >&2; break; }\n  cursor=$next\ndone\n```\n\n```ts [list-calls.ts]\ninterface CallPage {\n  data: Array\u003C{ id: string }>\n  has_more: boolean\n  next_cursor: string | null\n}\n\nconst apiKey = process.env.TOTEM_API_KEY\nif (!apiKey) throw new Error('Set TOTEM_API_KEY on your server')\nconst headers: Record\u003Cstring, string> = { Authorization: `Bearer ${apiKey}` }\nif (process.env.TOTEM_TEAM_ID) headers['Totem-Team'] = process.env.TOTEM_TEAM_ID\n\nlet cursor: string | null = null\nwhile (true) {\n  const url = new URL('https:\u002F\u002Fapi.totemstudio.ai\u002Fv1\u002Fcalls')\n  url.searchParams.set('limit', '100')\n  if (cursor) url.searchParams.set('cursor', cursor)\n  const response = await fetch(url, { headers, signal: AbortSignal.timeout(30_000) })\n  if (!response.ok) {\n    const problem = (await response.json().catch(() => ({}))) as { code?: string }\n    throw new Error(`Totem API ${response.status} ${problem.code ?? response.statusText}, request ${response.headers.get('Totem-Request-Id') ?? 'n\u002Fa'}`)\n  }\n  const page = (await response.json()) as CallPage\n  \u002F\u002F Your code goes here. Print ids, never whole records: they hold personal data.\n  for (const call of page.data) console.log(call.id)\n  if (!page.has_more) break\n  if (!page.next_cursor || page.next_cursor === cursor) throw new Error('The list did not move forward')\n  cursor = page.next_cursor\n}\n```\n\n```python [list_calls.py]\n# pip install requests\nimport os\n\nimport requests\n\nheaders = {\"Authorization\": f\"Bearer {os.environ['TOTEM_API_KEY']}\"}\nif os.environ.get(\"TOTEM_TEAM_ID\"):\n    headers[\"Totem-Team\"] = os.environ[\"TOTEM_TEAM_ID\"]\n\nparams = {\"limit\": 100}\nwhile True:\n    response = requests.get(\"https:\u002F\u002Fapi.totemstudio.ai\u002Fv1\u002Fcalls\", headers=headers, params=params, timeout=30)\n    if not response.ok:\n        try:\n            code = response.json().get(\"code\")\n        except ValueError:\n            code = None\n        raise RuntimeError(\n            f\"Totem API {response.status_code} {code or response.reason}, \"\n            f\"request {response.headers.get('Totem-Request-Id', 'n\u002Fa')}\"\n        )\n    page = response.json()\n    # Your code goes here. Print ids, never whole records: they hold personal data.\n    for call in page[\"data\"]:\n        print(call[\"id\"])\n    if not page[\"has_more\"]:\n        break\n    if not page[\"next_cursor\"] or page[\"next_cursor\"] == params.get(\"cursor\"):\n        raise RuntimeError(\"The list did not move forward\")\n    params[\"cursor\"] = page[\"next_cursor\"]\n```\n\nEach loop also stops when the API answers an error, or when a page repeats the cursor it was asked for, rather than asking for the same page forever. To keep a copy of a team, use the changes feed instead, as [Keeping a copy](#keeping-a-copy) explains.\n\n## Order\n\nA list comes in a stable order that means nothing: it is not by date, and not by name. If you need an order, sort on your side, for example by `created_at`.\n\n## Filters\n\n[`GET \u002Fv1\u002Fleads`](\u002Fdevelopers\u002Freference-leads#leads.list) is the only list with filters. It takes at most one of them, with one value:\n\n- `board`: the leads on one board (`brd_…`);\n- `stage`: the leads in one stage (`stg_…`);\n- `status`: the leads with one status, such as `qualified`.\n\n```bash\ncurl \"https:\u002F\u002Fapi.totemstudio.ai\u002Fv1\u002Fleads?status=qualified\" \\\n  -H \"Authorization: Bearer $TOTEM_API_KEY\" \\\n  -H \"Totem-Team: $TOTEM_TEAM_ID\"\n```\n\nTwo filters in one request get `400 invalid_request`, with `param` naming one of them. A board or stage the team does not have returns an empty page, not an error.\n\nThere is no `updated_since` filter. To find out what changed, use the changes feed: [Keep a copy in sync](\u002Fdevelopers\u002Fsync) explains it.\n\n## Lists that come whole\n\nThree lists are short, so they come whole, in one response, and take no `limit` or `cursor`:\n\n- [`GET \u002Fv1\u002Fteams`](\u002Fdevelopers\u002Freference-account#teams.list), the teams your key reads;\n- [`GET \u002Fv1\u002Fmembers`](\u002Fdevelopers\u002Freference-account#members.list), a team's members;\n- [`GET \u002Fv1\u002Fbooking_services`](\u002Fdevelopers\u002Freference-bookings#booking_services.list), a team's bookable services.\n\nThey use the same envelope, with `has_more: false` and `next_cursor: null`. Sending `limit` or `cursor` to one of them gets `400 invalid_request`.\n\n## Search is not a list\n\n[`POST \u002Fv1\u002Fleads\u002Fsearch`](\u002Fdevelopers\u002Freference-leads#leads.search) finds leads by exact phone or email, sent in the JSON body. It returns at most :api-const{name=\"SEARCH_MAX_RESULTS\"} leads, in one response that never pages: `has_more` is always `false` and `next_cursor` is always `null`. When many records share a phone number or an email address, search may not return them all.\n\n## Records that are not ready yet\n\nTotem prepares each record for the API shortly after it changes. Until then, lists and search skip a new record, and a request for it by id, such as [`GET \u002Fv1\u002Fleads\u002F{id}`](\u002Fdevelopers\u002Freference-leads#leads.get), answers `404 resource_missing`. Do not retry in a loop: the changes feed delivers the record as soon as it is ready.\n\n## Keeping a copy\n\nA list shows the records as they are now. It is the right tool to look something up, not to keep a copy: re-reading every list to find changes costs far more of your [daily quota](\u002Fdevelopers\u002Frate-limits) and still misses removals. To keep a copy of a team, follow the changes feed, as [Keep a copy in sync](\u002Fdevelopers\u002Fsync) explains.\n","guides",{"title":6,"description":404},"developers\u002Fpagination","pagination","wEfiy22wOhS-YHlEb22UiNYps2qEmRfloJuwXLooOpI",[419,434,455,503],{"section":420,"items":421},"start",[422,426,430],{"path":423,"title":424,"description":425},"\u002Fdevelopers","Overview","Read the leads, calls, conversations and bookings of your Totem teams from your own systems, and keep a copy in sync.",{"path":427,"title":428,"description":429},"\u002Fdevelopers\u002Fquickstart","Quickstart","Make your first requests to the Totem API in a few minutes. Check your key, find your team, list leads and start a sync.",{"path":431,"title":432,"description":433},"\u002Fdevelopers\u002Fauthentication","Authentication","Secret keys and where they may live, the Totem-Team header for keys with several teams, IP allowlists, and how to roll or revoke a key.",{"section":413,"items":435},[436,440,442,443,447,451],{"path":437,"title":438,"description":439},"\u002Fdevelopers\u002Ftypescript","TypeScript SDK","Use the official TypeScript client to read your Totem data, paginate lists and handle API errors from your server.",{"path":273,"title":274,"description":441},"Copy a team with the changes feed and keep the copy current. Apply pages safely, recover from errors, and start from a reference client that Totem tests.",{"path":411,"title":6,"description":404},{"path":444,"title":445,"description":446},"\u002Fdevelopers\u002Fcoverage","Data coverage","What Totem records, since when, and how to tell an empty value from a fact that was never recorded.",{"path":448,"title":449,"description":450},"\u002Fdevelopers\u002Ftesting","Testing and go-live","How to build and test an integration safely while the API has no test environment, and what to check before you go live.",{"path":452,"title":453,"description":454},"\u002Fdevelopers\u002Fsecurity","Security and personal data","How Totem protects API keys and the data the API serves, what it records about each key, where the API runs, and what to do with the personal data you copy.",{"section":456,"items":457},"reference",[458,462,466,469,474,479,484,489,494,499],{"path":459,"title":460,"description":461},"\u002Fdevelopers\u002Fconventions","Conventions","The notation these docs use, and the rules every response follows: ids, timestamps, empty values, amounts, enums, versions, stability, errors and untrusted text.",{"path":463,"title":464,"description":465},"\u002Fdevelopers\u002Ferrors","Errors","The error format, how your integration should handle each kind of error, and every error code with when it happens and what to do.",{"path":386,"title":467,"description":468},"Rate limits","How much a key can read per minute and per day, what counts toward each limit, how to retry after a 429, and how long an initial sync takes.",{"path":470,"title":471,"description":472,"apiTag":473},"\u002Fdevelopers\u002Freference-account","Account, teams and members","Check that a key works and what it can read, list its teams with their time zones, list each team's members, and check that the API is up.","account",{"path":475,"title":476,"description":477,"apiTag":478},"\u002Fdevelopers\u002Freference-leads","Leads, boards and activities","List, fetch and search a team's leads and clients, read each lead's activities, and list the boards and stages they move through.","leads",{"path":480,"title":481,"description":482,"apiTag":483},"\u002Fdevelopers\u002Freference-calls","Calls","List and fetch a team's calls, with their direction, timing and a normalized outcome. No recordings or transcripts.","calls",{"path":485,"title":486,"description":487,"apiTag":488},"\u002Fdevelopers\u002Freference-conversations","Conversations","List and fetch a team's conversations on every channel, with their status, assignee, whether a person has taken them over and when the last messages came and went. Never message content.","conversations",{"path":490,"title":491,"description":492,"apiTag":493},"\u002Fdevelopers\u002Freference-bookings","Bookings and services","List and fetch a team's bookings, and list the services its customers can book.","bookings",{"path":495,"title":496,"description":497,"apiTag":498},"\u002Fdevelopers\u002Freference-sync","Changes feed","One endpoint that copies a team and then returns every change, page by page.","sync",{"path":500,"title":501,"description":502},"\u002Fdevelopers\u002Fobjects","All objects","Every object the API returns, field by field, with an example of each.",{"section":504,"items":505},"resources",[506,510,514,518],{"path":507,"title":508,"description":509},"\u002Fdevelopers\u002Fchangelog","Changelog","Changes to the Totem API, newest first, each under the date it shipped.",{"path":511,"title":512,"description":513},"\u002Fdevelopers\u002Fsupport","Support","How to get help with the Totem API, what to include in a request, and what to do if a key leaks.",{"path":515,"title":516,"description":517},"\u002Fdevelopers\u002Fglossary","Glossary","The terms these docs use, defined in one place, each with a link to the page that explains it.",{"path":519,"title":520,"description":521},"\u002Fdevelopers\u002Ffaq","FAQ","Short answers to the questions developers ask most about the Totem API, with links to the details.","\u002Fdevelopers\u002Fes\u002Fpagination",{"ACTIVATION_MARGIN_MS":524,"ADMIN_ACCESS_DISCLOSURE_VERSION":525,"AUDIT_RETENTION_DAYS":526,"AUTH_FAILURE_LIMITS":527,"CAPTURE_LAG_FLOOR_MS":531,"CAPTURE_LAG_P99_MS":7,"CURSOR_MAX_AGE_DAYS":532,"DIRTY_TTL_DAYS":532,"FAMILY_TEAM_SCOPE":533,"FAMILY_TEAM_SCOPE_OPTIONS":534,"FEED_SETTLE_LAG_MS":536,"GATE_REFRESH_MS":524,"INTEGRATION_NAME_LENGTH":537,"INTEGRATION_PURPOSE_LENGTH":539,"KEY_CACHE_TTL_MS":542,"KEY_STATS_RETENTION_DAYS":543,"LEAD_RESPONSE_READ_BUDGET":544,"LIST_SCAN_MAX":547,"MAX_FAMILY_INTEGRATION_TEAMS":532,"MAX_INTEGRATIONS_PER_FAMILY":548,"MAX_IP_ALLOWLIST":529,"PAGE_LIMIT":549,"PLATFORM_CONFIG_TTL_MS":552,"REMOVAL_RETENTION_DAYS":553,"REQUEST_BODY_MAX_BYTES":554,"ROLL_GRACE_DEFAULT_HOURS":555,"ROLL_GRACE_HOURS":556,"SEARCH_MAX_RESULTS":548,"SENSITIVE_SECTOR_POLICY":559,"SENSITIVE_SECTOR_POLICY_OPTIONS":560,"SYNC_DAILY_BUDGET":562,"SYNC_VELOCITY":564,"TEAM_CACHE_TTL_MS":566},60000,"2026-10-17",365,{"perIpPerMinute":528,"perLookupIdPerMinute":529,"banMs":530},60,20,600000,300000,30,"explicit_list",[533,535],"all_family_teams",10000,{"min":395,"max":538},80,{"min":540,"max":541},1,280,5000,90,{"conversations":529,"messageReads":545,"calls":546},200,50,1000,10,{"default":550,"max":551},25,100,15000,45,16384,24,[557,540,555,558],0,168,"exclude",[559,561],"allow",{"floor":563,"recordsMultiplier":392},250000,{"perMinute":565,"burst":536},2000,30000,"1.0a","guide",[570,571,572,573,574,575,576,577],{"id":34,"text":35,"depth":395},{"id":138,"text":139,"depth":392},{"id":201,"text":202,"depth":395},{"id":211,"text":212,"depth":395},{"id":278,"text":279,"depth":395},{"id":335,"text":336,"depth":395},{"id":362,"text":363,"depth":395},{"id":380,"text":197,"depth":395},{"path":273,"title":274,"section":413},{"path":444,"title":445,"section":413},[],1792502789349]