Resumen de la API
La API REST de Joryio ofrece acceso mediante programación a todas las funciones de la plataforma.
Base URL
https://api-eu1.joryio.com/api
Actualmente, todos los espacios de trabajo se sirven desde una única región; los endpoints específicos por región se documentarán cuando se habiliten regiones adicionales.
Autenticación
Todas las solicitudes de la API requieren autenticación mediante una clave de API. Las claves se limitan a un único espacio de trabajo, incluyen un conjunto explícito de permisos y pueden restringirse a una lista de IP. Consulta la referencia de Claves de API para ver el catálogo completo de permisos y el comportamiento de la lista de IP permitidas.
GET /api/users/by-user-id/user_123
Host: api-eu1.joryio.com
Authorization: Bearer jry_live_your_api_key_here
Content-Type: application/json
Envía la clave como token Bearer en el encabezado Authorization de cada solicitud. Las claves siempre empiezan por jry_live_ (producción) o jry_test_ (pruebas). El prefijo visible de la clave (por ejemplo, jry_live_98f31a72) se puede registrar de forma segura; el resto es secreto.
Obtén tu clave de API
- Inicia sesión en el dashboard de Joryio.
- Ve a Configuración → Claves de API.
- Haz clic en Crear clave de API, elige los permisos que necesita la integración y copia el valor que se muestra una única vez.
No incluyas nunca claves de API en el control de versiones ni las expongas en código del lado cliente. El valor completo se muestra una sola vez después de crearlo; guárdalo inmediatamente en tu gestor de secretos.
Colección de Postman
La forma más rápida de explorar la API es importar la colección oficial en Postman. Incluye todos los endpoints públicos con un cuerpo de ejemplo, ya configurados con una variable {{baseUrl}} y autenticación mediante token Bearer.
- Descarga la colección.
- En Postman, elige Importar y suelta el archivo.
- Configura las variables de la colección:
baseUrl=https://api-eu1.joryio.com/apiytoken= tu clave de API (jry_live_...).
Formato de las solicitudes
Todas las solicitudes y respuestas usan JSON:
POST /users/track
Content-Type: application/json
{
"attributes": {
"externalId": "user_123",
"email": "[email protected]",
"attributes": {
"plan": "premium"
}
}
}
Formato de las respuestas
Los endpoints devuelven directamente el JSON del recurso; no existe un envoltorio como { "success": true, "data": ... }.
Respuesta correcta
Por ejemplo, GET /users/by-user-id/user_123 devuelve el propio objeto de usuario:
{
"id": "665f1e2a9b3c4d5e6f7a8b9c",
"userId": "665f1e2a9b3c4d5e6f7a8b9c",
"externalId": "user_123",
"phone": "+14155550123",
"attributes": { "plan": "premium" },
"createdAt": "2026-01-15T10:30:00.000Z",
"updatedAt": "2026-01-15T10:30:00.000Z"
}
id / userId son el identificador interno de Joryio; el identificador que proporcionaste se devuelve como externalId.
Respuesta de error
Todos los errores comparten una misma estructura, generada por un filtro global de excepciones:
{
"statusCode": 400,
"message": "Cannot create user without a valid identifier (userId, externalId, or email)",
"timestamp": "2026-01-15T10:30:00.000Z",
"path": "/users"
}
Los errores de validación de solicitudes (400) incluyen además una matriz errors con un mensaje por cada campo que no supera la validación:
{
"statusCode": 400,
"message": "Bad Request Exception",
"timestamp": "2026-01-15T10:30:00.000Z",
"path": "/api/users/track",
"errors": [
"eventName must be shorter than or equal to 500 characters"
]
}
Errores en el cuerpo de la solicitud
Un cuerpo que el servidor no puede leer se rechaza antes de llegar al endpoint, con un message que indica el problema. El cuerpo que enviaste nunca se devuelve en la respuesta.
| Estado | message | Causa |
|---|---|---|
400 | Request body is not valid JSON. | El JSON tiene un error de sintaxis. |
400 | Request body must be a JSON object or array - a bare string, number, boolean or null is not accepted. | El nivel superior no es un objeto {...} ni un array [...]. |
413 | Request body is too large - the limit is 1 MB. | El cuerpo supera 1 MB. |
415 | Request body charset is not supported - send UTF-8. | El Content-Type indica un charset distinto de UTF-8. |
Códigos de estado HTTP
| Código | Significado | Descripción |
|---|---|---|
200 | OK | La solicitud se realizó correctamente |
201 | Created | El recurso se creó correctamente |
204 | No Content | La eliminación se realizó correctamente (cuerpo de respuesta vacío) |
400 | Bad Request | Parámetros de solicitud no válidos |
401 | Unauthorized | Clave de API no válida o ausente |
403 | Forbidden | La clave de API no tiene los permisos necesarios |
404 | Not Found | No se encontró el recurso |
409 | Conflict | El recurso ya existe |
413 | Payload Too Large | Cuerpo de la solicitud mayor de 1 MB |
415 | Unsupported Media Type | Charset o codificación del cuerpo no compatible |
429 | Too Many Requests | Se superó el límite de velocidad |
500 | Internal Server Error | Se produjo un error de servidor |
503 | Service Unavailable | El servicio no está disponible temporalmente |
Límites de velocidad
Los endpoints autenticados de la API aplican un límite por organización. Son un techo contra el abuso, no un presupuesto de rendimiento: están muy por encima del uso normal, y la capacidad la gestiona nuestra flota de workers, no el rechazo de peticiones en el borde.
| Superficie | Límite | Por |
|---|---|---|
| Peticiones a la API | 1.200/min | tu organización |
Ingesta de contactos y eventos (POST /users/track) | 6.000/min | tu organización |
| Inicio de sesión y restablecimiento | 5-10/min | dirección IP |
Los límites autenticados se aplican por organización, no por IP: así una flota de servidores comparte un único presupuesto en lugar de que cada servidor tenga el suyo, y dos clientes tras la misma red nunca se afectan entre sí.
Los límites actuales de tu cuenta se muestran en Configuración -> Uso y pueden ampliarse. Si una importación masiva o un envío intenso se acerca a uno, escribe a [email protected].
Los receptores de webhooks entrantes - eventos de entrega, respuestas, callbacks de plataformas de comercio - no tienen límite, deliberadamente. Un webhook rechazado es una pérdida de datos, así que esos endpoints se protegen con verificación de firma en lugar de un contador.
X-RateLimit-Limit: 1200
X-RateLimit-Remaining: 1188
X-RateLimit-Reset: 60
HTTP/1.1 429 Too Many Requests
Retry-After: 42
{
"statusCode": 429,
"message": "Too Many Requests",
"timestamp": "2026-01-15T10:30:00.000Z",
"path": "/auth/login"
}
Gestionar los límites de velocidad
Los límites pueden introducirse o hacerse más estrictos con el tiempo. Trata siempre 429 como reintentable, respeta Retry-After e implementa un retroceso exponencial:
async function makeRequestWithRetry(url, options, maxRetries = 3) {
for (let i = 0; i < maxRetries; i++) {
const response = await fetch(url, options);
if (response.status === 429) {
const retryAfter = response.headers.get('Retry-After') || Math.pow(2, i);
await sleep(retryAfter * 1000);
continue;
}
return response;
}
}
Paginación
Los endpoints de lista se paginan con los parámetros de consulta limit / offset:
GET /users?limit=50&offset=100
Parámetros:
limit: resultados por página (los valores predeterminados y máximos varían por endpoint: usuarios, predeterminado 50 y máximo 200; segmentos, predeterminado y máximo 100; consulta de eventos, predeterminado 100 y máximo 1000).offset: número de elementos que se omiten (predeterminado: 0).
Respuesta:
Las respuestas de lista paginadas incluyen la página en una matriz data y un objeto pagination:
{
"data": [...],
"pagination": {
"total": 1234,
"limit": 50,
"offset": 100,
"hasMore": true
}
}
Algunos endpoints incluyen campos de paginación adicionales (como page / totalPages) y algunas listas pequeñas devuelven una matriz JSON sin envoltorio. La página de cada endpoint documenta su estructura exacta.
Filtrado
No hay una sintaxis de consulta genérica filter[field] o sort. En su lugar, los endpoints de lista exponen parámetros de filtro específicos para cada endpoint, por ejemplo:
GET /events/query?eventName=Order+Completed&startDate=2026-01-01&endDate=2026-01-31
GET /segments?q=vip&status=active&tags=onboarding
GET /users/search?query=jane
Los resultados se devuelven en un orden fijo, del más reciente al más antiguo.
Idempotencia
No existe un encabezado de solicitud Idempotency-Key genérico. La seguridad de los reintentos se proporciona en cada endpoint:
POST /users/trackes un upsert basado en tuexternalId- reintentar la misma solicitud actualiza el mismo perfil en vez de crear un duplicado.- Cada elemento de
eventsacepta unclientEventIdopcional; se utiliza como ID del evento almacenado, por lo que una solicitud reintentada con el mismoclientEventIdse deduplica. POST /campaigns/transactional/sendacepta unaidempotencyKeyopcional en el cuerpo; un reintento con la misma clave no realizará un envío duplicado.
Marcas de tiempo
Todas las marcas de tiempo están en formato ISO 8601 y usan la zona horaria UTC:
{
"createdAt": "2024-01-15T10:30:00.000Z",
"updatedAt": "2024-01-15T14:45:30.000Z"
}
Endpoints de la API
API de usuarios
| Método | Endpoint | Descripción |
|---|---|---|
| POST | /users/track | Crea o actualiza personas y registra sus eventos, compras y dispositivos push: cada campo acepta un objeto o una matriz (máximo 1000 attributes por solicitud) |
| GET | /users | Enumera usuarios (limit / offset) |
| GET | /users/search | Busca usuarios por email, nombre, teléfono o ID |
| GET | /users/:userId | Obtiene un usuario por ID interno de Joryio |
| GET | /users/by-user-id/:userId | Obtiene un usuario por tu userId |
| PUT | /users/:userId | Actualiza un usuario por ID interno |
| PUT | /users/by-user-id/:userId | Actualiza un usuario por tu userId |
| DELETE | /users/:userId | Elimina un usuario (devuelve 204) |
API de eventos
| Método | Endpoint | Descripción |
|---|---|---|
| POST | /users/track | Registra uno o varios eventos: events acepta un objeto o una matriz (máximo 500 por solicitud) |
| GET | /events/query | Consulta eventos con filtros |
| POST | /events/aggregate | Agrega métricas de eventos a lo largo del tiempo |
API de campañas
| Método | Endpoint | Descripción |
|---|---|---|
| POST | /campaigns | Crea una campaña |
| GET | /campaigns/:id | Obtiene una campaña |
| PUT | /campaigns/:id | Actualiza una campaña |
| DELETE | /campaigns/:id | Elimina una campaña |
| POST | /campaigns/:id/send | Envía una campaña |
| GET | /campaigns/:id/stats | Obtiene las estadísticas de una campaña |
API de segmentos
| Método | Endpoint | Descripción |
|---|---|---|
| POST | /segments | Crea un segmento |
| GET | /segments | Enumera segmentos |
| GET | /segments/:id | Obtiene un segmento |
| PUT | /segments/:id | Actualiza un segmento |
| POST | /segments/:id/archive | Archiva un segmento (los segmentos no se pueden eliminar de forma definitiva) |
| GET | /segments/:id/users | Obtiene los usuarios de un segmento |
| GET | /segments/:id/size | Obtiene el tamaño de un segmento |
API de aplicaciones
| Método | Endpoint | Descripción |
|---|---|---|
| POST | /apps | Crea una aplicación |
| GET | /apps/:id | Obtiene una aplicación |
| PUT | /apps/:id | Actualiza una aplicación |
| DELETE | /apps/:id | Elimina una aplicación |
| POST | /apps/:id/regenerate-key | Regenera la clave de SDK |
| GET | /apps/:id/stats | Obtiene las estadísticas de una aplicación |
SDK
Para integrarte más fácilmente, usa nuestros SDK oficiales:
- SDK web: npm install @joryio/web-sdk
- SDK para iOS: Swift Package / CocoaPods
- SDK para Android: Gradle
- SDK para React Native: npm install @joryio/react-native-sdk
Ejemplos
Crear un usuario
curl -X POST https://api-eu1.joryio.com/api/users/track \
-H "Authorization: Bearer jry_live_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"attributes": {
"externalId": "user_123",
"email": "[email protected]",
"attributes": {
"firstName": "John",
"lastName": "Doe",
"plan": "premium"
}
}
}'
Registrar un evento
curl -X POST https://api-eu1.joryio.com/api/users/track \
-H "Authorization: Bearer jry_live_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"events": {
"externalId": "user_123",
"name": "Order Completed",
"properties": { "orderId": "order_456", "total": 99.99 }
}
}'
Crear un segmento
curl -X POST https://api-eu1.joryio.com/api/segments \
-H "Authorization: Bearer jry_live_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"name": "Premium Users",
"description": "Users on premium plan",
"filterGroups": [{
"filters": [{
"type": "attribute",
"field": "plan",
"operator": "equals",
"value": "premium"
}],
"operator": "AND"
}],
"groupOperator": "AND"
}'
Enviar una campaña
El endpoint de envío no acepta un cuerpo de solicitud: la configuración guardada de la campaña determina qué se envía.
curl -X POST https://api-eu1.joryio.com/api/campaigns/:id/send \
-H "Authorization: Bearer jry_live_your_api_key"
Errores
No existe un vocabulario de códigos de error independiente y legible por máquinas. Usa el código de estado HTTP junto con el campo message del cuerpo de error estándar (consulta Formato de las respuestas):
{
"statusCode": 404,
"message": "Segment with ID 3f9d2c1e-7a54-4b2e-9c1d-8e6f5a4b3c2d not found",
"timestamp": "2026-01-15T10:30:00.000Z",
"path": "/segments/3f9d2c1e-7a54-4b2e-9c1d-8e6f5a4b3c2d"
}
Pruebas
Las claves pueden crearse con una etiqueta test (jry_test_...) para distinguir de un vistazo las claves de integración de las claves de producción; la etiqueta no cambia lo que puede hacer la clave. Para experimentar de forma segura, crea un espacio de trabajo independiente para las pruebas: los espacios de trabajo aíslan por completo perfiles, eventos y campañas, de modo que nada de lo que pruebes afecte a los datos ni a los destinatarios de producción. Los editores de campañas incluyen envíos de prueba por canal (emails de prueba y mensajes SMS de prueba).
Soporte
¿Necesitas ayuda?