[{"data":1,"prerenderedAt":629},["ShallowReactive",2],{"developers:\u002Fdevelopers\u002Fquickstart":3},{"doc":4,"lang":470,"nav":481,"alternatePath":572,"policy":573,"specVersion":617,"reference":7,"layout":618,"toc":619,"prev":626,"next":627,"operations":628,"overview":7,"heroExample":7},{"id":5,"title":6,"apiTag":7,"body":8,"description":467,"draft":468,"extension":469,"lang":470,"meta":471,"navTitle":7,"navigation":472,"order":473,"path":474,"rawbody":475,"section":476,"seo":477,"stem":478,"translationKey":479,"version":7,"__hash__":480},"developers_en\u002Fdevelopers\u002Fquickstart.md","Quickstart",null,{"type":9,"value":10,"toc":457},"minimark",[11,15,20,23,79,90,93,103,110,117,121,131,137,140,148,158,169,178,187,199,203,212,218,224,227,233,244,265,269,279,285,296,302,324,328,338,344,354,394,406,410],[12,13,14],"p",{},"This guide takes you from a new key to your first leads in four steps.",[16,17,19],"h2",{"id":18},"before-you-begin","Before you begin",[12,21,22],{},"You need:",[24,25,26,44,62],"ul",{},[27,28,29,33,34,43],"li",{},[30,31,32],"strong",{},"A Totem team on Pro or a higher plan",", or with API access agreed with Totem, whose legal setup is saved in ",[35,36,40],"a",{"href":37,"rel":38},"https:\u002F\u002Fapp.totemstudio.ai\u002Fprivacy-setup",[39],"nofollow",[30,41,42],{},"Privacy & agreements",".",[27,45,46,49,50,57,58,61],{},[30,47,48],{},"A secret key."," A team administrator opens ",[35,51,54],{"href":52,"rel":53},"https:\u002F\u002Fapp.totemstudio.ai\u002Fsettings?tab=developers",[39],[30,55,56],{},"Settings → Developers",", chooses ",[30,59,60],{},"Create API key"," and confirms read-only access to the team. Totem shows the key once: copy it straight into your server's secret store.",[27,63,64,70,71,78],{},[30,65,66],{},[67,68,69],"code",{},"curl",", and ",[35,72,75],{"href":73,"rel":74},"https:\u002F\u002Fjqlang.org",[39],[67,76,77],{},"jq"," for step 2.",[12,80,81,82,84,85,89],{},"If ",[30,83,60],{}," is missing or disabled, ",[35,86,88],{"href":87},"\u002Fdevelopers\u002Fauthentication#create-a-key","Create a key"," lists every requirement and what Settings says about each.",[12,91,92],{},"To follow this guide, load the key into an environment variable without typing it on the command line, so it never stays in your shell history. Run this, paste the key and press Enter; nothing is shown:",[94,95,101],"pre",{"className":96,"code":98,"language":99,"meta":100},[97],"language-bash","read -rs TOTEM_API_KEY && export TOTEM_API_KEY\n","bash","",[67,102,98],{"__ignoreMap":100},[12,104,105,106,109],{},"On a server, have your secret store provide ",[67,107,108],{},"TOTEM_API_KEY"," to the process instead.",[111,112,114],"dev-callout",{"type":113},"warning",[12,115,116],{},"The key reads administrator-level data. Keep it on your server: never in a browser, a mobile app, a URL or a public repository.",[16,118,120],{"id":119},"_1-check-your-key","1. Check your key",[12,122,123,124,130],{},"Call ",[35,125,127],{"href":126},"\u002Fdevelopers\u002Freference-account#me.get",[67,128,129],{},"GET \u002Fv1\u002Fme",":",[94,132,135],{"className":133,"code":134,"language":99,"meta":100},[97],"curl https:\u002F\u002Fapi.totemstudio.ai\u002Fv1\u002Fme \\\n  -H \"Authorization: Bearer $TOTEM_API_KEY\"\n",[67,136,134],{"__ignoreMap":100},[12,138,139],{},"The answer describes your key: the integration it belongs to, the last four characters of the key, the teams it reads and its permissions.",[94,141,146],{"className":142,"code":144,"language":145,"meta":100},[143],"language-json","{\n  \"object\": \"integration\",\n  \"id\": \"prn_m1NFIcoIU8smXfqR4ymgt6j07TfRF\",\n  \"livemode\": false,\n  \"name\": \"Example CRM sync\",\n  \"key\": {\n    \"last4\": \"Q7xZ\"\n  },\n  \"teams\": [\n    \"team_r9uYrIW4gSFJJ6hZQ9ip1UIpPaAD1CF7NR\"\n  ],\n  \"scopes\": [\n    \"teams:read\",\n    \"crm:read\",\n    \"calls:read\",\n    \"messages:read\",\n    \"calendar:read\"\n  ]\n}\n","json",[67,147,144],{"__ignoreMap":100},[12,149,150,151,154,155,43],{},"Your answer carries your own values and ",[67,152,153],{},"\"livemode\": true",". The examples in these docs are fictitious, so they say ",[67,156,157],{},"\"livemode\": false",[12,159,160,161,164,165,168],{},"The same request from Node.js 22 or later, as an ES module, and from Python 3 with the ",[67,162,163],{},"requests"," library (",[67,166,167],{},"pip install requests","), which the samples of the reference use too:",[94,170,176],{"className":171,"code":173,"filename":174,"language":175,"meta":100},[172],"language-js","const response = await fetch('https:\u002F\u002Fapi.totemstudio.ai\u002Fv1\u002Fme', {\n  headers: { Authorization: `Bearer ${process.env.TOTEM_API_KEY}` },\n  signal: AbortSignal.timeout(30_000),\n})\n\u002F\u002F Read the body as text: a refusal from the firewall in front of the API is not JSON.\nconsole.log(response.status, await response.text())\n","me.mjs","js",[67,177,173],{"__ignoreMap":100},[94,179,185],{"className":180,"code":182,"filename":183,"language":184,"meta":100},[181],"language-python","import os\n\nimport requests\n\nresponse = requests.get(\n    \"https:\u002F\u002Fapi.totemstudio.ai\u002Fv1\u002Fme\",\n    headers={\"Authorization\": f\"Bearer {os.environ['TOTEM_API_KEY']}\"},\n    timeout=30,\n)\n# Print the body as text: a refusal from the firewall in front of the API is not JSON.\nprint(response.status_code, response.text)\n","me.py","python",[67,186,182],{"__ignoreMap":100},[12,188,189,190,193,194,198],{},"A ",[67,191,192],{},"401"," means the key is missing, mistyped or no longer valid. ",[35,195,197],{"href":196},"\u002Fdevelopers\u002Fauthentication","Authentication"," lists every case.",[16,200,202],{"id":201},"_2-find-your-team","2. Find your team",[12,204,205,211],{},[35,206,208],{"href":207},"\u002Fdevelopers\u002Freference-account#teams.list",[67,209,210],{},"GET \u002Fv1\u002Fteams"," lists the teams your key reads:",[94,213,216],{"className":214,"code":215,"language":99,"meta":100},[97],"curl https:\u002F\u002Fapi.totemstudio.ai\u002Fv1\u002Fteams \\\n  -H \"Authorization: Bearer $TOTEM_API_KEY\"\n",[67,217,215],{"__ignoreMap":100},[94,219,222],{"className":220,"code":221,"language":145,"meta":100},[143],"{\n  \"object\": \"list\",\n  \"data\": [\n    {\n      \"id\": \"team_r9uYrIW4gSFJJ6hZQ9ip1UIpPaAD1CF7NR\",\n      \"object\": \"team\",\n      \"livemode\": false,\n      \"name\": \"Example Dental\",\n      \"timezone\": \"America\u002FNew_York\",\n      \"default_country\": \"US\"\n    }\n  ],\n  \"has_more\": false,\n  \"next_cursor\": null\n}\n",[67,223,221],{"__ignoreMap":100},[12,225,226],{},"Keep the first team's id for the next steps. If the request fails, the command says so instead of keeping a wrong value:",[94,228,231],{"className":229,"code":230,"language":99,"meta":100},[97],"TOTEM_TEAM_ID=$(curl -sS https:\u002F\u002Fapi.totemstudio.ai\u002Fv1\u002Fteams \\\n  -H \"Authorization: Bearer $TOTEM_API_KEY\" | jq -er '.data[0].id') \\\n  && export TOTEM_TEAM_ID \\\n  || { unset TOTEM_TEAM_ID; echo 'Step 2 failed: run the curl above to see the error.' >&2; }\n",[67,232,230],{"__ignoreMap":100},[12,234,235,236,239,240,243],{},"Requests that read one team's data, such as lists and the changes feed, name the team in the ",[67,237,238],{},"Totem-Team"," header. It only matters when a key reads several teams: a key with one team can leave it out, and a key with several gets ",[67,241,242],{},"400 team_required"," without it. The next steps always send it, so they work for both.",[111,245,248],{"type":246,"title":247},"note","Just added the team?",[12,249,250,251,70,254,256,257,260,261,264],{},"Totem prepares a team's data for the API when the team is added to an integration. Until that is done, requests for the team answer ",[67,252,253],{},"409 capture_not_ready",[30,255,56],{}," shows the team as ",[30,258,259],{},"Preparing data"," instead of ",[30,262,263],{},"Ready",". Wait a few minutes, then try again; a team with many records takes longer.",[16,266,268],{"id":267},"_3-list-leads","3. List leads",[12,270,271,272,278],{},"Ask ",[35,273,275],{"href":274},"\u002Fdevelopers\u002Freference-leads#leads.list",[67,276,277],{},"GET \u002Fv1\u002Fleads"," for the first five leads:",[94,280,283],{"className":281,"code":282,"language":99,"meta":100},[97],"curl \"https:\u002F\u002Fapi.totemstudio.ai\u002Fv1\u002Fleads?limit=5\" \\\n  -H \"Authorization: Bearer $TOTEM_API_KEY\" \\\n  -H \"Totem-Team: $TOTEM_TEAM_ID\"\n",[67,284,282],{"__ignoreMap":100},[12,286,287,288,291,292,295],{},"The answer is a list: the leads are in ",[67,289,290],{},"data",". A lead has many more fields than this example shows; the ",[35,293,294],{"href":274},"reference"," lists them all.",[94,297,300],{"className":298,"code":299,"language":145,"meta":100},[143],"{\n  \"object\": \"list\",\n  \"data\": [\n    {\n      \"id\": \"lead_r72EfbRNUzuqORFJR0O8HxAwEXd93jGUTYMaMMXKSF0QbLS8CiUKJpEP34LFQG\",\n      \"object\": \"lead\",\n      \"livemode\": false,\n      \"team\": \"team_r9uYrIW4gSFJJ6hZQ9ip1UIpPaAD1CF7NR\",\n      \"record_kind\": \"lead\",\n      \"name\": \"Alex\",\n      \"surname\": \"Rivera\",\n      \"email\": \"alex.rivera@example.com\",\n      \"phone\": \"+12025550123\",\n      \"status\": \"contacted\",\n      \"contact_permissions\": {\n        \"do_not_contact\": false,\n        \"email_opt_out\": false,\n        \"email_opt_out_at\": null,\n        \"whatsapp_consent\": null,\n        \"provenance\": {\n          \"do_not_contact\": null,\n          \"email_opt_out\": null,\n          \"whatsapp_consent\": null\n        }\n      },\n      \"board\": \"brd_r72EfbRNUzuqORFJR0O8HxAwEXd93jGUTYMaMMX3trzsVUZQuHcMSuDh1aRIst\",\n      \"stage\": \"stg_r72EfbRNUzuqORFJR0O8HxAwEXd93jGUTYMaMMXWBYWcKwVTY3CRZlVS3oYvs0\",\n      \"created_at\": \"2026-09-28T10:15:00.000Z\",\n      \"version\": 3,\n      \"updated_at\": \"2026-09-28T10:20:01.250Z\"\n    }\n  ],\n  \"has_more\": true,\n  \"next_cursor\": \"eyJ2IjoxLCJrIjoibGVhZCIsImEiOiJsZWFkX3I3MkVmYlJOVXp1cU9SRkpSME84SHhBd0VYZDkzakdVVFlNYU1NWEtTRjBRYkxTOENpVUtKcEVQMzRMRlFHIiwicyI6ImYzNTI3NzI2MTkwYiJ93iHyRg\"\n}\n",[67,301,299],{"__ignoreMap":100},[12,303,304,307,308,311,312,315,316,318,319,323],{},[67,305,306],{},"has_more: true"," means there are more leads. Send ",[67,309,310],{},"next_cursor"," back as the ",[67,313,314],{},"cursor"," parameter, with the same filters and the same ",[67,317,238],{},", to read the next page. ",[35,320,322],{"href":321},"\u002Fdevelopers\u002Fpagination","Lists and pagination"," explains paging and filters.",[16,325,327],{"id":326},"_4-start-a-sync","4. Start a sync",[12,329,330,331,337],{},"A list is a snapshot. To keep a copy of a team that stays current, read the changes feed, ",[35,332,334],{"href":333},"\u002Fdevelopers\u002Freference-sync#sync.changes",[67,335,336],{},"GET \u002Fv1\u002Fsync\u002Fchanges",". The first request has no cursor and starts the initial sync of the whole team:",[94,339,342],{"className":340,"code":341,"language":99,"meta":100},[97],"curl https:\u002F\u002Fapi.totemstudio.ai\u002Fv1\u002Fsync\u002Fchanges \\\n  -H \"Authorization: Bearer $TOTEM_API_KEY\" \\\n  -H \"Totem-Team: $TOTEM_TEAM_ID\"\n",[67,343,341],{"__ignoreMap":100},[12,345,346,347,349,350,353],{},"Each item in ",[67,348,290],{}," is a record of one ",[67,351,352],{},"kind",", such as a lead, a call or a booking, or a removal that tells you to delete a record. From there, your sync runs a simple loop:",[355,356,357,364,369,382],"ol",{},[27,358,359,360,363],{},"Apply every item of the page to your copy. Skip an item whose ",[67,361,362],{},"version"," is not greater than the one you already hold.",[27,365,366,367,43],{},"Once the whole page is saved, store ",[67,368,310],{},[27,370,371,372,375,376,379,380,43],{},"While ",[67,373,374],{},"has_more"," is ",[67,377,378],{},"true",", ask again, sending the stored cursor as ",[67,381,314],{},[27,383,384,385,375,387,390,391,393],{},"When ",[67,386,374],{},[67,388,389],{},"false",", your copy is current. Keep asking with the stored cursor every 1 to 5 minutes, and store the ",[67,392,310],{}," of every page, even an empty one.",[12,395,396,400,401,405],{},[35,397,399],{"href":398},"\u002Fdevelopers\u002Fsync","Keep a copy in sync"," covers the rest: removals, merges and transfers, crash recovery and a full resync. It also includes a ",[35,402,404],{"href":403},"\u002Fdevelopers\u002Fsync#tested-reference-client","tested reference client",", in TypeScript, that you can copy into your server.",[16,407,409],{"id":408},"next-steps","Next steps",[24,411,412,419,424,429,436,443,450],{},[27,413,414,418],{},[35,415,417],{"href":416},"\u002Fdevelopers\u002Ftypescript","TypeScript SDK",": use typed methods and automatic pagination from Node.js.",[27,420,421,423],{},[35,422,399],{"href":398},": build a sync that survives restarts.",[27,425,426,428],{},[35,427,197],{"href":196},": roll and revoke keys, and limit them to your IP addresses.",[27,430,431,435],{},[35,432,434],{"href":433},"\u002Fdevelopers\u002Fsecurity","Security and personal data",": what Totem protects, and what to do with the data you copy.",[27,437,438,442],{},[35,439,441],{"href":440},"\u002Fdevelopers\u002Ferrors","Errors",": what each error code means and what to do about it.",[27,444,445,449],{},[35,446,448],{"href":447},"\u002Fdevelopers\u002Ftesting","Testing and go-live",": what to check before your integration goes live.",[27,451,452,456],{},[35,453,455],{"href":454},"\u002Fdevelopers\u002Freference-leads","Leads reference",": every field of a lead, and how to search by phone or email.",{"title":100,"searchDepth":458,"depth":458,"links":459},3,[460,462,463,464,465,466],{"id":18,"depth":461,"text":19},2,{"id":119,"depth":461,"text":120},{"id":201,"depth":461,"text":202},{"id":267,"depth":461,"text":268},{"id":326,"depth":461,"text":327},{"id":408,"depth":461,"text":409},"Make your first requests to the Totem API in a few minutes. Check your key, find your team, list leads and start a sync.",false,"md","en",{},true,"2","\u002Fdevelopers\u002Fquickstart","---\ntitle: Quickstart\ndescription: Make your first requests to the Totem API in a few minutes. Check your key, find your team, list leads and start a sync.\nsection: start\norder: 2\ntranslationKey: quickstart\nlang: en\ndraft: false\n---\n\nThis guide takes you from a new key to your first leads in four steps.\n\n## Before you begin\n\nYou need:\n\n- **A Totem team on Pro or a higher plan**, or with API access agreed with Totem, whose legal setup is saved in [**Privacy & agreements**](https:\u002F\u002Fapp.totemstudio.ai\u002Fprivacy-setup).\n- **A secret key.** A team administrator opens [**Settings → Developers**](https:\u002F\u002Fapp.totemstudio.ai\u002Fsettings?tab=developers), chooses **Create API key** and confirms read-only access to the team. Totem shows the key once: copy it straight into your server's secret store.\n- **`curl`**, and [`jq`](https:\u002F\u002Fjqlang.org) for step 2.\n\nIf **Create API key** is missing or disabled, [Create a key](\u002Fdevelopers\u002Fauthentication#create-a-key) lists every requirement and what Settings says about each.\n\nTo follow this guide, load the key into an environment variable without typing it on the command line, so it never stays in your shell history. Run this, paste the key and press Enter; nothing is shown:\n\n```bash\nread -rs TOTEM_API_KEY && export TOTEM_API_KEY\n```\n\nOn a server, have your secret store provide `TOTEM_API_KEY` to the process instead.\n\n::dev-callout{type=\"warning\"}\nThe key reads administrator-level data. Keep it on your server: never in a browser, a mobile app, a URL or a public repository.\n::\n\n## 1. Check your key\n\nCall [`GET \u002Fv1\u002Fme`](\u002Fdevelopers\u002Freference-account#me.get):\n\n```bash\ncurl https:\u002F\u002Fapi.totemstudio.ai\u002Fv1\u002Fme \\\n  -H \"Authorization: Bearer $TOTEM_API_KEY\"\n```\n\nThe answer describes your key: the integration it belongs to, the last four characters of the key, the teams it reads and its permissions.\n\n```json\n{\n  \"object\": \"integration\",\n  \"id\": \"prn_m1NFIcoIU8smXfqR4ymgt6j07TfRF\",\n  \"livemode\": false,\n  \"name\": \"Example CRM sync\",\n  \"key\": {\n    \"last4\": \"Q7xZ\"\n  },\n  \"teams\": [\n    \"team_r9uYrIW4gSFJJ6hZQ9ip1UIpPaAD1CF7NR\"\n  ],\n  \"scopes\": [\n    \"teams:read\",\n    \"crm:read\",\n    \"calls:read\",\n    \"messages:read\",\n    \"calendar:read\"\n  ]\n}\n```\n\nYour answer carries your own values and `\"livemode\": true`. The examples in these docs are fictitious, so they say `\"livemode\": false`.\n\nThe same request from Node.js 22 or later, as an ES module, and from Python 3 with the `requests` library (`pip install requests`), which the samples of the reference use too:\n\n```js [me.mjs]\nconst response = await fetch('https:\u002F\u002Fapi.totemstudio.ai\u002Fv1\u002Fme', {\n  headers: { Authorization: `Bearer ${process.env.TOTEM_API_KEY}` },\n  signal: AbortSignal.timeout(30_000),\n})\n\u002F\u002F Read the body as text: a refusal from the firewall in front of the API is not JSON.\nconsole.log(response.status, await response.text())\n```\n\n```python [me.py]\nimport os\n\nimport requests\n\nresponse = requests.get(\n    \"https:\u002F\u002Fapi.totemstudio.ai\u002Fv1\u002Fme\",\n    headers={\"Authorization\": f\"Bearer {os.environ['TOTEM_API_KEY']}\"},\n    timeout=30,\n)\n# Print the body as text: a refusal from the firewall in front of the API is not JSON.\nprint(response.status_code, response.text)\n```\n\nA `401` means the key is missing, mistyped or no longer valid. [Authentication](\u002Fdevelopers\u002Fauthentication) lists every case.\n\n## 2. Find your team\n\n[`GET \u002Fv1\u002Fteams`](\u002Fdevelopers\u002Freference-account#teams.list) lists the teams your key reads:\n\n```bash\ncurl https:\u002F\u002Fapi.totemstudio.ai\u002Fv1\u002Fteams \\\n  -H \"Authorization: Bearer $TOTEM_API_KEY\"\n```\n\n```json\n{\n  \"object\": \"list\",\n  \"data\": [\n    {\n      \"id\": \"team_r9uYrIW4gSFJJ6hZQ9ip1UIpPaAD1CF7NR\",\n      \"object\": \"team\",\n      \"livemode\": false,\n      \"name\": \"Example Dental\",\n      \"timezone\": \"America\u002FNew_York\",\n      \"default_country\": \"US\"\n    }\n  ],\n  \"has_more\": false,\n  \"next_cursor\": null\n}\n```\n\nKeep the first team's id for the next steps. If the request fails, the command says so instead of keeping a wrong value:\n\n```bash\nTOTEM_TEAM_ID=$(curl -sS https:\u002F\u002Fapi.totemstudio.ai\u002Fv1\u002Fteams \\\n  -H \"Authorization: Bearer $TOTEM_API_KEY\" | jq -er '.data[0].id') \\\n  && export TOTEM_TEAM_ID \\\n  || { unset TOTEM_TEAM_ID; echo 'Step 2 failed: run the curl above to see the error.' >&2; }\n```\n\nRequests that read one team's data, such as lists and the changes feed, name the team in the `Totem-Team` header. It only matters when a key reads several teams: a key with one team can leave it out, and a key with several gets `400 team_required` without it. The next steps always send it, so they work for both.\n\n::dev-callout{type=\"note\" title=\"Just added the team?\"}\nTotem prepares a team's data for the API when the team is added to an integration. Until that is done, requests for the team answer `409 capture_not_ready`, and **Settings → Developers** shows the team as **Preparing data** instead of **Ready**. Wait a few minutes, then try again; a team with many records takes longer.\n::\n\n## 3. List leads\n\nAsk [`GET \u002Fv1\u002Fleads`](\u002Fdevelopers\u002Freference-leads#leads.list) for the first five leads:\n\n```bash\ncurl \"https:\u002F\u002Fapi.totemstudio.ai\u002Fv1\u002Fleads?limit=5\" \\\n  -H \"Authorization: Bearer $TOTEM_API_KEY\" \\\n  -H \"Totem-Team: $TOTEM_TEAM_ID\"\n```\n\nThe answer is a list: the leads are in `data`. A lead has many more fields than this example shows; the [reference](\u002Fdevelopers\u002Freference-leads#leads.list) lists them all.\n\n```json\n{\n  \"object\": \"list\",\n  \"data\": [\n    {\n      \"id\": \"lead_r72EfbRNUzuqORFJR0O8HxAwEXd93jGUTYMaMMXKSF0QbLS8CiUKJpEP34LFQG\",\n      \"object\": \"lead\",\n      \"livemode\": false,\n      \"team\": \"team_r9uYrIW4gSFJJ6hZQ9ip1UIpPaAD1CF7NR\",\n      \"record_kind\": \"lead\",\n      \"name\": \"Alex\",\n      \"surname\": \"Rivera\",\n      \"email\": \"alex.rivera@example.com\",\n      \"phone\": \"+12025550123\",\n      \"status\": \"contacted\",\n      \"contact_permissions\": {\n        \"do_not_contact\": false,\n        \"email_opt_out\": false,\n        \"email_opt_out_at\": null,\n        \"whatsapp_consent\": null,\n        \"provenance\": {\n          \"do_not_contact\": null,\n          \"email_opt_out\": null,\n          \"whatsapp_consent\": null\n        }\n      },\n      \"board\": \"brd_r72EfbRNUzuqORFJR0O8HxAwEXd93jGUTYMaMMX3trzsVUZQuHcMSuDh1aRIst\",\n      \"stage\": \"stg_r72EfbRNUzuqORFJR0O8HxAwEXd93jGUTYMaMMXWBYWcKwVTY3CRZlVS3oYvs0\",\n      \"created_at\": \"2026-09-28T10:15:00.000Z\",\n      \"version\": 3,\n      \"updated_at\": \"2026-09-28T10:20:01.250Z\"\n    }\n  ],\n  \"has_more\": true,\n  \"next_cursor\": \"eyJ2IjoxLCJrIjoibGVhZCIsImEiOiJsZWFkX3I3MkVmYlJOVXp1cU9SRkpSME84SHhBd0VYZDkzakdVVFlNYU1NWEtTRjBRYkxTOENpVUtKcEVQMzRMRlFHIiwicyI6ImYzNTI3NzI2MTkwYiJ93iHyRg\"\n}\n```\n\n`has_more: true` means there are more leads. Send `next_cursor` back as the `cursor` parameter, with the same filters and the same `Totem-Team`, to read the next page. [Lists and pagination](\u002Fdevelopers\u002Fpagination) explains paging and filters.\n\n## 4. Start a sync\n\nA list is a snapshot. To keep a copy of a team that stays current, read the changes feed, [`GET \u002Fv1\u002Fsync\u002Fchanges`](\u002Fdevelopers\u002Freference-sync#sync.changes). The first request has no cursor and starts the initial sync of the whole team:\n\n```bash\ncurl https:\u002F\u002Fapi.totemstudio.ai\u002Fv1\u002Fsync\u002Fchanges \\\n  -H \"Authorization: Bearer $TOTEM_API_KEY\" \\\n  -H \"Totem-Team: $TOTEM_TEAM_ID\"\n```\n\nEach item in `data` is a record of one `kind`, such as a lead, a call or a booking, or a removal that tells you to delete a record. From there, your sync runs a simple loop:\n\n1. Apply every item of the page to your copy. Skip an item whose `version` is not greater than the one you already hold.\n2. Once the whole page is saved, store `next_cursor`.\n3. While `has_more` is `true`, ask again, sending the stored cursor as `cursor`.\n4. When `has_more` is `false`, your copy is current. Keep asking with the stored cursor every 1 to 5 minutes, and store the `next_cursor` of every page, even an empty one.\n\n[Keep a copy in sync](\u002Fdevelopers\u002Fsync) covers the rest: removals, merges and transfers, crash recovery and a full resync. It also includes a [tested reference client](\u002Fdevelopers\u002Fsync#tested-reference-client), in TypeScript, that you can copy into your server.\n\n## Next steps\n\n- [TypeScript SDK](\u002Fdevelopers\u002Ftypescript): use typed methods and automatic pagination from Node.js.\n- [Keep a copy in sync](\u002Fdevelopers\u002Fsync): build a sync that survives restarts.\n- [Authentication](\u002Fdevelopers\u002Fauthentication): roll and revoke keys, and limit them to your IP addresses.\n- [Security and personal data](\u002Fdevelopers\u002Fsecurity): what Totem protects, and what to do with the data you copy.\n- [Errors](\u002Fdevelopers\u002Ferrors): what each error code means and what to do about it.\n- [Testing and go-live](\u002Fdevelopers\u002Ftesting): what to check before your integration goes live.\n- [Leads reference](\u002Fdevelopers\u002Freference-leads): every field of a lead, and how to search by phone or email.\n","start",{"title":6,"description":467},"developers\u002Fquickstart","quickstart","KTDufcNLcpbqa7lwXK2Kcg9RLsDyM1L7V3l0Qnzzhl4",[482,491,508,553],{"section":476,"items":483},[484,488,489],{"path":485,"title":486,"description":487},"\u002Fdevelopers","Overview","Read the leads, calls, conversations and bookings of your Totem teams from your own systems, and keep a copy in sync.",{"path":474,"title":6,"description":467},{"path":196,"title":197,"description":490},"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":492,"items":493},"guides",[494,496,498,500,504,506],{"path":416,"title":417,"description":495},"Use the official TypeScript client to read your Totem data, paginate lists and handle API errors from your server.",{"path":398,"title":399,"description":497},"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":321,"title":322,"description":499},"How list endpoints return records a page at a time, how to read every page, and what order and filters a list supports.",{"path":501,"title":502,"description":503},"\u002Fdevelopers\u002Fcoverage","Data coverage","What Totem records, since when, and how to tell an empty value from a fact that was never recorded.",{"path":447,"title":448,"description":505},"How to build and test an integration safely while the API has no test environment, and what to check before you go live.",{"path":433,"title":434,"description":507},"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":294,"items":509},[510,514,516,520,525,529,534,539,544,549],{"path":511,"title":512,"description":513},"\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":440,"title":441,"description":515},"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":517,"title":518,"description":519},"\u002Fdevelopers\u002Frate-limits","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":521,"title":522,"description":523,"apiTag":524},"\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":454,"title":526,"description":527,"apiTag":528},"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":530,"title":531,"description":532,"apiTag":533},"\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":535,"title":536,"description":537,"apiTag":538},"\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":540,"title":541,"description":542,"apiTag":543},"\u002Fdevelopers\u002Freference-bookings","Bookings and services","List and fetch a team's bookings, and list the services its customers can book.","bookings",{"path":545,"title":546,"description":547,"apiTag":548},"\u002Fdevelopers\u002Freference-sync","Changes feed","One endpoint that copies a team and then returns every change, page by page.","sync",{"path":550,"title":551,"description":552},"\u002Fdevelopers\u002Fobjects","All objects","Every object the API returns, field by field, with an example of each.",{"section":554,"items":555},"resources",[556,560,564,568],{"path":557,"title":558,"description":559},"\u002Fdevelopers\u002Fchangelog","Changelog","Changes to the Totem API, newest first, each under the date it shipped.",{"path":561,"title":562,"description":563},"\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":565,"title":566,"description":567},"\u002Fdevelopers\u002Fglossary","Glossary","The terms these docs use, defined in one place, each with a link to the page that explains it.",{"path":569,"title":570,"description":571},"\u002Fdevelopers\u002Ffaq","FAQ","Short answers to the questions developers ask most about the Totem API, with links to the details.","\u002Fdevelopers\u002Fes\u002Fquickstart",{"ACTIVATION_MARGIN_MS":574,"ADMIN_ACCESS_DISCLOSURE_VERSION":575,"AUDIT_RETENTION_DAYS":576,"AUTH_FAILURE_LIMITS":577,"CAPTURE_LAG_FLOOR_MS":581,"CAPTURE_LAG_P99_MS":7,"CURSOR_MAX_AGE_DAYS":582,"DIRTY_TTL_DAYS":582,"FAMILY_TEAM_SCOPE":583,"FAMILY_TEAM_SCOPE_OPTIONS":584,"FEED_SETTLE_LAG_MS":586,"GATE_REFRESH_MS":574,"INTEGRATION_NAME_LENGTH":587,"INTEGRATION_PURPOSE_LENGTH":589,"KEY_CACHE_TTL_MS":592,"KEY_STATS_RETENTION_DAYS":593,"LEAD_RESPONSE_READ_BUDGET":594,"LIST_SCAN_MAX":597,"MAX_FAMILY_INTEGRATION_TEAMS":582,"MAX_INTEGRATIONS_PER_FAMILY":598,"MAX_IP_ALLOWLIST":579,"PAGE_LIMIT":599,"PLATFORM_CONFIG_TTL_MS":602,"REMOVAL_RETENTION_DAYS":603,"REQUEST_BODY_MAX_BYTES":604,"ROLL_GRACE_DEFAULT_HOURS":605,"ROLL_GRACE_HOURS":606,"SEARCH_MAX_RESULTS":598,"SENSITIVE_SECTOR_POLICY":609,"SENSITIVE_SECTOR_POLICY_OPTIONS":610,"SYNC_DAILY_BUDGET":612,"SYNC_VELOCITY":614,"TEAM_CACHE_TTL_MS":616},60000,"2026-10-17",365,{"perIpPerMinute":578,"perLookupIdPerMinute":579,"banMs":580},60,20,600000,300000,30,"explicit_list",[583,585],"all_family_teams",10000,{"min":461,"max":588},80,{"min":590,"max":591},1,280,5000,90,{"conversations":579,"messageReads":595,"calls":596},200,50,1000,10,{"default":600,"max":601},25,100,15000,45,16384,24,[607,590,605,608],0,168,"exclude",[609,611],"allow",{"floor":613,"recordsMultiplier":458},250000,{"perMinute":615,"burst":586},2000,30000,"1.0a","guide",[620,621,622,623,624,625],{"id":18,"text":19,"depth":461},{"id":119,"text":120,"depth":461},{"id":201,"text":202,"depth":461},{"id":267,"text":268,"depth":461},{"id":326,"text":327,"depth":461},{"id":408,"text":409,"depth":461},{"path":485,"title":486,"section":476},{"path":196,"title":197,"section":476},[],1792502789422]