API REST v1
Autenticación, formato de respuestas, errores, paginación y límites.
Antes de empezar
- Una API key creada en Configuración, Desarrolladores
La API vive en https://api.tinkay.app/v1. Todas las respuestas son JSON y todos los endpoints requieren autenticación.
El workspace se deduce de la API key en el servidor: nunca se envía un identificador de workspace desde el cliente.
Autenticación
Mandá la key en el header Authorization. Las keys se crean y revocan en Configuración, Desarrolladores.
curl https://api.tinkay.app/v1/contacts \ -H "Authorization: Bearer dk_live_xxx"La API key es secreta: usala solo desde tu servidor. Para el navegador existe el token público del Messenger.
Scopes
Cada key tiene permisos acotados. Si le falta uno, la respuesta es 403 con el scope que hace falta.
| Campo | Tipo | Descripción |
|---|---|---|
contacts:read / contacts:write | scope | Leer y crear contactos. |
conversations:read / conversations:write | scope | Leer conversaciones y enviar mensajes. |
tickets:read / tickets:write | scope | Leer y crear tickets. |
articles:read | scope | Leer artículos del centro de ayuda. |
events:read | scope | Leer el feed de eventos. |
* | scope | Acceso total, incluida la gestión de webhooks. |
Formato de respuesta
Las listas devuelven data, next_cursor y has_more. Los objetos individuales devuelven data.
{ "data": [ { "id": "ct_8f2a", "name": "Camila Rodríguez", "email": "[email protected]" } ], "next_cursor": "ct_8f2a", "has_more": true}Errores
Todos los errores devuelven el mismo shape, con un code estable para tu lógica y un message en español para tus logs.
Códigos de estado
| Campo | Tipo | Descripción |
|---|---|---|
400 | invalid_json / missing_parameter | El cuerpo no es JSON válido o falta un parámetro obligatorio. |
401 | unauthorized | Falta el header o la key no existe. |
403 | forbidden | La key no tiene el scope necesario. |
404 | not_found | El recurso no existe en este workspace. |
409 | conflict | El recurso ya existe (por ejemplo, un contacto con ese email). |
422 | validation_error | El cuerpo es JSON válido pero los campos no pasan la validación. |
429 | rate_limited | Superaste el límite de requests. |
{ "error": { "code": "validation_error", "message": "Hay campos inválidos.", "details": [{ "path": ["email"], "message": "Invalid email" }] }}Paginación por cursor
Pasá limit (1 a 100, por defecto 25) y cursor. El cursor es el id del último elemento de la página anterior, que viene en next_cursor.
Cuando has_more es false, terminaste.
async function* allContacts() { let cursor = null; 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 ${process.env.TINKAY_API_KEY}` } }); const page = await res.json(); yield* page.data; cursor = page.next_cursor; } while (cursor);}Límites
600 requests por minuto por API key. Cada respuesta trae los headers para que puedas frenar antes de recibir un 429.
X-RateLimit-Limit: 600X-RateLimit-Remaining: 587X-RateLimit-Reset: 1772668800Si tenés que sincronizar seguido, usá webhooks en lugar de consultar en bucle: es más rápido y no consume límite.
Cómo saber que quedó bien
- `GET /v1/contacts` devuelve 200 con la key
- Sin el header devuelve 401
