[{"data":1,"prerenderedAt":640},["ShallowReactive",2],{"developers:\u002Fdevelopers\u002Fes\u002Ftypescript":3},{"doc":4,"lang":473,"nav":484,"alternatePath":584,"policy":585,"specVersion":629,"reference":7,"layout":630,"toc":631,"prev":637,"next":638,"operations":639,"overview":7,"heroExample":7},{"id":5,"title":6,"apiTag":7,"body":8,"description":470,"draft":471,"extension":472,"lang":473,"meta":474,"navTitle":7,"navigation":475,"order":476,"path":477,"rawbody":478,"section":479,"seo":480,"stem":481,"translationKey":482,"version":7,"__hash__":483},"developers_es\u002Fdevelopers\u002Fes\u002Ftypescript.md","SDK de TypeScript",null,{"type":9,"value":10,"toc":461},"minimark",[11,35,40,49,59,76,86,90,101,109,119,125,150,154,161,167,189,235,256,260,266,273,328,346,420,441,445],[12,13,14,15,19,20,24,25,34],"p",{},"El SDK oficial de TypeScript, ",[16,17,18],"code",{},"@totemstudioai\u002Fsdk",", cubre todos los endpoints de la API de solo lectura en ",[21,22,23],"strong",{},"Beta",". Se ejecuta en tu servidor con Node.js 22.12 o posterior. Un administrador del equipo crea su clave en ",[26,27,31],"a",{"href":28,"rel":29},"https:\u002F\u002Fapp.totemstudio.ai\u002Fsettings?tab=developers",[30],"nofollow",[21,32,33],{},"Ajustes → Desarrolladores","; el acceso a la API pública está incluido desde Pro.",[36,37,39],"h2",{"id":38},"instala-el-sdk-de-typescript","Instala el SDK de TypeScript",[12,41,42,43,48],{},"Instala el paquete de la versión publicada en el ",[26,44,47],{"href":45,"rel":46},"https:\u002F\u002Fgithub.com\u002Fkiketordera\u002Ftotem-studio-typescript",[30],"repositorio oficial de GitHub",". Contiene el paquete ya compilado, así que no necesita compilar nada y funciona con los scripts de instalación desactivados. Descárgalo con su suma de comprobación, compruébalo y después instala el archivo que comprobaste:",[50,51,57],"pre",{"className":52,"code":54,"language":55,"meta":56},[53],"language-bash","curl -fLO https:\u002F\u002Fgithub.com\u002Fkiketordera\u002Ftotem-studio-typescript\u002Freleases\u002Fdownload\u002Fv0.1.0-beta.2\u002Ftotemstudioai-sdk-0.1.0-beta.2.tgz\ncurl -fLO https:\u002F\u002Fgithub.com\u002Fkiketordera\u002Ftotem-studio-typescript\u002Freleases\u002Fdownload\u002Fv0.1.0-beta.2\u002Ftotemstudioai-sdk-0.1.0-beta.2.tgz.sha256\nshasum -a 256 -c totemstudioai-sdk-0.1.0-beta.2.tgz.sha256\nnpm install .\u002Ftotemstudioai-sdk-0.1.0-beta.2.tgz\n","bash","",[16,58,54],{"__ignoreMap":56},[12,60,61,62,67,68,71,72,75],{},"La suma de comprobación demuestra que la descarga está completa y sin cambios; viene de la misma ",[26,63,66],{"href":64,"rel":65},"https:\u002F\u002Fgithub.com\u002Fkiketordera\u002Ftotem-studio-typescript\u002Freleases",[30],"página de la versión"," que el paquete, así que no demuestra quién lo generó. npm registra la dependencia como una ruta ",[16,69,70],{},"file:"," a ese paquete, así que deja el paquete en la ruta desde la que lo instalaste. También puedes instalar la etiqueta de Git con ",[16,73,74],{},"npm install github:kiketordera\u002Ftotem-studio-typescript#v0.1.0-beta.2",", que compila el paquete durante la instalación y por eso necesita los scripts de instalación activados.",[12,77,78,79,81,82,85],{},"En ambos casos el paquete se importa como ",[16,80,18],{},". Todavía no está publicado en el registro de npm, así que un paquete con ese nombre en npm no es de Totem: no le des nunca tu clave e instala el SDK solo de una de las dos formas anteriores. Guarda la clave en el almacén de secretos de tu servidor y proporciónala como ",[16,83,84],{},"TOTEM_API_KEY",". No incluyas nunca este cliente ni su clave en un navegador ni en una aplicación móvil.",[36,87,89],{"id":88},"haz-una-petición-tipada","Haz una petición tipada",[12,91,92,93,96,97,100],{},"Los ejemplos de esta página son módulos ES y usan ",[16,94,95],{},"await"," en el nivel superior. En un proyecto CommonJS, colócalos dentro de una función ",[16,98,99],{},"async",".",[50,102,107],{"className":103,"code":105,"language":106,"meta":56},[104],"language-ts","import { TotemClient } from '@totemstudioai\u002Fsdk'\n\nconst apiKey = process.env.TOTEM_API_KEY\nif (!apiKey) throw new Error('Set TOTEM_API_KEY on your server')\n\nconst client = new TotemClient({ apiKey })\nconst integration = await client.me.get()\nconst teams = await client.teams.list()\nconsole.log(integration.id, teams.data.map(team => team.id))\n","ts",[16,108,105],{"__ignoreMap":56},[12,110,111,112,115,116,118],{},"Si la clave lee varios equipos, elige uno con la opción ",[16,113,114],{},"team"," del cliente. También puedes indicar ",[16,117,114],{}," en cada petición de las rutas que leen la colección de un equipo:",[50,120,123],{"className":121,"code":122,"language":106,"meta":56},[104],"const team = process.env.TOTEM_TEAM_ID\nif (!team) throw new Error('Set TOTEM_TEAM_ID on your server')\n\nconst client = new TotemClient({ apiKey, team })\nconst page = await client.leads.list({ limit: 100 })\nconst matching = await client.leads.search({ email: 'alex.rivera@example.com' })\nconsole.log(page.data.length, matching.data.length)\n",[16,124,122],{"__ignoreMap":56},[12,126,127,128,131,132,134,135,137,138,141,142,145,146,100],{},"Una ruta que lee un registro por su identificador, como ",[16,129,130],{},"client.leads.get(id)",", toma el equipo del identificador y no admite la opción ",[16,133,114],{},": compara el ",[16,136,114],{}," del registro con el que esperas. El cliente añade ",[16,139,140],{},"Authorization"," y ",[16,143,144],{},"Totem-Team",", codifica los filtros y devuelve objetos de respuesta tipados. Los nombres de los campos son los mismos que en el JSON de la API. Los campos con un conjunto fijo de valores están tipados de forma que un valor añadido más adelante siga compilando, porque ",[26,147,149],{"href":148},"\u002Fdevelopers\u002Fes\u002Fconventions","los enums pueden crecer",[36,151,153],{"id":152},"lee-todas-las-páginas-con-el-sdk","Lee todas las páginas con el SDK",[12,155,156,157,160],{},"Usa ",[16,158,159],{},"items"," para recorrer una lista sin gestionar sus cursores de página:",[50,162,165],{"className":163,"code":164,"language":106,"meta":56},[104],"for await (const lead of client.items('leads.list', { query: { limit: 100 } })) {\n  console.log(lead.id, lead.version)\n}\n",[16,166,164],{"__ignoreMap":56},[12,168,156,169,172,173,176,177,180,181,184,185,188],{},[16,170,171],{},"pages"," si necesitas el objeto completo de cada página. Se detiene cuando ",[16,174,175],{},"has_more"," es ",[16,178,179],{},"false","; no programa futuras consultas. ",[16,182,183],{},"timeoutMs"," se aplica a cada página; pasa ",[16,186,187],{},"signal: AbortSignal.timeout(ms)"," para limitar el recorrido completo.",[12,190,191,192,194,195,198,199,201,202,205,206,209,210,213,214,217,218,221,222,225,226,229,230,234],{},"Lee el feed de cambios con ",[16,193,171],{}," o con ",[16,196,197],{},"client.sync.changes({ cursor, limit: 100 })",", nunca con ",[16,200,159],{},": aplica cada página antes de guardar su ",[16,203,204],{},"next_cursor",", también en una página vacía con ",[16,207,208],{},"has_more: false",". Un ",[16,211,212],{},"cursor"," ",[16,215,216],{},"undefined"," o ",[16,219,220],{},"null"," empieza desde el principio. Guarda un cursor por equipo y vuelve a consultar cada 1 a 5 minutos. Usa ",[16,223,224],{},"isFeedRemoval(item)"," para distinguir una eliminación del estado actual de un registro, y aplica un elemento solo si su ",[16,227,228],{},"version"," es mayor que la que tienes. ",[26,231,233],{"href":232},"\u002Fdevelopers\u002Fes\u002Fsync","Sincroniza una copia"," explica las eliminaciones, las versiones y la resincronización completa; el SDK no guarda los datos ni los cursores por ti.",[236,237,240],"dev-callout",{"title":238,"type":239},"¿SDK o cliente de referencia?","note",[12,241,242,243,247,248,255],{},"Usa el SDK para las peticiones tipadas, las consultas y la paginación. Para mantener una copia de un equipo, parte del ",[26,244,246],{"href":245},"\u002Fdevelopers\u002Fes\u002Fsync#cliente-de-referencia-probado","cliente de referencia probado"," de la guía de sincronización, que implementa las marcas de eliminación, el marcado y barrido de la resincronización completa y un resultado por equipo, o sigue el ejemplo ",[26,249,252],{"href":250,"rel":251},"https:\u002F\u002Fgithub.com\u002Fkiketordera\u002Ftotem-studio-typescript\u002Fblob\u002Fmain\u002Fexamples\u002Fsync.ts",[30],[16,253,254],{},"examples\u002Fsync.ts"," del SDK, que sincroniza un equipo y deja la resincronización completa a tu almacenamiento.",[36,257,259],{"id":258},"errores-y-metadatos-del-sdk","Errores y metadatos del SDK",[50,261,264],{"className":262,"code":263,"language":106,"meta":56},[104],"import { TotemApiError, TotemError } from '@totemstudioai\u002Fsdk'\n\ntry {\n  const response = await client.requestWithResponse('me.get')\n  console.log(response.requestId, response.data.id)\n} catch (error) {\n  if (error instanceof TotemApiError) {\n    console.error(error.status, error.code, error.action, error.param, error.requestId)\n  } else if (error instanceof TotemError) {\n    console.error(error.name)\n  }\n  throw error\n}\n",[16,265,263],{"__ignoreMap":56},[12,267,268,269,272],{},"Todos los errores que lanza el SDK por una petición que ha enviado extienden ",[16,270,271],{},"TotemError",":",[274,275,276,302,318],"ul",{},[277,278,279,282,283,285,286,289,290,293,294,297,298,301],"li",{},[16,280,281],{},"TotemApiError"," para un error con el que respondió la API. Incluye el estado HTTP, el ",[16,284,16],{}," de error de la API, la ",[16,287,288],{},"action"," recomendada, el ",[16,291,292],{},"param"," que ha fallado, ",[16,295,296],{},"title",", el identificador de la petición y ",[16,299,300],{},"retryAfterMs"," cuando se proporciona.",[277,303,304,307,308,311,312,217,315,100],{},[16,305,306],{},"TotemProtocolError"," para una respuesta que el contrato no permite, como una respuesta correcta cuyo cuerpo no es JSON, una página de lista sin ",[16,309,310],{},"data"," o un cursor ausente o repetido durante ",[16,313,314],{},"pages()",[16,316,317],{},"items()",[277,319,320,323,324,327],{},[16,321,322],{},"TotemConnectionError"," para un fallo de red que los reintentos no resuelven, y ",[16,325,326],{},"TotemTimeoutError"," para el plazo de la petición.",[12,329,330,331,334,335,337,338,341,342,345],{},"Las opciones o los argumentos no válidos, como un ",[16,332,333],{},"apiKey"," o un ",[16,336,114],{}," vacíos, lanzan un ",[16,339,340],{},"TypeError"," normal antes de enviar ninguna petición, y el motivo de cancelación de tu propia ",[16,343,344],{},"signal"," se relanza sin cambios.",[12,347,348,349,351,352,355,356,355,359,355,362,355,365,355,368,355,371,355,374,217,377,380,381,384,385,389,390,393,394,217,397,400,401,404,405,408,409,412,413,415,416,100],{},"Decide según ",[16,350,288],{},", la agrupación de códigos de error que hace el SDK: ",[16,353,354],{},"fix_request",", ",[16,357,358],{},"check_credentials",[16,360,361],{},"resync",[16,363,364],{},"wait_capture",[16,366,367],{},"drop_team",[16,369,370],{},"keep_and_retry",[16,372,373],{},"retry_after",[16,375,376],{},"backoff",[16,378,379],{},"contact_support",". El ",[16,382,383],{},"errorCatalogue"," que exporta el SDK asigna a cada código documentado su acción, y el ",[26,386,388],{"href":387},"\u002Fdevelopers\u002Fes\u002Ferrors","catálogo de errores"," explica cada código; trata un código desconocido según su estado HTTP. En el feed de cambios, ",[16,391,392],{},"action === 'resync'"," (",[16,395,396],{},"resync_required",[16,398,399],{},"sync_cursor_expired",") significa una resincronización completa; ",[16,402,403],{},"capture_not_ready"," significa conservar tus datos y tu cursor y volver a intentarlo más tarde; ",[16,406,407],{},"team_not_accessible"," significa volver a leer ",[16,410,411],{},"GET \u002Fv1\u002Fteams"," y descartar el equipo solo si ya no aparece. Sin el SDK, decide según el ",[16,414,16],{}," de la respuesta. Guarda el identificador de la petición para ",[26,417,419],{"href":418},"\u002Fdevelopers\u002Fes\u002Fsupport","soporte",[12,421,422,423,426,427,430,431,355,433,355,436,141,438,440],{},"El cliente hace un número limitado de reintentos, con esperas aleatorizadas, ante fallos temporales y respeta ",[16,424,425],{},"Retry-After",". Si la espera supera ",[16,428,429],{},"maxRetryDelayMs",", o el tiempo que queda hasta el plazo de la petición, devuelve el error a tu código al momento en lugar de acortarla. Usa ",[16,432,183],{},[16,434,435],{},"maxRetries",[16,437,429],{},[16,439,344],{}," para ajustar el plazo de tu proceso. Los errores de autenticación, autorización o petición inválida necesitan una corrección, no un bucle de reintentos.",[36,442,444],{"id":443},"verifica-tu-integración-con-el-sdk","Verifica tu integración con el SDK",[12,446,447,448,451,452,455,456,460],{},"Inyecta una implementación de ",[16,449,450],{},"fetch"," con ",[16,453,454],{},"new TotemClient({ apiKey, fetch })"," para hacer pruebas HTTP deterministas. Prueba por separado cómo tu receptor gestiona las versiones, las eliminaciones y la persistencia del cursor, y después haz peticiones reales con tu propia clave. ",[26,457,459],{"href":458},"\u002Fdevelopers\u002Fes\u002Ftesting","Pruebas y puesta en producción"," enumera el comportamiento que debes verificar; una respuesta simulada no demuestra que funcionen una clave o un servidor reales.",{"title":56,"searchDepth":462,"depth":462,"links":463},3,[464,466,467,468,469],{"id":38,"depth":465,"text":39},2,{"id":88,"depth":465,"text":89},{"id":152,"depth":465,"text":153},{"id":258,"depth":465,"text":259},{"id":443,"depth":465,"text":444},"Usa el cliente oficial de TypeScript para leer tus datos de Totem, paginar listas y gestionar errores de la API desde tu servidor.",false,"md","es",{},true,"5","\u002Fdevelopers\u002Fes\u002Ftypescript","---\ntitle: SDK de TypeScript\ndescription: Usa el cliente oficial de TypeScript para leer tus datos de Totem, paginar listas y gestionar errores de la API desde tu servidor.\nsection: guides\norder: 5\ntranslationKey: typescript\nlang: es\ndraft: false\n---\n\nEl SDK oficial de TypeScript, `@totemstudioai\u002Fsdk`, cubre todos los endpoints de la API de solo lectura en **Beta**. Se ejecuta en tu servidor con Node.js 22.12 o posterior. Un administrador del equipo crea su clave en [**Ajustes → Desarrolladores**](https:\u002F\u002Fapp.totemstudio.ai\u002Fsettings?tab=developers); el acceso a la API pública está incluido desde Pro.\n\n## Instala el SDK de TypeScript\n\nInstala el paquete de la versión publicada en el [repositorio oficial de GitHub](https:\u002F\u002Fgithub.com\u002Fkiketordera\u002Ftotem-studio-typescript). Contiene el paquete ya compilado, así que no necesita compilar nada y funciona con los scripts de instalación desactivados. Descárgalo con su suma de comprobación, compruébalo y después instala el archivo que comprobaste:\n\n```bash\ncurl -fLO https:\u002F\u002Fgithub.com\u002Fkiketordera\u002Ftotem-studio-typescript\u002Freleases\u002Fdownload\u002Fv0.1.0-beta.2\u002Ftotemstudioai-sdk-0.1.0-beta.2.tgz\ncurl -fLO https:\u002F\u002Fgithub.com\u002Fkiketordera\u002Ftotem-studio-typescript\u002Freleases\u002Fdownload\u002Fv0.1.0-beta.2\u002Ftotemstudioai-sdk-0.1.0-beta.2.tgz.sha256\nshasum -a 256 -c totemstudioai-sdk-0.1.0-beta.2.tgz.sha256\nnpm install .\u002Ftotemstudioai-sdk-0.1.0-beta.2.tgz\n```\n\nLa suma de comprobación demuestra que la descarga está completa y sin cambios; viene de la misma [página de la versión](https:\u002F\u002Fgithub.com\u002Fkiketordera\u002Ftotem-studio-typescript\u002Freleases) que el paquete, así que no demuestra quién lo generó. npm registra la dependencia como una ruta `file:` a ese paquete, así que deja el paquete en la ruta desde la que lo instalaste. También puedes instalar la etiqueta de Git con `npm install github:kiketordera\u002Ftotem-studio-typescript#v0.1.0-beta.2`, que compila el paquete durante la instalación y por eso necesita los scripts de instalación activados.\n\nEn ambos casos el paquete se importa como `@totemstudioai\u002Fsdk`. Todavía no está publicado en el registro de npm, así que un paquete con ese nombre en npm no es de Totem: no le des nunca tu clave e instala el SDK solo de una de las dos formas anteriores. Guarda la clave en el almacén de secretos de tu servidor y proporciónala como `TOTEM_API_KEY`. No incluyas nunca este cliente ni su clave en un navegador ni en una aplicación móvil.\n\n## Haz una petición tipada\n\nLos ejemplos de esta página son módulos ES y usan `await` en el nivel superior. En un proyecto CommonJS, colócalos dentro de una función `async`.\n\n```ts\nimport { TotemClient } from '@totemstudioai\u002Fsdk'\n\nconst apiKey = process.env.TOTEM_API_KEY\nif (!apiKey) throw new Error('Set TOTEM_API_KEY on your server')\n\nconst client = new TotemClient({ apiKey })\nconst integration = await client.me.get()\nconst teams = await client.teams.list()\nconsole.log(integration.id, teams.data.map(team => team.id))\n```\n\nSi la clave lee varios equipos, elige uno con la opción `team` del cliente. También puedes indicar `team` en cada petición de las rutas que leen la colección de un equipo:\n\n```ts\nconst team = process.env.TOTEM_TEAM_ID\nif (!team) throw new Error('Set TOTEM_TEAM_ID on your server')\n\nconst client = new TotemClient({ apiKey, team })\nconst page = await client.leads.list({ limit: 100 })\nconst matching = await client.leads.search({ email: 'alex.rivera@example.com' })\nconsole.log(page.data.length, matching.data.length)\n```\n\nUna ruta que lee un registro por su identificador, como `client.leads.get(id)`, toma el equipo del identificador y no admite la opción `team`: compara el `team` del registro con el que esperas. El cliente añade `Authorization` y `Totem-Team`, codifica los filtros y devuelve objetos de respuesta tipados. Los nombres de los campos son los mismos que en el JSON de la API. Los campos con un conjunto fijo de valores están tipados de forma que un valor añadido más adelante siga compilando, porque [los enums pueden crecer](\u002Fdevelopers\u002Fes\u002Fconventions).\n\n## Lee todas las páginas con el SDK\n\nUsa `items` para recorrer una lista sin gestionar sus cursores de página:\n\n```ts\nfor await (const lead of client.items('leads.list', { query: { limit: 100 } })) {\n  console.log(lead.id, lead.version)\n}\n```\n\nUsa `pages` si necesitas el objeto completo de cada página. Se detiene cuando `has_more` es `false`; no programa futuras consultas. `timeoutMs` se aplica a cada página; pasa `signal: AbortSignal.timeout(ms)` para limitar el recorrido completo.\n\nLee el feed de cambios con `pages` o con `client.sync.changes({ cursor, limit: 100 })`, nunca con `items`: aplica cada página antes de guardar su `next_cursor`, también en una página vacía con `has_more: false`. Un `cursor` `undefined` o `null` empieza desde el principio. Guarda un cursor por equipo y vuelve a consultar cada 1 a 5 minutos. Usa `isFeedRemoval(item)` para distinguir una eliminación del estado actual de un registro, y aplica un elemento solo si su `version` es mayor que la que tienes. [Sincroniza una copia](\u002Fdevelopers\u002Fes\u002Fsync) explica las eliminaciones, las versiones y la resincronización completa; el SDK no guarda los datos ni los cursores por ti.\n\n::dev-callout{type=\"note\" title=\"¿SDK o cliente de referencia?\"}\nUsa el SDK para las peticiones tipadas, las consultas y la paginación. Para mantener una copia de un equipo, parte del [cliente de referencia probado](\u002Fdevelopers\u002Fes\u002Fsync#cliente-de-referencia-probado) de la guía de sincronización, que implementa las marcas de eliminación, el marcado y barrido de la resincronización completa y un resultado por equipo, o sigue el ejemplo [`examples\u002Fsync.ts`](https:\u002F\u002Fgithub.com\u002Fkiketordera\u002Ftotem-studio-typescript\u002Fblob\u002Fmain\u002Fexamples\u002Fsync.ts) del SDK, que sincroniza un equipo y deja la resincronización completa a tu almacenamiento.\n::\n\n## Errores y metadatos del SDK\n\n```ts\nimport { TotemApiError, TotemError } from '@totemstudioai\u002Fsdk'\n\ntry {\n  const response = await client.requestWithResponse('me.get')\n  console.log(response.requestId, response.data.id)\n} catch (error) {\n  if (error instanceof TotemApiError) {\n    console.error(error.status, error.code, error.action, error.param, error.requestId)\n  } else if (error instanceof TotemError) {\n    console.error(error.name)\n  }\n  throw error\n}\n```\n\nTodos los errores que lanza el SDK por una petición que ha enviado extienden `TotemError`:\n\n- `TotemApiError` para un error con el que respondió la API. Incluye el estado HTTP, el `code` de error de la API, la `action` recomendada, el `param` que ha fallado, `title`, el identificador de la petición y `retryAfterMs` cuando se proporciona.\n- `TotemProtocolError` para una respuesta que el contrato no permite, como una respuesta correcta cuyo cuerpo no es JSON, una página de lista sin `data` o un cursor ausente o repetido durante `pages()` o `items()`.\n- `TotemConnectionError` para un fallo de red que los reintentos no resuelven, y `TotemTimeoutError` para el plazo de la petición.\n\nLas opciones o los argumentos no válidos, como un `apiKey` o un `team` vacíos, lanzan un `TypeError` normal antes de enviar ninguna petición, y el motivo de cancelación de tu propia `signal` se relanza sin cambios.\n\nDecide según `action`, la agrupación de códigos de error que hace el SDK: `fix_request`, `check_credentials`, `resync`, `wait_capture`, `drop_team`, `keep_and_retry`, `retry_after`, `backoff` o `contact_support`. El `errorCatalogue` que exporta el SDK asigna a cada código documentado su acción, y el [catálogo de errores](\u002Fdevelopers\u002Fes\u002Ferrors) explica cada código; trata un código desconocido según su estado HTTP. En el feed de cambios, `action === 'resync'` (`resync_required` o `sync_cursor_expired`) significa una resincronización completa; `capture_not_ready` significa conservar tus datos y tu cursor y volver a intentarlo más tarde; `team_not_accessible` significa volver a leer `GET \u002Fv1\u002Fteams` y descartar el equipo solo si ya no aparece. Sin el SDK, decide según el `code` de la respuesta. Guarda el identificador de la petición para [soporte](\u002Fdevelopers\u002Fes\u002Fsupport).\n\nEl cliente hace un número limitado de reintentos, con esperas aleatorizadas, ante fallos temporales y respeta `Retry-After`. Si la espera supera `maxRetryDelayMs`, o el tiempo que queda hasta el plazo de la petición, devuelve el error a tu código al momento en lugar de acortarla. Usa `timeoutMs`, `maxRetries`, `maxRetryDelayMs` y `signal` para ajustar el plazo de tu proceso. Los errores de autenticación, autorización o petición inválida necesitan una corrección, no un bucle de reintentos.\n\n## Verifica tu integración con el SDK\n\nInyecta una implementación de `fetch` con `new TotemClient({ apiKey, fetch })` para hacer pruebas HTTP deterministas. Prueba por separado cómo tu receptor gestiona las versiones, las eliminaciones y la persistencia del cursor, y después haz peticiones reales con tu propia clave. [Pruebas y puesta en producción](\u002Fdevelopers\u002Fes\u002Ftesting) enumera el comportamiento que debes verificar; una respuesta simulada no demuestra que funcionen una clave o un servidor reales.\n","guides",{"title":6,"description":470},"developers\u002Fes\u002Ftypescript","typescript","s2HLzlnDaIcq56VfOFjpFTiVozDdRCBukFxB5kLF7Uk",[485,500,519,566],{"section":486,"items":487},"start",[488,492,496],{"path":489,"title":490,"description":491},"\u002Fdevelopers\u002Fes","Introducción","Lee desde tus propios sistemas los leads, las llamadas, las conversaciones y las citas de tus equipos de Totem, y mantén una copia sincronizada.",{"path":493,"title":494,"description":495},"\u002Fdevelopers\u002Fes\u002Fquickstart","Inicio rápido","Haz tus primeras peticiones a la API de Totem en unos minutos. Comprueba tu clave, busca tu equipo, lista leads e inicia una sincronización.",{"path":497,"title":498,"description":499},"\u002Fdevelopers\u002Fes\u002Fauthentication","Autenticación","Las claves secretas y dónde pueden guardarse, la cabecera Totem-Team de las claves con varios equipos, la lista de IP permitidas y cómo rotar o revocar una clave.",{"section":479,"items":501},[502,503,505,509,513,515],{"path":477,"title":6,"description":470},{"path":232,"title":233,"description":504},"Copia un equipo con el feed de cambios y mantén la copia al día. Aplica las páginas sin riesgos, recupérate de los errores y parte de un cliente de referencia que Totem prueba.",{"path":506,"title":507,"description":508},"\u002Fdevelopers\u002Fes\u002Fpagination","Listas y paginación","Cómo devuelven los endpoints de lista los registros página a página, cómo leer todas las páginas y qué orden y qué filtros admite una lista.",{"path":510,"title":511,"description":512},"\u002Fdevelopers\u002Fes\u002Fcoverage","Cobertura de los datos","Qué registra Totem, desde cuándo y cómo distinguir un valor vacío de un dato que nunca se registró.",{"path":458,"title":459,"description":514},"Cómo construir y probar una integración con seguridad mientras la API no tiene entorno de pruebas, y qué comprobar antes de pasar a producción.",{"path":516,"title":517,"description":518},"\u002Fdevelopers\u002Fes\u002Fsecurity","Seguridad y datos personales","Cómo protege Totem las claves de la API y los datos que sirve, qué registra de cada clave, dónde funciona la API y qué hacer con los datos personales que copias.",{"section":520,"items":521},"reference",[522,525,528,532,537,542,547,552,557,562],{"path":148,"title":523,"description":524},"Convenciones","La notación de esta documentación y las reglas que siguen todas las respuestas: identificadores, fechas, valores vacíos, importes, enumeraciones, versiones, estabilidad, errores y textos no fiables.",{"path":387,"title":526,"description":527},"Errores","El formato de los errores, cómo debe tratar tu integración cada tipo de error y todos los códigos de error, con cuándo ocurren y qué hacer.",{"path":529,"title":530,"description":531},"\u002Fdevelopers\u002Fes\u002Frate-limits","Límites de frecuencia y cuota","Cuánto puede leer una clave por minuto y por día, qué cuenta para cada límite, cómo reintentar tras un 429 y cuánto tarda una carga inicial.",{"path":533,"title":534,"description":535,"apiTag":536},"\u002Fdevelopers\u002Fes\u002Freference-account","Cuenta, equipos y miembros","Comprueba que una clave funciona y qué puede leer, lista sus equipos con su zona horaria, lista los miembros de cada equipo y comprueba que la API está funcionando.","account",{"path":538,"title":539,"description":540,"apiTag":541},"\u002Fdevelopers\u002Fes\u002Freference-leads","Leads, tableros y actividades","Lista, obtén y busca los leads y los clientes de un equipo, lee las actividades de cada lead y lista los tableros y las etapas por los que pasan.","leads",{"path":543,"title":544,"description":545,"apiTag":546},"\u002Fdevelopers\u002Fes\u002Freference-calls","Llamadas","Lista y obtén las llamadas de un equipo, con su dirección, su horario y un resultado normalizado. Sin grabaciones ni transcripciones.","calls",{"path":548,"title":549,"description":550,"apiTag":551},"\u002Fdevelopers\u002Fes\u002Freference-conversations","Conversaciones","Lista y obtén las conversaciones de un equipo en todos los canales, con su estado, la persona asignada, si una persona ha tomado el control y cuándo llegaron y salieron los últimos mensajes. Nunca el contenido de los mensajes.","conversations",{"path":553,"title":554,"description":555,"apiTag":556},"\u002Fdevelopers\u002Fes\u002Freference-bookings","Citas y servicios","Lista y obtén las citas de un equipo, y lista los servicios que pueden reservar sus clientes.","bookings",{"path":558,"title":559,"description":560,"apiTag":561},"\u002Fdevelopers\u002Fes\u002Freference-sync","Feed de cambios","Un único endpoint que copia un equipo y, después, devuelve cada cambio, página a página.","sync",{"path":563,"title":564,"description":565},"\u002Fdevelopers\u002Fes\u002Fobjects","Todos los objetos","Todos los objetos que devuelve la API, campo a campo, con un ejemplo de cada uno.",{"section":567,"items":568},"resources",[569,573,576,580],{"path":570,"title":571,"description":572},"\u002Fdevelopers\u002Fes\u002Fchangelog","Registro de cambios","Los cambios de la API de Totem, del más reciente al más antiguo, cada uno bajo la fecha en que se publicó.",{"path":418,"title":574,"description":575},"Soporte","Cómo pedir ayuda con la API de Totem, qué incluir en una solicitud y qué hacer si se filtra una clave.",{"path":577,"title":578,"description":579},"\u002Fdevelopers\u002Fes\u002Fglossary","Glosario","Los términos de esta documentación, definidos en un solo lugar, cada uno con un enlace a la página que lo explica.",{"path":581,"title":582,"description":583},"\u002Fdevelopers\u002Fes\u002Ffaq","Preguntas frecuentes","Respuestas breves a las preguntas más habituales sobre la API de Totem, con enlaces a los detalles.","\u002Fdevelopers\u002Ftypescript",{"ACTIVATION_MARGIN_MS":586,"ADMIN_ACCESS_DISCLOSURE_VERSION":587,"AUDIT_RETENTION_DAYS":588,"AUTH_FAILURE_LIMITS":589,"CAPTURE_LAG_FLOOR_MS":593,"CAPTURE_LAG_P99_MS":7,"CURSOR_MAX_AGE_DAYS":594,"DIRTY_TTL_DAYS":594,"FAMILY_TEAM_SCOPE":595,"FAMILY_TEAM_SCOPE_OPTIONS":596,"FEED_SETTLE_LAG_MS":598,"GATE_REFRESH_MS":586,"INTEGRATION_NAME_LENGTH":599,"INTEGRATION_PURPOSE_LENGTH":601,"KEY_CACHE_TTL_MS":604,"KEY_STATS_RETENTION_DAYS":605,"LEAD_RESPONSE_READ_BUDGET":606,"LIST_SCAN_MAX":609,"MAX_FAMILY_INTEGRATION_TEAMS":594,"MAX_INTEGRATIONS_PER_FAMILY":610,"MAX_IP_ALLOWLIST":591,"PAGE_LIMIT":611,"PLATFORM_CONFIG_TTL_MS":614,"REMOVAL_RETENTION_DAYS":615,"REQUEST_BODY_MAX_BYTES":616,"ROLL_GRACE_DEFAULT_HOURS":617,"ROLL_GRACE_HOURS":618,"SEARCH_MAX_RESULTS":610,"SENSITIVE_SECTOR_POLICY":621,"SENSITIVE_SECTOR_POLICY_OPTIONS":622,"SYNC_DAILY_BUDGET":624,"SYNC_VELOCITY":626,"TEAM_CACHE_TTL_MS":628},60000,"2026-10-17",365,{"perIpPerMinute":590,"perLookupIdPerMinute":591,"banMs":592},60,20,600000,300000,30,"explicit_list",[595,597],"all_family_teams",10000,{"min":465,"max":600},80,{"min":602,"max":603},1,280,5000,90,{"conversations":591,"messageReads":607,"calls":608},200,50,1000,10,{"default":612,"max":613},25,100,15000,45,16384,24,[619,602,617,620],0,168,"exclude",[621,623],"allow",{"floor":625,"recordsMultiplier":462},250000,{"perMinute":627,"burst":598},2000,30000,"1.0a","guide",[632,633,634,635,636],{"id":38,"text":39,"depth":465},{"id":88,"text":89,"depth":465},{"id":152,"text":153,"depth":465},{"id":258,"text":259,"depth":465},{"id":443,"text":444,"depth":465},{"path":497,"title":498,"section":486},{"path":232,"title":233,"section":479},[],1792502791348]