Convenciones de la API

Paginación, filtros, expansión, límites, errores, idempotencia y versionado.

Todas las respuestas son JSON. Las listas comparten la misma forma y los objetos siempre traen un campo object para que puedas despachar por tipo sin adivinar.

Los nombres de campo van en snake_case, en la respuesta y en el cuerpo que mandás. Las fechas son ISO 8601 en UTC y los importes vienen en pesos argentinos, sin decimales de centavos escondidos.

Nunca mandes el id del workspace: lo deducimos de la API key. Un campo que no aplica llega como null, no ausente, así podés tipar la respuesta sin campos opcionales por todos lados.

list response
{  "object": "list",  "data": [    { "object": "contact", "id": "ct_juan", "name": "Juan Pérez" }  ],  "next_cursor": "ct_camila",  "has_more": true}

Paginación por cursor

Las listas devuelven hasta 25 elementos por defecto y 100 como máximo. Cuando has_more es true, pasá next_cursor en el parámetro cursor para pedir la página siguiente.

No uses offsets: el cursor es estable aunque se creen registros mientras recorrés.

CampoTipoDescripción
limitnumberElementos por página. Entre 1 y 100. Por defecto 25.
cursorstringnext_cursor de la respuesta anterior.
async function* allContacts(key) {  let cursor;  do {    const url = new URL("https://api.tinkay.app/v1/contacts");    url.searchParams.set("limit", "100");    if (cursor) url.searchParams.set("cursor", cursor);    const res = await fetch(url, { headers: { Authorization: `Bearer ${key}` } });    const page = await res.json();    yield* page.data;    cursor = page.has_more ? page.next_cursor : null;  } while (cursor);}

Filtros y búsqueda

Cada recurso documenta sus filtros. Los valores múltiples se separan con coma y se combinan con o; filtros distintos se combinan con y.

Para buscar texto libre en varios recursos a la vez usá GET /v1/search.

Terminal
# Conversaciones abiertas o esperando, del equipo de soportecurl "https://api.tinkay.app/v1/conversations?status=open,waiting&team_id=team_soporte" \  -H "Authorization: Bearer dk_live_xxx"

Expandir relaciones

Evitá pedidos en cascada con expand. Cada recurso lista qué relaciones acepta.

Terminal
curl "https://api.tinkay.app/v1/conversations/cv_1832?expand=contact,assignee,messages" \  -H "Authorization: Bearer dk_live_xxx"

Cada relación expandida cuenta como un pedido para el límite de tasa. Expandí solo lo que vas a usar.

Límites de tasa

600 pedidos por minuto por API key. Cada respuesta trae el estado del límite en los headers.

Al llegar al límite devolvemos 429 con Retry-After en segundos. Esperá ese tiempo y reintentá con espera exponencial y jitter.

CampoTipoDescripción
X-RateLimit-LimitnumberPedidos permitidos en la ventana.
X-RateLimit-RemainingnumberPedidos que te quedan.
X-RateLimit-ResetnumberEpoch en segundos en que se reinicia la ventana.
Retry-AfternumberSolo en 429: segundos a esperar.
Reintento con espera
async function request(url, init, attempt = 0) {  const res = await fetch(url, init);  if (res.status !== 429 || attempt >= 5) return res;  const wait = Number(res.headers.get("Retry-After") ?? 1) * 1000;  const jitter = Math.random() * 250;  await new Promise((r) => setTimeout(r, wait * 2 ** attempt + jitter));  return request(url, init, attempt + 1);}

Errores

Los errores traen un objeto error con un code estable para programar contra él y un message en español para mostrar o registrar.

Códigos frecuentes

CampoTipoDescripción
unauthorized401Falta el header o la key no es válida.
forbidden403La key no tiene el permiso necesario.
not_found404El objeto no existe en este workspace.
validation_error422El cuerpo no cumple el esquema. Mirá details.
rate_limited429Superaste el límite de tasa.
JSON
{  "error": {    "code": "validation_error",    "message": "Hay campos inválidos.",    "details": [      { "path": ["email"], "message": "Formato de email inválido" }    ]  }}

Idempotencia

Todos los POST aceptan el header Idempotency-Key. Si repetís un pedido con la misma clave dentro de 24 horas devolvemos la respuesta original sin volver a crear nada.

Usá un identificador único por operación de negocio, por ejemplo el id del pedido de tu sistema.

Terminal
curl -X POST https://api.tinkay.app/v1/tickets \  -H "Authorization: Bearer dk_live_xxx" \  -H "Idempotency-Key: order-88213-refund" \  -H "Content-Type: application/json" \  -d '{ "subject": "Reintegro pedido 88213", "contact_email": "[email protected]" }'

Versionado

La versión vive en la URL (/v1) y el formato de los eventos en api_version.

Agregar campos o recursos no rompe. Los cambios que rompen se publican como versión nueva, con seis meses de convivencia.

Sandbox y producción

Las keys con prefijo dk_test_ operan sobre datos de prueba y no envían mensajes reales ni disparan webhooks a producción.

Las keys dk_live_ trabajan sobre datos reales. Nunca las uses en el navegador ni las subas al repositorio.

Si una key se filtró, revocala desde Configuración, Desarrolladores. La revocación es inmediata.