{"openapi":"3.1.0","info":{"title":"xYago Public API","description":"\nAPI de integración de xYago para los sistemas de las empresas cliente (ERP, nómina,\ncontrol de acceso...) y para que cualquier empresa envíe **notificaciones** a usuarios de\nxYago por el chat de la app, desde un remitente verificado. Permite sincronizar\n**áreas**, **empleados**, **vacaciones** y **registro de horas**, enviar **mensajes** con\nplantillas aprobadas, enviar y comprobar **códigos de verificación** (Verify) y recibir\n**webhooks** firmados con cada cambio.\n\nGuías paso a paso (primeros pasos, autenticación, errores, paginación, límites,\nwebhooks, importación, idempotencia, sandbox): [documentación](https://api.xyago.com/v1/docs/es/)\n· [English guide](https://api.xyago.com/v1/docs/en/).\n\n## Autenticación y entornos\n\nEnvía la API key en `Authorization: Bearer <api key>` (o en `X-Api-Key`). Las claves se\ncrean en el panel de xYago (**API Developer → Credenciales**) y se muestran una sola vez.\n\n| Prefijo | Entorno | Datos |\n|---|---|---|\n| `xy_live_` | live | Datos reales de la empresa |\n| `xy_test_` | test (sandbox) | Datos de ejemplo fijos; las escrituras no se guardan |\n\nLos dos entornos usan **la misma URL** (`https://api.xyago.com/v1`): el entorno lo decide\nla clave. La empresa sale siempre de la clave; nunca se envía en la URL ni en el cuerpo.\n\n## Errores\n\nTodas las respuestas de error tienen la forma\n`{\"error\": {\"code\": \"<código>\", \"message\": \"<texto>\"}}`. Decide por `code` (estable) y no\npor `message` (puede cambiar). Cada operación documenta sus códigos.\n\n## Cabeceras\n\n- `X-Request-Id` en todas las respuestas (puedes enviar el tuyo).\n- `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset` en las peticiones\n  autenticadas; al superar el límite, `429 rate_limited` con `Retry-After`.\n\n## Formatos\n\nJSON en UTF-8. Fechas y horas en ISO 8601 UTC con `Z` (`2026-03-02T14:05:00Z`); fechas\nsin hora como `YYYY-MM-DD`. Teléfonos en E.164 (`+573005550123`). Listados paginados\npor cursor: `{\"results\": [...], \"next_cursor\": \"...\" | null}`.\n","version":"v1"},"servers":[{"url":"https://api.xyago.com/v1","description":"Producción y sandbox: el entorno lo decide la API key (xy_live_ / xy_test_)."}],"paths":{"/areas":{"get":{"tags":["areas"],"summary":"Listar áreas","description":"Áreas **activas** de la empresa, de la más reciente a la más antigua, paginadas\npor cursor. Las áreas inactivas no aparecen.\n\nScope: `areas:read`.","operationId":"list_areas","parameters":[{"name":"cursor","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":512},{"type":"null"}],"description":"Cursor opaco de la página siguiente (`next_cursor` de la respuesta anterior). Omitir en la primera petición.","title":"Cursor"},"description":"Cursor opaco de la página siguiente (`next_cursor` de la respuesta anterior). Omitir en la primera petición."},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"description":"Tamaño de página (1-100, por defecto 50).","default":50,"title":"Limit"},"description":"Tamaño de página (1-100, por defecto 50)."},{"$ref":"#/components/parameters/XRequestId"}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AreaList"},"example":{"results":[{"id":"8b3e9f12-7c4d-4e5a-9b6c-1d2e3f405162","public_id":"AR-0004-02","name":"Bodega Medellín","parent_id":"6f1d2c3b-4a5e-4f60-8a71-0b2c3d4e5f60","status":"active","minimum_employees":3,"maximum_employees":12,"skills":["montacargas","inventarios"],"created_at":"2026-03-02T14:05:00Z","updated_at":"2026-03-02T14:05:00Z"},{"id":"6f1d2c3b-4a5e-4f60-8a71-0b2c3d4e5f60","public_id":"AR-0004","name":"Logística","status":"active","minimum_employees":5,"maximum_employees":40,"skills":[],"created_at":"2026-02-10T13:00:00Z","updated_at":"2026-02-18T16:30:00Z"}],"next_cursor":"MjAyNi0wMi0xMFQxMzowMDowMHw2ZjFkMmMzYg"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"}}},"400":{"description":"Petición incorrecta.\n\n- `invalid_cursor`: El `cursor` no es válido (manipulado o de otro listado). Repite la consulta sin cursor.","content":{"application/json":{"examples":{"invalid_cursor":{"summary":"invalid_cursor","description":"El `cursor` no es válido (manipulado o de otro listado). Repite la consulta sin cursor.","value":{"error":{"code":"invalid_cursor","message":"Invalid cursor."}}}},"schema":{"$ref":"#/components/schemas/ErrorSchema"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"401":{"description":"No autenticado.\n\n- `invalid_api_key`: Falta la API key o no es válida, está revocada o ha caducado.","content":{"application/json":{"examples":{"invalid_api_key":{"summary":"invalid_api_key","description":"Falta la API key o no es válida, está revocada o ha caducado.","value":{"error":{"code":"invalid_api_key","message":"Missing or invalid API key."}}}},"schema":{"$ref":"#/components/schemas/ErrorSchema"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"403":{"description":"Prohibido.\n\n- `api_disabled`: El producto (areas, employees...) no está habilitado en el panel para el entorno de la API key (live o test).\n- `endpoint_disabled`: El administrador desactivó este endpoint concreto en el panel.\n- `insufficient_scope`: La API key no tiene el scope que exige el endpoint (p. ej. `employees:write`).\n- `ip_not_allowed`: La IP de origen no está en la lista de IPs permitidas del producto.","content":{"application/json":{"examples":{"api_disabled":{"summary":"api_disabled","description":"El producto (areas, employees...) no está habilitado en el panel para el entorno de la API key (live o test).","value":{"error":{"code":"api_disabled","message":"The 'employees' API is not enabled for this environment."}}},"endpoint_disabled":{"summary":"endpoint_disabled","description":"El administrador desactivó este endpoint concreto en el panel.","value":{"error":{"code":"endpoint_disabled","message":"This endpoint is disabled."}}},"insufficient_scope":{"summary":"insufficient_scope","description":"La API key no tiene el scope que exige el endpoint (p. ej. `employees:write`).","value":{"error":{"code":"insufficient_scope","message":"This API key lacks the 'employees:write' scope."}}},"ip_not_allowed":{"summary":"ip_not_allowed","description":"La IP de origen no está en la lista de IPs permitidas del producto.","value":{"error":{"code":"ip_not_allowed","message":"Requests from this IP are not allowed."}}}},"schema":{"$ref":"#/components/schemas/ErrorSchema"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"422":{"description":"Datos no válidos.\n\n- `validation_error`: El cuerpo o los parámetros no cumplen el esquema (campo obligatorio, formato, campo desconocido...). `message` indica el campo.","content":{"application/json":{"examples":{"validation_error":{"summary":"validation_error","description":"El cuerpo o los parámetros no cumplen el esquema (campo obligatorio, formato, campo desconocido...). `message` indica el campo.","value":{"error":{"code":"validation_error","message":"phone_number: String should match pattern '^\\+[1-9][0-9]{6,14}$'"}}}},"schema":{"$ref":"#/components/schemas/ErrorSchema"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"429":{"description":"Demasiadas peticiones.\n\n- `rate_limited`: Se superó el límite de peticiones por minuto de la API key o, en `POST /messages`, un tope de envío del remitente (mensajes por minuto, por destinatario y día, destinatarios nuevos por día) o, en Verify, un tope de códigos (espera entre códigos, por teléfono, por remitente) o de códigos erróneos por hora. Espera los segundos de `Retry-After`.","content":{"application/json":{"examples":{"rate_limited":{"summary":"rate_limited","description":"Se superó el límite de peticiones por minuto de la API key o, en `POST /messages`, un tope de envío del remitente (mensajes por minuto, por destinatario y día, destinatarios nuevos por día) o, en Verify, un tope de códigos (espera entre códigos, por teléfono, por remitente) o de códigos erróneos por hora. Espera los segundos de `Retry-After`.","value":{"error":{"code":"rate_limited","message":"Rate limit exceeded. Retry after the indicated number of seconds."}}}},"schema":{"$ref":"#/components/schemas/ErrorSchema"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"},"Retry-After":{"$ref":"#/components/headers/Retry-After"}}},"502":{"description":"Error de un servicio interno.\n\n- `upstream_error`: Un servicio interno de xYago falló o no respondió a tiempo. Reintenta con backoff (en las escrituras, comprueba antes si ya se aplicaron).","content":{"application/json":{"examples":{"upstream_error":{"summary":"upstream_error","description":"Un servicio interno de xYago falló o no respondió a tiempo. Reintenta con backoff (en las escrituras, comprueba antes si ya se aplicaron).","value":{"error":{"code":"upstream_error","message":"An internal service failed to process the request. Try again later."}}}},"schema":{"$ref":"#/components/schemas/ErrorSchema"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}}},"x-codeSamples":[{"lang":"Shell","label":"curl","source":"curl -X GET 'https://api.xyago.com/v1/areas' \\\n  -H \"Authorization: Bearer $XYAGO_API_KEY\""},{"lang":"Python","label":"Python","source":"import os\n\nimport requests\n\nresponse = requests.get(\n    \"https://api.xyago.com/v1/areas\",\n    headers={\n        \"Authorization\": f\"Bearer {os.environ['XYAGO_API_KEY']}\",\n    },\n    timeout=30,\n)\nresponse.raise_for_status()\nprint(response.headers['X-Request-Id'], response.json())"},{"lang":"JavaScript","label":"JavaScript","source":"const response = await fetch(\"https://api.xyago.com/v1/areas\", {\n  method: \"GET\",\n  headers: {\n    Authorization: `Bearer ${process.env.XYAGO_API_KEY}`,\n  },\n});\nconst data = await response.json();\nif (!response.ok) throw new Error(`${data.error.code}: ${data.error.message}`);\nconsole.log(response.headers.get(\"X-Request-Id\"), data);"},{"lang":"C#","label":"C#","source":"using System.Net.Http.Headers;\nusing System.Text;\n\nusing var http = new HttpClient();\nhttp.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(\n    \"Bearer\", Environment.GetEnvironmentVariable(\"XYAGO_API_KEY\"));\n\nvar request = new HttpRequestMessage(new HttpMethod(\"GET\"), \"https://api.xyago.com/v1/areas\");\nvar response = await http.SendAsync(request);\nvar body = await response.Content.ReadAsStringAsync();\nConsole.WriteLine($\"{(int)response.StatusCode} {body}\");"}]},"post":{"tags":["areas"],"summary":"Crear un área","description":"Crea un área en la empresa de la API key (la empresa nunca se envía: sale de la\nclave). Campos desconocidos → `422 validation_error`. `parent_id` debe ser un\nárea activa de la empresa. Emite el webhook `area.created`.\n\nScope: `areas:write`.","operationId":"create_area","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AreaCreate"},"examples":{"subarea":{"summary":"Subárea de Logística","value":{"name":"Bodega Medellín","parent_id":"6f1d2c3b-4a5e-4f60-8a71-0b2c3d4e5f60","minimum_employees":3,"maximum_employees":12,"skills":["montacargas","inventarios"]}},"raiz":{"summary":"Área raíz sin skills","value":{"name":"Talento Humano","minimum_employees":1,"maximum_employees":6}}}}}},"responses":{"201":{"description":"Creado","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Area"},"example":{"id":"8b3e9f12-7c4d-4e5a-9b6c-1d2e3f405162","public_id":"AR-0004-02","name":"Bodega Medellín","parent_id":"6f1d2c3b-4a5e-4f60-8a71-0b2c3d4e5f60","status":"active","minimum_employees":3,"maximum_employees":12,"skills":["montacargas","inventarios"],"created_at":"2026-03-02T14:05:00Z","updated_at":"2026-03-02T14:05:00Z"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"}}},"401":{"description":"No autenticado.\n\n- `invalid_api_key`: Falta la API key o no es válida, está revocada o ha caducado.","content":{"application/json":{"examples":{"invalid_api_key":{"summary":"invalid_api_key","description":"Falta la API key o no es válida, está revocada o ha caducado.","value":{"error":{"code":"invalid_api_key","message":"Missing or invalid API key."}}}},"schema":{"$ref":"#/components/schemas/ErrorSchema"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"403":{"description":"Prohibido.\n\n- `api_disabled`: El producto (areas, employees...) no está habilitado en el panel para el entorno de la API key (live o test).\n- `endpoint_disabled`: El administrador desactivó este endpoint concreto en el panel.\n- `insufficient_scope`: La API key no tiene el scope que exige el endpoint (p. ej. `employees:write`).\n- `ip_not_allowed`: La IP de origen no está en la lista de IPs permitidas del producto.","content":{"application/json":{"examples":{"api_disabled":{"summary":"api_disabled","description":"El producto (areas, employees...) no está habilitado en el panel para el entorno de la API key (live o test).","value":{"error":{"code":"api_disabled","message":"The 'employees' API is not enabled for this environment."}}},"endpoint_disabled":{"summary":"endpoint_disabled","description":"El administrador desactivó este endpoint concreto en el panel.","value":{"error":{"code":"endpoint_disabled","message":"This endpoint is disabled."}}},"insufficient_scope":{"summary":"insufficient_scope","description":"La API key no tiene el scope que exige el endpoint (p. ej. `employees:write`).","value":{"error":{"code":"insufficient_scope","message":"This API key lacks the 'employees:write' scope."}}},"ip_not_allowed":{"summary":"ip_not_allowed","description":"La IP de origen no está en la lista de IPs permitidas del producto.","value":{"error":{"code":"ip_not_allowed","message":"Requests from this IP are not allowed."}}}},"schema":{"$ref":"#/components/schemas/ErrorSchema"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"409":{"description":"Conflicto.\n\n- `area_already_exists`: Ya existe un área con ese nombre bajo el mismo padre.","content":{"application/json":{"examples":{"area_already_exists":{"summary":"area_already_exists","description":"Ya existe un área con ese nombre bajo el mismo padre.","value":{"error":{"code":"area_already_exists","message":"An area with this name already exists under the same parent."}}}},"schema":{"$ref":"#/components/schemas/ErrorSchema"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"422":{"description":"Datos no válidos.\n\n- `validation_error`: El cuerpo o los parámetros no cumplen el esquema (campo obligatorio, formato, campo desconocido...). `message` indica el campo.\n- `invalid_parent_id`: `parent_id` no corresponde a un área activa de la empresa.","content":{"application/json":{"examples":{"validation_error":{"summary":"validation_error","description":"El cuerpo o los parámetros no cumplen el esquema (campo obligatorio, formato, campo desconocido...). `message` indica el campo.","value":{"error":{"code":"validation_error","message":"phone_number: String should match pattern '^\\+[1-9][0-9]{6,14}$'"}}},"invalid_parent_id":{"summary":"invalid_parent_id","description":"`parent_id` no corresponde a un área activa de la empresa.","value":{"error":{"code":"invalid_parent_id","message":"parent_id does not reference an active area."}}}},"schema":{"$ref":"#/components/schemas/ErrorSchema"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"429":{"description":"Demasiadas peticiones.\n\n- `rate_limited`: Se superó el límite de peticiones por minuto de la API key o, en `POST /messages`, un tope de envío del remitente (mensajes por minuto, por destinatario y día, destinatarios nuevos por día) o, en Verify, un tope de códigos (espera entre códigos, por teléfono, por remitente) o de códigos erróneos por hora. Espera los segundos de `Retry-After`.","content":{"application/json":{"examples":{"rate_limited":{"summary":"rate_limited","description":"Se superó el límite de peticiones por minuto de la API key o, en `POST /messages`, un tope de envío del remitente (mensajes por minuto, por destinatario y día, destinatarios nuevos por día) o, en Verify, un tope de códigos (espera entre códigos, por teléfono, por remitente) o de códigos erróneos por hora. Espera los segundos de `Retry-After`.","value":{"error":{"code":"rate_limited","message":"Rate limit exceeded. Retry after the indicated number of seconds."}}}},"schema":{"$ref":"#/components/schemas/ErrorSchema"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"},"Retry-After":{"$ref":"#/components/headers/Retry-After"}}},"502":{"description":"Error de un servicio interno.\n\n- `upstream_error`: Un servicio interno de xYago falló o no respondió a tiempo. Reintenta con backoff (en las escrituras, comprueba antes si ya se aplicaron).","content":{"application/json":{"examples":{"upstream_error":{"summary":"upstream_error","description":"Un servicio interno de xYago falló o no respondió a tiempo. Reintenta con backoff (en las escrituras, comprueba antes si ya se aplicaron).","value":{"error":{"code":"upstream_error","message":"An internal service failed to process the request. Try again later."}}}},"schema":{"$ref":"#/components/schemas/ErrorSchema"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}}},"parameters":[{"$ref":"#/components/parameters/XRequestId"}],"x-codeSamples":[{"lang":"Shell","label":"curl","source":"curl -X POST 'https://api.xyago.com/v1/areas' \\\n  -H \"Authorization: Bearer $XYAGO_API_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d @- <<'JSON'\n{\n  \"name\": \"Bodega Medellín\",\n  \"parent_id\": \"6f1d2c3b-4a5e-4f60-8a71-0b2c3d4e5f60\",\n  \"minimum_employees\": 3,\n  \"maximum_employees\": 12,\n  \"skills\": [\n    \"montacargas\",\n    \"inventarios\"\n  ]\n}\nJSON"},{"lang":"Python","label":"Python","source":"import os\n\nimport requests\n\npayload = {\n    \"name\": \"Bodega Medellín\",\n    \"parent_id\": \"6f1d2c3b-4a5e-4f60-8a71-0b2c3d4e5f60\",\n    \"minimum_employees\": 3,\n    \"maximum_employees\": 12,\n    \"skills\": [\n        \"montacargas\",\n        \"inventarios\"\n    ]\n}\n\nresponse = requests.post(\n    \"https://api.xyago.com/v1/areas\",\n    headers={\n        \"Authorization\": f\"Bearer {os.environ['XYAGO_API_KEY']}\",\n    },\n    json=payload,\n    timeout=30,\n)\nresponse.raise_for_status()\nprint(response.headers['X-Request-Id'], response.json())"},{"lang":"JavaScript","label":"JavaScript","source":"const payload = {\n  \"name\": \"Bodega Medellín\",\n  \"parent_id\": \"6f1d2c3b-4a5e-4f60-8a71-0b2c3d4e5f60\",\n  \"minimum_employees\": 3,\n  \"maximum_employees\": 12,\n  \"skills\": [\n    \"montacargas\",\n    \"inventarios\"\n  ]\n};\n\nconst response = await fetch(\"https://api.xyago.com/v1/areas\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.XYAGO_API_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify(payload),\n});\nconst data = await response.json();\nif (!response.ok) throw new Error(`${data.error.code}: ${data.error.message}`);\nconsole.log(response.headers.get(\"X-Request-Id\"), data);"},{"lang":"C#","label":"C#","source":"using System.Net.Http.Headers;\nusing System.Text;\n\nusing var http = new HttpClient();\nhttp.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(\n    \"Bearer\", Environment.GetEnvironmentVariable(\"XYAGO_API_KEY\"));\n\nvar request = new HttpRequestMessage(new HttpMethod(\"POST\"), \"https://api.xyago.com/v1/areas\");\nvar payload = \"\"\"\n{\n  \"name\": \"Bodega Medellín\",\n  \"parent_id\": \"6f1d2c3b-4a5e-4f60-8a71-0b2c3d4e5f60\",\n  \"minimum_employees\": 3,\n  \"maximum_employees\": 12,\n  \"skills\": [\n    \"montacargas\",\n    \"inventarios\"\n  ]\n}\n\"\"\";\nrequest.Content = new StringContent(payload, Encoding.UTF8, \"application/json\");\nvar response = await http.SendAsync(request);\nvar body = await response.Content.ReadAsStringAsync();\nConsole.WriteLine($\"{(int)response.StatusCode} {body}\");"}]}},"/areas/{id}":{"get":{"tags":["areas"],"summary":"Obtener un área","description":"Detalle de un área de la empresa. Un id de otra empresa responde `404`, igual\nque uno inexistente.\n\nScope: `areas:read`.","operationId":"get_area","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid","description":"Id del área.","title":"Id"},"description":"Id del área.","examples":{"ejemplo":{"summary":"Ejemplo","value":"8b3e9f12-7c4d-4e5a-9b6c-1d2e3f405162"}}},{"$ref":"#/components/parameters/XRequestId"}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Area"},"example":{"id":"8b3e9f12-7c4d-4e5a-9b6c-1d2e3f405162","public_id":"AR-0004-02","name":"Bodega Medellín","parent_id":"6f1d2c3b-4a5e-4f60-8a71-0b2c3d4e5f60","status":"active","minimum_employees":3,"maximum_employees":12,"skills":["montacargas","inventarios"],"created_at":"2026-03-02T14:05:00Z","updated_at":"2026-03-02T14:05:00Z"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"}}},"401":{"description":"No autenticado.\n\n- `invalid_api_key`: Falta la API key o no es válida, está revocada o ha caducado.","content":{"application/json":{"examples":{"invalid_api_key":{"summary":"invalid_api_key","description":"Falta la API key o no es válida, está revocada o ha caducado.","value":{"error":{"code":"invalid_api_key","message":"Missing or invalid API key."}}}},"schema":{"$ref":"#/components/schemas/ErrorSchema"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"403":{"description":"Prohibido.\n\n- `api_disabled`: El producto (areas, employees...) no está habilitado en el panel para el entorno de la API key (live o test).\n- `endpoint_disabled`: El administrador desactivó este endpoint concreto en el panel.\n- `insufficient_scope`: La API key no tiene el scope que exige el endpoint (p. ej. `employees:write`).\n- `ip_not_allowed`: La IP de origen no está en la lista de IPs permitidas del producto.","content":{"application/json":{"examples":{"api_disabled":{"summary":"api_disabled","description":"El producto (areas, employees...) no está habilitado en el panel para el entorno de la API key (live o test).","value":{"error":{"code":"api_disabled","message":"The 'employees' API is not enabled for this environment."}}},"endpoint_disabled":{"summary":"endpoint_disabled","description":"El administrador desactivó este endpoint concreto en el panel.","value":{"error":{"code":"endpoint_disabled","message":"This endpoint is disabled."}}},"insufficient_scope":{"summary":"insufficient_scope","description":"La API key no tiene el scope que exige el endpoint (p. ej. `employees:write`).","value":{"error":{"code":"insufficient_scope","message":"This API key lacks the 'employees:write' scope."}}},"ip_not_allowed":{"summary":"ip_not_allowed","description":"La IP de origen no está en la lista de IPs permitidas del producto.","value":{"error":{"code":"ip_not_allowed","message":"Requests from this IP are not allowed."}}}},"schema":{"$ref":"#/components/schemas/ErrorSchema"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"404":{"description":"No encontrado.\n\n- `not_found`: El recurso no existe o pertenece a otra empresa (nunca se distingue).","content":{"application/json":{"examples":{"not_found":{"summary":"not_found","description":"El recurso no existe o pertenece a otra empresa (nunca se distingue).","value":{"error":{"code":"not_found","message":"Employee not found."}}}},"schema":{"$ref":"#/components/schemas/ErrorSchema"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"422":{"description":"Datos no válidos.\n\n- `validation_error`: El cuerpo o los parámetros no cumplen el esquema (campo obligatorio, formato, campo desconocido...). `message` indica el campo.","content":{"application/json":{"examples":{"validation_error":{"summary":"validation_error","description":"El cuerpo o los parámetros no cumplen el esquema (campo obligatorio, formato, campo desconocido...). `message` indica el campo.","value":{"error":{"code":"validation_error","message":"phone_number: String should match pattern '^\\+[1-9][0-9]{6,14}$'"}}}},"schema":{"$ref":"#/components/schemas/ErrorSchema"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"429":{"description":"Demasiadas peticiones.\n\n- `rate_limited`: Se superó el límite de peticiones por minuto de la API key o, en `POST /messages`, un tope de envío del remitente (mensajes por minuto, por destinatario y día, destinatarios nuevos por día) o, en Verify, un tope de códigos (espera entre códigos, por teléfono, por remitente) o de códigos erróneos por hora. Espera los segundos de `Retry-After`.","content":{"application/json":{"examples":{"rate_limited":{"summary":"rate_limited","description":"Se superó el límite de peticiones por minuto de la API key o, en `POST /messages`, un tope de envío del remitente (mensajes por minuto, por destinatario y día, destinatarios nuevos por día) o, en Verify, un tope de códigos (espera entre códigos, por teléfono, por remitente) o de códigos erróneos por hora. Espera los segundos de `Retry-After`.","value":{"error":{"code":"rate_limited","message":"Rate limit exceeded. Retry after the indicated number of seconds."}}}},"schema":{"$ref":"#/components/schemas/ErrorSchema"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"},"Retry-After":{"$ref":"#/components/headers/Retry-After"}}},"502":{"description":"Error de un servicio interno.\n\n- `upstream_error`: Un servicio interno de xYago falló o no respondió a tiempo. Reintenta con backoff (en las escrituras, comprueba antes si ya se aplicaron).","content":{"application/json":{"examples":{"upstream_error":{"summary":"upstream_error","description":"Un servicio interno de xYago falló o no respondió a tiempo. Reintenta con backoff (en las escrituras, comprueba antes si ya se aplicaron).","value":{"error":{"code":"upstream_error","message":"An internal service failed to process the request. Try again later."}}}},"schema":{"$ref":"#/components/schemas/ErrorSchema"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}}},"x-codeSamples":[{"lang":"Shell","label":"curl","source":"curl -X GET 'https://api.xyago.com/v1/areas/8b3e9f12-7c4d-4e5a-9b6c-1d2e3f405162' \\\n  -H \"Authorization: Bearer $XYAGO_API_KEY\""},{"lang":"Python","label":"Python","source":"import os\n\nimport requests\n\nresponse = requests.get(\n    \"https://api.xyago.com/v1/areas/8b3e9f12-7c4d-4e5a-9b6c-1d2e3f405162\",\n    headers={\n        \"Authorization\": f\"Bearer {os.environ['XYAGO_API_KEY']}\",\n    },\n    timeout=30,\n)\nresponse.raise_for_status()\nprint(response.headers['X-Request-Id'], response.json())"},{"lang":"JavaScript","label":"JavaScript","source":"const response = await fetch(\"https://api.xyago.com/v1/areas/8b3e9f12-7c4d-4e5a-9b6c-1d2e3f405162\", {\n  method: \"GET\",\n  headers: {\n    Authorization: `Bearer ${process.env.XYAGO_API_KEY}`,\n  },\n});\nconst data = await response.json();\nif (!response.ok) throw new Error(`${data.error.code}: ${data.error.message}`);\nconsole.log(response.headers.get(\"X-Request-Id\"), data);"},{"lang":"C#","label":"C#","source":"using System.Net.Http.Headers;\nusing System.Text;\n\nusing var http = new HttpClient();\nhttp.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(\n    \"Bearer\", Environment.GetEnvironmentVariable(\"XYAGO_API_KEY\"));\n\nvar request = new HttpRequestMessage(new HttpMethod(\"GET\"), \"https://api.xyago.com/v1/areas/8b3e9f12-7c4d-4e5a-9b6c-1d2e3f405162\");\nvar response = await http.SendAsync(request);\nvar body = await response.Content.ReadAsStringAsync();\nConsole.WriteLine($\"{(int)response.StatusCode} {body}\");"}]}},"/employees":{"get":{"tags":["employees"],"summary":"Listar empleados","description":"Empleados de la empresa (activos e inactivos), paginados por cursor en el orden\ndel panel (número de empleado). Con `external_id` devuelve 0 o 1 resultado: es la\nforma recomendada de saber si un empleado de tu ERP ya existe en xYago. Al pasar\n`cursor` se mantiene el tamaño de página de la primera petición.\n\nScope: `employees:read`.","operationId":"list_employees","parameters":[{"name":"cursor","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":512},{"type":"null"}],"description":"Cursor opaco de la página siguiente (`next_cursor` de la respuesta anterior). Omitir en la primera petición.","title":"Cursor"},"description":"Cursor opaco de la página siguiente (`next_cursor` de la respuesta anterior). Omitir en la primera petición."},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"description":"Tamaño de página (1-100, por defecto 50).","default":50,"title":"Limit"},"description":"Tamaño de página (1-100, por defecto 50)."},{"name":"external_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string","pattern":"^[A-Za-z0-9._:@/\\-]{1,128}$"},{"type":"null"}],"description":"Busca por el identificador de tu ERP: devuelve 0 o 1 resultado.","title":"External Id"},"description":"Busca por el identificador de tu ERP: devuelve 0 o 1 resultado."},{"$ref":"#/components/parameters/XRequestId"}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmployeeList"},"example":{"results":[{"id":"2c9a4e71-5b3d-4f8e-a6c2-7d1e0f9b3a54","external_id":"SIESA-000123","first_name":"Valentina Rojas Castaño","email":"valentina.rojas@example.com","phone_number":"+573005550123","document_number":"1020304050","job":{"id":"9d4f6a28-3b1c-4e7d-b5a9-0e2f4c6d8a13","name":"Auxiliar de bodega"},"area_ids":["8b3e9f12-7c4d-4e5a-9b6c-1d2e3f405162"],"status":"active","contract_type":"Full-time fixed-term","hire_date":"2026-02-01","contract_end_date":"2027-01-31"},{"id":"5e7b1d93-2a4c-4b6e-8f0a-3c5d7e9f1b26","external_id":"SIESA-000087","first_name":"Andrés Felipe Mejía","email":"andres.mejia@example.com","phone_number":"+573105550187","document_number":"79856412","job":{"id":"9d4f6a28-3b1c-4e7d-b5a9-0e2f4c6d8a13","name":"Conductor"},"area_ids":["8b3e9f12-7c4d-4e5a-9b6c-1d2e3f405162"],"status":"inactive","contract_type":"Full-time permanent","hire_date":"2023-06-15"}],"next_cursor":"eyJwIjogMiwgInMiOiA1MH0"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"}}},"400":{"description":"Petición incorrecta.\n\n- `invalid_cursor`: El `cursor` no es válido (manipulado o de otro listado). Repite la consulta sin cursor.","content":{"application/json":{"examples":{"invalid_cursor":{"summary":"invalid_cursor","description":"El `cursor` no es válido (manipulado o de otro listado). Repite la consulta sin cursor.","value":{"error":{"code":"invalid_cursor","message":"Invalid cursor."}}}},"schema":{"$ref":"#/components/schemas/ErrorSchema"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"401":{"description":"No autenticado.\n\n- `invalid_api_key`: Falta la API key o no es válida, está revocada o ha caducado.","content":{"application/json":{"examples":{"invalid_api_key":{"summary":"invalid_api_key","description":"Falta la API key o no es válida, está revocada o ha caducado.","value":{"error":{"code":"invalid_api_key","message":"Missing or invalid API key."}}}},"schema":{"$ref":"#/components/schemas/ErrorSchema"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"403":{"description":"Prohibido.\n\n- `api_disabled`: El producto (areas, employees...) no está habilitado en el panel para el entorno de la API key (live o test).\n- `endpoint_disabled`: El administrador desactivó este endpoint concreto en el panel.\n- `insufficient_scope`: La API key no tiene el scope que exige el endpoint (p. ej. `employees:write`).\n- `ip_not_allowed`: La IP de origen no está en la lista de IPs permitidas del producto.","content":{"application/json":{"examples":{"api_disabled":{"summary":"api_disabled","description":"El producto (areas, employees...) no está habilitado en el panel para el entorno de la API key (live o test).","value":{"error":{"code":"api_disabled","message":"The 'employees' API is not enabled for this environment."}}},"endpoint_disabled":{"summary":"endpoint_disabled","description":"El administrador desactivó este endpoint concreto en el panel.","value":{"error":{"code":"endpoint_disabled","message":"This endpoint is disabled."}}},"insufficient_scope":{"summary":"insufficient_scope","description":"La API key no tiene el scope que exige el endpoint (p. ej. `employees:write`).","value":{"error":{"code":"insufficient_scope","message":"This API key lacks the 'employees:write' scope."}}},"ip_not_allowed":{"summary":"ip_not_allowed","description":"La IP de origen no está en la lista de IPs permitidas del producto.","value":{"error":{"code":"ip_not_allowed","message":"Requests from this IP are not allowed."}}}},"schema":{"$ref":"#/components/schemas/ErrorSchema"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"422":{"description":"Datos no válidos.\n\n- `validation_error`: El cuerpo o los parámetros no cumplen el esquema (campo obligatorio, formato, campo desconocido...). `message` indica el campo.","content":{"application/json":{"examples":{"validation_error":{"summary":"validation_error","description":"El cuerpo o los parámetros no cumplen el esquema (campo obligatorio, formato, campo desconocido...). `message` indica el campo.","value":{"error":{"code":"validation_error","message":"phone_number: String should match pattern '^\\+[1-9][0-9]{6,14}$'"}}}},"schema":{"$ref":"#/components/schemas/ErrorSchema"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"429":{"description":"Demasiadas peticiones.\n\n- `rate_limited`: Se superó el límite de peticiones por minuto de la API key o, en `POST /messages`, un tope de envío del remitente (mensajes por minuto, por destinatario y día, destinatarios nuevos por día) o, en Verify, un tope de códigos (espera entre códigos, por teléfono, por remitente) o de códigos erróneos por hora. Espera los segundos de `Retry-After`.","content":{"application/json":{"examples":{"rate_limited":{"summary":"rate_limited","description":"Se superó el límite de peticiones por minuto de la API key o, en `POST /messages`, un tope de envío del remitente (mensajes por minuto, por destinatario y día, destinatarios nuevos por día) o, en Verify, un tope de códigos (espera entre códigos, por teléfono, por remitente) o de códigos erróneos por hora. Espera los segundos de `Retry-After`.","value":{"error":{"code":"rate_limited","message":"Rate limit exceeded. Retry after the indicated number of seconds."}}}},"schema":{"$ref":"#/components/schemas/ErrorSchema"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"},"Retry-After":{"$ref":"#/components/headers/Retry-After"}}},"502":{"description":"Error de un servicio interno.\n\n- `upstream_error`: Un servicio interno de xYago falló o no respondió a tiempo. Reintenta con backoff (en las escrituras, comprueba antes si ya se aplicaron).","content":{"application/json":{"examples":{"upstream_error":{"summary":"upstream_error","description":"Un servicio interno de xYago falló o no respondió a tiempo. Reintenta con backoff (en las escrituras, comprueba antes si ya se aplicaron).","value":{"error":{"code":"upstream_error","message":"An internal service failed to process the request. Try again later."}}}},"schema":{"$ref":"#/components/schemas/ErrorSchema"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}}},"x-codeSamples":[{"lang":"Shell","label":"curl","source":"curl -X GET 'https://api.xyago.com/v1/employees' \\\n  -H \"Authorization: Bearer $XYAGO_API_KEY\""},{"lang":"Python","label":"Python","source":"import os\n\nimport requests\n\nresponse = requests.get(\n    \"https://api.xyago.com/v1/employees\",\n    headers={\n        \"Authorization\": f\"Bearer {os.environ['XYAGO_API_KEY']}\",\n    },\n    timeout=30,\n)\nresponse.raise_for_status()\nprint(response.headers['X-Request-Id'], response.json())"},{"lang":"JavaScript","label":"JavaScript","source":"const response = await fetch(\"https://api.xyago.com/v1/employees\", {\n  method: \"GET\",\n  headers: {\n    Authorization: `Bearer ${process.env.XYAGO_API_KEY}`,\n  },\n});\nconst data = await response.json();\nif (!response.ok) throw new Error(`${data.error.code}: ${data.error.message}`);\nconsole.log(response.headers.get(\"X-Request-Id\"), data);"},{"lang":"C#","label":"C#","source":"using System.Net.Http.Headers;\nusing System.Text;\n\nusing var http = new HttpClient();\nhttp.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(\n    \"Bearer\", Environment.GetEnvironmentVariable(\"XYAGO_API_KEY\"));\n\nvar request = new HttpRequestMessage(new HttpMethod(\"GET\"), \"https://api.xyago.com/v1/employees\");\nvar response = await http.SendAsync(request);\nvar body = await response.Content.ReadAsStringAsync();\nConsole.WriteLine($\"{(int)response.StatusCode} {body}\");"}]},"post":{"tags":["employees"],"summary":"Crear un empleado","description":"Da de alta un empleado en la empresa de la API key, con los mismos efectos que el\nalta desde el panel. `phone_number` en formato E.164 (p. ej. `+573005550123`).\n`contract_end_date` es obligatoria salvo para `Full-time permanent` y\n`Part-time permanent`. Envía `external_id` (el id de tu ERP) para poder\nreintentar sin duplicar: ver la guía de idempotencia. Emite `employee.created`.\n\nScope: `employees:write`.","operationId":"create_employee","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmployeeCreate"},"examples":{"termino_fijo":{"summary":"Contrato a término fijo","value":{"external_id":"SIESA-000123","first_name":"Valentina","last_name":"Rojas Castaño","email":"valentina.rojas@example.com","phone_number":"+573005550123","document_number":"1020304050","job_title":"Auxiliar de bodega","contract_type":"Full-time fixed-term","hire_date":"2026-02-01","contract_end_date":"2027-01-31","work_shifts":[{"day":"Monday","start":"07:00","end":"16:00"},{"day":"Tuesday","start":"07:00","end":"16:00"},{"day":"Wednesday","start":"07:00","end":"16:00"},{"day":"Thursday","start":"07:00","end":"16:00"},{"day":"Friday","start":"07:00","end":"16:00"},{"day":"Saturday","start":"08:00","end":"12:00"}],"vacation_days":15}},"indefinido":{"summary":"Contrato indefinido","value":{"external_id":"SIESA-000087","first_name":"Andrés Felipe","last_name":"Mejía","phone_number":"+573105550187","job_title":"Conductor","contract_type":"Full-time permanent","hire_date":"2023-06-15","work_shifts":[{"day":"Monday","start":"06:00","end":"14:00"},{"day":"Wednesday","start":"06:00","end":"14:00"},{"day":"Friday","start":"06:00","end":"14:00"}]}}}}}},"responses":{"201":{"description":"Creado","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Employee"},"example":{"id":"2c9a4e71-5b3d-4f8e-a6c2-7d1e0f9b3a54","external_id":"SIESA-000123","first_name":"Valentina Rojas Castaño","email":"valentina.rojas@example.com","phone_number":"+573005550123","document_number":"1020304050","job":{"id":"9d4f6a28-3b1c-4e7d-b5a9-0e2f4c6d8a13","name":"Auxiliar de bodega"},"area_ids":["8b3e9f12-7c4d-4e5a-9b6c-1d2e3f405162"],"status":"active","contract_type":"Full-time fixed-term","hire_date":"2026-02-01","contract_end_date":"2027-01-31"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"}}},"401":{"description":"No autenticado.\n\n- `invalid_api_key`: Falta la API key o no es válida, está revocada o ha caducado.","content":{"application/json":{"examples":{"invalid_api_key":{"summary":"invalid_api_key","description":"Falta la API key o no es válida, está revocada o ha caducado.","value":{"error":{"code":"invalid_api_key","message":"Missing or invalid API key."}}}},"schema":{"$ref":"#/components/schemas/ErrorSchema"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"403":{"description":"Prohibido.\n\n- `api_disabled`: El producto (areas, employees...) no está habilitado en el panel para el entorno de la API key (live o test).\n- `endpoint_disabled`: El administrador desactivó este endpoint concreto en el panel.\n- `insufficient_scope`: La API key no tiene el scope que exige el endpoint (p. ej. `employees:write`).\n- `ip_not_allowed`: La IP de origen no está en la lista de IPs permitidas del producto.","content":{"application/json":{"examples":{"api_disabled":{"summary":"api_disabled","description":"El producto (areas, employees...) no está habilitado en el panel para el entorno de la API key (live o test).","value":{"error":{"code":"api_disabled","message":"The 'employees' API is not enabled for this environment."}}},"endpoint_disabled":{"summary":"endpoint_disabled","description":"El administrador desactivó este endpoint concreto en el panel.","value":{"error":{"code":"endpoint_disabled","message":"This endpoint is disabled."}}},"insufficient_scope":{"summary":"insufficient_scope","description":"La API key no tiene el scope que exige el endpoint (p. ej. `employees:write`).","value":{"error":{"code":"insufficient_scope","message":"This API key lacks the 'employees:write' scope."}}},"ip_not_allowed":{"summary":"ip_not_allowed","description":"La IP de origen no está en la lista de IPs permitidas del producto.","value":{"error":{"code":"ip_not_allowed","message":"Requests from this IP are not allowed."}}}},"schema":{"$ref":"#/components/schemas/ErrorSchema"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"409":{"description":"Conflicto.\n\n- `external_id_already_exists`: El `external_id` ya está asignado a otro empleado de la empresa.\n- `employee_already_exists`: Ya hay un empleado con ese `phone_number` en la empresa.\n- `email_already_registered`: El `email` ya lo usa otro empleado.\n- `document_number_already_exists`: El `document_number` ya lo usa otro empleado de la empresa.\n- `external_id_in_progress`: Otra petición está creando ahora mismo un empleado con ese `external_id`. Reintenta en unos segundos (y consulta antes por `external_id`).","content":{"application/json":{"examples":{"external_id_already_exists":{"summary":"external_id_already_exists","description":"El `external_id` ya está asignado a otro empleado de la empresa.","value":{"error":{"code":"external_id_already_exists","message":"external_id is already assigned to another employee of the company."}}},"employee_already_exists":{"summary":"employee_already_exists","description":"Ya hay un empleado con ese `phone_number` en la empresa.","value":{"error":{"code":"employee_already_exists","message":"An employee with this phone_number already exists in the company."}}},"email_already_registered":{"summary":"email_already_registered","description":"El `email` ya lo usa otro empleado.","value":{"error":{"code":"email_already_registered","message":"This email is already used by another employee."}}},"document_number_already_exists":{"summary":"document_number_already_exists","description":"El `document_number` ya lo usa otro empleado de la empresa.","value":{"error":{"code":"document_number_already_exists","message":"document_number is already used by another employee of the company."}}},"external_id_in_progress":{"summary":"external_id_in_progress","description":"Otra petición está creando ahora mismo un empleado con ese `external_id`. Reintenta en unos segundos (y consulta antes por `external_id`).","value":{"error":{"code":"external_id_in_progress","message":"Another request is creating an employee with this external_id. Retry later."}}}},"schema":{"$ref":"#/components/schemas/ErrorSchema"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"422":{"description":"Datos no válidos.\n\n- `validation_error`: El cuerpo o los parámetros no cumplen el esquema (campo obligatorio, formato, campo desconocido...). `message` indica el campo.\n- `invalid_employee_data`: xYago rechazó los datos del empleado. Revisa los campos.","content":{"application/json":{"examples":{"validation_error":{"summary":"validation_error","description":"El cuerpo o los parámetros no cumplen el esquema (campo obligatorio, formato, campo desconocido...). `message` indica el campo.","value":{"error":{"code":"validation_error","message":"phone_number: String should match pattern '^\\+[1-9][0-9]{6,14}$'"}}},"invalid_employee_data":{"summary":"invalid_employee_data","description":"xYago rechazó los datos del empleado. Revisa los campos.","value":{"error":{"code":"invalid_employee_data","message":"The employee data was rejected. Check the fields."}}}},"schema":{"$ref":"#/components/schemas/ErrorSchema"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"429":{"description":"Demasiadas peticiones.\n\n- `rate_limited`: Se superó el límite de peticiones por minuto de la API key o, en `POST /messages`, un tope de envío del remitente (mensajes por minuto, por destinatario y día, destinatarios nuevos por día) o, en Verify, un tope de códigos (espera entre códigos, por teléfono, por remitente) o de códigos erróneos por hora. Espera los segundos de `Retry-After`.","content":{"application/json":{"examples":{"rate_limited":{"summary":"rate_limited","description":"Se superó el límite de peticiones por minuto de la API key o, en `POST /messages`, un tope de envío del remitente (mensajes por minuto, por destinatario y día, destinatarios nuevos por día) o, en Verify, un tope de códigos (espera entre códigos, por teléfono, por remitente) o de códigos erróneos por hora. Espera los segundos de `Retry-After`.","value":{"error":{"code":"rate_limited","message":"Rate limit exceeded. Retry after the indicated number of seconds."}}}},"schema":{"$ref":"#/components/schemas/ErrorSchema"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"},"Retry-After":{"$ref":"#/components/headers/Retry-After"}}},"502":{"description":"Error de un servicio interno.\n\n- `upstream_error`: Un servicio interno de xYago falló o no respondió a tiempo. Reintenta con backoff (en las escrituras, comprueba antes si ya se aplicaron).","content":{"application/json":{"examples":{"upstream_error":{"summary":"upstream_error","description":"Un servicio interno de xYago falló o no respondió a tiempo. Reintenta con backoff (en las escrituras, comprueba antes si ya se aplicaron).","value":{"error":{"code":"upstream_error","message":"An internal service failed to process the request. Try again later."}}}},"schema":{"$ref":"#/components/schemas/ErrorSchema"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}}},"parameters":[{"$ref":"#/components/parameters/XRequestId"}],"x-codeSamples":[{"lang":"Shell","label":"curl","source":"curl -X POST 'https://api.xyago.com/v1/employees' \\\n  -H \"Authorization: Bearer $XYAGO_API_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d @- <<'JSON'\n{\n  \"external_id\": \"SIESA-000123\",\n  \"first_name\": \"Valentina\",\n  \"last_name\": \"Rojas Castaño\",\n  \"email\": \"valentina.rojas@example.com\",\n  \"phone_number\": \"+573005550123\",\n  \"document_number\": \"1020304050\",\n  \"job_title\": \"Auxiliar de bodega\",\n  \"contract_type\": \"Full-time fixed-term\",\n  \"hire_date\": \"2026-02-01\",\n  \"contract_end_date\": \"2027-01-31\",\n  \"work_shifts\": [\n    {\n      \"day\": \"Monday\",\n      \"start\": \"07:00\",\n      \"end\": \"16:00\"\n    },\n    {\n      \"day\": \"Tuesday\",\n      \"start\": \"07:00\",\n      \"end\": \"16:00\"\n    },\n    {\n      \"day\": \"Wednesday\",\n      \"start\": \"07:00\",\n      \"end\": \"16:00\"\n    },\n    {\n      \"day\": \"Thursday\",\n      \"start\": \"07:00\",\n      \"end\": \"16:00\"\n    },\n    {\n      \"day\": \"Friday\",\n      \"start\": \"07:00\",\n      \"end\": \"16:00\"\n    },\n    {\n      \"day\": \"Saturday\",\n      \"start\": \"08:00\",\n      \"end\": \"12:00\"\n    }\n  ],\n  \"vacation_days\": 15\n}\nJSON"},{"lang":"Python","label":"Python","source":"import os\n\nimport requests\n\npayload = {\n    \"external_id\": \"SIESA-000123\",\n    \"first_name\": \"Valentina\",\n    \"last_name\": \"Rojas Castaño\",\n    \"email\": \"valentina.rojas@example.com\",\n    \"phone_number\": \"+573005550123\",\n    \"document_number\": \"1020304050\",\n    \"job_title\": \"Auxiliar de bodega\",\n    \"contract_type\": \"Full-time fixed-term\",\n    \"hire_date\": \"2026-02-01\",\n    \"contract_end_date\": \"2027-01-31\",\n    \"work_shifts\": [\n        {\n            \"day\": \"Monday\",\n            \"start\": \"07:00\",\n            \"end\": \"16:00\"\n        },\n        {\n            \"day\": \"Tuesday\",\n            \"start\": \"07:00\",\n            \"end\": \"16:00\"\n        },\n        {\n            \"day\": \"Wednesday\",\n            \"start\": \"07:00\",\n            \"end\": \"16:00\"\n        },\n        {\n            \"day\": \"Thursday\",\n            \"start\": \"07:00\",\n            \"end\": \"16:00\"\n        },\n        {\n            \"day\": \"Friday\",\n            \"start\": \"07:00\",\n            \"end\": \"16:00\"\n        },\n        {\n            \"day\": \"Saturday\",\n            \"start\": \"08:00\",\n            \"end\": \"12:00\"\n        }\n    ],\n    \"vacation_days\": 15\n}\n\nresponse = requests.post(\n    \"https://api.xyago.com/v1/employees\",\n    headers={\n        \"Authorization\": f\"Bearer {os.environ['XYAGO_API_KEY']}\",\n    },\n    json=payload,\n    timeout=30,\n)\nresponse.raise_for_status()\nprint(response.headers['X-Request-Id'], response.json())"},{"lang":"JavaScript","label":"JavaScript","source":"const payload = {\n  \"external_id\": \"SIESA-000123\",\n  \"first_name\": \"Valentina\",\n  \"last_name\": \"Rojas Castaño\",\n  \"email\": \"valentina.rojas@example.com\",\n  \"phone_number\": \"+573005550123\",\n  \"document_number\": \"1020304050\",\n  \"job_title\": \"Auxiliar de bodega\",\n  \"contract_type\": \"Full-time fixed-term\",\n  \"hire_date\": \"2026-02-01\",\n  \"contract_end_date\": \"2027-01-31\",\n  \"work_shifts\": [\n    {\n      \"day\": \"Monday\",\n      \"start\": \"07:00\",\n      \"end\": \"16:00\"\n    },\n    {\n      \"day\": \"Tuesday\",\n      \"start\": \"07:00\",\n      \"end\": \"16:00\"\n    },\n    {\n      \"day\": \"Wednesday\",\n      \"start\": \"07:00\",\n      \"end\": \"16:00\"\n    },\n    {\n      \"day\": \"Thursday\",\n      \"start\": \"07:00\",\n      \"end\": \"16:00\"\n    },\n    {\n      \"day\": \"Friday\",\n      \"start\": \"07:00\",\n      \"end\": \"16:00\"\n    },\n    {\n      \"day\": \"Saturday\",\n      \"start\": \"08:00\",\n      \"end\": \"12:00\"\n    }\n  ],\n  \"vacation_days\": 15\n};\n\nconst response = await fetch(\"https://api.xyago.com/v1/employees\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.XYAGO_API_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify(payload),\n});\nconst data = await response.json();\nif (!response.ok) throw new Error(`${data.error.code}: ${data.error.message}`);\nconsole.log(response.headers.get(\"X-Request-Id\"), data);"},{"lang":"C#","label":"C#","source":"using System.Net.Http.Headers;\nusing System.Text;\n\nusing var http = new HttpClient();\nhttp.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(\n    \"Bearer\", Environment.GetEnvironmentVariable(\"XYAGO_API_KEY\"));\n\nvar request = new HttpRequestMessage(new HttpMethod(\"POST\"), \"https://api.xyago.com/v1/employees\");\nvar payload = \"\"\"\n{\n  \"external_id\": \"SIESA-000123\",\n  \"first_name\": \"Valentina\",\n  \"last_name\": \"Rojas Castaño\",\n  \"email\": \"valentina.rojas@example.com\",\n  \"phone_number\": \"+573005550123\",\n  \"document_number\": \"1020304050\",\n  \"job_title\": \"Auxiliar de bodega\",\n  \"contract_type\": \"Full-time fixed-term\",\n  \"hire_date\": \"2026-02-01\",\n  \"contract_end_date\": \"2027-01-31\",\n  \"work_shifts\": [\n    {\n      \"day\": \"Monday\",\n      \"start\": \"07:00\",\n      \"end\": \"16:00\"\n    },\n    {\n      \"day\": \"Tuesday\",\n      \"start\": \"07:00\",\n      \"end\": \"16:00\"\n    },\n    {\n      \"day\": \"Wednesday\",\n      \"start\": \"07:00\",\n      \"end\": \"16:00\"\n    },\n    {\n      \"day\": \"Thursday\",\n      \"start\": \"07:00\",\n      \"end\": \"16:00\"\n    },\n    {\n      \"day\": \"Friday\",\n      \"start\": \"07:00\",\n      \"end\": \"16:00\"\n    },\n    {\n      \"day\": \"Saturday\",\n      \"start\": \"08:00\",\n      \"end\": \"12:00\"\n    }\n  ],\n  \"vacation_days\": 15\n}\n\"\"\";\nrequest.Content = new StringContent(payload, Encoding.UTF8, \"application/json\");\nvar response = await http.SendAsync(request);\nvar body = await response.Content.ReadAsStringAsync();\nConsole.WriteLine($\"{(int)response.StatusCode} {body}\");"}]}},"/employees/import":{"post":{"tags":["employees"],"summary":"Importar empleados (upsert asíncrono por external_id)","description":"Encola una importación de 1 a 500 filas y responde `202` con el `job_id`. Cada\nfila tiene la forma de `POST /employees` con `external_id` **obligatorio** (clave\ndel upsert):\n\n- `external_id` desconocido → alta (la fila necesita todos los campos obligatorios\n  del alta).\n- `external_id` ya asociado a un empleado → edición parcial (solo los campos\n  enviados; `work_shifts` y `vacation_days` se ignoran).\n\nUna fila con error no aborta el job. Reenviar el mismo fichero no duplica\nempleados, y si un job idéntico sigue en cola o en proceso se devuelve el mismo\n`job_id`. Consulta el progreso con `GET /employees/import/{job_id}`. Emite\n`employee.created` / `employee.updated` por fila.\n\nScope: `employees:write`.","operationId":"import_employees","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmployeeImportRequest"},"examples":{"mixta":{"summary":"Alta, edición y una fila con error","value":{"rows":[{"external_id":"SIESA-000123","first_name":"Valentina","last_name":"Rojas Castaño","email":"valentina.rojas@example.com","phone_number":"+573005550123","document_number":"1020304050","job_title":"Auxiliar de bodega","contract_type":"Full-time fixed-term","hire_date":"2026-02-01","contract_end_date":"2027-01-31","work_shifts":[{"day":"Monday","start":"07:00","end":"16:00"},{"day":"Tuesday","start":"07:00","end":"16:00"},{"day":"Wednesday","start":"07:00","end":"16:00"},{"day":"Thursday","start":"07:00","end":"16:00"},{"day":"Friday","start":"07:00","end":"16:00"},{"day":"Saturday","start":"08:00","end":"12:00"}],"vacation_days":15},{"external_id":"SIESA-000087","job_title":"Conductor de reparto"},{"external_id":"SIESA-000150","first_name":"Camilo"}]}}}}},"required":true},"responses":{"202":{"description":"Aceptado: se procesa en segundo plano","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmployeeImportAccepted"},"example":{"job_id":"0b8c6d4e-2f1a-4c3b-9e5d-7a6b8c9d0e1f","status":"queued"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"}}},"401":{"description":"No autenticado.\n\n- `invalid_api_key`: Falta la API key o no es válida, está revocada o ha caducado.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorSchema"},"examples":{"invalid_api_key":{"summary":"invalid_api_key","description":"Falta la API key o no es válida, está revocada o ha caducado.","value":{"error":{"code":"invalid_api_key","message":"Missing or invalid API key."}}}}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"403":{"description":"Prohibido.\n\n- `api_disabled`: El producto (areas, employees...) no está habilitado en el panel para el entorno de la API key (live o test).\n- `endpoint_disabled`: El administrador desactivó este endpoint concreto en el panel.\n- `insufficient_scope`: La API key no tiene el scope que exige el endpoint (p. ej. `employees:write`).\n- `ip_not_allowed`: La IP de origen no está en la lista de IPs permitidas del producto.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorSchema"},"examples":{"api_disabled":{"summary":"api_disabled","description":"El producto (areas, employees...) no está habilitado en el panel para el entorno de la API key (live o test).","value":{"error":{"code":"api_disabled","message":"The 'employees' API is not enabled for this environment."}}},"endpoint_disabled":{"summary":"endpoint_disabled","description":"El administrador desactivó este endpoint concreto en el panel.","value":{"error":{"code":"endpoint_disabled","message":"This endpoint is disabled."}}},"insufficient_scope":{"summary":"insufficient_scope","description":"La API key no tiene el scope que exige el endpoint (p. ej. `employees:write`).","value":{"error":{"code":"insufficient_scope","message":"This API key lacks the 'employees:write' scope."}}},"ip_not_allowed":{"summary":"ip_not_allowed","description":"La IP de origen no está en la lista de IPs permitidas del producto.","value":{"error":{"code":"ip_not_allowed","message":"Requests from this IP are not allowed."}}}}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"422":{"description":"Datos no válidos.\n\n- `validation_error`: El cuerpo o los parámetros no cumplen el esquema (campo obligatorio, formato, campo desconocido...). `message` indica el campo.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorSchema"},"examples":{"validation_error":{"summary":"validation_error","description":"El cuerpo o los parámetros no cumplen el esquema (campo obligatorio, formato, campo desconocido...). `message` indica el campo.","value":{"error":{"code":"validation_error","message":"phone_number: String should match pattern '^\\+[1-9][0-9]{6,14}$'"}}}}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"429":{"description":"Demasiadas peticiones.\n\n- `rate_limited`: Se superó el límite de peticiones por minuto de la API key o, en `POST /messages`, un tope de envío del remitente (mensajes por minuto, por destinatario y día, destinatarios nuevos por día) o, en Verify, un tope de códigos (espera entre códigos, por teléfono, por remitente) o de códigos erróneos por hora. Espera los segundos de `Retry-After`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorSchema"},"examples":{"rate_limited":{"summary":"rate_limited","description":"Se superó el límite de peticiones por minuto de la API key o, en `POST /messages`, un tope de envío del remitente (mensajes por minuto, por destinatario y día, destinatarios nuevos por día) o, en Verify, un tope de códigos (espera entre códigos, por teléfono, por remitente) o de códigos erróneos por hora. Espera los segundos de `Retry-After`.","value":{"error":{"code":"rate_limited","message":"Rate limit exceeded. Retry after the indicated number of seconds."}}}}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"},"Retry-After":{"$ref":"#/components/headers/Retry-After"}}},"502":{"description":"Error de un servicio interno.\n\n- `upstream_error`: Un servicio interno de xYago falló o no respondió a tiempo. Reintenta con backoff (en las escrituras, comprueba antes si ya se aplicaron).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorSchema"},"examples":{"upstream_error":{"summary":"upstream_error","description":"Un servicio interno de xYago falló o no respondió a tiempo. Reintenta con backoff (en las escrituras, comprueba antes si ya se aplicaron).","value":{"error":{"code":"upstream_error","message":"An internal service failed to process the request. Try again later."}}}}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"503":{"description":"Servicio no disponible.\n\n- `import_queue_unavailable`: No se pudo encolar la importación. Reintenta más tarde.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorSchema"},"examples":{"import_queue_unavailable":{"summary":"import_queue_unavailable","description":"No se pudo encolar la importación. Reintenta más tarde.","value":{"error":{"code":"import_queue_unavailable","message":"The import could not be queued. Try again later."}}}}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}}},"parameters":[{"$ref":"#/components/parameters/XRequestId"}],"x-codeSamples":[{"lang":"Shell","label":"curl","source":"curl -X POST 'https://api.xyago.com/v1/employees/import' \\\n  -H \"Authorization: Bearer $XYAGO_API_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d @- <<'JSON'\n{\n  \"rows\": [\n    {\n      \"external_id\": \"SIESA-000123\",\n      \"first_name\": \"Valentina\",\n      \"last_name\": \"Rojas Castaño\",\n      \"email\": \"valentina.rojas@example.com\",\n      \"phone_number\": \"+573005550123\",\n      \"document_number\": \"1020304050\",\n      \"job_title\": \"Auxiliar de bodega\",\n      \"contract_type\": \"Full-time fixed-term\",\n      \"hire_date\": \"2026-02-01\",\n      \"contract_end_date\": \"2027-01-31\",\n      \"work_shifts\": [\n        {\n          \"day\": \"Monday\",\n          \"start\": \"07:00\",\n          \"end\": \"16:00\"\n        },\n        {\n          \"day\": \"Tuesday\",\n          \"start\": \"07:00\",\n          \"end\": \"16:00\"\n        },\n        {\n          \"day\": \"Wednesday\",\n          \"start\": \"07:00\",\n          \"end\": \"16:00\"\n        },\n        {\n          \"day\": \"Thursday\",\n          \"start\": \"07:00\",\n          \"end\": \"16:00\"\n        },\n        {\n          \"day\": \"Friday\",\n          \"start\": \"07:00\",\n          \"end\": \"16:00\"\n        },\n        {\n          \"day\": \"Saturday\",\n          \"start\": \"08:00\",\n          \"end\": \"12:00\"\n        }\n      ],\n      \"vacation_days\": 15\n    },\n    {\n      \"external_id\": \"SIESA-000087\",\n      \"job_title\": \"Conductor de reparto\"\n    },\n    {\n      \"external_id\": \"SIESA-000150\",\n      \"first_name\": \"Camilo\"\n    }\n  ]\n}\nJSON"},{"lang":"Python","label":"Python","source":"import os\n\nimport requests\n\npayload = {\n    \"rows\": [\n        {\n            \"external_id\": \"SIESA-000123\",\n            \"first_name\": \"Valentina\",\n            \"last_name\": \"Rojas Castaño\",\n            \"email\": \"valentina.rojas@example.com\",\n            \"phone_number\": \"+573005550123\",\n            \"document_number\": \"1020304050\",\n            \"job_title\": \"Auxiliar de bodega\",\n            \"contract_type\": \"Full-time fixed-term\",\n            \"hire_date\": \"2026-02-01\",\n            \"contract_end_date\": \"2027-01-31\",\n            \"work_shifts\": [\n                {\n                    \"day\": \"Monday\",\n                    \"start\": \"07:00\",\n                    \"end\": \"16:00\"\n                },\n                {\n                    \"day\": \"Tuesday\",\n                    \"start\": \"07:00\",\n                    \"end\": \"16:00\"\n                },\n                {\n                    \"day\": \"Wednesday\",\n                    \"start\": \"07:00\",\n                    \"end\": \"16:00\"\n                },\n                {\n                    \"day\": \"Thursday\",\n                    \"start\": \"07:00\",\n                    \"end\": \"16:00\"\n                },\n                {\n                    \"day\": \"Friday\",\n                    \"start\": \"07:00\",\n                    \"end\": \"16:00\"\n                },\n                {\n                    \"day\": \"Saturday\",\n                    \"start\": \"08:00\",\n                    \"end\": \"12:00\"\n                }\n            ],\n            \"vacation_days\": 15\n        },\n        {\n            \"external_id\": \"SIESA-000087\",\n            \"job_title\": \"Conductor de reparto\"\n        },\n        {\n            \"external_id\": \"SIESA-000150\",\n            \"first_name\": \"Camilo\"\n        }\n    ]\n}\n\nresponse = requests.post(\n    \"https://api.xyago.com/v1/employees/import\",\n    headers={\n        \"Authorization\": f\"Bearer {os.environ['XYAGO_API_KEY']}\",\n    },\n    json=payload,\n    timeout=30,\n)\nresponse.raise_for_status()\nprint(response.headers['X-Request-Id'], response.json())"},{"lang":"JavaScript","label":"JavaScript","source":"const payload = {\n  \"rows\": [\n    {\n      \"external_id\": \"SIESA-000123\",\n      \"first_name\": \"Valentina\",\n      \"last_name\": \"Rojas Castaño\",\n      \"email\": \"valentina.rojas@example.com\",\n      \"phone_number\": \"+573005550123\",\n      \"document_number\": \"1020304050\",\n      \"job_title\": \"Auxiliar de bodega\",\n      \"contract_type\": \"Full-time fixed-term\",\n      \"hire_date\": \"2026-02-01\",\n      \"contract_end_date\": \"2027-01-31\",\n      \"work_shifts\": [\n        {\n          \"day\": \"Monday\",\n          \"start\": \"07:00\",\n          \"end\": \"16:00\"\n        },\n        {\n          \"day\": \"Tuesday\",\n          \"start\": \"07:00\",\n          \"end\": \"16:00\"\n        },\n        {\n          \"day\": \"Wednesday\",\n          \"start\": \"07:00\",\n          \"end\": \"16:00\"\n        },\n        {\n          \"day\": \"Thursday\",\n          \"start\": \"07:00\",\n          \"end\": \"16:00\"\n        },\n        {\n          \"day\": \"Friday\",\n          \"start\": \"07:00\",\n          \"end\": \"16:00\"\n        },\n        {\n          \"day\": \"Saturday\",\n          \"start\": \"08:00\",\n          \"end\": \"12:00\"\n        }\n      ],\n      \"vacation_days\": 15\n    },\n    {\n      \"external_id\": \"SIESA-000087\",\n      \"job_title\": \"Conductor de reparto\"\n    },\n    {\n      \"external_id\": \"SIESA-000150\",\n      \"first_name\": \"Camilo\"\n    }\n  ]\n};\n\nconst response = await fetch(\"https://api.xyago.com/v1/employees/import\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.XYAGO_API_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify(payload),\n});\nconst data = await response.json();\nif (!response.ok) throw new Error(`${data.error.code}: ${data.error.message}`);\nconsole.log(response.headers.get(\"X-Request-Id\"), data);"},{"lang":"C#","label":"C#","source":"using System.Net.Http.Headers;\nusing System.Text;\n\nusing var http = new HttpClient();\nhttp.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(\n    \"Bearer\", Environment.GetEnvironmentVariable(\"XYAGO_API_KEY\"));\n\nvar request = new HttpRequestMessage(new HttpMethod(\"POST\"), \"https://api.xyago.com/v1/employees/import\");\nvar payload = \"\"\"\n{\n  \"rows\": [\n    {\n      \"external_id\": \"SIESA-000123\",\n      \"first_name\": \"Valentina\",\n      \"last_name\": \"Rojas Castaño\",\n      \"email\": \"valentina.rojas@example.com\",\n      \"phone_number\": \"+573005550123\",\n      \"document_number\": \"1020304050\",\n      \"job_title\": \"Auxiliar de bodega\",\n      \"contract_type\": \"Full-time fixed-term\",\n      \"hire_date\": \"2026-02-01\",\n      \"contract_end_date\": \"2027-01-31\",\n      \"work_shifts\": [\n        {\n          \"day\": \"Monday\",\n          \"start\": \"07:00\",\n          \"end\": \"16:00\"\n        },\n        {\n          \"day\": \"Tuesday\",\n          \"start\": \"07:00\",\n          \"end\": \"16:00\"\n        },\n        {\n          \"day\": \"Wednesday\",\n          \"start\": \"07:00\",\n          \"end\": \"16:00\"\n        },\n        {\n          \"day\": \"Thursday\",\n          \"start\": \"07:00\",\n          \"end\": \"16:00\"\n        },\n        {\n          \"day\": \"Friday\",\n          \"start\": \"07:00\",\n          \"end\": \"16:00\"\n        },\n        {\n          \"day\": \"Saturday\",\n          \"start\": \"08:00\",\n          \"end\": \"12:00\"\n        }\n      ],\n      \"vacation_days\": 15\n    },\n    {\n      \"external_id\": \"SIESA-000087\",\n      \"job_title\": \"Conductor de reparto\"\n    },\n    {\n      \"external_id\": \"SIESA-000150\",\n      \"first_name\": \"Camilo\"\n    }\n  ]\n}\n\"\"\";\nrequest.Content = new StringContent(payload, Encoding.UTF8, \"application/json\");\nvar response = await http.SendAsync(request);\nvar body = await response.Content.ReadAsStringAsync();\nConsole.WriteLine($\"{(int)response.StatusCode} {body}\");"}]}},"/employees/import/{job_id}":{"get":{"tags":["employees"],"summary":"Consultar una importación","description":"Progreso (`status`: `queued` → `processing` → `completed` | `failed`) y resultado\npor fila (`created`, `updated` o `failed` con `error_code` y `error_message`).\nConsulta cada pocos segundos hasta que `status` sea `completed` o `failed`. Un\njob de otra empresa o de otro entorno (live/test) responde `404`.\n\nScope: `employees:read`.","operationId":"get_import","parameters":[{"name":"job_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","description":"`job_id` devuelto al crear la importación.","title":"Job Id"},"description":"`job_id` devuelto al crear la importación.","examples":{"ejemplo":{"summary":"Ejemplo","value":"0b8c6d4e-2f1a-4c3b-9e5d-7a6b8c9d0e1f"}}},{"$ref":"#/components/parameters/XRequestId"}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmployeeImportJob"},"example":{"job_id":"0b8c6d4e-2f1a-4c3b-9e5d-7a6b8c9d0e1f","status":"completed","total_rows":3,"processed_rows":3,"created":1,"updated":1,"failed":1,"created_at":"2026-03-02T15:00:00Z","started_at":"2026-03-02T15:00:01Z","finished_at":"2026-03-02T15:00:04Z","results":[{"row_index":0,"external_id":"SIESA-000123","status":"created","employee_id":"2c9a4e71-5b3d-4f8e-a6c2-7d1e0f9b3a54"},{"row_index":1,"external_id":"SIESA-000087","status":"updated","employee_id":"5e7b1d93-2a4c-4b6e-8f0a-3c5d7e9f1b26"},{"row_index":2,"external_id":"SIESA-000150","status":"failed","error_code":"validation_error","error_message":"phone_number: Field required; job_title: Field required"}]}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"}}},"401":{"description":"No autenticado.\n\n- `invalid_api_key`: Falta la API key o no es válida, está revocada o ha caducado.","content":{"application/json":{"examples":{"invalid_api_key":{"summary":"invalid_api_key","description":"Falta la API key o no es válida, está revocada o ha caducado.","value":{"error":{"code":"invalid_api_key","message":"Missing or invalid API key."}}}},"schema":{"$ref":"#/components/schemas/ErrorSchema"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"403":{"description":"Prohibido.\n\n- `api_disabled`: El producto (areas, employees...) no está habilitado en el panel para el entorno de la API key (live o test).\n- `endpoint_disabled`: El administrador desactivó este endpoint concreto en el panel.\n- `insufficient_scope`: La API key no tiene el scope que exige el endpoint (p. ej. `employees:write`).\n- `ip_not_allowed`: La IP de origen no está en la lista de IPs permitidas del producto.","content":{"application/json":{"examples":{"api_disabled":{"summary":"api_disabled","description":"El producto (areas, employees...) no está habilitado en el panel para el entorno de la API key (live o test).","value":{"error":{"code":"api_disabled","message":"The 'employees' API is not enabled for this environment."}}},"endpoint_disabled":{"summary":"endpoint_disabled","description":"El administrador desactivó este endpoint concreto en el panel.","value":{"error":{"code":"endpoint_disabled","message":"This endpoint is disabled."}}},"insufficient_scope":{"summary":"insufficient_scope","description":"La API key no tiene el scope que exige el endpoint (p. ej. `employees:write`).","value":{"error":{"code":"insufficient_scope","message":"This API key lacks the 'employees:write' scope."}}},"ip_not_allowed":{"summary":"ip_not_allowed","description":"La IP de origen no está en la lista de IPs permitidas del producto.","value":{"error":{"code":"ip_not_allowed","message":"Requests from this IP are not allowed."}}}},"schema":{"$ref":"#/components/schemas/ErrorSchema"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"404":{"description":"No encontrado.\n\n- `not_found`: El recurso no existe o pertenece a otra empresa (nunca se distingue).","content":{"application/json":{"examples":{"not_found":{"summary":"not_found","description":"El recurso no existe o pertenece a otra empresa (nunca se distingue).","value":{"error":{"code":"not_found","message":"Employee not found."}}}},"schema":{"$ref":"#/components/schemas/ErrorSchema"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"422":{"description":"Datos no válidos.\n\n- `validation_error`: El cuerpo o los parámetros no cumplen el esquema (campo obligatorio, formato, campo desconocido...). `message` indica el campo.","content":{"application/json":{"examples":{"validation_error":{"summary":"validation_error","description":"El cuerpo o los parámetros no cumplen el esquema (campo obligatorio, formato, campo desconocido...). `message` indica el campo.","value":{"error":{"code":"validation_error","message":"phone_number: String should match pattern '^\\+[1-9][0-9]{6,14}$'"}}}},"schema":{"$ref":"#/components/schemas/ErrorSchema"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"429":{"description":"Demasiadas peticiones.\n\n- `rate_limited`: Se superó el límite de peticiones por minuto de la API key o, en `POST /messages`, un tope de envío del remitente (mensajes por minuto, por destinatario y día, destinatarios nuevos por día) o, en Verify, un tope de códigos (espera entre códigos, por teléfono, por remitente) o de códigos erróneos por hora. Espera los segundos de `Retry-After`.","content":{"application/json":{"examples":{"rate_limited":{"summary":"rate_limited","description":"Se superó el límite de peticiones por minuto de la API key o, en `POST /messages`, un tope de envío del remitente (mensajes por minuto, por destinatario y día, destinatarios nuevos por día) o, en Verify, un tope de códigos (espera entre códigos, por teléfono, por remitente) o de códigos erróneos por hora. Espera los segundos de `Retry-After`.","value":{"error":{"code":"rate_limited","message":"Rate limit exceeded. Retry after the indicated number of seconds."}}}},"schema":{"$ref":"#/components/schemas/ErrorSchema"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"},"Retry-After":{"$ref":"#/components/headers/Retry-After"}}},"502":{"description":"Error de un servicio interno.\n\n- `upstream_error`: Un servicio interno de xYago falló o no respondió a tiempo. Reintenta con backoff (en las escrituras, comprueba antes si ya se aplicaron).","content":{"application/json":{"examples":{"upstream_error":{"summary":"upstream_error","description":"Un servicio interno de xYago falló o no respondió a tiempo. Reintenta con backoff (en las escrituras, comprueba antes si ya se aplicaron).","value":{"error":{"code":"upstream_error","message":"An internal service failed to process the request. Try again later."}}}},"schema":{"$ref":"#/components/schemas/ErrorSchema"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}}},"x-codeSamples":[{"lang":"Shell","label":"curl","source":"curl -X GET 'https://api.xyago.com/v1/employees/import/0b8c6d4e-2f1a-4c3b-9e5d-7a6b8c9d0e1f' \\\n  -H \"Authorization: Bearer $XYAGO_API_KEY\""},{"lang":"Python","label":"Python","source":"import os\n\nimport requests\n\nresponse = requests.get(\n    \"https://api.xyago.com/v1/employees/import/0b8c6d4e-2f1a-4c3b-9e5d-7a6b8c9d0e1f\",\n    headers={\n        \"Authorization\": f\"Bearer {os.environ['XYAGO_API_KEY']}\",\n    },\n    timeout=30,\n)\nresponse.raise_for_status()\nprint(response.headers['X-Request-Id'], response.json())"},{"lang":"JavaScript","label":"JavaScript","source":"const response = await fetch(\"https://api.xyago.com/v1/employees/import/0b8c6d4e-2f1a-4c3b-9e5d-7a6b8c9d0e1f\", {\n  method: \"GET\",\n  headers: {\n    Authorization: `Bearer ${process.env.XYAGO_API_KEY}`,\n  },\n});\nconst data = await response.json();\nif (!response.ok) throw new Error(`${data.error.code}: ${data.error.message}`);\nconsole.log(response.headers.get(\"X-Request-Id\"), data);"},{"lang":"C#","label":"C#","source":"using System.Net.Http.Headers;\nusing System.Text;\n\nusing var http = new HttpClient();\nhttp.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(\n    \"Bearer\", Environment.GetEnvironmentVariable(\"XYAGO_API_KEY\"));\n\nvar request = new HttpRequestMessage(new HttpMethod(\"GET\"), \"https://api.xyago.com/v1/employees/import/0b8c6d4e-2f1a-4c3b-9e5d-7a6b8c9d0e1f\");\nvar response = await http.SendAsync(request);\nvar body = await response.Content.ReadAsStringAsync();\nConsole.WriteLine($\"{(int)response.StatusCode} {body}\");"}]}},"/employees/{id}":{"get":{"tags":["employees"],"summary":"Obtener un empleado","description":"Detalle de un empleado de la empresa. Un id de otra empresa responde `404`.\n\nNota: xYago guarda un único nombre por empresa; `first_name` trae el nombre\ncompleto y `last_name` es siempre `null`.\n\nScope: `employees:read`.","operationId":"get_employee","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid","description":"Id del empleado.","title":"Id"},"description":"Id del empleado.","examples":{"ejemplo":{"summary":"Ejemplo","value":"2c9a4e71-5b3d-4f8e-a6c2-7d1e0f9b3a54"}}},{"$ref":"#/components/parameters/XRequestId"}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Employee"},"example":{"id":"2c9a4e71-5b3d-4f8e-a6c2-7d1e0f9b3a54","external_id":"SIESA-000123","first_name":"Valentina Rojas Castaño","email":"valentina.rojas@example.com","phone_number":"+573005550123","document_number":"1020304050","job":{"id":"9d4f6a28-3b1c-4e7d-b5a9-0e2f4c6d8a13","name":"Auxiliar de bodega"},"area_ids":["8b3e9f12-7c4d-4e5a-9b6c-1d2e3f405162"],"status":"active","contract_type":"Full-time fixed-term","hire_date":"2026-02-01","contract_end_date":"2027-01-31"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"}}},"401":{"description":"No autenticado.\n\n- `invalid_api_key`: Falta la API key o no es válida, está revocada o ha caducado.","content":{"application/json":{"examples":{"invalid_api_key":{"summary":"invalid_api_key","description":"Falta la API key o no es válida, está revocada o ha caducado.","value":{"error":{"code":"invalid_api_key","message":"Missing or invalid API key."}}}},"schema":{"$ref":"#/components/schemas/ErrorSchema"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"403":{"description":"Prohibido.\n\n- `api_disabled`: El producto (areas, employees...) no está habilitado en el panel para el entorno de la API key (live o test).\n- `endpoint_disabled`: El administrador desactivó este endpoint concreto en el panel.\n- `insufficient_scope`: La API key no tiene el scope que exige el endpoint (p. ej. `employees:write`).\n- `ip_not_allowed`: La IP de origen no está en la lista de IPs permitidas del producto.","content":{"application/json":{"examples":{"api_disabled":{"summary":"api_disabled","description":"El producto (areas, employees...) no está habilitado en el panel para el entorno de la API key (live o test).","value":{"error":{"code":"api_disabled","message":"The 'employees' API is not enabled for this environment."}}},"endpoint_disabled":{"summary":"endpoint_disabled","description":"El administrador desactivó este endpoint concreto en el panel.","value":{"error":{"code":"endpoint_disabled","message":"This endpoint is disabled."}}},"insufficient_scope":{"summary":"insufficient_scope","description":"La API key no tiene el scope que exige el endpoint (p. ej. `employees:write`).","value":{"error":{"code":"insufficient_scope","message":"This API key lacks the 'employees:write' scope."}}},"ip_not_allowed":{"summary":"ip_not_allowed","description":"La IP de origen no está en la lista de IPs permitidas del producto.","value":{"error":{"code":"ip_not_allowed","message":"Requests from this IP are not allowed."}}}},"schema":{"$ref":"#/components/schemas/ErrorSchema"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"404":{"description":"No encontrado.\n\n- `not_found`: El recurso no existe o pertenece a otra empresa (nunca se distingue).","content":{"application/json":{"examples":{"not_found":{"summary":"not_found","description":"El recurso no existe o pertenece a otra empresa (nunca se distingue).","value":{"error":{"code":"not_found","message":"Employee not found."}}}},"schema":{"$ref":"#/components/schemas/ErrorSchema"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"422":{"description":"Datos no válidos.\n\n- `validation_error`: El cuerpo o los parámetros no cumplen el esquema (campo obligatorio, formato, campo desconocido...). `message` indica el campo.","content":{"application/json":{"examples":{"validation_error":{"summary":"validation_error","description":"El cuerpo o los parámetros no cumplen el esquema (campo obligatorio, formato, campo desconocido...). `message` indica el campo.","value":{"error":{"code":"validation_error","message":"phone_number: String should match pattern '^\\+[1-9][0-9]{6,14}$'"}}}},"schema":{"$ref":"#/components/schemas/ErrorSchema"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"429":{"description":"Demasiadas peticiones.\n\n- `rate_limited`: Se superó el límite de peticiones por minuto de la API key o, en `POST /messages`, un tope de envío del remitente (mensajes por minuto, por destinatario y día, destinatarios nuevos por día) o, en Verify, un tope de códigos (espera entre códigos, por teléfono, por remitente) o de códigos erróneos por hora. Espera los segundos de `Retry-After`.","content":{"application/json":{"examples":{"rate_limited":{"summary":"rate_limited","description":"Se superó el límite de peticiones por minuto de la API key o, en `POST /messages`, un tope de envío del remitente (mensajes por minuto, por destinatario y día, destinatarios nuevos por día) o, en Verify, un tope de códigos (espera entre códigos, por teléfono, por remitente) o de códigos erróneos por hora. Espera los segundos de `Retry-After`.","value":{"error":{"code":"rate_limited","message":"Rate limit exceeded. Retry after the indicated number of seconds."}}}},"schema":{"$ref":"#/components/schemas/ErrorSchema"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"},"Retry-After":{"$ref":"#/components/headers/Retry-After"}}},"502":{"description":"Error de un servicio interno.\n\n- `upstream_error`: Un servicio interno de xYago falló o no respondió a tiempo. Reintenta con backoff (en las escrituras, comprueba antes si ya se aplicaron).","content":{"application/json":{"examples":{"upstream_error":{"summary":"upstream_error","description":"Un servicio interno de xYago falló o no respondió a tiempo. Reintenta con backoff (en las escrituras, comprueba antes si ya se aplicaron).","value":{"error":{"code":"upstream_error","message":"An internal service failed to process the request. Try again later."}}}},"schema":{"$ref":"#/components/schemas/ErrorSchema"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}}},"x-codeSamples":[{"lang":"Shell","label":"curl","source":"curl -X GET 'https://api.xyago.com/v1/employees/2c9a4e71-5b3d-4f8e-a6c2-7d1e0f9b3a54' \\\n  -H \"Authorization: Bearer $XYAGO_API_KEY\""},{"lang":"Python","label":"Python","source":"import os\n\nimport requests\n\nresponse = requests.get(\n    \"https://api.xyago.com/v1/employees/2c9a4e71-5b3d-4f8e-a6c2-7d1e0f9b3a54\",\n    headers={\n        \"Authorization\": f\"Bearer {os.environ['XYAGO_API_KEY']}\",\n    },\n    timeout=30,\n)\nresponse.raise_for_status()\nprint(response.headers['X-Request-Id'], response.json())"},{"lang":"JavaScript","label":"JavaScript","source":"const response = await fetch(\"https://api.xyago.com/v1/employees/2c9a4e71-5b3d-4f8e-a6c2-7d1e0f9b3a54\", {\n  method: \"GET\",\n  headers: {\n    Authorization: `Bearer ${process.env.XYAGO_API_KEY}`,\n  },\n});\nconst data = await response.json();\nif (!response.ok) throw new Error(`${data.error.code}: ${data.error.message}`);\nconsole.log(response.headers.get(\"X-Request-Id\"), data);"},{"lang":"C#","label":"C#","source":"using System.Net.Http.Headers;\nusing System.Text;\n\nusing var http = new HttpClient();\nhttp.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(\n    \"Bearer\", Environment.GetEnvironmentVariable(\"XYAGO_API_KEY\"));\n\nvar request = new HttpRequestMessage(new HttpMethod(\"GET\"), \"https://api.xyago.com/v1/employees/2c9a4e71-5b3d-4f8e-a6c2-7d1e0f9b3a54\");\nvar response = await http.SendAsync(request);\nvar body = await response.Content.ReadAsStringAsync();\nConsole.WriteLine($\"{(int)response.StatusCode} {body}\");"}]},"patch":{"tags":["employees"],"summary":"Editar un empleado","description":"Edición parcial: solo se cambian los campos enviados (al menos uno; `null` no\nestá permitido). Mismos campos que el alta salvo `work_shifts` y\n`vacation_days`. Emite `employee.updated`.\n\nScope: `employees:write`.","operationId":"update_employee","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid","description":"Id del empleado.","title":"Id"},"description":"Id del empleado.","examples":{"ejemplo":{"summary":"Ejemplo","value":"2c9a4e71-5b3d-4f8e-a6c2-7d1e0f9b3a54"}}},{"$ref":"#/components/parameters/XRequestId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmployeeUpdate"},"examples":{"puesto":{"summary":"Cambio de puesto y correo","value":{"email":"valentina.rojas@example.com","job_title":"Coordinadora de bodega"}}}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Employee"},"example":{"id":"2c9a4e71-5b3d-4f8e-a6c2-7d1e0f9b3a54","external_id":"SIESA-000123","first_name":"Valentina Rojas Castaño","email":"valentina.rojas@example.com","phone_number":"+573005550123","document_number":"1020304050","job":{"id":"9d4f6a28-3b1c-4e7d-b5a9-0e2f4c6d8a13","name":"Auxiliar de bodega"},"area_ids":["8b3e9f12-7c4d-4e5a-9b6c-1d2e3f405162"],"status":"active","contract_type":"Full-time fixed-term","hire_date":"2026-02-01","contract_end_date":"2027-01-31"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"}}},"401":{"description":"No autenticado.\n\n- `invalid_api_key`: Falta la API key o no es válida, está revocada o ha caducado.","content":{"application/json":{"examples":{"invalid_api_key":{"summary":"invalid_api_key","description":"Falta la API key o no es válida, está revocada o ha caducado.","value":{"error":{"code":"invalid_api_key","message":"Missing or invalid API key."}}}},"schema":{"$ref":"#/components/schemas/ErrorSchema"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"403":{"description":"Prohibido.\n\n- `api_disabled`: El producto (areas, employees...) no está habilitado en el panel para el entorno de la API key (live o test).\n- `endpoint_disabled`: El administrador desactivó este endpoint concreto en el panel.\n- `insufficient_scope`: La API key no tiene el scope que exige el endpoint (p. ej. `employees:write`).\n- `ip_not_allowed`: La IP de origen no está en la lista de IPs permitidas del producto.","content":{"application/json":{"examples":{"api_disabled":{"summary":"api_disabled","description":"El producto (areas, employees...) no está habilitado en el panel para el entorno de la API key (live o test).","value":{"error":{"code":"api_disabled","message":"The 'employees' API is not enabled for this environment."}}},"endpoint_disabled":{"summary":"endpoint_disabled","description":"El administrador desactivó este endpoint concreto en el panel.","value":{"error":{"code":"endpoint_disabled","message":"This endpoint is disabled."}}},"insufficient_scope":{"summary":"insufficient_scope","description":"La API key no tiene el scope que exige el endpoint (p. ej. `employees:write`).","value":{"error":{"code":"insufficient_scope","message":"This API key lacks the 'employees:write' scope."}}},"ip_not_allowed":{"summary":"ip_not_allowed","description":"La IP de origen no está en la lista de IPs permitidas del producto.","value":{"error":{"code":"ip_not_allowed","message":"Requests from this IP are not allowed."}}}},"schema":{"$ref":"#/components/schemas/ErrorSchema"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"404":{"description":"No encontrado.\n\n- `not_found`: El recurso no existe o pertenece a otra empresa (nunca se distingue).","content":{"application/json":{"examples":{"not_found":{"summary":"not_found","description":"El recurso no existe o pertenece a otra empresa (nunca se distingue).","value":{"error":{"code":"not_found","message":"Employee not found."}}}},"schema":{"$ref":"#/components/schemas/ErrorSchema"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"409":{"description":"Conflicto.\n\n- `external_id_already_exists`: El `external_id` ya está asignado a otro empleado de la empresa.\n- `employee_already_exists`: Ya hay un empleado con ese `phone_number` en la empresa.\n- `email_already_registered`: El `email` ya lo usa otro empleado.\n- `document_number_already_exists`: El `document_number` ya lo usa otro empleado de la empresa.","content":{"application/json":{"examples":{"external_id_already_exists":{"summary":"external_id_already_exists","description":"El `external_id` ya está asignado a otro empleado de la empresa.","value":{"error":{"code":"external_id_already_exists","message":"external_id is already assigned to another employee of the company."}}},"employee_already_exists":{"summary":"employee_already_exists","description":"Ya hay un empleado con ese `phone_number` en la empresa.","value":{"error":{"code":"employee_already_exists","message":"An employee with this phone_number already exists in the company."}}},"email_already_registered":{"summary":"email_already_registered","description":"El `email` ya lo usa otro empleado.","value":{"error":{"code":"email_already_registered","message":"This email is already used by another employee."}}},"document_number_already_exists":{"summary":"document_number_already_exists","description":"El `document_number` ya lo usa otro empleado de la empresa.","value":{"error":{"code":"document_number_already_exists","message":"document_number is already used by another employee of the company."}}}},"schema":{"$ref":"#/components/schemas/ErrorSchema"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"422":{"description":"Datos no válidos.\n\n- `validation_error`: El cuerpo o los parámetros no cumplen el esquema (campo obligatorio, formato, campo desconocido...). `message` indica el campo.\n- `invalid_employee_data`: xYago rechazó los datos del empleado. Revisa los campos.\n- `employee_has_no_job`: Se intentó cambiar el puesto de un empleado sin puesto en la empresa.","content":{"application/json":{"examples":{"validation_error":{"summary":"validation_error","description":"El cuerpo o los parámetros no cumplen el esquema (campo obligatorio, formato, campo desconocido...). `message` indica el campo.","value":{"error":{"code":"validation_error","message":"phone_number: String should match pattern '^\\+[1-9][0-9]{6,14}$'"}}},"invalid_employee_data":{"summary":"invalid_employee_data","description":"xYago rechazó los datos del empleado. Revisa los campos.","value":{"error":{"code":"invalid_employee_data","message":"The employee data was rejected. Check the fields."}}},"employee_has_no_job":{"summary":"employee_has_no_job","description":"Se intentó cambiar el puesto de un empleado sin puesto en la empresa.","value":{"error":{"code":"employee_has_no_job","message":"The employee has no job in the company to update."}}}},"schema":{"$ref":"#/components/schemas/ErrorSchema"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"429":{"description":"Demasiadas peticiones.\n\n- `rate_limited`: Se superó el límite de peticiones por minuto de la API key o, en `POST /messages`, un tope de envío del remitente (mensajes por minuto, por destinatario y día, destinatarios nuevos por día) o, en Verify, un tope de códigos (espera entre códigos, por teléfono, por remitente) o de códigos erróneos por hora. Espera los segundos de `Retry-After`.","content":{"application/json":{"examples":{"rate_limited":{"summary":"rate_limited","description":"Se superó el límite de peticiones por minuto de la API key o, en `POST /messages`, un tope de envío del remitente (mensajes por minuto, por destinatario y día, destinatarios nuevos por día) o, en Verify, un tope de códigos (espera entre códigos, por teléfono, por remitente) o de códigos erróneos por hora. Espera los segundos de `Retry-After`.","value":{"error":{"code":"rate_limited","message":"Rate limit exceeded. Retry after the indicated number of seconds."}}}},"schema":{"$ref":"#/components/schemas/ErrorSchema"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"},"Retry-After":{"$ref":"#/components/headers/Retry-After"}}},"502":{"description":"Error de un servicio interno.\n\n- `upstream_error`: Un servicio interno de xYago falló o no respondió a tiempo. Reintenta con backoff (en las escrituras, comprueba antes si ya se aplicaron).","content":{"application/json":{"examples":{"upstream_error":{"summary":"upstream_error","description":"Un servicio interno de xYago falló o no respondió a tiempo. Reintenta con backoff (en las escrituras, comprueba antes si ya se aplicaron).","value":{"error":{"code":"upstream_error","message":"An internal service failed to process the request. Try again later."}}}},"schema":{"$ref":"#/components/schemas/ErrorSchema"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}}},"x-codeSamples":[{"lang":"Shell","label":"curl","source":"curl -X PATCH 'https://api.xyago.com/v1/employees/2c9a4e71-5b3d-4f8e-a6c2-7d1e0f9b3a54' \\\n  -H \"Authorization: Bearer $XYAGO_API_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d @- <<'JSON'\n{\n  \"email\": \"valentina.rojas@example.com\",\n  \"job_title\": \"Coordinadora de bodega\"\n}\nJSON"},{"lang":"Python","label":"Python","source":"import os\n\nimport requests\n\npayload = {\n    \"email\": \"valentina.rojas@example.com\",\n    \"job_title\": \"Coordinadora de bodega\"\n}\n\nresponse = requests.patch(\n    \"https://api.xyago.com/v1/employees/2c9a4e71-5b3d-4f8e-a6c2-7d1e0f9b3a54\",\n    headers={\n        \"Authorization\": f\"Bearer {os.environ['XYAGO_API_KEY']}\",\n    },\n    json=payload,\n    timeout=30,\n)\nresponse.raise_for_status()\nprint(response.headers['X-Request-Id'], response.json())"},{"lang":"JavaScript","label":"JavaScript","source":"const payload = {\n  \"email\": \"valentina.rojas@example.com\",\n  \"job_title\": \"Coordinadora de bodega\"\n};\n\nconst response = await fetch(\"https://api.xyago.com/v1/employees/2c9a4e71-5b3d-4f8e-a6c2-7d1e0f9b3a54\", {\n  method: \"PATCH\",\n  headers: {\n    Authorization: `Bearer ${process.env.XYAGO_API_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify(payload),\n});\nconst data = await response.json();\nif (!response.ok) throw new Error(`${data.error.code}: ${data.error.message}`);\nconsole.log(response.headers.get(\"X-Request-Id\"), data);"},{"lang":"C#","label":"C#","source":"using System.Net.Http.Headers;\nusing System.Text;\n\nusing var http = new HttpClient();\nhttp.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(\n    \"Bearer\", Environment.GetEnvironmentVariable(\"XYAGO_API_KEY\"));\n\nvar request = new HttpRequestMessage(new HttpMethod(\"PATCH\"), \"https://api.xyago.com/v1/employees/2c9a4e71-5b3d-4f8e-a6c2-7d1e0f9b3a54\");\nvar payload = \"\"\"\n{\n  \"email\": \"valentina.rojas@example.com\",\n  \"job_title\": \"Coordinadora de bodega\"\n}\n\"\"\";\nrequest.Content = new StringContent(payload, Encoding.UTF8, \"application/json\");\nvar response = await http.SendAsync(request);\nvar body = await response.Content.ReadAsStringAsync();\nConsole.WriteLine($\"{(int)response.StatusCode} {body}\");"}]}},"/employees/{id}/deactivate":{"post":{"tags":["employees"],"summary":"Desactivar un empleado","description":"Desactiva al empleado en la empresa (mismo cambio de estado que el panel; no lo\nborra ni lo desvincula). Idempotente: si ya estaba inactivo responde `200` sin\ncambios. Emite `employee.deactivated` solo cuando cambia el estado.\n\nScope: `employees:write`.","operationId":"deactivate_employee","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid","description":"Id del empleado.","title":"Id"},"description":"Id del empleado.","examples":{"ejemplo":{"summary":"Ejemplo","value":"2c9a4e71-5b3d-4f8e-a6c2-7d1e0f9b3a54"}}},{"$ref":"#/components/parameters/XRequestId"}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Employee"},"example":{"id":"2c9a4e71-5b3d-4f8e-a6c2-7d1e0f9b3a54","external_id":"SIESA-000123","first_name":"Valentina Rojas Castaño","email":"valentina.rojas@example.com","phone_number":"+573005550123","document_number":"1020304050","job":{"id":"9d4f6a28-3b1c-4e7d-b5a9-0e2f4c6d8a13","name":"Auxiliar de bodega"},"area_ids":["8b3e9f12-7c4d-4e5a-9b6c-1d2e3f405162"],"status":"inactive","contract_type":"Full-time fixed-term","hire_date":"2026-02-01","contract_end_date":"2027-01-31"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"}}},"401":{"description":"No autenticado.\n\n- `invalid_api_key`: Falta la API key o no es válida, está revocada o ha caducado.","content":{"application/json":{"examples":{"invalid_api_key":{"summary":"invalid_api_key","description":"Falta la API key o no es válida, está revocada o ha caducado.","value":{"error":{"code":"invalid_api_key","message":"Missing or invalid API key."}}}},"schema":{"$ref":"#/components/schemas/ErrorSchema"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"403":{"description":"Prohibido.\n\n- `api_disabled`: El producto (areas, employees...) no está habilitado en el panel para el entorno de la API key (live o test).\n- `endpoint_disabled`: El administrador desactivó este endpoint concreto en el panel.\n- `insufficient_scope`: La API key no tiene el scope que exige el endpoint (p. ej. `employees:write`).\n- `ip_not_allowed`: La IP de origen no está en la lista de IPs permitidas del producto.","content":{"application/json":{"examples":{"api_disabled":{"summary":"api_disabled","description":"El producto (areas, employees...) no está habilitado en el panel para el entorno de la API key (live o test).","value":{"error":{"code":"api_disabled","message":"The 'employees' API is not enabled for this environment."}}},"endpoint_disabled":{"summary":"endpoint_disabled","description":"El administrador desactivó este endpoint concreto en el panel.","value":{"error":{"code":"endpoint_disabled","message":"This endpoint is disabled."}}},"insufficient_scope":{"summary":"insufficient_scope","description":"La API key no tiene el scope que exige el endpoint (p. ej. `employees:write`).","value":{"error":{"code":"insufficient_scope","message":"This API key lacks the 'employees:write' scope."}}},"ip_not_allowed":{"summary":"ip_not_allowed","description":"La IP de origen no está en la lista de IPs permitidas del producto.","value":{"error":{"code":"ip_not_allowed","message":"Requests from this IP are not allowed."}}}},"schema":{"$ref":"#/components/schemas/ErrorSchema"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"404":{"description":"No encontrado.\n\n- `not_found`: El recurso no existe o pertenece a otra empresa (nunca se distingue).","content":{"application/json":{"examples":{"not_found":{"summary":"not_found","description":"El recurso no existe o pertenece a otra empresa (nunca se distingue).","value":{"error":{"code":"not_found","message":"Employee not found."}}}},"schema":{"$ref":"#/components/schemas/ErrorSchema"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"422":{"description":"Datos no válidos.\n\n- `validation_error`: El cuerpo o los parámetros no cumplen el esquema (campo obligatorio, formato, campo desconocido...). `message` indica el campo.","content":{"application/json":{"examples":{"validation_error":{"summary":"validation_error","description":"El cuerpo o los parámetros no cumplen el esquema (campo obligatorio, formato, campo desconocido...). `message` indica el campo.","value":{"error":{"code":"validation_error","message":"phone_number: String should match pattern '^\\+[1-9][0-9]{6,14}$'"}}}},"schema":{"$ref":"#/components/schemas/ErrorSchema"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"429":{"description":"Demasiadas peticiones.\n\n- `rate_limited`: Se superó el límite de peticiones por minuto de la API key o, en `POST /messages`, un tope de envío del remitente (mensajes por minuto, por destinatario y día, destinatarios nuevos por día) o, en Verify, un tope de códigos (espera entre códigos, por teléfono, por remitente) o de códigos erróneos por hora. Espera los segundos de `Retry-After`.","content":{"application/json":{"examples":{"rate_limited":{"summary":"rate_limited","description":"Se superó el límite de peticiones por minuto de la API key o, en `POST /messages`, un tope de envío del remitente (mensajes por minuto, por destinatario y día, destinatarios nuevos por día) o, en Verify, un tope de códigos (espera entre códigos, por teléfono, por remitente) o de códigos erróneos por hora. Espera los segundos de `Retry-After`.","value":{"error":{"code":"rate_limited","message":"Rate limit exceeded. Retry after the indicated number of seconds."}}}},"schema":{"$ref":"#/components/schemas/ErrorSchema"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"},"Retry-After":{"$ref":"#/components/headers/Retry-After"}}},"502":{"description":"Error de un servicio interno.\n\n- `upstream_error`: Un servicio interno de xYago falló o no respondió a tiempo. Reintenta con backoff (en las escrituras, comprueba antes si ya se aplicaron).","content":{"application/json":{"examples":{"upstream_error":{"summary":"upstream_error","description":"Un servicio interno de xYago falló o no respondió a tiempo. Reintenta con backoff (en las escrituras, comprueba antes si ya se aplicaron).","value":{"error":{"code":"upstream_error","message":"An internal service failed to process the request. Try again later."}}}},"schema":{"$ref":"#/components/schemas/ErrorSchema"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}}},"x-codeSamples":[{"lang":"Shell","label":"curl","source":"curl -X POST 'https://api.xyago.com/v1/employees/2c9a4e71-5b3d-4f8e-a6c2-7d1e0f9b3a54/deactivate' \\\n  -H \"Authorization: Bearer $XYAGO_API_KEY\""},{"lang":"Python","label":"Python","source":"import os\n\nimport requests\n\nresponse = requests.post(\n    \"https://api.xyago.com/v1/employees/2c9a4e71-5b3d-4f8e-a6c2-7d1e0f9b3a54/deactivate\",\n    headers={\n        \"Authorization\": f\"Bearer {os.environ['XYAGO_API_KEY']}\",\n    },\n    timeout=30,\n)\nresponse.raise_for_status()\nprint(response.headers['X-Request-Id'], response.json())"},{"lang":"JavaScript","label":"JavaScript","source":"const response = await fetch(\"https://api.xyago.com/v1/employees/2c9a4e71-5b3d-4f8e-a6c2-7d1e0f9b3a54/deactivate\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.XYAGO_API_KEY}`,\n  },\n});\nconst data = await response.json();\nif (!response.ok) throw new Error(`${data.error.code}: ${data.error.message}`);\nconsole.log(response.headers.get(\"X-Request-Id\"), data);"},{"lang":"C#","label":"C#","source":"using System.Net.Http.Headers;\nusing System.Text;\n\nusing var http = new HttpClient();\nhttp.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(\n    \"Bearer\", Environment.GetEnvironmentVariable(\"XYAGO_API_KEY\"));\n\nvar request = new HttpRequestMessage(new HttpMethod(\"POST\"), \"https://api.xyago.com/v1/employees/2c9a4e71-5b3d-4f8e-a6c2-7d1e0f9b3a54/deactivate\");\nvar response = await http.SendAsync(request);\nvar body = await response.Content.ReadAsStringAsync();\nConsole.WriteLine($\"{(int)response.StatusCode} {body}\");"}]}},"/vacations":{"get":{"tags":["vacations"],"summary":"Listar solicitudes de vacaciones","description":"Solicitudes de vacaciones de la empresa, de la más reciente a la más antigua.\n\nScope: `vacations:read`.","operationId":"list_vacations","parameters":[{"name":"status","in":"query","required":false,"schema":{"anyOf":[{"enum":["pending","approved","rejected","cancelled"],"type":"string"},{"type":"null"}],"description":"Filtra por estado.","title":"Status"},"description":"Filtra por estado."},{"name":"employee_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"description":"Filtra por empleado (uno de otra empresa devuelve una lista vacía).","title":"Employee Id"},"description":"Filtra por empleado (uno de otra empresa devuelve una lista vacía)."},{"name":"cursor","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":512},{"type":"null"}],"description":"Cursor opaco de la página siguiente (`next_cursor` de la respuesta anterior). Omitir en la primera petición.","title":"Cursor"},"description":"Cursor opaco de la página siguiente (`next_cursor` de la respuesta anterior). Omitir en la primera petición."},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"description":"Tamaño de página (1-100, por defecto 50).","default":50,"title":"Limit"},"description":"Tamaño de página (1-100, por defecto 50)."},{"$ref":"#/components/parameters/XRequestId"}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VacationList"},"example":{"results":[{"id":"65f1a2b3c4d5e6f708192a3b","employee_id":"2c9a4e71-5b3d-4f8e-a6c2-7d1e0f9b3a54","start_date":"2026-12-21","end_date":"2027-01-08","days":13,"status":"pending","reason":"Vacaciones de fin de año","created_at":"2026-11-03T13:20:00Z"},{"id":"65f1a2b3c4d5e6f708192a3b","employee_id":"2c9a4e71-5b3d-4f8e-a6c2-7d1e0f9b3a54","start_date":"2026-12-21","end_date":"2027-01-08","days":13,"status":"approved","reason":"Vacaciones de fin de año","created_at":"2026-11-03T13:20:00Z","decided_at":"2026-11-04T15:02:00Z"}]}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"}}},"400":{"description":"Petición incorrecta.\n\n- `invalid_cursor`: El `cursor` no es válido (manipulado o de otro listado). Repite la consulta sin cursor.","content":{"application/json":{"examples":{"invalid_cursor":{"summary":"invalid_cursor","description":"El `cursor` no es válido (manipulado o de otro listado). Repite la consulta sin cursor.","value":{"error":{"code":"invalid_cursor","message":"Invalid cursor."}}}},"schema":{"$ref":"#/components/schemas/ErrorSchema"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"401":{"description":"No autenticado.\n\n- `invalid_api_key`: Falta la API key o no es válida, está revocada o ha caducado.","content":{"application/json":{"examples":{"invalid_api_key":{"summary":"invalid_api_key","description":"Falta la API key o no es válida, está revocada o ha caducado.","value":{"error":{"code":"invalid_api_key","message":"Missing or invalid API key."}}}},"schema":{"$ref":"#/components/schemas/ErrorSchema"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"403":{"description":"Prohibido.\n\n- `api_disabled`: El producto (areas, employees...) no está habilitado en el panel para el entorno de la API key (live o test).\n- `endpoint_disabled`: El administrador desactivó este endpoint concreto en el panel.\n- `insufficient_scope`: La API key no tiene el scope que exige el endpoint (p. ej. `employees:write`).\n- `ip_not_allowed`: La IP de origen no está en la lista de IPs permitidas del producto.","content":{"application/json":{"examples":{"api_disabled":{"summary":"api_disabled","description":"El producto (areas, employees...) no está habilitado en el panel para el entorno de la API key (live o test).","value":{"error":{"code":"api_disabled","message":"The 'employees' API is not enabled for this environment."}}},"endpoint_disabled":{"summary":"endpoint_disabled","description":"El administrador desactivó este endpoint concreto en el panel.","value":{"error":{"code":"endpoint_disabled","message":"This endpoint is disabled."}}},"insufficient_scope":{"summary":"insufficient_scope","description":"La API key no tiene el scope que exige el endpoint (p. ej. `employees:write`).","value":{"error":{"code":"insufficient_scope","message":"This API key lacks the 'employees:write' scope."}}},"ip_not_allowed":{"summary":"ip_not_allowed","description":"La IP de origen no está en la lista de IPs permitidas del producto.","value":{"error":{"code":"ip_not_allowed","message":"Requests from this IP are not allowed."}}}},"schema":{"$ref":"#/components/schemas/ErrorSchema"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"422":{"description":"Datos no válidos.\n\n- `validation_error`: El cuerpo o los parámetros no cumplen el esquema (campo obligatorio, formato, campo desconocido...). `message` indica el campo.","content":{"application/json":{"examples":{"validation_error":{"summary":"validation_error","description":"El cuerpo o los parámetros no cumplen el esquema (campo obligatorio, formato, campo desconocido...). `message` indica el campo.","value":{"error":{"code":"validation_error","message":"phone_number: String should match pattern '^\\+[1-9][0-9]{6,14}$'"}}}},"schema":{"$ref":"#/components/schemas/ErrorSchema"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"429":{"description":"Demasiadas peticiones.\n\n- `rate_limited`: Se superó el límite de peticiones por minuto de la API key o, en `POST /messages`, un tope de envío del remitente (mensajes por minuto, por destinatario y día, destinatarios nuevos por día) o, en Verify, un tope de códigos (espera entre códigos, por teléfono, por remitente) o de códigos erróneos por hora. Espera los segundos de `Retry-After`.","content":{"application/json":{"examples":{"rate_limited":{"summary":"rate_limited","description":"Se superó el límite de peticiones por minuto de la API key o, en `POST /messages`, un tope de envío del remitente (mensajes por minuto, por destinatario y día, destinatarios nuevos por día) o, en Verify, un tope de códigos (espera entre códigos, por teléfono, por remitente) o de códigos erróneos por hora. Espera los segundos de `Retry-After`.","value":{"error":{"code":"rate_limited","message":"Rate limit exceeded. Retry after the indicated number of seconds."}}}},"schema":{"$ref":"#/components/schemas/ErrorSchema"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"},"Retry-After":{"$ref":"#/components/headers/Retry-After"}}},"502":{"description":"Error de un servicio interno.\n\n- `upstream_error`: Un servicio interno de xYago falló o no respondió a tiempo. Reintenta con backoff (en las escrituras, comprueba antes si ya se aplicaron).","content":{"application/json":{"examples":{"upstream_error":{"summary":"upstream_error","description":"Un servicio interno de xYago falló o no respondió a tiempo. Reintenta con backoff (en las escrituras, comprueba antes si ya se aplicaron).","value":{"error":{"code":"upstream_error","message":"An internal service failed to process the request. Try again later."}}}},"schema":{"$ref":"#/components/schemas/ErrorSchema"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}}},"x-codeSamples":[{"lang":"Shell","label":"curl","source":"curl -X GET 'https://api.xyago.com/v1/vacations' \\\n  -H \"Authorization: Bearer $XYAGO_API_KEY\""},{"lang":"Python","label":"Python","source":"import os\n\nimport requests\n\nresponse = requests.get(\n    \"https://api.xyago.com/v1/vacations\",\n    headers={\n        \"Authorization\": f\"Bearer {os.environ['XYAGO_API_KEY']}\",\n    },\n    timeout=30,\n)\nresponse.raise_for_status()\nprint(response.headers['X-Request-Id'], response.json())"},{"lang":"JavaScript","label":"JavaScript","source":"const response = await fetch(\"https://api.xyago.com/v1/vacations\", {\n  method: \"GET\",\n  headers: {\n    Authorization: `Bearer ${process.env.XYAGO_API_KEY}`,\n  },\n});\nconst data = await response.json();\nif (!response.ok) throw new Error(`${data.error.code}: ${data.error.message}`);\nconsole.log(response.headers.get(\"X-Request-Id\"), data);"},{"lang":"C#","label":"C#","source":"using System.Net.Http.Headers;\nusing System.Text;\n\nusing var http = new HttpClient();\nhttp.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(\n    \"Bearer\", Environment.GetEnvironmentVariable(\"XYAGO_API_KEY\"));\n\nvar request = new HttpRequestMessage(new HttpMethod(\"GET\"), \"https://api.xyago.com/v1/vacations\");\nvar response = await http.SendAsync(request);\nvar body = await response.Content.ReadAsStringAsync();\nConsole.WriteLine($\"{(int)response.StatusCode} {body}\");"}]},"post":{"tags":["vacations"],"summary":"Solicitar vacaciones para un empleado","description":"Crea una solicitud de días completos (`start_date`..`end_date`, ambos incluidos,\nmáximo 366 días), igual que el alta desde el panel. Queda `pending`, o\n`approved` si el tipo de vacaciones de la empresa no requiere aprobación.\nEmite `vacation.requested`.\n\nScope: `vacations:write`.","operationId":"create_vacation","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VacationCreate"},"examples":{"fin_de_ano":{"summary":"Vacaciones de fin de año","value":{"employee_id":"2c9a4e71-5b3d-4f8e-a6c2-7d1e0f9b3a54","start_date":"2026-12-21","end_date":"2027-01-08","reason":"Vacaciones de fin de año"}}}}}},"responses":{"201":{"description":"Creado","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Vacation"},"example":{"id":"65f1a2b3c4d5e6f708192a3b","employee_id":"2c9a4e71-5b3d-4f8e-a6c2-7d1e0f9b3a54","start_date":"2026-12-21","end_date":"2027-01-08","days":13,"status":"pending","reason":"Vacaciones de fin de año","created_at":"2026-11-03T13:20:00Z"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"}}},"401":{"description":"No autenticado.\n\n- `invalid_api_key`: Falta la API key o no es válida, está revocada o ha caducado.","content":{"application/json":{"examples":{"invalid_api_key":{"summary":"invalid_api_key","description":"Falta la API key o no es válida, está revocada o ha caducado.","value":{"error":{"code":"invalid_api_key","message":"Missing or invalid API key."}}}},"schema":{"$ref":"#/components/schemas/ErrorSchema"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"403":{"description":"Prohibido.\n\n- `api_disabled`: El producto (areas, employees...) no está habilitado en el panel para el entorno de la API key (live o test).\n- `endpoint_disabled`: El administrador desactivó este endpoint concreto en el panel.\n- `insufficient_scope`: La API key no tiene el scope que exige el endpoint (p. ej. `employees:write`).\n- `ip_not_allowed`: La IP de origen no está en la lista de IPs permitidas del producto.","content":{"application/json":{"examples":{"api_disabled":{"summary":"api_disabled","description":"El producto (areas, employees...) no está habilitado en el panel para el entorno de la API key (live o test).","value":{"error":{"code":"api_disabled","message":"The 'employees' API is not enabled for this environment."}}},"endpoint_disabled":{"summary":"endpoint_disabled","description":"El administrador desactivó este endpoint concreto en el panel.","value":{"error":{"code":"endpoint_disabled","message":"This endpoint is disabled."}}},"insufficient_scope":{"summary":"insufficient_scope","description":"La API key no tiene el scope que exige el endpoint (p. ej. `employees:write`).","value":{"error":{"code":"insufficient_scope","message":"This API key lacks the 'employees:write' scope."}}},"ip_not_allowed":{"summary":"ip_not_allowed","description":"La IP de origen no está en la lista de IPs permitidas del producto.","value":{"error":{"code":"ip_not_allowed","message":"Requests from this IP are not allowed."}}}},"schema":{"$ref":"#/components/schemas/ErrorSchema"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"409":{"description":"Conflicto.\n\n- `vacation_overlap`: El empleado ya tiene vacaciones aprobadas en ese rango.","content":{"application/json":{"examples":{"vacation_overlap":{"summary":"vacation_overlap","description":"El empleado ya tiene vacaciones aprobadas en ese rango.","value":{"error":{"code":"vacation_overlap","message":"The employee already has approved vacations in that range."}}}},"schema":{"$ref":"#/components/schemas/ErrorSchema"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"422":{"description":"Datos no válidos.\n\n- `validation_error`: El cuerpo o los parámetros no cumplen el esquema (campo obligatorio, formato, campo desconocido...). `message` indica el campo.\n- `invalid_employee_id`: `employee_id` no corresponde a un empleado de la empresa.\n- `invalid_vacation`: xYago rechazó la solicitud de vacaciones.\n- `vacation_type_not_configured`: La empresa no tiene configurado el tipo de ausencia de vacaciones en xYago.\n- `vacation_requires_document`: La empresa exige un documento de soporte para las vacaciones; no se pueden solicitar por la API.","content":{"application/json":{"examples":{"validation_error":{"summary":"validation_error","description":"El cuerpo o los parámetros no cumplen el esquema (campo obligatorio, formato, campo desconocido...). `message` indica el campo.","value":{"error":{"code":"validation_error","message":"phone_number: String should match pattern '^\\+[1-9][0-9]{6,14}$'"}}},"invalid_employee_id":{"summary":"invalid_employee_id","description":"`employee_id` no corresponde a un empleado de la empresa.","value":{"error":{"code":"invalid_employee_id","message":"employee_id does not reference an employee of the company."}}},"invalid_vacation":{"summary":"invalid_vacation","description":"xYago rechazó la solicitud de vacaciones.","value":{"error":{"code":"invalid_vacation","message":"The vacation request was rejected."}}},"vacation_type_not_configured":{"summary":"vacation_type_not_configured","description":"La empresa no tiene configurado el tipo de ausencia de vacaciones en xYago.","value":{"error":{"code":"vacation_type_not_configured","message":"The company has no vacation time-off type configured."}}},"vacation_requires_document":{"summary":"vacation_requires_document","description":"La empresa exige un documento de soporte para las vacaciones; no se pueden solicitar por la API.","value":{"error":{"code":"vacation_requires_document","message":"The company requires a supporting document for vacations."}}}},"schema":{"$ref":"#/components/schemas/ErrorSchema"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"429":{"description":"Demasiadas peticiones.\n\n- `rate_limited`: Se superó el límite de peticiones por minuto de la API key o, en `POST /messages`, un tope de envío del remitente (mensajes por minuto, por destinatario y día, destinatarios nuevos por día) o, en Verify, un tope de códigos (espera entre códigos, por teléfono, por remitente) o de códigos erróneos por hora. Espera los segundos de `Retry-After`.","content":{"application/json":{"examples":{"rate_limited":{"summary":"rate_limited","description":"Se superó el límite de peticiones por minuto de la API key o, en `POST /messages`, un tope de envío del remitente (mensajes por minuto, por destinatario y día, destinatarios nuevos por día) o, en Verify, un tope de códigos (espera entre códigos, por teléfono, por remitente) o de códigos erróneos por hora. Espera los segundos de `Retry-After`.","value":{"error":{"code":"rate_limited","message":"Rate limit exceeded. Retry after the indicated number of seconds."}}}},"schema":{"$ref":"#/components/schemas/ErrorSchema"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"},"Retry-After":{"$ref":"#/components/headers/Retry-After"}}},"502":{"description":"Error de un servicio interno.\n\n- `upstream_error`: Un servicio interno de xYago falló o no respondió a tiempo. Reintenta con backoff (en las escrituras, comprueba antes si ya se aplicaron).","content":{"application/json":{"examples":{"upstream_error":{"summary":"upstream_error","description":"Un servicio interno de xYago falló o no respondió a tiempo. Reintenta con backoff (en las escrituras, comprueba antes si ya se aplicaron).","value":{"error":{"code":"upstream_error","message":"An internal service failed to process the request. Try again later."}}}},"schema":{"$ref":"#/components/schemas/ErrorSchema"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}}},"parameters":[{"$ref":"#/components/parameters/XRequestId"}],"x-codeSamples":[{"lang":"Shell","label":"curl","source":"curl -X POST 'https://api.xyago.com/v1/vacations' \\\n  -H \"Authorization: Bearer $XYAGO_API_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d @- <<'JSON'\n{\n  \"employee_id\": \"2c9a4e71-5b3d-4f8e-a6c2-7d1e0f9b3a54\",\n  \"start_date\": \"2026-12-21\",\n  \"end_date\": \"2027-01-08\",\n  \"reason\": \"Vacaciones de fin de año\"\n}\nJSON"},{"lang":"Python","label":"Python","source":"import os\n\nimport requests\n\npayload = {\n    \"employee_id\": \"2c9a4e71-5b3d-4f8e-a6c2-7d1e0f9b3a54\",\n    \"start_date\": \"2026-12-21\",\n    \"end_date\": \"2027-01-08\",\n    \"reason\": \"Vacaciones de fin de año\"\n}\n\nresponse = requests.post(\n    \"https://api.xyago.com/v1/vacations\",\n    headers={\n        \"Authorization\": f\"Bearer {os.environ['XYAGO_API_KEY']}\",\n    },\n    json=payload,\n    timeout=30,\n)\nresponse.raise_for_status()\nprint(response.headers['X-Request-Id'], response.json())"},{"lang":"JavaScript","label":"JavaScript","source":"const payload = {\n  \"employee_id\": \"2c9a4e71-5b3d-4f8e-a6c2-7d1e0f9b3a54\",\n  \"start_date\": \"2026-12-21\",\n  \"end_date\": \"2027-01-08\",\n  \"reason\": \"Vacaciones de fin de año\"\n};\n\nconst response = await fetch(\"https://api.xyago.com/v1/vacations\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.XYAGO_API_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify(payload),\n});\nconst data = await response.json();\nif (!response.ok) throw new Error(`${data.error.code}: ${data.error.message}`);\nconsole.log(response.headers.get(\"X-Request-Id\"), data);"},{"lang":"C#","label":"C#","source":"using System.Net.Http.Headers;\nusing System.Text;\n\nusing var http = new HttpClient();\nhttp.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(\n    \"Bearer\", Environment.GetEnvironmentVariable(\"XYAGO_API_KEY\"));\n\nvar request = new HttpRequestMessage(new HttpMethod(\"POST\"), \"https://api.xyago.com/v1/vacations\");\nvar payload = \"\"\"\n{\n  \"employee_id\": \"2c9a4e71-5b3d-4f8e-a6c2-7d1e0f9b3a54\",\n  \"start_date\": \"2026-12-21\",\n  \"end_date\": \"2027-01-08\",\n  \"reason\": \"Vacaciones de fin de año\"\n}\n\"\"\";\nrequest.Content = new StringContent(payload, Encoding.UTF8, \"application/json\");\nvar response = await http.SendAsync(request);\nvar body = await response.Content.ReadAsStringAsync();\nConsole.WriteLine($\"{(int)response.StatusCode} {body}\");"}]}},"/vacations/{id}/approve":{"patch":{"tags":["vacations"],"summary":"Aprobar una solicitud de vacaciones","description":"Aprueba una solicitud `pending` (con los mismos efectos que desde el panel). Una\nsolicitud ya decidida responde `409 vacation_already_decided`. Emite\n`vacation.approved`.\n\nScope: `vacations:write`.","operationId":"approve_vacation","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","pattern":"^[0-9a-f]{24}$","description":"Id de la solicitud (24 caracteres hexadecimales).","title":"Id"},"description":"Id de la solicitud (24 caracteres hexadecimales).","examples":{"ejemplo":{"summary":"Ejemplo","value":"65f1a2b3c4d5e6f708192a3b"}}},{"$ref":"#/components/parameters/XRequestId"}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Vacation"},"example":{"id":"65f1a2b3c4d5e6f708192a3b","employee_id":"2c9a4e71-5b3d-4f8e-a6c2-7d1e0f9b3a54","start_date":"2026-12-21","end_date":"2027-01-08","days":13,"status":"approved","reason":"Vacaciones de fin de año","created_at":"2026-11-03T13:20:00Z","decided_at":"2026-11-04T15:02:00Z"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"}}},"401":{"description":"No autenticado.\n\n- `invalid_api_key`: Falta la API key o no es válida, está revocada o ha caducado.","content":{"application/json":{"examples":{"invalid_api_key":{"summary":"invalid_api_key","description":"Falta la API key o no es válida, está revocada o ha caducado.","value":{"error":{"code":"invalid_api_key","message":"Missing or invalid API key."}}}},"schema":{"$ref":"#/components/schemas/ErrorSchema"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"403":{"description":"Prohibido.\n\n- `api_disabled`: El producto (areas, employees...) no está habilitado en el panel para el entorno de la API key (live o test).\n- `endpoint_disabled`: El administrador desactivó este endpoint concreto en el panel.\n- `insufficient_scope`: La API key no tiene el scope que exige el endpoint (p. ej. `employees:write`).\n- `ip_not_allowed`: La IP de origen no está en la lista de IPs permitidas del producto.","content":{"application/json":{"examples":{"api_disabled":{"summary":"api_disabled","description":"El producto (areas, employees...) no está habilitado en el panel para el entorno de la API key (live o test).","value":{"error":{"code":"api_disabled","message":"The 'employees' API is not enabled for this environment."}}},"endpoint_disabled":{"summary":"endpoint_disabled","description":"El administrador desactivó este endpoint concreto en el panel.","value":{"error":{"code":"endpoint_disabled","message":"This endpoint is disabled."}}},"insufficient_scope":{"summary":"insufficient_scope","description":"La API key no tiene el scope que exige el endpoint (p. ej. `employees:write`).","value":{"error":{"code":"insufficient_scope","message":"This API key lacks the 'employees:write' scope."}}},"ip_not_allowed":{"summary":"ip_not_allowed","description":"La IP de origen no está en la lista de IPs permitidas del producto.","value":{"error":{"code":"ip_not_allowed","message":"Requests from this IP are not allowed."}}}},"schema":{"$ref":"#/components/schemas/ErrorSchema"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"404":{"description":"No encontrado.\n\n- `not_found`: El recurso no existe o pertenece a otra empresa (nunca se distingue).","content":{"application/json":{"examples":{"not_found":{"summary":"not_found","description":"El recurso no existe o pertenece a otra empresa (nunca se distingue).","value":{"error":{"code":"not_found","message":"Employee not found."}}}},"schema":{"$ref":"#/components/schemas/ErrorSchema"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"409":{"description":"Conflicto.\n\n- `vacation_already_decided`: La solicitud ya estaba aprobada o rechazada.","content":{"application/json":{"examples":{"vacation_already_decided":{"summary":"vacation_already_decided","description":"La solicitud ya estaba aprobada o rechazada.","value":{"error":{"code":"vacation_already_decided","message":"This vacation request was already decided."}}}},"schema":{"$ref":"#/components/schemas/ErrorSchema"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"422":{"description":"Datos no válidos.\n\n- `validation_error`: El cuerpo o los parámetros no cumplen el esquema (campo obligatorio, formato, campo desconocido...). `message` indica el campo.","content":{"application/json":{"examples":{"validation_error":{"summary":"validation_error","description":"El cuerpo o los parámetros no cumplen el esquema (campo obligatorio, formato, campo desconocido...). `message` indica el campo.","value":{"error":{"code":"validation_error","message":"phone_number: String should match pattern '^\\+[1-9][0-9]{6,14}$'"}}}},"schema":{"$ref":"#/components/schemas/ErrorSchema"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"429":{"description":"Demasiadas peticiones.\n\n- `rate_limited`: Se superó el límite de peticiones por minuto de la API key o, en `POST /messages`, un tope de envío del remitente (mensajes por minuto, por destinatario y día, destinatarios nuevos por día) o, en Verify, un tope de códigos (espera entre códigos, por teléfono, por remitente) o de códigos erróneos por hora. Espera los segundos de `Retry-After`.","content":{"application/json":{"examples":{"rate_limited":{"summary":"rate_limited","description":"Se superó el límite de peticiones por minuto de la API key o, en `POST /messages`, un tope de envío del remitente (mensajes por minuto, por destinatario y día, destinatarios nuevos por día) o, en Verify, un tope de códigos (espera entre códigos, por teléfono, por remitente) o de códigos erróneos por hora. Espera los segundos de `Retry-After`.","value":{"error":{"code":"rate_limited","message":"Rate limit exceeded. Retry after the indicated number of seconds."}}}},"schema":{"$ref":"#/components/schemas/ErrorSchema"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"},"Retry-After":{"$ref":"#/components/headers/Retry-After"}}},"502":{"description":"Error de un servicio interno.\n\n- `upstream_error`: Un servicio interno de xYago falló o no respondió a tiempo. Reintenta con backoff (en las escrituras, comprueba antes si ya se aplicaron).","content":{"application/json":{"examples":{"upstream_error":{"summary":"upstream_error","description":"Un servicio interno de xYago falló o no respondió a tiempo. Reintenta con backoff (en las escrituras, comprueba antes si ya se aplicaron).","value":{"error":{"code":"upstream_error","message":"An internal service failed to process the request. Try again later."}}}},"schema":{"$ref":"#/components/schemas/ErrorSchema"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}}},"x-codeSamples":[{"lang":"Shell","label":"curl","source":"curl -X PATCH 'https://api.xyago.com/v1/vacations/65f1a2b3c4d5e6f708192a3b/approve' \\\n  -H \"Authorization: Bearer $XYAGO_API_KEY\""},{"lang":"Python","label":"Python","source":"import os\n\nimport requests\n\nresponse = requests.patch(\n    \"https://api.xyago.com/v1/vacations/65f1a2b3c4d5e6f708192a3b/approve\",\n    headers={\n        \"Authorization\": f\"Bearer {os.environ['XYAGO_API_KEY']}\",\n    },\n    timeout=30,\n)\nresponse.raise_for_status()\nprint(response.headers['X-Request-Id'], response.json())"},{"lang":"JavaScript","label":"JavaScript","source":"const response = await fetch(\"https://api.xyago.com/v1/vacations/65f1a2b3c4d5e6f708192a3b/approve\", {\n  method: \"PATCH\",\n  headers: {\n    Authorization: `Bearer ${process.env.XYAGO_API_KEY}`,\n  },\n});\nconst data = await response.json();\nif (!response.ok) throw new Error(`${data.error.code}: ${data.error.message}`);\nconsole.log(response.headers.get(\"X-Request-Id\"), data);"},{"lang":"C#","label":"C#","source":"using System.Net.Http.Headers;\nusing System.Text;\n\nusing var http = new HttpClient();\nhttp.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(\n    \"Bearer\", Environment.GetEnvironmentVariable(\"XYAGO_API_KEY\"));\n\nvar request = new HttpRequestMessage(new HttpMethod(\"PATCH\"), \"https://api.xyago.com/v1/vacations/65f1a2b3c4d5e6f708192a3b/approve\");\nvar response = await http.SendAsync(request);\nvar body = await response.Content.ReadAsStringAsync();\nConsole.WriteLine($\"{(int)response.StatusCode} {body}\");"}]}},"/vacations/{id}/reject":{"patch":{"tags":["vacations"],"summary":"Rechazar una solicitud de vacaciones","description":"Rechaza una solicitud `pending` (como desde el panel). Una solicitud ya decidida\nresponde `409 vacation_already_decided`. Emite `vacation.rejected`.\n\nScope: `vacations:write`.","operationId":"reject_vacation","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","pattern":"^[0-9a-f]{24}$","description":"Id de la solicitud (24 caracteres hexadecimales).","title":"Id"},"description":"Id de la solicitud (24 caracteres hexadecimales).","examples":{"ejemplo":{"summary":"Ejemplo","value":"65f1a2b3c4d5e6f708192a3b"}}},{"$ref":"#/components/parameters/XRequestId"}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Vacation"},"example":{"id":"65f1a2b3c4d5e6f708192a3b","employee_id":"2c9a4e71-5b3d-4f8e-a6c2-7d1e0f9b3a54","start_date":"2026-12-21","end_date":"2027-01-08","days":13,"status":"rejected","reason":"Vacaciones de fin de año","created_at":"2026-11-03T13:20:00Z","decided_at":"2026-11-04T15:02:00Z"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"}}},"401":{"description":"No autenticado.\n\n- `invalid_api_key`: Falta la API key o no es válida, está revocada o ha caducado.","content":{"application/json":{"examples":{"invalid_api_key":{"summary":"invalid_api_key","description":"Falta la API key o no es válida, está revocada o ha caducado.","value":{"error":{"code":"invalid_api_key","message":"Missing or invalid API key."}}}},"schema":{"$ref":"#/components/schemas/ErrorSchema"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"403":{"description":"Prohibido.\n\n- `api_disabled`: El producto (areas, employees...) no está habilitado en el panel para el entorno de la API key (live o test).\n- `endpoint_disabled`: El administrador desactivó este endpoint concreto en el panel.\n- `insufficient_scope`: La API key no tiene el scope que exige el endpoint (p. ej. `employees:write`).\n- `ip_not_allowed`: La IP de origen no está en la lista de IPs permitidas del producto.","content":{"application/json":{"examples":{"api_disabled":{"summary":"api_disabled","description":"El producto (areas, employees...) no está habilitado en el panel para el entorno de la API key (live o test).","value":{"error":{"code":"api_disabled","message":"The 'employees' API is not enabled for this environment."}}},"endpoint_disabled":{"summary":"endpoint_disabled","description":"El administrador desactivó este endpoint concreto en el panel.","value":{"error":{"code":"endpoint_disabled","message":"This endpoint is disabled."}}},"insufficient_scope":{"summary":"insufficient_scope","description":"La API key no tiene el scope que exige el endpoint (p. ej. `employees:write`).","value":{"error":{"code":"insufficient_scope","message":"This API key lacks the 'employees:write' scope."}}},"ip_not_allowed":{"summary":"ip_not_allowed","description":"La IP de origen no está en la lista de IPs permitidas del producto.","value":{"error":{"code":"ip_not_allowed","message":"Requests from this IP are not allowed."}}}},"schema":{"$ref":"#/components/schemas/ErrorSchema"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"404":{"description":"No encontrado.\n\n- `not_found`: El recurso no existe o pertenece a otra empresa (nunca se distingue).","content":{"application/json":{"examples":{"not_found":{"summary":"not_found","description":"El recurso no existe o pertenece a otra empresa (nunca se distingue).","value":{"error":{"code":"not_found","message":"Employee not found."}}}},"schema":{"$ref":"#/components/schemas/ErrorSchema"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"409":{"description":"Conflicto.\n\n- `vacation_already_decided`: La solicitud ya estaba aprobada o rechazada.","content":{"application/json":{"examples":{"vacation_already_decided":{"summary":"vacation_already_decided","description":"La solicitud ya estaba aprobada o rechazada.","value":{"error":{"code":"vacation_already_decided","message":"This vacation request was already decided."}}}},"schema":{"$ref":"#/components/schemas/ErrorSchema"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"422":{"description":"Datos no válidos.\n\n- `validation_error`: El cuerpo o los parámetros no cumplen el esquema (campo obligatorio, formato, campo desconocido...). `message` indica el campo.","content":{"application/json":{"examples":{"validation_error":{"summary":"validation_error","description":"El cuerpo o los parámetros no cumplen el esquema (campo obligatorio, formato, campo desconocido...). `message` indica el campo.","value":{"error":{"code":"validation_error","message":"phone_number: String should match pattern '^\\+[1-9][0-9]{6,14}$'"}}}},"schema":{"$ref":"#/components/schemas/ErrorSchema"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"429":{"description":"Demasiadas peticiones.\n\n- `rate_limited`: Se superó el límite de peticiones por minuto de la API key o, en `POST /messages`, un tope de envío del remitente (mensajes por minuto, por destinatario y día, destinatarios nuevos por día) o, en Verify, un tope de códigos (espera entre códigos, por teléfono, por remitente) o de códigos erróneos por hora. Espera los segundos de `Retry-After`.","content":{"application/json":{"examples":{"rate_limited":{"summary":"rate_limited","description":"Se superó el límite de peticiones por minuto de la API key o, en `POST /messages`, un tope de envío del remitente (mensajes por minuto, por destinatario y día, destinatarios nuevos por día) o, en Verify, un tope de códigos (espera entre códigos, por teléfono, por remitente) o de códigos erróneos por hora. Espera los segundos de `Retry-After`.","value":{"error":{"code":"rate_limited","message":"Rate limit exceeded. Retry after the indicated number of seconds."}}}},"schema":{"$ref":"#/components/schemas/ErrorSchema"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"},"Retry-After":{"$ref":"#/components/headers/Retry-After"}}},"502":{"description":"Error de un servicio interno.\n\n- `upstream_error`: Un servicio interno de xYago falló o no respondió a tiempo. Reintenta con backoff (en las escrituras, comprueba antes si ya se aplicaron).","content":{"application/json":{"examples":{"upstream_error":{"summary":"upstream_error","description":"Un servicio interno de xYago falló o no respondió a tiempo. Reintenta con backoff (en las escrituras, comprueba antes si ya se aplicaron).","value":{"error":{"code":"upstream_error","message":"An internal service failed to process the request. Try again later."}}}},"schema":{"$ref":"#/components/schemas/ErrorSchema"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}}},"x-codeSamples":[{"lang":"Shell","label":"curl","source":"curl -X PATCH 'https://api.xyago.com/v1/vacations/65f1a2b3c4d5e6f708192a3b/reject' \\\n  -H \"Authorization: Bearer $XYAGO_API_KEY\""},{"lang":"Python","label":"Python","source":"import os\n\nimport requests\n\nresponse = requests.patch(\n    \"https://api.xyago.com/v1/vacations/65f1a2b3c4d5e6f708192a3b/reject\",\n    headers={\n        \"Authorization\": f\"Bearer {os.environ['XYAGO_API_KEY']}\",\n    },\n    timeout=30,\n)\nresponse.raise_for_status()\nprint(response.headers['X-Request-Id'], response.json())"},{"lang":"JavaScript","label":"JavaScript","source":"const response = await fetch(\"https://api.xyago.com/v1/vacations/65f1a2b3c4d5e6f708192a3b/reject\", {\n  method: \"PATCH\",\n  headers: {\n    Authorization: `Bearer ${process.env.XYAGO_API_KEY}`,\n  },\n});\nconst data = await response.json();\nif (!response.ok) throw new Error(`${data.error.code}: ${data.error.message}`);\nconsole.log(response.headers.get(\"X-Request-Id\"), data);"},{"lang":"C#","label":"C#","source":"using System.Net.Http.Headers;\nusing System.Text;\n\nusing var http = new HttpClient();\nhttp.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(\n    \"Bearer\", Environment.GetEnvironmentVariable(\"XYAGO_API_KEY\"));\n\nvar request = new HttpRequestMessage(new HttpMethod(\"PATCH\"), \"https://api.xyago.com/v1/vacations/65f1a2b3c4d5e6f708192a3b/reject\");\nvar response = await http.SendAsync(request);\nvar body = await response.Content.ReadAsStringAsync();\nConsole.WriteLine($\"{(int)response.StatusCode} {body}\");"}]}},"/employees/{id}/vacation-balance":{"get":{"tags":["vacations"],"summary":"Saldo de vacaciones de un empleado","description":"Días que corresponden en el año (`total_days`, prorrateados), usados\n(solicitudes aprobadas que empiezan ese año) y disponibles. Un empleado de otra\nempresa responde `404`.\n\nScope: `vacations:read`.","operationId":"vacation_balance","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid","description":"Id del empleado.","title":"Id"},"description":"Id del empleado.","examples":{"ejemplo":{"summary":"Ejemplo","value":"2c9a4e71-5b3d-4f8e-a6c2-7d1e0f9b3a54"}}},{"name":"year","in":"query","required":false,"schema":{"anyOf":[{"type":"integer","maximum":2100,"minimum":2000},{"type":"null"}],"description":"Año (por defecto el año en curso, UTC).","title":"Year"},"description":"Año (por defecto el año en curso, UTC)."},{"$ref":"#/components/parameters/XRequestId"}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VacationBalance"},"example":{"employee_id":"2c9a4e71-5b3d-4f8e-a6c2-7d1e0f9b3a54","year":2026,"total_days":15.0,"used_days":6.0,"available_days":9.0}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"}}},"401":{"description":"No autenticado.\n\n- `invalid_api_key`: Falta la API key o no es válida, está revocada o ha caducado.","content":{"application/json":{"examples":{"invalid_api_key":{"summary":"invalid_api_key","description":"Falta la API key o no es válida, está revocada o ha caducado.","value":{"error":{"code":"invalid_api_key","message":"Missing or invalid API key."}}}},"schema":{"$ref":"#/components/schemas/ErrorSchema"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"403":{"description":"Prohibido.\n\n- `api_disabled`: El producto (areas, employees...) no está habilitado en el panel para el entorno de la API key (live o test).\n- `endpoint_disabled`: El administrador desactivó este endpoint concreto en el panel.\n- `insufficient_scope`: La API key no tiene el scope que exige el endpoint (p. ej. `employees:write`).\n- `ip_not_allowed`: La IP de origen no está en la lista de IPs permitidas del producto.","content":{"application/json":{"examples":{"api_disabled":{"summary":"api_disabled","description":"El producto (areas, employees...) no está habilitado en el panel para el entorno de la API key (live o test).","value":{"error":{"code":"api_disabled","message":"The 'employees' API is not enabled for this environment."}}},"endpoint_disabled":{"summary":"endpoint_disabled","description":"El administrador desactivó este endpoint concreto en el panel.","value":{"error":{"code":"endpoint_disabled","message":"This endpoint is disabled."}}},"insufficient_scope":{"summary":"insufficient_scope","description":"La API key no tiene el scope que exige el endpoint (p. ej. `employees:write`).","value":{"error":{"code":"insufficient_scope","message":"This API key lacks the 'employees:write' scope."}}},"ip_not_allowed":{"summary":"ip_not_allowed","description":"La IP de origen no está en la lista de IPs permitidas del producto.","value":{"error":{"code":"ip_not_allowed","message":"Requests from this IP are not allowed."}}}},"schema":{"$ref":"#/components/schemas/ErrorSchema"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"404":{"description":"No encontrado.\n\n- `not_found`: El recurso no existe o pertenece a otra empresa (nunca se distingue).","content":{"application/json":{"examples":{"not_found":{"summary":"not_found","description":"El recurso no existe o pertenece a otra empresa (nunca se distingue).","value":{"error":{"code":"not_found","message":"Employee not found."}}}},"schema":{"$ref":"#/components/schemas/ErrorSchema"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"422":{"description":"Datos no válidos.\n\n- `validation_error`: El cuerpo o los parámetros no cumplen el esquema (campo obligatorio, formato, campo desconocido...). `message` indica el campo.","content":{"application/json":{"examples":{"validation_error":{"summary":"validation_error","description":"El cuerpo o los parámetros no cumplen el esquema (campo obligatorio, formato, campo desconocido...). `message` indica el campo.","value":{"error":{"code":"validation_error","message":"phone_number: String should match pattern '^\\+[1-9][0-9]{6,14}$'"}}}},"schema":{"$ref":"#/components/schemas/ErrorSchema"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"429":{"description":"Demasiadas peticiones.\n\n- `rate_limited`: Se superó el límite de peticiones por minuto de la API key o, en `POST /messages`, un tope de envío del remitente (mensajes por minuto, por destinatario y día, destinatarios nuevos por día) o, en Verify, un tope de códigos (espera entre códigos, por teléfono, por remitente) o de códigos erróneos por hora. Espera los segundos de `Retry-After`.","content":{"application/json":{"examples":{"rate_limited":{"summary":"rate_limited","description":"Se superó el límite de peticiones por minuto de la API key o, en `POST /messages`, un tope de envío del remitente (mensajes por minuto, por destinatario y día, destinatarios nuevos por día) o, en Verify, un tope de códigos (espera entre códigos, por teléfono, por remitente) o de códigos erróneos por hora. Espera los segundos de `Retry-After`.","value":{"error":{"code":"rate_limited","message":"Rate limit exceeded. Retry after the indicated number of seconds."}}}},"schema":{"$ref":"#/components/schemas/ErrorSchema"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"},"Retry-After":{"$ref":"#/components/headers/Retry-After"}}},"502":{"description":"Error de un servicio interno.\n\n- `upstream_error`: Un servicio interno de xYago falló o no respondió a tiempo. Reintenta con backoff (en las escrituras, comprueba antes si ya se aplicaron).","content":{"application/json":{"examples":{"upstream_error":{"summary":"upstream_error","description":"Un servicio interno de xYago falló o no respondió a tiempo. Reintenta con backoff (en las escrituras, comprueba antes si ya se aplicaron).","value":{"error":{"code":"upstream_error","message":"An internal service failed to process the request. Try again later."}}}},"schema":{"$ref":"#/components/schemas/ErrorSchema"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}}},"x-codeSamples":[{"lang":"Shell","label":"curl","source":"curl -X GET 'https://api.xyago.com/v1/employees/2c9a4e71-5b3d-4f8e-a6c2-7d1e0f9b3a54/vacation-balance' \\\n  -H \"Authorization: Bearer $XYAGO_API_KEY\""},{"lang":"Python","label":"Python","source":"import os\n\nimport requests\n\nresponse = requests.get(\n    \"https://api.xyago.com/v1/employees/2c9a4e71-5b3d-4f8e-a6c2-7d1e0f9b3a54/vacation-balance\",\n    headers={\n        \"Authorization\": f\"Bearer {os.environ['XYAGO_API_KEY']}\",\n    },\n    timeout=30,\n)\nresponse.raise_for_status()\nprint(response.headers['X-Request-Id'], response.json())"},{"lang":"JavaScript","label":"JavaScript","source":"const response = await fetch(\"https://api.xyago.com/v1/employees/2c9a4e71-5b3d-4f8e-a6c2-7d1e0f9b3a54/vacation-balance\", {\n  method: \"GET\",\n  headers: {\n    Authorization: `Bearer ${process.env.XYAGO_API_KEY}`,\n  },\n});\nconst data = await response.json();\nif (!response.ok) throw new Error(`${data.error.code}: ${data.error.message}`);\nconsole.log(response.headers.get(\"X-Request-Id\"), data);"},{"lang":"C#","label":"C#","source":"using System.Net.Http.Headers;\nusing System.Text;\n\nusing var http = new HttpClient();\nhttp.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(\n    \"Bearer\", Environment.GetEnvironmentVariable(\"XYAGO_API_KEY\"));\n\nvar request = new HttpRequestMessage(new HttpMethod(\"GET\"), \"https://api.xyago.com/v1/employees/2c9a4e71-5b3d-4f8e-a6c2-7d1e0f9b3a54/vacation-balance\");\nvar response = await http.SendAsync(request);\nvar body = await response.Content.ReadAsStringAsync();\nConsole.WriteLine($\"{(int)response.StatusCode} {body}\");"}]}},"/time-entries":{"get":{"tags":["time_entries"],"summary":"Listar tramos de horas de un empleado","description":"Tramos trabajados (cerrados) del empleado en la empresa que tocan el rango\n`from`..`to` (días UTC, máximo 31 días), del más reciente al más antiguo.\nUn empleado de otra empresa responde `404`.\n\nScope: `time:read`.","operationId":"list_time_entries","parameters":[{"name":"employee_id","in":"query","required":true,"schema":{"type":"string","format":"uuid","description":"Id del empleado.","title":"Employee Id"},"description":"Id del empleado.","examples":{"ejemplo":{"summary":"Ejemplo","value":"2c9a4e71-5b3d-4f8e-a6c2-7d1e0f9b3a54"}}},{"name":"from","in":"query","required":true,"schema":{"type":"string","format":"date","description":"Primer día (UTC), incluido.","title":"From"},"description":"Primer día (UTC), incluido.","examples":{"ejemplo":{"summary":"Ejemplo","value":"2026-03-01"}}},{"name":"to","in":"query","required":true,"schema":{"type":"string","format":"date","description":"Último día (UTC), incluido. Máximo 31 días desde `from`.","title":"To"},"description":"Último día (UTC), incluido. Máximo 31 días desde `from`.","examples":{"ejemplo":{"summary":"Ejemplo","value":"2026-03-07"}}},{"name":"cursor","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":512},{"type":"null"}],"description":"Cursor opaco de la página siguiente (`next_cursor` de la respuesta anterior). Omitir en la primera petición.","title":"Cursor"},"description":"Cursor opaco de la página siguiente (`next_cursor` de la respuesta anterior). Omitir en la primera petición."},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"description":"Tamaño de página (1-100, por defecto 50).","default":50,"title":"Limit"},"description":"Tamaño de página (1-100, por defecto 50)."},{"$ref":"#/components/parameters/XRequestId"}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TimeEntryList"},"example":{"results":[{"id":"c4e2a6f8-1b3d-4a5c-8e7f-9a0b1c2d3e4f","employee_id":"2c9a4e71-5b3d-4f8e-a6c2-7d1e0f9b3a54","started_at":"2026-03-02T12:00:00Z","ended_at":"2026-03-02T21:00:00Z","duration_minutes":540,"source":"api","note":"Turno registrado desde Siesa","created_at":"2026-03-02T21:05:00Z"}]}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"}}},"400":{"description":"Petición incorrecta.\n\n- `invalid_cursor`: El `cursor` no es válido (manipulado o de otro listado). Repite la consulta sin cursor.","content":{"application/json":{"examples":{"invalid_cursor":{"summary":"invalid_cursor","description":"El `cursor` no es válido (manipulado o de otro listado). Repite la consulta sin cursor.","value":{"error":{"code":"invalid_cursor","message":"Invalid cursor."}}}},"schema":{"$ref":"#/components/schemas/ErrorSchema"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"401":{"description":"No autenticado.\n\n- `invalid_api_key`: Falta la API key o no es válida, está revocada o ha caducado.","content":{"application/json":{"examples":{"invalid_api_key":{"summary":"invalid_api_key","description":"Falta la API key o no es válida, está revocada o ha caducado.","value":{"error":{"code":"invalid_api_key","message":"Missing or invalid API key."}}}},"schema":{"$ref":"#/components/schemas/ErrorSchema"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"403":{"description":"Prohibido.\n\n- `api_disabled`: El producto (areas, employees...) no está habilitado en el panel para el entorno de la API key (live o test).\n- `endpoint_disabled`: El administrador desactivó este endpoint concreto en el panel.\n- `insufficient_scope`: La API key no tiene el scope que exige el endpoint (p. ej. `employees:write`).\n- `ip_not_allowed`: La IP de origen no está en la lista de IPs permitidas del producto.","content":{"application/json":{"examples":{"api_disabled":{"summary":"api_disabled","description":"El producto (areas, employees...) no está habilitado en el panel para el entorno de la API key (live o test).","value":{"error":{"code":"api_disabled","message":"The 'employees' API is not enabled for this environment."}}},"endpoint_disabled":{"summary":"endpoint_disabled","description":"El administrador desactivó este endpoint concreto en el panel.","value":{"error":{"code":"endpoint_disabled","message":"This endpoint is disabled."}}},"insufficient_scope":{"summary":"insufficient_scope","description":"La API key no tiene el scope que exige el endpoint (p. ej. `employees:write`).","value":{"error":{"code":"insufficient_scope","message":"This API key lacks the 'employees:write' scope."}}},"ip_not_allowed":{"summary":"ip_not_allowed","description":"La IP de origen no está en la lista de IPs permitidas del producto.","value":{"error":{"code":"ip_not_allowed","message":"Requests from this IP are not allowed."}}}},"schema":{"$ref":"#/components/schemas/ErrorSchema"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"404":{"description":"No encontrado.\n\n- `not_found`: El recurso no existe o pertenece a otra empresa (nunca se distingue).","content":{"application/json":{"examples":{"not_found":{"summary":"not_found","description":"El recurso no existe o pertenece a otra empresa (nunca se distingue).","value":{"error":{"code":"not_found","message":"Employee not found."}}}},"schema":{"$ref":"#/components/schemas/ErrorSchema"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"422":{"description":"Datos no válidos.\n\n- `validation_error`: El cuerpo o los parámetros no cumplen el esquema (campo obligatorio, formato, campo desconocido...). `message` indica el campo.\n- `invalid_date_range`: Rango `from`..`to` no válido: `from` posterior a `to` o más de 31 días.","content":{"application/json":{"examples":{"validation_error":{"summary":"validation_error","description":"El cuerpo o los parámetros no cumplen el esquema (campo obligatorio, formato, campo desconocido...). `message` indica el campo.","value":{"error":{"code":"validation_error","message":"phone_number: String should match pattern '^\\+[1-9][0-9]{6,14}$'"}}},"invalid_date_range":{"summary":"invalid_date_range","description":"Rango `from`..`to` no válido: `from` posterior a `to` o más de 31 días.","value":{"error":{"code":"invalid_date_range","message":"The range cannot be longer than 31 days."}}}},"schema":{"$ref":"#/components/schemas/ErrorSchema"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"429":{"description":"Demasiadas peticiones.\n\n- `rate_limited`: Se superó el límite de peticiones por minuto de la API key o, en `POST /messages`, un tope de envío del remitente (mensajes por minuto, por destinatario y día, destinatarios nuevos por día) o, en Verify, un tope de códigos (espera entre códigos, por teléfono, por remitente) o de códigos erróneos por hora. Espera los segundos de `Retry-After`.","content":{"application/json":{"examples":{"rate_limited":{"summary":"rate_limited","description":"Se superó el límite de peticiones por minuto de la API key o, en `POST /messages`, un tope de envío del remitente (mensajes por minuto, por destinatario y día, destinatarios nuevos por día) o, en Verify, un tope de códigos (espera entre códigos, por teléfono, por remitente) o de códigos erróneos por hora. Espera los segundos de `Retry-After`.","value":{"error":{"code":"rate_limited","message":"Rate limit exceeded. Retry after the indicated number of seconds."}}}},"schema":{"$ref":"#/components/schemas/ErrorSchema"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"},"Retry-After":{"$ref":"#/components/headers/Retry-After"}}},"502":{"description":"Error de un servicio interno.\n\n- `upstream_error`: Un servicio interno de xYago falló o no respondió a tiempo. Reintenta con backoff (en las escrituras, comprueba antes si ya se aplicaron).","content":{"application/json":{"examples":{"upstream_error":{"summary":"upstream_error","description":"Un servicio interno de xYago falló o no respondió a tiempo. Reintenta con backoff (en las escrituras, comprueba antes si ya se aplicaron).","value":{"error":{"code":"upstream_error","message":"An internal service failed to process the request. Try again later."}}}},"schema":{"$ref":"#/components/schemas/ErrorSchema"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}}},"x-codeSamples":[{"lang":"Shell","label":"curl","source":"curl -X GET 'https://api.xyago.com/v1/time-entries?employee_id=2c9a4e71-5b3d-4f8e-a6c2-7d1e0f9b3a54&from=2026-03-01&to=2026-03-07' \\\n  -H \"Authorization: Bearer $XYAGO_API_KEY\""},{"lang":"Python","label":"Python","source":"import os\n\nimport requests\n\nresponse = requests.get(\n    \"https://api.xyago.com/v1/time-entries?employee_id=2c9a4e71-5b3d-4f8e-a6c2-7d1e0f9b3a54&from=2026-03-01&to=2026-03-07\",\n    headers={\n        \"Authorization\": f\"Bearer {os.environ['XYAGO_API_KEY']}\",\n    },\n    timeout=30,\n)\nresponse.raise_for_status()\nprint(response.headers['X-Request-Id'], response.json())"},{"lang":"JavaScript","label":"JavaScript","source":"const response = await fetch(\"https://api.xyago.com/v1/time-entries?employee_id=2c9a4e71-5b3d-4f8e-a6c2-7d1e0f9b3a54&from=2026-03-01&to=2026-03-07\", {\n  method: \"GET\",\n  headers: {\n    Authorization: `Bearer ${process.env.XYAGO_API_KEY}`,\n  },\n});\nconst data = await response.json();\nif (!response.ok) throw new Error(`${data.error.code}: ${data.error.message}`);\nconsole.log(response.headers.get(\"X-Request-Id\"), data);"},{"lang":"C#","label":"C#","source":"using System.Net.Http.Headers;\nusing System.Text;\n\nusing var http = new HttpClient();\nhttp.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(\n    \"Bearer\", Environment.GetEnvironmentVariable(\"XYAGO_API_KEY\"));\n\nvar request = new HttpRequestMessage(new HttpMethod(\"GET\"), \"https://api.xyago.com/v1/time-entries?employee_id=2c9a4e71-5b3d-4f8e-a6c2-7d1e0f9b3a54&from=2026-03-01&to=2026-03-07\");\nvar response = await http.SendAsync(request);\nvar body = await response.Content.ReadAsStringAsync();\nConsole.WriteLine($\"{(int)response.StatusCode} {body}\");"}]},"post":{"tags":["time_entries"],"summary":"Registrar un tramo de horas","description":"Registra un tramo trabajado ya terminado (el mismo registro manual que la app).\nFechas ISO 8601; sin zona horaria se interpretan en UTC (envía el offset, p. ej.\n`-05:00` para Colombia). `ended_at` posterior a `started_at`, como máximo 24 h y\nno en el futuro. Emite el webhook `time_entry.created`.\n\nScope: `time:write`.","operationId":"create_time_entry","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TimeEntryCreate"},"examples":{"turno":{"summary":"Turno de 9 horas (hora de Colombia)","value":{"employee_id":"2c9a4e71-5b3d-4f8e-a6c2-7d1e0f9b3a54","started_at":"2026-03-02T07:00:00-05:00","ended_at":"2026-03-02T16:00:00-05:00","note":"Turno registrado desde Siesa"}}}}}},"responses":{"201":{"description":"Creado","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TimeEntry"},"example":{"id":"c4e2a6f8-1b3d-4a5c-8e7f-9a0b1c2d3e4f","employee_id":"2c9a4e71-5b3d-4f8e-a6c2-7d1e0f9b3a54","started_at":"2026-03-02T12:00:00Z","ended_at":"2026-03-02T21:00:00Z","duration_minutes":540,"source":"api","note":"Turno registrado desde Siesa","created_at":"2026-03-02T21:05:00Z"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"}}},"401":{"description":"No autenticado.\n\n- `invalid_api_key`: Falta la API key o no es válida, está revocada o ha caducado.","content":{"application/json":{"examples":{"invalid_api_key":{"summary":"invalid_api_key","description":"Falta la API key o no es válida, está revocada o ha caducado.","value":{"error":{"code":"invalid_api_key","message":"Missing or invalid API key."}}}},"schema":{"$ref":"#/components/schemas/ErrorSchema"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"403":{"description":"Prohibido.\n\n- `api_disabled`: El producto (areas, employees...) no está habilitado en el panel para el entorno de la API key (live o test).\n- `endpoint_disabled`: El administrador desactivó este endpoint concreto en el panel.\n- `insufficient_scope`: La API key no tiene el scope que exige el endpoint (p. ej. `employees:write`).\n- `ip_not_allowed`: La IP de origen no está en la lista de IPs permitidas del producto.","content":{"application/json":{"examples":{"api_disabled":{"summary":"api_disabled","description":"El producto (areas, employees...) no está habilitado en el panel para el entorno de la API key (live o test).","value":{"error":{"code":"api_disabled","message":"The 'employees' API is not enabled for this environment."}}},"endpoint_disabled":{"summary":"endpoint_disabled","description":"El administrador desactivó este endpoint concreto en el panel.","value":{"error":{"code":"endpoint_disabled","message":"This endpoint is disabled."}}},"insufficient_scope":{"summary":"insufficient_scope","description":"La API key no tiene el scope que exige el endpoint (p. ej. `employees:write`).","value":{"error":{"code":"insufficient_scope","message":"This API key lacks the 'employees:write' scope."}}},"ip_not_allowed":{"summary":"ip_not_allowed","description":"La IP de origen no está en la lista de IPs permitidas del producto.","value":{"error":{"code":"ip_not_allowed","message":"Requests from this IP are not allowed."}}}},"schema":{"$ref":"#/components/schemas/ErrorSchema"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"409":{"description":"Conflicto.\n\n- `time_entry_overlap`: El tramo se solapa con otro tramo del empleado en la empresa.","content":{"application/json":{"examples":{"time_entry_overlap":{"summary":"time_entry_overlap","description":"El tramo se solapa con otro tramo del empleado en la empresa.","value":{"error":{"code":"time_entry_overlap","message":"The time entry overlaps another entry of the employee."}}}},"schema":{"$ref":"#/components/schemas/ErrorSchema"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"422":{"description":"Datos no válidos.\n\n- `validation_error`: El cuerpo o los parámetros no cumplen el esquema (campo obligatorio, formato, campo desconocido...). `message` indica el campo.\n- `invalid_employee_id`: `employee_id` no corresponde a un empleado de la empresa.\n- `invalid_time_range`: El tramo termina en el futuro.\n- `invalid_time_entry`: xYago rechazó el tramo.","content":{"application/json":{"examples":{"validation_error":{"summary":"validation_error","description":"El cuerpo o los parámetros no cumplen el esquema (campo obligatorio, formato, campo desconocido...). `message` indica el campo.","value":{"error":{"code":"validation_error","message":"phone_number: String should match pattern '^\\+[1-9][0-9]{6,14}$'"}}},"invalid_employee_id":{"summary":"invalid_employee_id","description":"`employee_id` no corresponde a un empleado de la empresa.","value":{"error":{"code":"invalid_employee_id","message":"employee_id does not reference an employee of the company."}}},"invalid_time_range":{"summary":"invalid_time_range","description":"El tramo termina en el futuro.","value":{"error":{"code":"invalid_time_range","message":"A time entry cannot end in the future."}}},"invalid_time_entry":{"summary":"invalid_time_entry","description":"xYago rechazó el tramo.","value":{"error":{"code":"invalid_time_entry","message":"The time entry was rejected."}}}},"schema":{"$ref":"#/components/schemas/ErrorSchema"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"429":{"description":"Demasiadas peticiones.\n\n- `rate_limited`: Se superó el límite de peticiones por minuto de la API key o, en `POST /messages`, un tope de envío del remitente (mensajes por minuto, por destinatario y día, destinatarios nuevos por día) o, en Verify, un tope de códigos (espera entre códigos, por teléfono, por remitente) o de códigos erróneos por hora. Espera los segundos de `Retry-After`.","content":{"application/json":{"examples":{"rate_limited":{"summary":"rate_limited","description":"Se superó el límite de peticiones por minuto de la API key o, en `POST /messages`, un tope de envío del remitente (mensajes por minuto, por destinatario y día, destinatarios nuevos por día) o, en Verify, un tope de códigos (espera entre códigos, por teléfono, por remitente) o de códigos erróneos por hora. Espera los segundos de `Retry-After`.","value":{"error":{"code":"rate_limited","message":"Rate limit exceeded. Retry after the indicated number of seconds."}}}},"schema":{"$ref":"#/components/schemas/ErrorSchema"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"},"Retry-After":{"$ref":"#/components/headers/Retry-After"}}},"502":{"description":"Error de un servicio interno.\n\n- `upstream_error`: Un servicio interno de xYago falló o no respondió a tiempo. Reintenta con backoff (en las escrituras, comprueba antes si ya se aplicaron).","content":{"application/json":{"examples":{"upstream_error":{"summary":"upstream_error","description":"Un servicio interno de xYago falló o no respondió a tiempo. Reintenta con backoff (en las escrituras, comprueba antes si ya se aplicaron).","value":{"error":{"code":"upstream_error","message":"An internal service failed to process the request. Try again later."}}}},"schema":{"$ref":"#/components/schemas/ErrorSchema"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}}},"parameters":[{"$ref":"#/components/parameters/XRequestId"}],"x-codeSamples":[{"lang":"Shell","label":"curl","source":"curl -X POST 'https://api.xyago.com/v1/time-entries' \\\n  -H \"Authorization: Bearer $XYAGO_API_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d @- <<'JSON'\n{\n  \"employee_id\": \"2c9a4e71-5b3d-4f8e-a6c2-7d1e0f9b3a54\",\n  \"started_at\": \"2026-03-02T07:00:00-05:00\",\n  \"ended_at\": \"2026-03-02T16:00:00-05:00\",\n  \"note\": \"Turno registrado desde Siesa\"\n}\nJSON"},{"lang":"Python","label":"Python","source":"import os\n\nimport requests\n\npayload = {\n    \"employee_id\": \"2c9a4e71-5b3d-4f8e-a6c2-7d1e0f9b3a54\",\n    \"started_at\": \"2026-03-02T07:00:00-05:00\",\n    \"ended_at\": \"2026-03-02T16:00:00-05:00\",\n    \"note\": \"Turno registrado desde Siesa\"\n}\n\nresponse = requests.post(\n    \"https://api.xyago.com/v1/time-entries\",\n    headers={\n        \"Authorization\": f\"Bearer {os.environ['XYAGO_API_KEY']}\",\n    },\n    json=payload,\n    timeout=30,\n)\nresponse.raise_for_status()\nprint(response.headers['X-Request-Id'], response.json())"},{"lang":"JavaScript","label":"JavaScript","source":"const payload = {\n  \"employee_id\": \"2c9a4e71-5b3d-4f8e-a6c2-7d1e0f9b3a54\",\n  \"started_at\": \"2026-03-02T07:00:00-05:00\",\n  \"ended_at\": \"2026-03-02T16:00:00-05:00\",\n  \"note\": \"Turno registrado desde Siesa\"\n};\n\nconst response = await fetch(\"https://api.xyago.com/v1/time-entries\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.XYAGO_API_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify(payload),\n});\nconst data = await response.json();\nif (!response.ok) throw new Error(`${data.error.code}: ${data.error.message}`);\nconsole.log(response.headers.get(\"X-Request-Id\"), data);"},{"lang":"C#","label":"C#","source":"using System.Net.Http.Headers;\nusing System.Text;\n\nusing var http = new HttpClient();\nhttp.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(\n    \"Bearer\", Environment.GetEnvironmentVariable(\"XYAGO_API_KEY\"));\n\nvar request = new HttpRequestMessage(new HttpMethod(\"POST\"), \"https://api.xyago.com/v1/time-entries\");\nvar payload = \"\"\"\n{\n  \"employee_id\": \"2c9a4e71-5b3d-4f8e-a6c2-7d1e0f9b3a54\",\n  \"started_at\": \"2026-03-02T07:00:00-05:00\",\n  \"ended_at\": \"2026-03-02T16:00:00-05:00\",\n  \"note\": \"Turno registrado desde Siesa\"\n}\n\"\"\";\nrequest.Content = new StringContent(payload, Encoding.UTF8, \"application/json\");\nvar response = await http.SendAsync(request);\nvar body = await response.Content.ReadAsStringAsync();\nConsole.WriteLine($\"{(int)response.StatusCode} {body}\");"}]}},"/messages":{"post":{"tags":["messages"],"summary":"Enviar una notificación con plantilla","description":"Envía la plantilla `template` del remitente `sender_id` al teléfono `to` por el\nchat de xYago. Solo plantillas aprobadas por xYago (en sandbox también `draft`) y\nsolo remitentes aprobados (en sandbox cualquiera de la cuenta, a los números de\nprueba confirmados en el panel).\n\n- Si el destinatario aún no usa xYago el mensaje queda `pending_install` hasta 7\n  días y se entrega en cuanto instale la app y verifique su número (webhook\n  `message.sent`); si no, termina `failed` con `recipient_not_on_xyago`\n  (webhook `message.failed`).\n- Si el destinatario no puede recibir mensajes del remitente responde\n  `409 undeliverable` (no reintentes).\n- Topes por remitente: mensajes por minuto (el límite del producto), por\n  destinatario y día y destinatarios nuevos por día → `429 rate_limited`.\n- Botones: los de la plantilla se envían tal cual; si un botón `url` lleva\n  `{{1}}`, su valor va en `button_variables` (por índice del botón). Una\n  respuesta rápida pulsada llega como webhook `message.button_clicked`.\n\nScope: `messages:write`.","operationId":"send_message","parameters":[{"name":"Idempotency-Key","in":"header","required":true,"schema":{"type":"string","pattern":"^[A-Za-z0-9._:-]{8,128}$","description":"Obligatoria. Clave única por envío (8-128 caracteres `[A-Za-z0-9._:-]`, p. ej. un UUID o el id del aviso en tu sistema). Un reintento con la misma clave nunca duplica el mensaje.","title":"Idempotency-Key"},"description":"Obligatoria. Clave única por envío (8-128 caracteres `[A-Za-z0-9._:-]`, p. ej. un UUID o el id del aviso en tu sistema). Un reintento con la misma clave nunca duplica el mensaje.","examples":{"ejemplo":{"summary":"Ejemplo","value":"pedido-TE-48213-enviado"}}},{"$ref":"#/components/parameters/XRequestId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MessageCreate"},"examples":{"pedido_enviado":{"summary":"Aviso de pedido enviado","value":{"to":"+573005550123","sender_id":"tiendaexpress","template":"pedido_enviado","language":"es","variables":["Valentina","TE-48213"],"reference":"CLI-000123","button_variables":{"0":"TE-48213"}}}}}}},"responses":{"202":{"description":"Aceptado: entregado al chat (`sent`) o en espera de que el destinatario instale xYago (`pending_install`). Una repetición con la misma `Idempotency-Key` y el mismo cuerpo responde `200` con el envío original y la cabecera `Idempotent-Replayed: true`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Message"},"example":{"id":"7a1c9e35-2b4d-4f6a-8c0e-1d3f5a7b9c21","status":"sent","to_masked":"+57 ••• ••• 0123","sender_id":"tiendaexpress","template":"pedido_enviado","language":"es","reference":"CLI-000123","created_at":"2026-11-04T15:00:00Z","sent_at":"2026-11-04T15:00:01Z"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"}}},"401":{"description":"No autenticado.\n\n- `invalid_api_key`: Falta la API key o no es válida, está revocada o ha caducado.","content":{"application/json":{"examples":{"invalid_api_key":{"summary":"invalid_api_key","description":"Falta la API key o no es válida, está revocada o ha caducado.","value":{"error":{"code":"invalid_api_key","message":"Missing or invalid API key."}}}},"schema":{"$ref":"#/components/schemas/ErrorSchema"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"403":{"description":"Prohibido.\n\n- `api_disabled`: El producto (areas, employees...) no está habilitado en el panel para el entorno de la API key (live o test).\n- `endpoint_disabled`: El administrador desactivó este endpoint concreto en el panel.\n- `insufficient_scope`: La API key no tiene el scope que exige el endpoint (p. ej. `employees:write`).\n- `ip_not_allowed`: La IP de origen no está en la lista de IPs permitidas del producto.\n- `new_recipients_paused`: xYago pausó los envíos del remitente a destinatarios nuevos (demasiados teléfonos sin xYago). Los destinatarios que ya recibieron mensajes siguen funcionando. Contacta con xYago.","content":{"application/json":{"examples":{"api_disabled":{"summary":"api_disabled","description":"El producto (areas, employees...) no está habilitado en el panel para el entorno de la API key (live o test).","value":{"error":{"code":"api_disabled","message":"The 'employees' API is not enabled for this environment."}}},"endpoint_disabled":{"summary":"endpoint_disabled","description":"El administrador desactivó este endpoint concreto en el panel.","value":{"error":{"code":"endpoint_disabled","message":"This endpoint is disabled."}}},"insufficient_scope":{"summary":"insufficient_scope","description":"La API key no tiene el scope que exige el endpoint (p. ej. `employees:write`).","value":{"error":{"code":"insufficient_scope","message":"This API key lacks the 'employees:write' scope."}}},"ip_not_allowed":{"summary":"ip_not_allowed","description":"La IP de origen no está en la lista de IPs permitidas del producto.","value":{"error":{"code":"ip_not_allowed","message":"Requests from this IP are not allowed."}}},"new_recipients_paused":{"summary":"new_recipients_paused","description":"xYago pausó los envíos del remitente a destinatarios nuevos (demasiados teléfonos sin xYago). Los destinatarios que ya recibieron mensajes siguen funcionando. Contacta con xYago.","value":{"error":{"code":"new_recipients_paused","message":"Sending to new recipients is paused for this sender. Contact xYago."}}}},"schema":{"$ref":"#/components/schemas/ErrorSchema"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"404":{"description":"No encontrado.\n\n- `sender_not_found`: El `sender_id` no es un remitente de tu cuenta.\n- `template_not_found`: El remitente no tiene una plantilla con ese nombre.","content":{"application/json":{"examples":{"sender_not_found":{"summary":"sender_not_found","description":"El `sender_id` no es un remitente de tu cuenta.","value":{"error":{"code":"sender_not_found","message":"Sender not found."}}},"template_not_found":{"summary":"template_not_found","description":"El remitente no tiene una plantilla con ese nombre.","value":{"error":{"code":"template_not_found","message":"The sender has no template named 'order_shipped'."}}}},"schema":{"$ref":"#/components/schemas/ErrorSchema"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"409":{"description":"Conflicto.\n\n- `sender_not_approved`: El remitente aún no está aprobado por xYago (en `live` solo envían remitentes aprobados).\n- `sender_suspended`: xYago suspendió el remitente (o la cuenta). Contacta con xYago.\n- `template_not_approved`: La plantilla no está aprobada (en sandbox también vale una en `draft`).\n- `template_paused`: xYago pausó la plantilla (p. ej. por quejas de los usuarios). No se puede usar hasta que xYago la reactive; contacta con xYago.\n- `idempotency_key_reused`: Ya usaste esa `Idempotency-Key` con otro cuerpo. Usa una clave nueva por envío distinto.\n- `undeliverable`: El mensaje no se puede entregar a este destinatario. No reintentes.","content":{"application/json":{"examples":{"sender_not_approved":{"summary":"sender_not_approved","description":"El remitente aún no está aprobado por xYago (en `live` solo envían remitentes aprobados).","value":{"error":{"code":"sender_not_approved","message":"The sender is not approved yet."}}},"sender_suspended":{"summary":"sender_suspended","description":"xYago suspendió el remitente (o la cuenta). Contacta con xYago.","value":{"error":{"code":"sender_suspended","message":"The sender is suspended."}}},"template_not_approved":{"summary":"template_not_approved","description":"La plantilla no está aprobada (en sandbox también vale una en `draft`).","value":{"error":{"code":"template_not_approved","message":"Template 'order_shipped' is not approved (status 'draft')."}}},"template_paused":{"summary":"template_paused","description":"xYago pausó la plantilla (p. ej. por quejas de los usuarios). No se puede usar hasta que xYago la reactive; contacta con xYago.","value":{"error":{"code":"template_paused","message":"Template 'order_shipped' is paused by xYago."}}},"idempotency_key_reused":{"summary":"idempotency_key_reused","description":"Ya usaste esa `Idempotency-Key` con otro cuerpo. Usa una clave nueva por envío distinto.","value":{"error":{"code":"idempotency_key_reused","message":"Idempotency-Key was already used with a different request."}}},"undeliverable":{"summary":"undeliverable","description":"El mensaje no se puede entregar a este destinatario. No reintentes.","value":{"error":{"code":"undeliverable","message":"The message cannot be delivered to this recipient."}}}},"schema":{"$ref":"#/components/schemas/ErrorSchema"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"422":{"description":"Datos no válidos.\n\n- `validation_error`: El cuerpo o los parámetros no cumplen el esquema (campo obligatorio, formato, campo desconocido...). `message` indica el campo.\n- `invalid_phone`: `to` no es un teléfono E.164 válido (al menos 10 dígitos).\n- `template_variables_mismatch`: El número de `variables` no coincide con el de la plantilla (`variables_count`).\n- `invalid_variables`: Una variable supera 256 caracteres o contiene un enlace.\n- `invalid_button_variables`: Falta el valor de `button_variables` de un botón de enlace con variable, sobra uno (el botón no tiene variable) o no es válido (texto, máx. 200 caracteres).\n- `sandbox_recipient_not_allowed`: Con una clave `xy_test_` solo se envía a los números de prueba confirmados en el panel.\n- `invalid_message`: xYago rechazó el contenido del mensaje.","content":{"application/json":{"examples":{"validation_error":{"summary":"validation_error","description":"El cuerpo o los parámetros no cumplen el esquema (campo obligatorio, formato, campo desconocido...). `message` indica el campo.","value":{"error":{"code":"validation_error","message":"phone_number: String should match pattern '^\\+[1-9][0-9]{6,14}$'"}}},"invalid_phone":{"summary":"invalid_phone","description":"`to` no es un teléfono E.164 válido (al menos 10 dígitos).","value":{"error":{"code":"invalid_phone","message":"Phone number must be in E.164 format (e.g. +573001234567)."}}},"template_variables_mismatch":{"summary":"template_variables_mismatch","description":"El número de `variables` no coincide con el de la plantilla (`variables_count`).","value":{"error":{"code":"template_variables_mismatch","message":"template 'order_shipped' expects 2 variables, got 1"}}},"invalid_variables":{"summary":"invalid_variables","description":"Una variable supera 256 caracteres o contiene un enlace.","value":{"error":{"code":"invalid_variables","message":"each variable must be at most 256 chars"}}},"invalid_button_variables":{"summary":"invalid_button_variables","description":"Falta el valor de `button_variables` de un botón de enlace con variable, sobra uno (el botón no tiene variable) o no es válido (texto, máx. 200 caracteres).","value":{"error":{"code":"invalid_button_variables","message":"button_variables['0'] is required (the button URL has {{1}})"}}},"sandbox_recipient_not_allowed":{"summary":"sandbox_recipient_not_allowed","description":"Con una clave `xy_test_` solo se envía a los números de prueba confirmados en el panel.","value":{"error":{"code":"sandbox_recipient_not_allowed","message":"In sandbox, messages can only be sent to confirmed test numbers."}}},"invalid_message":{"summary":"invalid_message","description":"xYago rechazó el contenido del mensaje.","value":{"error":{"code":"invalid_message","message":"The chat rejected the message content."}}}},"schema":{"$ref":"#/components/schemas/ErrorSchema"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"429":{"description":"Demasiadas peticiones.\n\n- `rate_limited`: Se superó el límite de peticiones por minuto de la API key o, en `POST /messages`, un tope de envío del remitente (mensajes por minuto, por destinatario y día, destinatarios nuevos por día) o, en Verify, un tope de códigos (espera entre códigos, por teléfono, por remitente) o de códigos erróneos por hora. Espera los segundos de `Retry-After`.","content":{"application/json":{"examples":{"rate_limited":{"summary":"rate_limited","description":"Se superó el límite de peticiones por minuto de la API key o, en `POST /messages`, un tope de envío del remitente (mensajes por minuto, por destinatario y día, destinatarios nuevos por día) o, en Verify, un tope de códigos (espera entre códigos, por teléfono, por remitente) o de códigos erróneos por hora. Espera los segundos de `Retry-After`.","value":{"error":{"code":"rate_limited","message":"Rate limit exceeded. Retry after the indicated number of seconds."}}}},"schema":{"$ref":"#/components/schemas/ErrorSchema"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"},"Retry-After":{"$ref":"#/components/headers/Retry-After"}}},"502":{"description":"Error de un servicio interno.\n\n- `upstream_error`: Un servicio interno de xYago falló o no respondió a tiempo. Reintenta con backoff (en las escrituras, comprueba antes si ya se aplicaron).","content":{"application/json":{"examples":{"upstream_error":{"summary":"upstream_error","description":"Un servicio interno de xYago falló o no respondió a tiempo. Reintenta con backoff (en las escrituras, comprueba antes si ya se aplicaron).","value":{"error":{"code":"upstream_error","message":"An internal service failed to process the request. Try again later."}}}},"schema":{"$ref":"#/components/schemas/ErrorSchema"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"503":{"description":"Servicio no disponible.\n\n- `limits_unavailable`: Los límites de envío no se pueden comprobar ahora mismo; por seguridad no se envía. Reintenta con backoff y la misma `Idempotency-Key`.\n- `messaging_not_configured`: La mensajería no está disponible temporalmente. Contacta con soporte.","content":{"application/json":{"examples":{"limits_unavailable":{"summary":"limits_unavailable","description":"Los límites de envío no se pueden comprobar ahora mismo; por seguridad no se envía. Reintenta con backoff y la misma `Idempotency-Key`.","value":{"error":{"code":"limits_unavailable","message":"Sending is temporarily unavailable. Retry later."}}},"messaging_not_configured":{"summary":"messaging_not_configured","description":"La mensajería no está disponible temporalmente. Contacta con soporte.","value":{"error":{"code":"messaging_not_configured","message":"Business messaging is not configured."}}}},"schema":{"$ref":"#/components/schemas/ErrorSchema"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}}},"x-codeSamples":[{"lang":"Shell","label":"curl","source":"curl -X POST 'https://api.xyago.com/v1/messages' \\\n  -H \"Authorization: Bearer $XYAGO_API_KEY\" \\\n  -H \"Idempotency-Key: pedido-TE-48213-enviado\" \\\n  -H \"Content-Type: application/json\" \\\n  -d @- <<'JSON'\n{\n  \"to\": \"+573005550123\",\n  \"sender_id\": \"tiendaexpress\",\n  \"template\": \"pedido_enviado\",\n  \"language\": \"es\",\n  \"variables\": [\n    \"Valentina\",\n    \"TE-48213\"\n  ],\n  \"reference\": \"CLI-000123\",\n  \"button_variables\": {\n    \"0\": \"TE-48213\"\n  }\n}\nJSON"},{"lang":"Python","label":"Python","source":"import os\n\nimport requests\n\npayload = {\n    \"to\": \"+573005550123\",\n    \"sender_id\": \"tiendaexpress\",\n    \"template\": \"pedido_enviado\",\n    \"language\": \"es\",\n    \"variables\": [\n        \"Valentina\",\n        \"TE-48213\"\n    ],\n    \"reference\": \"CLI-000123\",\n    \"button_variables\": {\n        \"0\": \"TE-48213\"\n    }\n}\n\nresponse = requests.post(\n    \"https://api.xyago.com/v1/messages\",\n    headers={\n        \"Authorization\": f\"Bearer {os.environ['XYAGO_API_KEY']}\",\n        \"Idempotency-Key\": \"pedido-TE-48213-enviado\",\n    },\n    json=payload,\n    timeout=30,\n)\nresponse.raise_for_status()\nprint(response.headers['X-Request-Id'], response.json())"},{"lang":"JavaScript","label":"JavaScript","source":"const payload = {\n  \"to\": \"+573005550123\",\n  \"sender_id\": \"tiendaexpress\",\n  \"template\": \"pedido_enviado\",\n  \"language\": \"es\",\n  \"variables\": [\n    \"Valentina\",\n    \"TE-48213\"\n  ],\n  \"reference\": \"CLI-000123\",\n  \"button_variables\": {\n    \"0\": \"TE-48213\"\n  }\n};\n\nconst response = await fetch(\"https://api.xyago.com/v1/messages\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.XYAGO_API_KEY}`,\n    \"Idempotency-Key\": \"pedido-TE-48213-enviado\",\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify(payload),\n});\nconst data = await response.json();\nif (!response.ok) throw new Error(`${data.error.code}: ${data.error.message}`);\nconsole.log(response.headers.get(\"X-Request-Id\"), data);"},{"lang":"C#","label":"C#","source":"using System.Net.Http.Headers;\nusing System.Text;\n\nusing var http = new HttpClient();\nhttp.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(\n    \"Bearer\", Environment.GetEnvironmentVariable(\"XYAGO_API_KEY\"));\n\nvar request = new HttpRequestMessage(new HttpMethod(\"POST\"), \"https://api.xyago.com/v1/messages\");\nrequest.Headers.Add(\"Idempotency-Key\", \"pedido-TE-48213-enviado\");\nvar payload = \"\"\"\n{\n  \"to\": \"+573005550123\",\n  \"sender_id\": \"tiendaexpress\",\n  \"template\": \"pedido_enviado\",\n  \"language\": \"es\",\n  \"variables\": [\n    \"Valentina\",\n    \"TE-48213\"\n  ],\n  \"reference\": \"CLI-000123\",\n  \"button_variables\": {\n    \"0\": \"TE-48213\"\n  }\n}\n\"\"\";\nrequest.Content = new StringContent(payload, Encoding.UTF8, \"application/json\");\nvar response = await http.SendAsync(request);\nvar body = await response.Content.ReadAsStringAsync();\nConsole.WriteLine($\"{(int)response.StatusCode} {body}\");"}]},"get":{"tags":["messages"],"summary":"Listar envíos","description":"Envíos del entorno de la API key (live o sandbox), del más reciente al más\nantiguo. Nunca incluye el texto ni el teléfono completo.\n\nScope: `messages:read`.","operationId":"list_messages","parameters":[{"name":"status","in":"query","required":false,"schema":{"anyOf":[{"enum":["accepted","pending_install","sent","delivered","read","failed"],"type":"string"},{"type":"null"}],"description":"Filtra por estado.","title":"Status"},"description":"Filtra por estado."},{"name":"sender_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":32},{"type":"null"}],"description":"Filtra por remitente (`business_id`).","title":"Sender Id"},"description":"Filtra por remitente (`business_id`)."},{"name":"reference","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":64},{"type":"null"}],"description":"Filtra por tu `reference`.","title":"Reference"},"description":"Filtra por tu `reference`."},{"name":"cursor","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":512},{"type":"null"}],"description":"Cursor opaco de la página siguiente (`next_cursor` de la respuesta anterior). Omitir en la primera petición.","title":"Cursor"},"description":"Cursor opaco de la página siguiente (`next_cursor` de la respuesta anterior). Omitir en la primera petición."},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"description":"Tamaño de página (1-100, por defecto 50).","default":50,"title":"Limit"},"description":"Tamaño de página (1-100, por defecto 50)."},{"$ref":"#/components/parameters/XRequestId"}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MessageList"},"example":{"results":[{"id":"3e5a7c91-4b6d-4e8f-a0c2-5d7f9b1e3a64","status":"pending_install","to_masked":"+57 ••• ••• 0187","sender_id":"tiendaexpress","template":"pedido_enviado","language":"es","reference":"CLI-000087","created_at":"2026-11-04T15:00:00Z","expires_at":"2026-11-11T15:00:00Z"},{"id":"7a1c9e35-2b4d-4f6a-8c0e-1d3f5a7b9c21","status":"read","to_masked":"+57 ••• ••• 0123","sender_id":"tiendaexpress","template":"pedido_enviado","language":"es","reference":"CLI-000123","created_at":"2026-11-04T15:00:00Z","sent_at":"2026-11-04T15:00:01Z","delivered_at":"2026-11-04T15:02:10Z","read_at":"2026-11-04T15:07:42Z"}],"next_cursor":"eyJ0IjogIjIwMjYtMTEtMDRUMTU6MDA6MDAiLCAiaWQiOiAiN2ExYyJ9"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"}}},"400":{"description":"Petición incorrecta.\n\n- `invalid_cursor`: El `cursor` no es válido (manipulado o de otro listado). Repite la consulta sin cursor.","content":{"application/json":{"examples":{"invalid_cursor":{"summary":"invalid_cursor","description":"El `cursor` no es válido (manipulado o de otro listado). Repite la consulta sin cursor.","value":{"error":{"code":"invalid_cursor","message":"Invalid cursor."}}}},"schema":{"$ref":"#/components/schemas/ErrorSchema"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"401":{"description":"No autenticado.\n\n- `invalid_api_key`: Falta la API key o no es válida, está revocada o ha caducado.","content":{"application/json":{"examples":{"invalid_api_key":{"summary":"invalid_api_key","description":"Falta la API key o no es válida, está revocada o ha caducado.","value":{"error":{"code":"invalid_api_key","message":"Missing or invalid API key."}}}},"schema":{"$ref":"#/components/schemas/ErrorSchema"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"403":{"description":"Prohibido.\n\n- `api_disabled`: El producto (areas, employees...) no está habilitado en el panel para el entorno de la API key (live o test).\n- `endpoint_disabled`: El administrador desactivó este endpoint concreto en el panel.\n- `insufficient_scope`: La API key no tiene el scope que exige el endpoint (p. ej. `employees:write`).\n- `ip_not_allowed`: La IP de origen no está en la lista de IPs permitidas del producto.","content":{"application/json":{"examples":{"api_disabled":{"summary":"api_disabled","description":"El producto (areas, employees...) no está habilitado en el panel para el entorno de la API key (live o test).","value":{"error":{"code":"api_disabled","message":"The 'employees' API is not enabled for this environment."}}},"endpoint_disabled":{"summary":"endpoint_disabled","description":"El administrador desactivó este endpoint concreto en el panel.","value":{"error":{"code":"endpoint_disabled","message":"This endpoint is disabled."}}},"insufficient_scope":{"summary":"insufficient_scope","description":"La API key no tiene el scope que exige el endpoint (p. ej. `employees:write`).","value":{"error":{"code":"insufficient_scope","message":"This API key lacks the 'employees:write' scope."}}},"ip_not_allowed":{"summary":"ip_not_allowed","description":"La IP de origen no está en la lista de IPs permitidas del producto.","value":{"error":{"code":"ip_not_allowed","message":"Requests from this IP are not allowed."}}}},"schema":{"$ref":"#/components/schemas/ErrorSchema"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"422":{"description":"Datos no válidos.\n\n- `validation_error`: El cuerpo o los parámetros no cumplen el esquema (campo obligatorio, formato, campo desconocido...). `message` indica el campo.","content":{"application/json":{"examples":{"validation_error":{"summary":"validation_error","description":"El cuerpo o los parámetros no cumplen el esquema (campo obligatorio, formato, campo desconocido...). `message` indica el campo.","value":{"error":{"code":"validation_error","message":"phone_number: String should match pattern '^\\+[1-9][0-9]{6,14}$'"}}}},"schema":{"$ref":"#/components/schemas/ErrorSchema"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"429":{"description":"Demasiadas peticiones.\n\n- `rate_limited`: Se superó el límite de peticiones por minuto de la API key o, en `POST /messages`, un tope de envío del remitente (mensajes por minuto, por destinatario y día, destinatarios nuevos por día) o, en Verify, un tope de códigos (espera entre códigos, por teléfono, por remitente) o de códigos erróneos por hora. Espera los segundos de `Retry-After`.","content":{"application/json":{"examples":{"rate_limited":{"summary":"rate_limited","description":"Se superó el límite de peticiones por minuto de la API key o, en `POST /messages`, un tope de envío del remitente (mensajes por minuto, por destinatario y día, destinatarios nuevos por día) o, en Verify, un tope de códigos (espera entre códigos, por teléfono, por remitente) o de códigos erróneos por hora. Espera los segundos de `Retry-After`.","value":{"error":{"code":"rate_limited","message":"Rate limit exceeded. Retry after the indicated number of seconds."}}}},"schema":{"$ref":"#/components/schemas/ErrorSchema"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"},"Retry-After":{"$ref":"#/components/headers/Retry-After"}}},"502":{"description":"Error de un servicio interno.\n\n- `upstream_error`: Un servicio interno de xYago falló o no respondió a tiempo. Reintenta con backoff (en las escrituras, comprueba antes si ya se aplicaron).","content":{"application/json":{"examples":{"upstream_error":{"summary":"upstream_error","description":"Un servicio interno de xYago falló o no respondió a tiempo. Reintenta con backoff (en las escrituras, comprueba antes si ya se aplicaron).","value":{"error":{"code":"upstream_error","message":"An internal service failed to process the request. Try again later."}}}},"schema":{"$ref":"#/components/schemas/ErrorSchema"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}}},"x-codeSamples":[{"lang":"Shell","label":"curl","source":"curl -X GET 'https://api.xyago.com/v1/messages' \\\n  -H \"Authorization: Bearer $XYAGO_API_KEY\""},{"lang":"Python","label":"Python","source":"import os\n\nimport requests\n\nresponse = requests.get(\n    \"https://api.xyago.com/v1/messages\",\n    headers={\n        \"Authorization\": f\"Bearer {os.environ['XYAGO_API_KEY']}\",\n    },\n    timeout=30,\n)\nresponse.raise_for_status()\nprint(response.headers['X-Request-Id'], response.json())"},{"lang":"JavaScript","label":"JavaScript","source":"const response = await fetch(\"https://api.xyago.com/v1/messages\", {\n  method: \"GET\",\n  headers: {\n    Authorization: `Bearer ${process.env.XYAGO_API_KEY}`,\n  },\n});\nconst data = await response.json();\nif (!response.ok) throw new Error(`${data.error.code}: ${data.error.message}`);\nconsole.log(response.headers.get(\"X-Request-Id\"), data);"},{"lang":"C#","label":"C#","source":"using System.Net.Http.Headers;\nusing System.Text;\n\nusing var http = new HttpClient();\nhttp.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(\n    \"Bearer\", Environment.GetEnvironmentVariable(\"XYAGO_API_KEY\"));\n\nvar request = new HttpRequestMessage(new HttpMethod(\"GET\"), \"https://api.xyago.com/v1/messages\");\nvar response = await http.SendAsync(request);\nvar body = await response.Content.ReadAsStringAsync();\nConsole.WriteLine($\"{(int)response.StatusCode} {body}\");"}]}},"/messages/{id}":{"get":{"tags":["messages"],"summary":"Consultar un envío","description":"Estado actual de un envío. Un envío de otra cuenta o del otro entorno responde\n`404`.\n\nScope: `messages:read`.","operationId":"get_message","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid","description":"Id del envío (`id` de la respuesta de `POST /messages`).","title":"Id"},"description":"Id del envío (`id` de la respuesta de `POST /messages`).","examples":{"ejemplo":{"summary":"Ejemplo","value":"7a1c9e35-2b4d-4f6a-8c0e-1d3f5a7b9c21"}}},{"$ref":"#/components/parameters/XRequestId"}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Message"},"example":{"id":"7a1c9e35-2b4d-4f6a-8c0e-1d3f5a7b9c21","status":"read","to_masked":"+57 ••• ••• 0123","sender_id":"tiendaexpress","template":"pedido_enviado","language":"es","reference":"CLI-000123","created_at":"2026-11-04T15:00:00Z","sent_at":"2026-11-04T15:00:01Z","delivered_at":"2026-11-04T15:02:10Z","read_at":"2026-11-04T15:07:42Z"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"}}},"401":{"description":"No autenticado.\n\n- `invalid_api_key`: Falta la API key o no es válida, está revocada o ha caducado.","content":{"application/json":{"examples":{"invalid_api_key":{"summary":"invalid_api_key","description":"Falta la API key o no es válida, está revocada o ha caducado.","value":{"error":{"code":"invalid_api_key","message":"Missing or invalid API key."}}}},"schema":{"$ref":"#/components/schemas/ErrorSchema"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"403":{"description":"Prohibido.\n\n- `api_disabled`: El producto (areas, employees...) no está habilitado en el panel para el entorno de la API key (live o test).\n- `endpoint_disabled`: El administrador desactivó este endpoint concreto en el panel.\n- `insufficient_scope`: La API key no tiene el scope que exige el endpoint (p. ej. `employees:write`).\n- `ip_not_allowed`: La IP de origen no está en la lista de IPs permitidas del producto.","content":{"application/json":{"examples":{"api_disabled":{"summary":"api_disabled","description":"El producto (areas, employees...) no está habilitado en el panel para el entorno de la API key (live o test).","value":{"error":{"code":"api_disabled","message":"The 'employees' API is not enabled for this environment."}}},"endpoint_disabled":{"summary":"endpoint_disabled","description":"El administrador desactivó este endpoint concreto en el panel.","value":{"error":{"code":"endpoint_disabled","message":"This endpoint is disabled."}}},"insufficient_scope":{"summary":"insufficient_scope","description":"La API key no tiene el scope que exige el endpoint (p. ej. `employees:write`).","value":{"error":{"code":"insufficient_scope","message":"This API key lacks the 'employees:write' scope."}}},"ip_not_allowed":{"summary":"ip_not_allowed","description":"La IP de origen no está en la lista de IPs permitidas del producto.","value":{"error":{"code":"ip_not_allowed","message":"Requests from this IP are not allowed."}}}},"schema":{"$ref":"#/components/schemas/ErrorSchema"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"404":{"description":"No encontrado.\n\n- `not_found`: El recurso no existe o pertenece a otra empresa (nunca se distingue).","content":{"application/json":{"examples":{"not_found":{"summary":"not_found","description":"El recurso no existe o pertenece a otra empresa (nunca se distingue).","value":{"error":{"code":"not_found","message":"Employee not found."}}}},"schema":{"$ref":"#/components/schemas/ErrorSchema"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"422":{"description":"Datos no válidos.\n\n- `validation_error`: El cuerpo o los parámetros no cumplen el esquema (campo obligatorio, formato, campo desconocido...). `message` indica el campo.","content":{"application/json":{"examples":{"validation_error":{"summary":"validation_error","description":"El cuerpo o los parámetros no cumplen el esquema (campo obligatorio, formato, campo desconocido...). `message` indica el campo.","value":{"error":{"code":"validation_error","message":"phone_number: String should match pattern '^\\+[1-9][0-9]{6,14}$'"}}}},"schema":{"$ref":"#/components/schemas/ErrorSchema"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"429":{"description":"Demasiadas peticiones.\n\n- `rate_limited`: Se superó el límite de peticiones por minuto de la API key o, en `POST /messages`, un tope de envío del remitente (mensajes por minuto, por destinatario y día, destinatarios nuevos por día) o, en Verify, un tope de códigos (espera entre códigos, por teléfono, por remitente) o de códigos erróneos por hora. Espera los segundos de `Retry-After`.","content":{"application/json":{"examples":{"rate_limited":{"summary":"rate_limited","description":"Se superó el límite de peticiones por minuto de la API key o, en `POST /messages`, un tope de envío del remitente (mensajes por minuto, por destinatario y día, destinatarios nuevos por día) o, en Verify, un tope de códigos (espera entre códigos, por teléfono, por remitente) o de códigos erróneos por hora. Espera los segundos de `Retry-After`.","value":{"error":{"code":"rate_limited","message":"Rate limit exceeded. Retry after the indicated number of seconds."}}}},"schema":{"$ref":"#/components/schemas/ErrorSchema"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"},"Retry-After":{"$ref":"#/components/headers/Retry-After"}}},"502":{"description":"Error de un servicio interno.\n\n- `upstream_error`: Un servicio interno de xYago falló o no respondió a tiempo. Reintenta con backoff (en las escrituras, comprueba antes si ya se aplicaron).","content":{"application/json":{"examples":{"upstream_error":{"summary":"upstream_error","description":"Un servicio interno de xYago falló o no respondió a tiempo. Reintenta con backoff (en las escrituras, comprueba antes si ya se aplicaron).","value":{"error":{"code":"upstream_error","message":"An internal service failed to process the request. Try again later."}}}},"schema":{"$ref":"#/components/schemas/ErrorSchema"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}}},"x-codeSamples":[{"lang":"Shell","label":"curl","source":"curl -X GET 'https://api.xyago.com/v1/messages/7a1c9e35-2b4d-4f6a-8c0e-1d3f5a7b9c21' \\\n  -H \"Authorization: Bearer $XYAGO_API_KEY\""},{"lang":"Python","label":"Python","source":"import os\n\nimport requests\n\nresponse = requests.get(\n    \"https://api.xyago.com/v1/messages/7a1c9e35-2b4d-4f6a-8c0e-1d3f5a7b9c21\",\n    headers={\n        \"Authorization\": f\"Bearer {os.environ['XYAGO_API_KEY']}\",\n    },\n    timeout=30,\n)\nresponse.raise_for_status()\nprint(response.headers['X-Request-Id'], response.json())"},{"lang":"JavaScript","label":"JavaScript","source":"const response = await fetch(\"https://api.xyago.com/v1/messages/7a1c9e35-2b4d-4f6a-8c0e-1d3f5a7b9c21\", {\n  method: \"GET\",\n  headers: {\n    Authorization: `Bearer ${process.env.XYAGO_API_KEY}`,\n  },\n});\nconst data = await response.json();\nif (!response.ok) throw new Error(`${data.error.code}: ${data.error.message}`);\nconsole.log(response.headers.get(\"X-Request-Id\"), data);"},{"lang":"C#","label":"C#","source":"using System.Net.Http.Headers;\nusing System.Text;\n\nusing var http = new HttpClient();\nhttp.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(\n    \"Bearer\", Environment.GetEnvironmentVariable(\"XYAGO_API_KEY\"));\n\nvar request = new HttpRequestMessage(new HttpMethod(\"GET\"), \"https://api.xyago.com/v1/messages/7a1c9e35-2b4d-4f6a-8c0e-1d3f5a7b9c21\");\nvar response = await http.SendAsync(request);\nvar body = await response.Content.ReadAsStringAsync();\nConsole.WriteLine($\"{(int)response.StatusCode} {body}\");"}]}},"/senders":{"get":{"tags":["senders"],"summary":"Listar remitentes","description":"Remitentes de la cuenta en cualquier estado (`draft`, `pending_review`,\n`approved`, `rejected`, `suspended`). Se crean y envían a revisión en el panel.\n\nScope: `messages:read`.","operationId":"list_senders","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SenderList"},"example":{"results":[{"id":"tiendaexpress","display_name":"Tienda Express","logo_url":"https://cdn.tiendaexpress.example.com/logo.png","description":"Avisos de tus pedidos de Tienda Express.","website":"https://tiendaexpress.example.com","category":"comercio","status":"approved","verified":true,"new_recipients_paused":false,"invite_url":"https://web.xyago.com/app/b/tiendaexpress","created_at":"2026-11-02T14:00:00Z","updated_at":"2026-11-03T09:30:00Z"}]}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"}}},"401":{"description":"No autenticado.\n\n- `invalid_api_key`: Falta la API key o no es válida, está revocada o ha caducado.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorSchema"},"examples":{"invalid_api_key":{"summary":"invalid_api_key","description":"Falta la API key o no es válida, está revocada o ha caducado.","value":{"error":{"code":"invalid_api_key","message":"Missing or invalid API key."}}}}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"403":{"description":"Prohibido.\n\n- `api_disabled`: El producto (areas, employees...) no está habilitado en el panel para el entorno de la API key (live o test).\n- `endpoint_disabled`: El administrador desactivó este endpoint concreto en el panel.\n- `insufficient_scope`: La API key no tiene el scope que exige el endpoint (p. ej. `employees:write`).\n- `ip_not_allowed`: La IP de origen no está en la lista de IPs permitidas del producto.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorSchema"},"examples":{"api_disabled":{"summary":"api_disabled","description":"El producto (areas, employees...) no está habilitado en el panel para el entorno de la API key (live o test).","value":{"error":{"code":"api_disabled","message":"The 'employees' API is not enabled for this environment."}}},"endpoint_disabled":{"summary":"endpoint_disabled","description":"El administrador desactivó este endpoint concreto en el panel.","value":{"error":{"code":"endpoint_disabled","message":"This endpoint is disabled."}}},"insufficient_scope":{"summary":"insufficient_scope","description":"La API key no tiene el scope que exige el endpoint (p. ej. `employees:write`).","value":{"error":{"code":"insufficient_scope","message":"This API key lacks the 'employees:write' scope."}}},"ip_not_allowed":{"summary":"ip_not_allowed","description":"La IP de origen no está en la lista de IPs permitidas del producto.","value":{"error":{"code":"ip_not_allowed","message":"Requests from this IP are not allowed."}}}}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"422":{"description":"Datos no válidos.\n\n- `validation_error`: El cuerpo o los parámetros no cumplen el esquema (campo obligatorio, formato, campo desconocido...). `message` indica el campo.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorSchema"},"examples":{"validation_error":{"summary":"validation_error","description":"El cuerpo o los parámetros no cumplen el esquema (campo obligatorio, formato, campo desconocido...). `message` indica el campo.","value":{"error":{"code":"validation_error","message":"phone_number: String should match pattern '^\\+[1-9][0-9]{6,14}$'"}}}}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"429":{"description":"Demasiadas peticiones.\n\n- `rate_limited`: Se superó el límite de peticiones por minuto de la API key o, en `POST /messages`, un tope de envío del remitente (mensajes por minuto, por destinatario y día, destinatarios nuevos por día) o, en Verify, un tope de códigos (espera entre códigos, por teléfono, por remitente) o de códigos erróneos por hora. Espera los segundos de `Retry-After`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorSchema"},"examples":{"rate_limited":{"summary":"rate_limited","description":"Se superó el límite de peticiones por minuto de la API key o, en `POST /messages`, un tope de envío del remitente (mensajes por minuto, por destinatario y día, destinatarios nuevos por día) o, en Verify, un tope de códigos (espera entre códigos, por teléfono, por remitente) o de códigos erróneos por hora. Espera los segundos de `Retry-After`.","value":{"error":{"code":"rate_limited","message":"Rate limit exceeded. Retry after the indicated number of seconds."}}}}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"},"Retry-After":{"$ref":"#/components/headers/Retry-After"}}},"502":{"description":"Error de un servicio interno.\n\n- `upstream_error`: Un servicio interno de xYago falló o no respondió a tiempo. Reintenta con backoff (en las escrituras, comprueba antes si ya se aplicaron).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorSchema"},"examples":{"upstream_error":{"summary":"upstream_error","description":"Un servicio interno de xYago falló o no respondió a tiempo. Reintenta con backoff (en las escrituras, comprueba antes si ya se aplicaron).","value":{"error":{"code":"upstream_error","message":"An internal service failed to process the request. Try again later."}}}}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}}},"parameters":[{"$ref":"#/components/parameters/XRequestId"}],"x-codeSamples":[{"lang":"Shell","label":"curl","source":"curl -X GET 'https://api.xyago.com/v1/senders' \\\n  -H \"Authorization: Bearer $XYAGO_API_KEY\""},{"lang":"Python","label":"Python","source":"import os\n\nimport requests\n\nresponse = requests.get(\n    \"https://api.xyago.com/v1/senders\",\n    headers={\n        \"Authorization\": f\"Bearer {os.environ['XYAGO_API_KEY']}\",\n    },\n    timeout=30,\n)\nresponse.raise_for_status()\nprint(response.headers['X-Request-Id'], response.json())"},{"lang":"JavaScript","label":"JavaScript","source":"const response = await fetch(\"https://api.xyago.com/v1/senders\", {\n  method: \"GET\",\n  headers: {\n    Authorization: `Bearer ${process.env.XYAGO_API_KEY}`,\n  },\n});\nconst data = await response.json();\nif (!response.ok) throw new Error(`${data.error.code}: ${data.error.message}`);\nconsole.log(response.headers.get(\"X-Request-Id\"), data);"},{"lang":"C#","label":"C#","source":"using System.Net.Http.Headers;\nusing System.Text;\n\nusing var http = new HttpClient();\nhttp.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(\n    \"Bearer\", Environment.GetEnvironmentVariable(\"XYAGO_API_KEY\"));\n\nvar request = new HttpRequestMessage(new HttpMethod(\"GET\"), \"https://api.xyago.com/v1/senders\");\nvar response = await http.SendAsync(request);\nvar body = await response.Content.ReadAsStringAsync();\nConsole.WriteLine($\"{(int)response.StatusCode} {body}\");"}]}},"/senders/{id}":{"get":{"tags":["senders"],"summary":"Consultar un remitente","description":"Perfil y estado de revisión de un remitente de la cuenta (otro → `404`).\n\nScope: `messages:read`.","operationId":"get_sender","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","pattern":"^[a-z0-9][a-z0-9_-]{2,31}$","description":"`business_id` del remitente.","title":"Id"},"description":"`business_id` del remitente.","examples":{"ejemplo":{"summary":"Ejemplo","value":"tiendaexpress"}}},{"$ref":"#/components/parameters/XRequestId"}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Sender"},"example":{"id":"tiendaexpress","display_name":"Tienda Express","logo_url":"https://cdn.tiendaexpress.example.com/logo.png","description":"Avisos de tus pedidos de Tienda Express.","website":"https://tiendaexpress.example.com","category":"comercio","status":"approved","verified":true,"new_recipients_paused":false,"invite_url":"https://web.xyago.com/app/b/tiendaexpress","created_at":"2026-11-02T14:00:00Z","updated_at":"2026-11-03T09:30:00Z"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"}}},"401":{"description":"No autenticado.\n\n- `invalid_api_key`: Falta la API key o no es válida, está revocada o ha caducado.","content":{"application/json":{"examples":{"invalid_api_key":{"summary":"invalid_api_key","description":"Falta la API key o no es válida, está revocada o ha caducado.","value":{"error":{"code":"invalid_api_key","message":"Missing or invalid API key."}}}},"schema":{"$ref":"#/components/schemas/ErrorSchema"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"403":{"description":"Prohibido.\n\n- `api_disabled`: El producto (areas, employees...) no está habilitado en el panel para el entorno de la API key (live o test).\n- `endpoint_disabled`: El administrador desactivó este endpoint concreto en el panel.\n- `insufficient_scope`: La API key no tiene el scope que exige el endpoint (p. ej. `employees:write`).\n- `ip_not_allowed`: La IP de origen no está en la lista de IPs permitidas del producto.","content":{"application/json":{"examples":{"api_disabled":{"summary":"api_disabled","description":"El producto (areas, employees...) no está habilitado en el panel para el entorno de la API key (live o test).","value":{"error":{"code":"api_disabled","message":"The 'employees' API is not enabled for this environment."}}},"endpoint_disabled":{"summary":"endpoint_disabled","description":"El administrador desactivó este endpoint concreto en el panel.","value":{"error":{"code":"endpoint_disabled","message":"This endpoint is disabled."}}},"insufficient_scope":{"summary":"insufficient_scope","description":"La API key no tiene el scope que exige el endpoint (p. ej. `employees:write`).","value":{"error":{"code":"insufficient_scope","message":"This API key lacks the 'employees:write' scope."}}},"ip_not_allowed":{"summary":"ip_not_allowed","description":"La IP de origen no está en la lista de IPs permitidas del producto.","value":{"error":{"code":"ip_not_allowed","message":"Requests from this IP are not allowed."}}}},"schema":{"$ref":"#/components/schemas/ErrorSchema"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"404":{"description":"No encontrado.\n\n- `not_found`: El recurso no existe o pertenece a otra empresa (nunca se distingue).","content":{"application/json":{"examples":{"not_found":{"summary":"not_found","description":"El recurso no existe o pertenece a otra empresa (nunca se distingue).","value":{"error":{"code":"not_found","message":"Employee not found."}}}},"schema":{"$ref":"#/components/schemas/ErrorSchema"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"422":{"description":"Datos no válidos.\n\n- `validation_error`: El cuerpo o los parámetros no cumplen el esquema (campo obligatorio, formato, campo desconocido...). `message` indica el campo.","content":{"application/json":{"examples":{"validation_error":{"summary":"validation_error","description":"El cuerpo o los parámetros no cumplen el esquema (campo obligatorio, formato, campo desconocido...). `message` indica el campo.","value":{"error":{"code":"validation_error","message":"phone_number: String should match pattern '^\\+[1-9][0-9]{6,14}$'"}}}},"schema":{"$ref":"#/components/schemas/ErrorSchema"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"429":{"description":"Demasiadas peticiones.\n\n- `rate_limited`: Se superó el límite de peticiones por minuto de la API key o, en `POST /messages`, un tope de envío del remitente (mensajes por minuto, por destinatario y día, destinatarios nuevos por día) o, en Verify, un tope de códigos (espera entre códigos, por teléfono, por remitente) o de códigos erróneos por hora. Espera los segundos de `Retry-After`.","content":{"application/json":{"examples":{"rate_limited":{"summary":"rate_limited","description":"Se superó el límite de peticiones por minuto de la API key o, en `POST /messages`, un tope de envío del remitente (mensajes por minuto, por destinatario y día, destinatarios nuevos por día) o, en Verify, un tope de códigos (espera entre códigos, por teléfono, por remitente) o de códigos erróneos por hora. Espera los segundos de `Retry-After`.","value":{"error":{"code":"rate_limited","message":"Rate limit exceeded. Retry after the indicated number of seconds."}}}},"schema":{"$ref":"#/components/schemas/ErrorSchema"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"},"Retry-After":{"$ref":"#/components/headers/Retry-After"}}},"502":{"description":"Error de un servicio interno.\n\n- `upstream_error`: Un servicio interno de xYago falló o no respondió a tiempo. Reintenta con backoff (en las escrituras, comprueba antes si ya se aplicaron).","content":{"application/json":{"examples":{"upstream_error":{"summary":"upstream_error","description":"Un servicio interno de xYago falló o no respondió a tiempo. Reintenta con backoff (en las escrituras, comprueba antes si ya se aplicaron).","value":{"error":{"code":"upstream_error","message":"An internal service failed to process the request. Try again later."}}}},"schema":{"$ref":"#/components/schemas/ErrorSchema"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}}},"x-codeSamples":[{"lang":"Shell","label":"curl","source":"curl -X GET 'https://api.xyago.com/v1/senders/tiendaexpress' \\\n  -H \"Authorization: Bearer $XYAGO_API_KEY\""},{"lang":"Python","label":"Python","source":"import os\n\nimport requests\n\nresponse = requests.get(\n    \"https://api.xyago.com/v1/senders/tiendaexpress\",\n    headers={\n        \"Authorization\": f\"Bearer {os.environ['XYAGO_API_KEY']}\",\n    },\n    timeout=30,\n)\nresponse.raise_for_status()\nprint(response.headers['X-Request-Id'], response.json())"},{"lang":"JavaScript","label":"JavaScript","source":"const response = await fetch(\"https://api.xyago.com/v1/senders/tiendaexpress\", {\n  method: \"GET\",\n  headers: {\n    Authorization: `Bearer ${process.env.XYAGO_API_KEY}`,\n  },\n});\nconst data = await response.json();\nif (!response.ok) throw new Error(`${data.error.code}: ${data.error.message}`);\nconsole.log(response.headers.get(\"X-Request-Id\"), data);"},{"lang":"C#","label":"C#","source":"using System.Net.Http.Headers;\nusing System.Text;\n\nusing var http = new HttpClient();\nhttp.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(\n    \"Bearer\", Environment.GetEnvironmentVariable(\"XYAGO_API_KEY\"));\n\nvar request = new HttpRequestMessage(new HttpMethod(\"GET\"), \"https://api.xyago.com/v1/senders/tiendaexpress\");\nvar response = await http.SendAsync(request);\nvar body = await response.Content.ReadAsStringAsync();\nConsole.WriteLine($\"{(int)response.StatusCode} {body}\");"}]}},"/templates":{"get":{"tags":["templates"],"summary":"Listar plantillas","description":"Plantillas de la cuenta con su estado de aprobación, más recientes primero. Se\ncrean en el panel y las aprueba xYago. No incluye las versiones sustituidas\n(`superseded`); se pueden consultar por id.\n\nScope: `messages:read`.","operationId":"list_templates","parameters":[{"name":"sender_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":32},{"type":"null"}],"description":"Filtra por remitente (`business_id`).","title":"Sender Id"},"description":"Filtra por remitente (`business_id`)."},{"$ref":"#/components/parameters/XRequestId"}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TemplateList"},"example":{"results":[{"id":"d2f4a6c8-0e1b-4d3f-9a5c-7e9b1d3f5a70","sender_id":"tiendaexpress","name":"pedido_enviado","category":"utility","default_language":"es","languages":{"es":"Hola {{1}}, tu pedido {{2}} va en camino.","en":"Hi {{1}}, your order {{2}} is on its way."},"variables_count":2,"buttons":[{"type":"url","texts":{"es":"Seguir pedido","en":"Track order"},"url":"https://tiendaexpress.example.com/pedidos/{{1}}"},{"type":"quick_reply","texts":{"es":"No lo pedí","en":"Not my order"},"payload":"order_not_mine"}],"version":1,"status":"approved","review_flags":[],"created_at":"2026-11-03T10:00:00Z","updated_at":"2026-11-03T16:45:00Z"}]}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"}}},"401":{"description":"No autenticado.\n\n- `invalid_api_key`: Falta la API key o no es válida, está revocada o ha caducado.","content":{"application/json":{"examples":{"invalid_api_key":{"summary":"invalid_api_key","description":"Falta la API key o no es válida, está revocada o ha caducado.","value":{"error":{"code":"invalid_api_key","message":"Missing or invalid API key."}}}},"schema":{"$ref":"#/components/schemas/ErrorSchema"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"403":{"description":"Prohibido.\n\n- `api_disabled`: El producto (areas, employees...) no está habilitado en el panel para el entorno de la API key (live o test).\n- `endpoint_disabled`: El administrador desactivó este endpoint concreto en el panel.\n- `insufficient_scope`: La API key no tiene el scope que exige el endpoint (p. ej. `employees:write`).\n- `ip_not_allowed`: La IP de origen no está en la lista de IPs permitidas del producto.","content":{"application/json":{"examples":{"api_disabled":{"summary":"api_disabled","description":"El producto (areas, employees...) no está habilitado en el panel para el entorno de la API key (live o test).","value":{"error":{"code":"api_disabled","message":"The 'employees' API is not enabled for this environment."}}},"endpoint_disabled":{"summary":"endpoint_disabled","description":"El administrador desactivó este endpoint concreto en el panel.","value":{"error":{"code":"endpoint_disabled","message":"This endpoint is disabled."}}},"insufficient_scope":{"summary":"insufficient_scope","description":"La API key no tiene el scope que exige el endpoint (p. ej. `employees:write`).","value":{"error":{"code":"insufficient_scope","message":"This API key lacks the 'employees:write' scope."}}},"ip_not_allowed":{"summary":"ip_not_allowed","description":"La IP de origen no está en la lista de IPs permitidas del producto.","value":{"error":{"code":"ip_not_allowed","message":"Requests from this IP are not allowed."}}}},"schema":{"$ref":"#/components/schemas/ErrorSchema"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"422":{"description":"Datos no válidos.\n\n- `validation_error`: El cuerpo o los parámetros no cumplen el esquema (campo obligatorio, formato, campo desconocido...). `message` indica el campo.","content":{"application/json":{"examples":{"validation_error":{"summary":"validation_error","description":"El cuerpo o los parámetros no cumplen el esquema (campo obligatorio, formato, campo desconocido...). `message` indica el campo.","value":{"error":{"code":"validation_error","message":"phone_number: String should match pattern '^\\+[1-9][0-9]{6,14}$'"}}}},"schema":{"$ref":"#/components/schemas/ErrorSchema"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"429":{"description":"Demasiadas peticiones.\n\n- `rate_limited`: Se superó el límite de peticiones por minuto de la API key o, en `POST /messages`, un tope de envío del remitente (mensajes por minuto, por destinatario y día, destinatarios nuevos por día) o, en Verify, un tope de códigos (espera entre códigos, por teléfono, por remitente) o de códigos erróneos por hora. Espera los segundos de `Retry-After`.","content":{"application/json":{"examples":{"rate_limited":{"summary":"rate_limited","description":"Se superó el límite de peticiones por minuto de la API key o, en `POST /messages`, un tope de envío del remitente (mensajes por minuto, por destinatario y día, destinatarios nuevos por día) o, en Verify, un tope de códigos (espera entre códigos, por teléfono, por remitente) o de códigos erróneos por hora. Espera los segundos de `Retry-After`.","value":{"error":{"code":"rate_limited","message":"Rate limit exceeded. Retry after the indicated number of seconds."}}}},"schema":{"$ref":"#/components/schemas/ErrorSchema"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"},"Retry-After":{"$ref":"#/components/headers/Retry-After"}}},"502":{"description":"Error de un servicio interno.\n\n- `upstream_error`: Un servicio interno de xYago falló o no respondió a tiempo. Reintenta con backoff (en las escrituras, comprueba antes si ya se aplicaron).","content":{"application/json":{"examples":{"upstream_error":{"summary":"upstream_error","description":"Un servicio interno de xYago falló o no respondió a tiempo. Reintenta con backoff (en las escrituras, comprueba antes si ya se aplicaron).","value":{"error":{"code":"upstream_error","message":"An internal service failed to process the request. Try again later."}}}},"schema":{"$ref":"#/components/schemas/ErrorSchema"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}}},"x-codeSamples":[{"lang":"Shell","label":"curl","source":"curl -X GET 'https://api.xyago.com/v1/templates' \\\n  -H \"Authorization: Bearer $XYAGO_API_KEY\""},{"lang":"Python","label":"Python","source":"import os\n\nimport requests\n\nresponse = requests.get(\n    \"https://api.xyago.com/v1/templates\",\n    headers={\n        \"Authorization\": f\"Bearer {os.environ['XYAGO_API_KEY']}\",\n    },\n    timeout=30,\n)\nresponse.raise_for_status()\nprint(response.headers['X-Request-Id'], response.json())"},{"lang":"JavaScript","label":"JavaScript","source":"const response = await fetch(\"https://api.xyago.com/v1/templates\", {\n  method: \"GET\",\n  headers: {\n    Authorization: `Bearer ${process.env.XYAGO_API_KEY}`,\n  },\n});\nconst data = await response.json();\nif (!response.ok) throw new Error(`${data.error.code}: ${data.error.message}`);\nconsole.log(response.headers.get(\"X-Request-Id\"), data);"},{"lang":"C#","label":"C#","source":"using System.Net.Http.Headers;\nusing System.Text;\n\nusing var http = new HttpClient();\nhttp.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(\n    \"Bearer\", Environment.GetEnvironmentVariable(\"XYAGO_API_KEY\"));\n\nvar request = new HttpRequestMessage(new HttpMethod(\"GET\"), \"https://api.xyago.com/v1/templates\");\nvar response = await http.SendAsync(request);\nvar body = await response.Content.ReadAsStringAsync();\nConsole.WriteLine($\"{(int)response.StatusCode} {body}\");"}]}},"/templates/{id}":{"get":{"tags":["templates"],"summary":"Consultar una plantilla","description":"Una plantilla de la cuenta (otra → `404`).\n\nScope: `messages:read`.","operationId":"get_template","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid","description":"Id de la plantilla.","title":"Id"},"description":"Id de la plantilla.","examples":{"ejemplo":{"summary":"Ejemplo","value":"d2f4a6c8-0e1b-4d3f-9a5c-7e9b1d3f5a70"}}},{"$ref":"#/components/parameters/XRequestId"}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Template"},"example":{"id":"d2f4a6c8-0e1b-4d3f-9a5c-7e9b1d3f5a70","sender_id":"tiendaexpress","name":"pedido_enviado","category":"utility","default_language":"es","languages":{"es":"Hola {{1}}, tu pedido {{2}} va en camino.","en":"Hi {{1}}, your order {{2}} is on its way."},"variables_count":2,"buttons":[{"type":"url","texts":{"es":"Seguir pedido","en":"Track order"},"url":"https://tiendaexpress.example.com/pedidos/{{1}}"},{"type":"quick_reply","texts":{"es":"No lo pedí","en":"Not my order"},"payload":"order_not_mine"}],"version":1,"status":"approved","review_flags":[],"created_at":"2026-11-03T10:00:00Z","updated_at":"2026-11-03T16:45:00Z"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"}}},"401":{"description":"No autenticado.\n\n- `invalid_api_key`: Falta la API key o no es válida, está revocada o ha caducado.","content":{"application/json":{"examples":{"invalid_api_key":{"summary":"invalid_api_key","description":"Falta la API key o no es válida, está revocada o ha caducado.","value":{"error":{"code":"invalid_api_key","message":"Missing or invalid API key."}}}},"schema":{"$ref":"#/components/schemas/ErrorSchema"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"403":{"description":"Prohibido.\n\n- `api_disabled`: El producto (areas, employees...) no está habilitado en el panel para el entorno de la API key (live o test).\n- `endpoint_disabled`: El administrador desactivó este endpoint concreto en el panel.\n- `insufficient_scope`: La API key no tiene el scope que exige el endpoint (p. ej. `employees:write`).\n- `ip_not_allowed`: La IP de origen no está en la lista de IPs permitidas del producto.","content":{"application/json":{"examples":{"api_disabled":{"summary":"api_disabled","description":"El producto (areas, employees...) no está habilitado en el panel para el entorno de la API key (live o test).","value":{"error":{"code":"api_disabled","message":"The 'employees' API is not enabled for this environment."}}},"endpoint_disabled":{"summary":"endpoint_disabled","description":"El administrador desactivó este endpoint concreto en el panel.","value":{"error":{"code":"endpoint_disabled","message":"This endpoint is disabled."}}},"insufficient_scope":{"summary":"insufficient_scope","description":"La API key no tiene el scope que exige el endpoint (p. ej. `employees:write`).","value":{"error":{"code":"insufficient_scope","message":"This API key lacks the 'employees:write' scope."}}},"ip_not_allowed":{"summary":"ip_not_allowed","description":"La IP de origen no está en la lista de IPs permitidas del producto.","value":{"error":{"code":"ip_not_allowed","message":"Requests from this IP are not allowed."}}}},"schema":{"$ref":"#/components/schemas/ErrorSchema"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"404":{"description":"No encontrado.\n\n- `not_found`: El recurso no existe o pertenece a otra empresa (nunca se distingue).","content":{"application/json":{"examples":{"not_found":{"summary":"not_found","description":"El recurso no existe o pertenece a otra empresa (nunca se distingue).","value":{"error":{"code":"not_found","message":"Employee not found."}}}},"schema":{"$ref":"#/components/schemas/ErrorSchema"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"422":{"description":"Datos no válidos.\n\n- `validation_error`: El cuerpo o los parámetros no cumplen el esquema (campo obligatorio, formato, campo desconocido...). `message` indica el campo.","content":{"application/json":{"examples":{"validation_error":{"summary":"validation_error","description":"El cuerpo o los parámetros no cumplen el esquema (campo obligatorio, formato, campo desconocido...). `message` indica el campo.","value":{"error":{"code":"validation_error","message":"phone_number: String should match pattern '^\\+[1-9][0-9]{6,14}$'"}}}},"schema":{"$ref":"#/components/schemas/ErrorSchema"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"429":{"description":"Demasiadas peticiones.\n\n- `rate_limited`: Se superó el límite de peticiones por minuto de la API key o, en `POST /messages`, un tope de envío del remitente (mensajes por minuto, por destinatario y día, destinatarios nuevos por día) o, en Verify, un tope de códigos (espera entre códigos, por teléfono, por remitente) o de códigos erróneos por hora. Espera los segundos de `Retry-After`.","content":{"application/json":{"examples":{"rate_limited":{"summary":"rate_limited","description":"Se superó el límite de peticiones por minuto de la API key o, en `POST /messages`, un tope de envío del remitente (mensajes por minuto, por destinatario y día, destinatarios nuevos por día) o, en Verify, un tope de códigos (espera entre códigos, por teléfono, por remitente) o de códigos erróneos por hora. Espera los segundos de `Retry-After`.","value":{"error":{"code":"rate_limited","message":"Rate limit exceeded. Retry after the indicated number of seconds."}}}},"schema":{"$ref":"#/components/schemas/ErrorSchema"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"},"Retry-After":{"$ref":"#/components/headers/Retry-After"}}},"502":{"description":"Error de un servicio interno.\n\n- `upstream_error`: Un servicio interno de xYago falló o no respondió a tiempo. Reintenta con backoff (en las escrituras, comprueba antes si ya se aplicaron).","content":{"application/json":{"examples":{"upstream_error":{"summary":"upstream_error","description":"Un servicio interno de xYago falló o no respondió a tiempo. Reintenta con backoff (en las escrituras, comprueba antes si ya se aplicaron).","value":{"error":{"code":"upstream_error","message":"An internal service failed to process the request. Try again later."}}}},"schema":{"$ref":"#/components/schemas/ErrorSchema"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}}},"x-codeSamples":[{"lang":"Shell","label":"curl","source":"curl -X GET 'https://api.xyago.com/v1/templates/d2f4a6c8-0e1b-4d3f-9a5c-7e9b1d3f5a70' \\\n  -H \"Authorization: Bearer $XYAGO_API_KEY\""},{"lang":"Python","label":"Python","source":"import os\n\nimport requests\n\nresponse = requests.get(\n    \"https://api.xyago.com/v1/templates/d2f4a6c8-0e1b-4d3f-9a5c-7e9b1d3f5a70\",\n    headers={\n        \"Authorization\": f\"Bearer {os.environ['XYAGO_API_KEY']}\",\n    },\n    timeout=30,\n)\nresponse.raise_for_status()\nprint(response.headers['X-Request-Id'], response.json())"},{"lang":"JavaScript","label":"JavaScript","source":"const response = await fetch(\"https://api.xyago.com/v1/templates/d2f4a6c8-0e1b-4d3f-9a5c-7e9b1d3f5a70\", {\n  method: \"GET\",\n  headers: {\n    Authorization: `Bearer ${process.env.XYAGO_API_KEY}`,\n  },\n});\nconst data = await response.json();\nif (!response.ok) throw new Error(`${data.error.code}: ${data.error.message}`);\nconsole.log(response.headers.get(\"X-Request-Id\"), data);"},{"lang":"C#","label":"C#","source":"using System.Net.Http.Headers;\nusing System.Text;\n\nusing var http = new HttpClient();\nhttp.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(\n    \"Bearer\", Environment.GetEnvironmentVariable(\"XYAGO_API_KEY\"));\n\nvar request = new HttpRequestMessage(new HttpMethod(\"GET\"), \"https://api.xyago.com/v1/templates/d2f4a6c8-0e1b-4d3f-9a5c-7e9b1d3f5a70\");\nvar response = await http.SendAsync(request);\nvar body = await response.Content.ReadAsStringAsync();\nConsole.WriteLine($\"{(int)response.StatusCode} {body}\");"}]}},"/verify/start":{"post":{"tags":["verify"],"summary":"Enviar un código de verificación","description":"Genera un código de 6 dígitos (válido 10 minutos, 5 intentos) y lo envía al\nteléfono `to` por el chat de xYago, en nombre del remitente `sender_id`, con un\ntexto fijo de xYago en el idioma `language` (la empresa no puede cambiarlo). El\ncódigo nunca se devuelve: el usuario lo escribe en tu app y lo compruebas con\n`POST /verify/check`.\n\n- Si el teléfono no usa xYago responde `404 recipient_not_on_xyago` al momento\n  (sin espera ni reintento): envía el código por tu otro canal (p. ej. SMS).\n- Si el usuario no puede recibir mensajes del remitente: `409 undeliverable`.\n- Un nuevo código al mismo teléfono sustituye al anterior (que pasa a\n  `canceled`). Entre dos códigos al mismo teléfono hay que esperar 30 s; además\n  hay topes por teléfono y por remitente → `429 rate_limited` con `Retry-After`.\n- Si la respuesta se pierde (timeout, `502`), repite con la misma\n  `Idempotency-Key` y el mismo cuerpo: si el código ya llegó al chat recibes la\n  misma verificación. Si no llegó, se envía uno nuevo (el anterior deja de\n  valer) con la misma espera de 30 s desde el intento anterior, los mismos\n  topes (`429 rate_limited` con `Retry-After`) y la pausa de Verify.\n- Sandbox (`xy_test_`): solo a los números de prueba confirmados en el panel.\n\nScope: `verify:write`.","operationId":"start_verification","parameters":[{"name":"Idempotency-Key","in":"header","required":true,"schema":{"type":"string","pattern":"^[A-Za-z0-9._:-]{8,128}$","description":"Obligatoria. Clave única por código pedido (8-128 caracteres `[A-Za-z0-9._:-]`, p. ej. un UUID). Un reintento con la misma clave nunca envía dos códigos.","title":"Idempotency-Key"},"description":"Obligatoria. Clave única por código pedido (8-128 caracteres `[A-Za-z0-9._:-]`, p. ej. un UUID). Un reintento con la misma clave nunca envía dos códigos.","examples":{"ejemplo":{"summary":"Ejemplo","value":"login-7f3a9c2e-0001"}}},{"$ref":"#/components/parameters/XRequestId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VerificationStart"},"examples":{"login":{"summary":"Código para iniciar sesión","value":{"to":"+573005550123","sender_id":"tiendaexpress","language":"es","reference":"login-CLI-000123"}}}}}},"responses":{"201":{"description":"Código enviado al chat de xYago del usuario (`pending`, `delivery_status: sent`). Una repetición con la misma `Idempotency-Key` y el mismo cuerpo responde `200` con la verificación original y la cabecera `Idempotent-Replayed: true`. La respuesta nunca contiene el código.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Verification"},"example":{"id":"3e8b1f6a-9c2d-4a7e-b5f0-6d1c2a3b4e59","object":"verification","sender_id":"tiendaexpress","to_masked":"+57 ••• ••• 0123","channel":"xyago_chat","language":"es","reference":"login-CLI-000123","status":"pending","delivery_status":"sent","attempts":0,"max_attempts":5,"expires_at":"2026-11-04T15:10:00Z","created_at":"2026-11-04T15:00:00Z"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"}}},"401":{"description":"No autenticado.\n\n- `invalid_api_key`: Falta la API key o no es válida, está revocada o ha caducado.","content":{"application/json":{"examples":{"invalid_api_key":{"summary":"invalid_api_key","description":"Falta la API key o no es válida, está revocada o ha caducado.","value":{"error":{"code":"invalid_api_key","message":"Missing or invalid API key."}}}},"schema":{"$ref":"#/components/schemas/ErrorSchema"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"403":{"description":"Prohibido.\n\n- `api_disabled`: El producto (areas, employees...) no está habilitado en el panel para el entorno de la API key (live o test).\n- `endpoint_disabled`: El administrador desactivó este endpoint concreto en el panel.\n- `insufficient_scope`: La API key no tiene el scope que exige el endpoint (p. ej. `employees:write`).\n- `ip_not_allowed`: La IP de origen no está en la lista de IPs permitidas del producto.\n- `verify_paused`: xYago pausó Verify para el remitente (demasiados teléfonos sin xYago). Las verificaciones ya iniciadas se pueden seguir comprobando. Contacta con xYago.","content":{"application/json":{"examples":{"api_disabled":{"summary":"api_disabled","description":"El producto (areas, employees...) no está habilitado en el panel para el entorno de la API key (live o test).","value":{"error":{"code":"api_disabled","message":"The 'employees' API is not enabled for this environment."}}},"endpoint_disabled":{"summary":"endpoint_disabled","description":"El administrador desactivó este endpoint concreto en el panel.","value":{"error":{"code":"endpoint_disabled","message":"This endpoint is disabled."}}},"insufficient_scope":{"summary":"insufficient_scope","description":"La API key no tiene el scope que exige el endpoint (p. ej. `employees:write`).","value":{"error":{"code":"insufficient_scope","message":"This API key lacks the 'employees:write' scope."}}},"ip_not_allowed":{"summary":"ip_not_allowed","description":"La IP de origen no está en la lista de IPs permitidas del producto.","value":{"error":{"code":"ip_not_allowed","message":"Requests from this IP are not allowed."}}},"verify_paused":{"summary":"verify_paused","description":"xYago pausó Verify para el remitente (demasiados teléfonos sin xYago). Las verificaciones ya iniciadas se pueden seguir comprobando. Contacta con xYago.","value":{"error":{"code":"verify_paused","message":"Verify is paused for this sender. Contact xYago."}}}},"schema":{"$ref":"#/components/schemas/ErrorSchema"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"404":{"description":"No encontrado.\n\n- `sender_not_found`: El `sender_id` no es un remitente de tu cuenta.\n- `recipient_not_on_xyago`: `POST /verify/start`: el teléfono no usa xYago. No se envió nada ni se reintentará: envía el código por tu otro canal (p. ej. SMS).","content":{"application/json":{"examples":{"sender_not_found":{"summary":"sender_not_found","description":"El `sender_id` no es un remitente de tu cuenta.","value":{"error":{"code":"sender_not_found","message":"Sender not found."}}},"recipient_not_on_xyago":{"summary":"recipient_not_on_xyago","description":"`POST /verify/start`: el teléfono no usa xYago. No se envió nada ni se reintentará: envía el código por tu otro canal (p. ej. SMS).","value":{"error":{"code":"recipient_not_on_xyago","message":"The recipient does not use xYago. Send the code by another channel."}}}},"schema":{"$ref":"#/components/schemas/ErrorSchema"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"409":{"description":"Conflicto.\n\n- `sender_not_approved`: El remitente aún no está aprobado por xYago (en `live` solo envían remitentes aprobados).\n- `sender_suspended`: xYago suspendió el remitente (o la cuenta). Contacta con xYago.\n- `idempotency_key_reused`: Ya usaste esa `Idempotency-Key` con otro cuerpo. Usa una clave nueva por envío distinto.\n- `undeliverable`: El mensaje no se puede entregar a este destinatario. No reintentes.","content":{"application/json":{"examples":{"sender_not_approved":{"summary":"sender_not_approved","description":"El remitente aún no está aprobado por xYago (en `live` solo envían remitentes aprobados).","value":{"error":{"code":"sender_not_approved","message":"The sender is not approved yet."}}},"sender_suspended":{"summary":"sender_suspended","description":"xYago suspendió el remitente (o la cuenta). Contacta con xYago.","value":{"error":{"code":"sender_suspended","message":"The sender is suspended."}}},"idempotency_key_reused":{"summary":"idempotency_key_reused","description":"Ya usaste esa `Idempotency-Key` con otro cuerpo. Usa una clave nueva por envío distinto.","value":{"error":{"code":"idempotency_key_reused","message":"Idempotency-Key was already used with a different request."}}},"undeliverable":{"summary":"undeliverable","description":"El mensaje no se puede entregar a este destinatario. No reintentes.","value":{"error":{"code":"undeliverable","message":"The message cannot be delivered to this recipient."}}}},"schema":{"$ref":"#/components/schemas/ErrorSchema"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"422":{"description":"Datos no válidos.\n\n- `validation_error`: El cuerpo o los parámetros no cumplen el esquema (campo obligatorio, formato, campo desconocido...). `message` indica el campo.\n- `invalid_phone`: `to` no es un teléfono E.164 válido (al menos 10 dígitos).\n- `sandbox_recipient_not_allowed`: Con una clave `xy_test_` solo se envía a los números de prueba confirmados en el panel.\n- `invalid_message`: xYago rechazó el contenido del mensaje.","content":{"application/json":{"examples":{"validation_error":{"summary":"validation_error","description":"El cuerpo o los parámetros no cumplen el esquema (campo obligatorio, formato, campo desconocido...). `message` indica el campo.","value":{"error":{"code":"validation_error","message":"phone_number: String should match pattern '^\\+[1-9][0-9]{6,14}$'"}}},"invalid_phone":{"summary":"invalid_phone","description":"`to` no es un teléfono E.164 válido (al menos 10 dígitos).","value":{"error":{"code":"invalid_phone","message":"Phone number must be in E.164 format (e.g. +573001234567)."}}},"sandbox_recipient_not_allowed":{"summary":"sandbox_recipient_not_allowed","description":"Con una clave `xy_test_` solo se envía a los números de prueba confirmados en el panel.","value":{"error":{"code":"sandbox_recipient_not_allowed","message":"In sandbox, messages can only be sent to confirmed test numbers."}}},"invalid_message":{"summary":"invalid_message","description":"xYago rechazó el contenido del mensaje.","value":{"error":{"code":"invalid_message","message":"The chat rejected the message content."}}}},"schema":{"$ref":"#/components/schemas/ErrorSchema"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"429":{"description":"Demasiadas peticiones.\n\n- `rate_limited`: Se superó el límite de peticiones por minuto de la API key o, en `POST /messages`, un tope de envío del remitente (mensajes por minuto, por destinatario y día, destinatarios nuevos por día) o, en Verify, un tope de códigos (espera entre códigos, por teléfono, por remitente) o de códigos erróneos por hora. Espera los segundos de `Retry-After`.","content":{"application/json":{"examples":{"rate_limited":{"summary":"rate_limited","description":"Se superó el límite de peticiones por minuto de la API key o, en `POST /messages`, un tope de envío del remitente (mensajes por minuto, por destinatario y día, destinatarios nuevos por día) o, en Verify, un tope de códigos (espera entre códigos, por teléfono, por remitente) o de códigos erróneos por hora. Espera los segundos de `Retry-After`.","value":{"error":{"code":"rate_limited","message":"Rate limit exceeded. Retry after the indicated number of seconds."}}}},"schema":{"$ref":"#/components/schemas/ErrorSchema"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"},"Retry-After":{"$ref":"#/components/headers/Retry-After"}}},"502":{"description":"Error de un servicio interno.\n\n- `upstream_error`: Un servicio interno de xYago falló o no respondió a tiempo. Reintenta con backoff (en las escrituras, comprueba antes si ya se aplicaron).","content":{"application/json":{"examples":{"upstream_error":{"summary":"upstream_error","description":"Un servicio interno de xYago falló o no respondió a tiempo. Reintenta con backoff (en las escrituras, comprueba antes si ya se aplicaron).","value":{"error":{"code":"upstream_error","message":"An internal service failed to process the request. Try again later."}}}},"schema":{"$ref":"#/components/schemas/ErrorSchema"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"503":{"description":"Servicio no disponible.\n\n- `limits_unavailable`: Los límites de envío no se pueden comprobar ahora mismo; por seguridad no se envía. Reintenta con backoff y la misma `Idempotency-Key`.\n- `messaging_not_configured`: La mensajería no está disponible temporalmente. Contacta con soporte.","content":{"application/json":{"examples":{"limits_unavailable":{"summary":"limits_unavailable","description":"Los límites de envío no se pueden comprobar ahora mismo; por seguridad no se envía. Reintenta con backoff y la misma `Idempotency-Key`.","value":{"error":{"code":"limits_unavailable","message":"Sending is temporarily unavailable. Retry later."}}},"messaging_not_configured":{"summary":"messaging_not_configured","description":"La mensajería no está disponible temporalmente. Contacta con soporte.","value":{"error":{"code":"messaging_not_configured","message":"Business messaging is not configured."}}}},"schema":{"$ref":"#/components/schemas/ErrorSchema"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}}},"x-codeSamples":[{"lang":"Shell","label":"curl","source":"curl -X POST 'https://api.xyago.com/v1/verify/start' \\\n  -H \"Authorization: Bearer $XYAGO_API_KEY\" \\\n  -H \"Idempotency-Key: login-7f3a9c2e-0001\" \\\n  -H \"Content-Type: application/json\" \\\n  -d @- <<'JSON'\n{\n  \"to\": \"+573005550123\",\n  \"sender_id\": \"tiendaexpress\",\n  \"language\": \"es\",\n  \"reference\": \"login-CLI-000123\"\n}\nJSON"},{"lang":"Python","label":"Python","source":"import os\n\nimport requests\n\npayload = {\n    \"to\": \"+573005550123\",\n    \"sender_id\": \"tiendaexpress\",\n    \"language\": \"es\",\n    \"reference\": \"login-CLI-000123\"\n}\n\nresponse = requests.post(\n    \"https://api.xyago.com/v1/verify/start\",\n    headers={\n        \"Authorization\": f\"Bearer {os.environ['XYAGO_API_KEY']}\",\n        \"Idempotency-Key\": \"login-7f3a9c2e-0001\",\n    },\n    json=payload,\n    timeout=30,\n)\nresponse.raise_for_status()\nprint(response.headers['X-Request-Id'], response.json())"},{"lang":"JavaScript","label":"JavaScript","source":"const payload = {\n  \"to\": \"+573005550123\",\n  \"sender_id\": \"tiendaexpress\",\n  \"language\": \"es\",\n  \"reference\": \"login-CLI-000123\"\n};\n\nconst response = await fetch(\"https://api.xyago.com/v1/verify/start\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.XYAGO_API_KEY}`,\n    \"Idempotency-Key\": \"login-7f3a9c2e-0001\",\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify(payload),\n});\nconst data = await response.json();\nif (!response.ok) throw new Error(`${data.error.code}: ${data.error.message}`);\nconsole.log(response.headers.get(\"X-Request-Id\"), data);"},{"lang":"C#","label":"C#","source":"using System.Net.Http.Headers;\nusing System.Text;\n\nusing var http = new HttpClient();\nhttp.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(\n    \"Bearer\", Environment.GetEnvironmentVariable(\"XYAGO_API_KEY\"));\n\nvar request = new HttpRequestMessage(new HttpMethod(\"POST\"), \"https://api.xyago.com/v1/verify/start\");\nrequest.Headers.Add(\"Idempotency-Key\", \"login-7f3a9c2e-0001\");\nvar payload = \"\"\"\n{\n  \"to\": \"+573005550123\",\n  \"sender_id\": \"tiendaexpress\",\n  \"language\": \"es\",\n  \"reference\": \"login-CLI-000123\"\n}\n\"\"\";\nrequest.Content = new StringContent(payload, Encoding.UTF8, \"application/json\");\nvar response = await http.SendAsync(request);\nvar body = await response.Content.ReadAsStringAsync();\nConsole.WriteLine($\"{(int)response.StatusCode} {body}\");"}]}},"/verify/check":{"post":{"tags":["verify"],"summary":"Comprobar un código","description":"Comprueba el código que escribió el usuario. Correcto → `valid: true`,\n`status: verified` (un solo uso: otra comprobación responde\n`409 verification_not_pending`). Erróneo → `200` con `valid: false` y\n`attempts_remaining`; al quinto error la verificación pasa a `too_many_attempts`.\nCaducada (10 min) → `409 verification_expired`. Una verificación de otra cuenta\no del otro entorno → `404`. Hay además un tope de códigos erróneos por teléfono\ny hora (`429 rate_limited`).\n\nScope: `verify:write`.","operationId":"check_verification","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VerificationCheck"},"examples":{"codigo":{"summary":"Código escrito por el usuario","value":{"verification_id":"3e8b1f6a-9c2d-4a7e-b5f0-6d1c2a3b4e59","code":"482913"}}}}},"required":true},"responses":{"200":{"description":"Resultado de la comprobación: `valid: true` (verificada; el código ya no sirve) o `valid: false` con los intentos que quedan.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VerificationCheckResult"},"example":{"id":"3e8b1f6a-9c2d-4a7e-b5f0-6d1c2a3b4e59","valid":true,"status":"verified","attempts_remaining":0}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"}}},"401":{"description":"No autenticado.\n\n- `invalid_api_key`: Falta la API key o no es válida, está revocada o ha caducado.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorSchema"},"examples":{"invalid_api_key":{"summary":"invalid_api_key","description":"Falta la API key o no es válida, está revocada o ha caducado.","value":{"error":{"code":"invalid_api_key","message":"Missing or invalid API key."}}}}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"403":{"description":"Prohibido.\n\n- `api_disabled`: El producto (areas, employees...) no está habilitado en el panel para el entorno de la API key (live o test).\n- `endpoint_disabled`: El administrador desactivó este endpoint concreto en el panel.\n- `insufficient_scope`: La API key no tiene el scope que exige el endpoint (p. ej. `employees:write`).\n- `ip_not_allowed`: La IP de origen no está en la lista de IPs permitidas del producto.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorSchema"},"examples":{"api_disabled":{"summary":"api_disabled","description":"El producto (areas, employees...) no está habilitado en el panel para el entorno de la API key (live o test).","value":{"error":{"code":"api_disabled","message":"The 'employees' API is not enabled for this environment."}}},"endpoint_disabled":{"summary":"endpoint_disabled","description":"El administrador desactivó este endpoint concreto en el panel.","value":{"error":{"code":"endpoint_disabled","message":"This endpoint is disabled."}}},"insufficient_scope":{"summary":"insufficient_scope","description":"La API key no tiene el scope que exige el endpoint (p. ej. `employees:write`).","value":{"error":{"code":"insufficient_scope","message":"This API key lacks the 'employees:write' scope."}}},"ip_not_allowed":{"summary":"ip_not_allowed","description":"La IP de origen no está en la lista de IPs permitidas del producto.","value":{"error":{"code":"ip_not_allowed","message":"Requests from this IP are not allowed."}}}}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"404":{"description":"No encontrado.\n\n- `not_found`: El recurso no existe o pertenece a otra empresa (nunca se distingue).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorSchema"},"examples":{"not_found":{"summary":"not_found","description":"El recurso no existe o pertenece a otra empresa (nunca se distingue).","value":{"error":{"code":"not_found","message":"Employee not found."}}}}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"409":{"description":"Conflicto.\n\n- `verification_expired`: El código caducó (10 minutos). Inicia una verificación nueva.\n- `max_attempts_reached`: Se agotaron los 5 intentos de la verificación. Inicia una nueva.\n- `verification_not_pending`: La verificación ya no admite códigos: ya se verificó (un solo uso), se sustituyó por otra más reciente (`canceled`) o no se pudo enviar (`failed`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorSchema"},"examples":{"verification_expired":{"summary":"verification_expired","description":"El código caducó (10 minutos). Inicia una verificación nueva.","value":{"error":{"code":"verification_expired","message":"The verification expired. Start a new one."}}},"max_attempts_reached":{"summary":"max_attempts_reached","description":"Se agotaron los 5 intentos de la verificación. Inicia una nueva.","value":{"error":{"code":"max_attempts_reached","message":"Too many wrong codes. Start a new verification."}}},"verification_not_pending":{"summary":"verification_not_pending","description":"La verificación ya no admite códigos: ya se verificó (un solo uso), se sustituyó por otra más reciente (`canceled`) o no se pudo enviar (`failed`).","value":{"error":{"code":"verification_not_pending","message":"The verification is verified."}}}}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"422":{"description":"Datos no válidos.\n\n- `validation_error`: El cuerpo o los parámetros no cumplen el esquema (campo obligatorio, formato, campo desconocido...). `message` indica el campo.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorSchema"},"examples":{"validation_error":{"summary":"validation_error","description":"El cuerpo o los parámetros no cumplen el esquema (campo obligatorio, formato, campo desconocido...). `message` indica el campo.","value":{"error":{"code":"validation_error","message":"phone_number: String should match pattern '^\\+[1-9][0-9]{6,14}$'"}}}}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"429":{"description":"Demasiadas peticiones.\n\n- `rate_limited`: Se superó el límite de peticiones por minuto de la API key o, en `POST /messages`, un tope de envío del remitente (mensajes por minuto, por destinatario y día, destinatarios nuevos por día) o, en Verify, un tope de códigos (espera entre códigos, por teléfono, por remitente) o de códigos erróneos por hora. Espera los segundos de `Retry-After`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorSchema"},"examples":{"rate_limited":{"summary":"rate_limited","description":"Se superó el límite de peticiones por minuto de la API key o, en `POST /messages`, un tope de envío del remitente (mensajes por minuto, por destinatario y día, destinatarios nuevos por día) o, en Verify, un tope de códigos (espera entre códigos, por teléfono, por remitente) o de códigos erróneos por hora. Espera los segundos de `Retry-After`.","value":{"error":{"code":"rate_limited","message":"Rate limit exceeded. Retry after the indicated number of seconds."}}}}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"},"Retry-After":{"$ref":"#/components/headers/Retry-After"}}},"502":{"description":"Error de un servicio interno.\n\n- `upstream_error`: Un servicio interno de xYago falló o no respondió a tiempo. Reintenta con backoff (en las escrituras, comprueba antes si ya se aplicaron).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorSchema"},"examples":{"upstream_error":{"summary":"upstream_error","description":"Un servicio interno de xYago falló o no respondió a tiempo. Reintenta con backoff (en las escrituras, comprueba antes si ya se aplicaron).","value":{"error":{"code":"upstream_error","message":"An internal service failed to process the request. Try again later."}}}}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}}},"parameters":[{"$ref":"#/components/parameters/XRequestId"}],"x-codeSamples":[{"lang":"Shell","label":"curl","source":"curl -X POST 'https://api.xyago.com/v1/verify/check' \\\n  -H \"Authorization: Bearer $XYAGO_API_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d @- <<'JSON'\n{\n  \"verification_id\": \"3e8b1f6a-9c2d-4a7e-b5f0-6d1c2a3b4e59\",\n  \"code\": \"482913\"\n}\nJSON"},{"lang":"Python","label":"Python","source":"import os\n\nimport requests\n\npayload = {\n    \"verification_id\": \"3e8b1f6a-9c2d-4a7e-b5f0-6d1c2a3b4e59\",\n    \"code\": \"482913\"\n}\n\nresponse = requests.post(\n    \"https://api.xyago.com/v1/verify/check\",\n    headers={\n        \"Authorization\": f\"Bearer {os.environ['XYAGO_API_KEY']}\",\n    },\n    json=payload,\n    timeout=30,\n)\nresponse.raise_for_status()\nprint(response.headers['X-Request-Id'], response.json())"},{"lang":"JavaScript","label":"JavaScript","source":"const payload = {\n  \"verification_id\": \"3e8b1f6a-9c2d-4a7e-b5f0-6d1c2a3b4e59\",\n  \"code\": \"482913\"\n};\n\nconst response = await fetch(\"https://api.xyago.com/v1/verify/check\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.XYAGO_API_KEY}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify(payload),\n});\nconst data = await response.json();\nif (!response.ok) throw new Error(`${data.error.code}: ${data.error.message}`);\nconsole.log(response.headers.get(\"X-Request-Id\"), data);"},{"lang":"C#","label":"C#","source":"using System.Net.Http.Headers;\nusing System.Text;\n\nusing var http = new HttpClient();\nhttp.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(\n    \"Bearer\", Environment.GetEnvironmentVariable(\"XYAGO_API_KEY\"));\n\nvar request = new HttpRequestMessage(new HttpMethod(\"POST\"), \"https://api.xyago.com/v1/verify/check\");\nvar payload = \"\"\"\n{\n  \"verification_id\": \"3e8b1f6a-9c2d-4a7e-b5f0-6d1c2a3b4e59\",\n  \"code\": \"482913\"\n}\n\"\"\";\nrequest.Content = new StringContent(payload, Encoding.UTF8, \"application/json\");\nvar response = await http.SendAsync(request);\nvar body = await response.Content.ReadAsStringAsync();\nConsole.WriteLine($\"{(int)response.StatusCode} {body}\");"}]}},"/verify/{id}":{"get":{"tags":["verify"],"summary":"Consultar una verificación","description":"Estado actual de una verificación (nunca incluye el código). Una de otra cuenta o\ndel otro entorno responde `404`.\n\nScope: `verify:read`.","operationId":"get_verification","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid","description":"Id de la verificación (`id` de `POST /verify/start`).","title":"Id"},"description":"Id de la verificación (`id` de `POST /verify/start`).","examples":{"ejemplo":{"summary":"Ejemplo","value":"3e8b1f6a-9c2d-4a7e-b5f0-6d1c2a3b4e59"}}},{"$ref":"#/components/parameters/XRequestId"}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Verification"},"example":{"id":"3e8b1f6a-9c2d-4a7e-b5f0-6d1c2a3b4e59","object":"verification","sender_id":"tiendaexpress","to_masked":"+57 ••• ••• 0123","channel":"xyago_chat","language":"es","reference":"login-CLI-000123","status":"verified","delivery_status":"read","attempts":1,"max_attempts":5,"expires_at":"2026-11-04T15:10:00Z","verified_at":"2026-11-04T15:01:12Z","created_at":"2026-11-04T15:00:00Z"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"}}},"401":{"description":"No autenticado.\n\n- `invalid_api_key`: Falta la API key o no es válida, está revocada o ha caducado.","content":{"application/json":{"examples":{"invalid_api_key":{"summary":"invalid_api_key","description":"Falta la API key o no es válida, está revocada o ha caducado.","value":{"error":{"code":"invalid_api_key","message":"Missing or invalid API key."}}}},"schema":{"$ref":"#/components/schemas/ErrorSchema"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"403":{"description":"Prohibido.\n\n- `api_disabled`: El producto (areas, employees...) no está habilitado en el panel para el entorno de la API key (live o test).\n- `endpoint_disabled`: El administrador desactivó este endpoint concreto en el panel.\n- `insufficient_scope`: La API key no tiene el scope que exige el endpoint (p. ej. `employees:write`).\n- `ip_not_allowed`: La IP de origen no está en la lista de IPs permitidas del producto.","content":{"application/json":{"examples":{"api_disabled":{"summary":"api_disabled","description":"El producto (areas, employees...) no está habilitado en el panel para el entorno de la API key (live o test).","value":{"error":{"code":"api_disabled","message":"The 'employees' API is not enabled for this environment."}}},"endpoint_disabled":{"summary":"endpoint_disabled","description":"El administrador desactivó este endpoint concreto en el panel.","value":{"error":{"code":"endpoint_disabled","message":"This endpoint is disabled."}}},"insufficient_scope":{"summary":"insufficient_scope","description":"La API key no tiene el scope que exige el endpoint (p. ej. `employees:write`).","value":{"error":{"code":"insufficient_scope","message":"This API key lacks the 'employees:write' scope."}}},"ip_not_allowed":{"summary":"ip_not_allowed","description":"La IP de origen no está en la lista de IPs permitidas del producto.","value":{"error":{"code":"ip_not_allowed","message":"Requests from this IP are not allowed."}}}},"schema":{"$ref":"#/components/schemas/ErrorSchema"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"404":{"description":"No encontrado.\n\n- `not_found`: El recurso no existe o pertenece a otra empresa (nunca se distingue).","content":{"application/json":{"examples":{"not_found":{"summary":"not_found","description":"El recurso no existe o pertenece a otra empresa (nunca se distingue).","value":{"error":{"code":"not_found","message":"Employee not found."}}}},"schema":{"$ref":"#/components/schemas/ErrorSchema"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"422":{"description":"Datos no válidos.\n\n- `validation_error`: El cuerpo o los parámetros no cumplen el esquema (campo obligatorio, formato, campo desconocido...). `message` indica el campo.","content":{"application/json":{"examples":{"validation_error":{"summary":"validation_error","description":"El cuerpo o los parámetros no cumplen el esquema (campo obligatorio, formato, campo desconocido...). `message` indica el campo.","value":{"error":{"code":"validation_error","message":"phone_number: String should match pattern '^\\+[1-9][0-9]{6,14}$'"}}}},"schema":{"$ref":"#/components/schemas/ErrorSchema"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"429":{"description":"Demasiadas peticiones.\n\n- `rate_limited`: Se superó el límite de peticiones por minuto de la API key o, en `POST /messages`, un tope de envío del remitente (mensajes por minuto, por destinatario y día, destinatarios nuevos por día) o, en Verify, un tope de códigos (espera entre códigos, por teléfono, por remitente) o de códigos erróneos por hora. Espera los segundos de `Retry-After`.","content":{"application/json":{"examples":{"rate_limited":{"summary":"rate_limited","description":"Se superó el límite de peticiones por minuto de la API key o, en `POST /messages`, un tope de envío del remitente (mensajes por minuto, por destinatario y día, destinatarios nuevos por día) o, en Verify, un tope de códigos (espera entre códigos, por teléfono, por remitente) o de códigos erróneos por hora. Espera los segundos de `Retry-After`.","value":{"error":{"code":"rate_limited","message":"Rate limit exceeded. Retry after the indicated number of seconds."}}}},"schema":{"$ref":"#/components/schemas/ErrorSchema"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"},"Retry-After":{"$ref":"#/components/headers/Retry-After"}}},"502":{"description":"Error de un servicio interno.\n\n- `upstream_error`: Un servicio interno de xYago falló o no respondió a tiempo. Reintenta con backoff (en las escrituras, comprueba antes si ya se aplicaron).","content":{"application/json":{"examples":{"upstream_error":{"summary":"upstream_error","description":"Un servicio interno de xYago falló o no respondió a tiempo. Reintenta con backoff (en las escrituras, comprueba antes si ya se aplicaron).","value":{"error":{"code":"upstream_error","message":"An internal service failed to process the request. Try again later."}}}},"schema":{"$ref":"#/components/schemas/ErrorSchema"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}}},"x-codeSamples":[{"lang":"Shell","label":"curl","source":"curl -X GET 'https://api.xyago.com/v1/verify/3e8b1f6a-9c2d-4a7e-b5f0-6d1c2a3b4e59' \\\n  -H \"Authorization: Bearer $XYAGO_API_KEY\""},{"lang":"Python","label":"Python","source":"import os\n\nimport requests\n\nresponse = requests.get(\n    \"https://api.xyago.com/v1/verify/3e8b1f6a-9c2d-4a7e-b5f0-6d1c2a3b4e59\",\n    headers={\n        \"Authorization\": f\"Bearer {os.environ['XYAGO_API_KEY']}\",\n    },\n    timeout=30,\n)\nresponse.raise_for_status()\nprint(response.headers['X-Request-Id'], response.json())"},{"lang":"JavaScript","label":"JavaScript","source":"const response = await fetch(\"https://api.xyago.com/v1/verify/3e8b1f6a-9c2d-4a7e-b5f0-6d1c2a3b4e59\", {\n  method: \"GET\",\n  headers: {\n    Authorization: `Bearer ${process.env.XYAGO_API_KEY}`,\n  },\n});\nconst data = await response.json();\nif (!response.ok) throw new Error(`${data.error.code}: ${data.error.message}`);\nconsole.log(response.headers.get(\"X-Request-Id\"), data);"},{"lang":"C#","label":"C#","source":"using System.Net.Http.Headers;\nusing System.Text;\n\nusing var http = new HttpClient();\nhttp.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(\n    \"Bearer\", Environment.GetEnvironmentVariable(\"XYAGO_API_KEY\"));\n\nvar request = new HttpRequestMessage(new HttpMethod(\"GET\"), \"https://api.xyago.com/v1/verify/3e8b1f6a-9c2d-4a7e-b5f0-6d1c2a3b4e59\");\nvar response = await http.SendAsync(request);\nvar body = await response.Content.ReadAsStringAsync();\nConsole.WriteLine($\"{(int)response.StatusCode} {body}\");"}]}}},"components":{"schemas":{"Area":{"properties":{"id":{"type":"string","format":"uuid","title":"Id","description":"Identificador del area."},"public_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Public Id","description":"Identificador de negocio legible, unico en la empresa."},"name":{"type":"string","title":"Name"},"parent_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Parent Id","description":"Area padre (null si es un area raiz)."},"status":{"type":"string","enum":["active","inactive"],"title":"Status"},"minimum_employees":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Minimum Employees"},"maximum_employees":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Maximum Employees"},"skills":{"items":{"type":"string"},"type":"array","title":"Skills"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"updated_at":{"type":"string","format":"date-time","title":"Updated At"}},"type":"object","required":["id","name","status","created_at","updated_at"],"title":"Area"},"AreaCreate":{"properties":{"name":{"type":"string","maxLength":80,"minLength":1,"title":"Name"},"parent_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Parent Id"},"minimum_employees":{"type":"integer","maximum":100000.0,"minimum":1.0,"title":"Minimum Employees"},"maximum_employees":{"type":"integer","maximum":100000.0,"minimum":1.0,"title":"Maximum Employees"},"skills":{"items":{"type":"string"},"type":"array","maxItems":20,"title":"Skills"}},"additionalProperties":false,"type":"object","required":["name","minimum_employees","maximum_employees"],"title":"AreaCreate","description":"Campos extra (p. ej. company_id) se rechazan: la empresa sale de la API key."},"AreaList":{"properties":{"results":{"items":{"$ref":"#/components/schemas/Area"},"type":"array","title":"Results"},"next_cursor":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Next Cursor","description":"Cursor de la siguiente pagina; null si no hay mas."}},"type":"object","required":["results"],"title":"AreaList"},"Employee":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"external_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"External Id","description":"Identificador del cliente (ERP), unico en la empresa."},"first_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"First Name","description":"Nombre del empleado en la empresa. xYago guarda un unico campo de nombre: aqui va el nombre completo."},"last_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Last Name","description":"Reservado: hoy siempre null (el apellido enviado en altas y ediciones se guarda unido a first_name)."},"email":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Email"},"phone_number":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Phone Number","description":"E.164, p. ej. +34612345678."},"document_number":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Document Number","description":"Numero de empleado/identificacion en la empresa."},"job":{"anyOf":[{"$ref":"#/components/schemas/EmployeeJob"},{"type":"null"}]},"area_ids":{"items":{"type":"string","format":"uuid"},"type":"array","title":"Area Ids"},"status":{"type":"string","enum":["active","inactive"],"title":"Status"},"contract_type":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Contract Type"},"hire_date":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"Hire Date","description":"Inicio del contrato actual."},"contract_end_date":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"Contract End Date"},"created_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Created At","description":"null: el servicio de empleados no lo expone todavia."},"updated_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Updated At","description":"null: el servicio de empleados no lo expone todavia."}},"type":"object","required":["id","status"],"title":"Employee"},"EmployeeCreate":{"properties":{"external_id":{"anyOf":[{"type":"string","pattern":"^[A-Za-z0-9._:@/\\-]{1,128}$"},{"type":"null"}],"title":"External Id"},"first_name":{"type":"string","maxLength":100,"minLength":1,"title":"First Name"},"last_name":{"anyOf":[{"type":"string","maxLength":100},{"type":"null"}],"title":"Last Name"},"email":{"anyOf":[{"type":"string","maxLength":254},{"type":"null"}],"title":"Email"},"phone_number":{"type":"string","pattern":"^\\+[1-9][0-9]{6,14}$","title":"Phone Number"},"document_number":{"anyOf":[{"type":"string","maxLength":20},{"type":"null"}],"title":"Document Number"},"job_title":{"type":"string","maxLength":100,"minLength":1,"title":"Job Title"},"contract_type":{"type":"string","maxLength":100,"minLength":1,"title":"Contract Type"},"hire_date":{"type":"string","format":"date","title":"Hire Date"},"contract_end_date":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"Contract End Date"},"work_shifts":{"items":{"$ref":"#/components/schemas/WorkShift"},"type":"array","maxItems":21,"minItems":1,"title":"Work Shifts"},"vacation_days":{"anyOf":[{"type":"integer","maximum":366.0,"minimum":0.0},{"type":"null"}],"title":"Vacation Days","description":"Dias de vacaciones anuales (null/0 = sin vacaciones)."}},"additionalProperties":false,"type":"object","required":["first_name","phone_number","job_title","contract_type","hire_date","work_shifts"],"title":"EmployeeCreate","description":"Alta. Campos extra (company_id, area_ids...) -> 422: la empresa sale de la API key."},"EmployeeImportAccepted":{"properties":{"job_id":{"type":"string","format":"uuid","title":"Job Id"},"status":{"type":"string","enum":["queued","processing","completed","failed"],"title":"Status"}},"type":"object","required":["job_id","status"],"title":"EmployeeImportAccepted"},"EmployeeImportJob":{"properties":{"job_id":{"type":"string","format":"uuid","title":"Job Id"},"status":{"type":"string","enum":["queued","processing","completed","failed"],"title":"Status"},"total_rows":{"type":"integer","title":"Total Rows"},"processed_rows":{"type":"integer","title":"Processed Rows"},"created":{"type":"integer","title":"Created"},"updated":{"type":"integer","title":"Updated"},"failed":{"type":"integer","title":"Failed"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"started_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Started At"},"finished_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Finished At"},"results":{"items":{"$ref":"#/components/schemas/EmployeeImportRowResult"},"type":"array","title":"Results"}},"type":"object","required":["job_id","status","total_rows","processed_rows","created","updated","failed","created_at","results"],"title":"EmployeeImportJob"},"EmployeeImportRequest":{"properties":{"rows":{"items":{"type":"object"},"type":"array","maxItems":500,"minItems":1,"title":"Rows","description":"Hasta 500 filas con la forma de POST /employees; `external_id` obligatorio (clave del upsert). Una fila de un empleado ya mapeado se aplica como PATCH (solo los campos enviados)."}},"additionalProperties":false,"type":"object","required":["rows"],"title":"EmployeeImportRequest"},"EmployeeImportRowResult":{"properties":{"row_index":{"type":"integer","title":"Row Index"},"external_id":{"type":"string","title":"External Id"},"status":{"type":"string","enum":["created","updated","failed"],"title":"Status"},"employee_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Employee Id"},"error_code":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Error Code"},"error_message":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Error Message"}},"type":"object","required":["row_index","external_id","status"],"title":"EmployeeImportRowResult"},"EmployeeJob":{"properties":{"id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Id"},"name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Name","description":"Puesto (rol)."}},"type":"object","title":"EmployeeJob"},"EmployeeList":{"properties":{"results":{"items":{"$ref":"#/components/schemas/Employee"},"type":"array","title":"Results"},"next_cursor":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Next Cursor","description":"Cursor de la siguiente pagina; null si no hay mas."}},"type":"object","required":["results"],"title":"EmployeeList"},"EmployeeUpdate":{"properties":{"external_id":{"anyOf":[{"type":"string","pattern":"^[A-Za-z0-9._:@/\\-]{1,128}$"},{"type":"null"}],"title":"External Id"},"first_name":{"anyOf":[{"type":"string","maxLength":100},{"type":"null"}],"title":"First Name"},"last_name":{"anyOf":[{"type":"string","maxLength":100},{"type":"null"}],"title":"Last Name"},"email":{"anyOf":[{"type":"string","maxLength":254},{"type":"null"}],"title":"Email"},"phone_number":{"anyOf":[{"type":"string","pattern":"^\\+[1-9][0-9]{6,14}$"},{"type":"null"}],"title":"Phone Number"},"document_number":{"anyOf":[{"type":"string","maxLength":20},{"type":"null"}],"title":"Document Number"},"job_title":{"anyOf":[{"type":"string","maxLength":100},{"type":"null"}],"title":"Job Title"},"contract_type":{"anyOf":[{"type":"string","maxLength":100},{"type":"null"}],"title":"Contract Type"},"hire_date":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"Hire Date"},"contract_end_date":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"Contract End Date"}},"additionalProperties":false,"type":"object","title":"EmployeeUpdate","description":"Edicion parcial: solo los campos enviados. Al menos uno."},"ErrorDetailSchema":{"properties":{"code":{"type":"string","title":"Code"},"message":{"type":"string","title":"Message"}},"type":"object","required":["code","message"],"title":"ErrorDetailSchema"},"ErrorSchema":{"properties":{"error":{"$ref":"#/components/schemas/ErrorDetailSchema"}},"type":"object","required":["error"],"title":"ErrorSchema"},"Message":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"status":{"type":"string","enum":["accepted","pending_install","sent","delivered","read","failed"],"title":"Status","description":"accepted → pending_install → sent → delivered → read; o failed (ver `failure_code`)."},"to_masked":{"type":"string","title":"To Masked","description":"Teléfono enmascarado (`+57 ••• ••• 0123`)."},"sender_id":{"type":"string","title":"Sender Id"},"template":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Template","description":"Nombre de la plantilla usada."},"language":{"anyOf":[{"type":"string","enum":["es","en","de"]},{"type":"null"}],"title":"Language","description":"Idioma con el que se envió."},"reference":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Reference"},"failure_code":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Failure Code","description":"Solo si `failed`: `recipient_not_on_xyago` (no instaló xYago en 7 días), `undeliverable`, `sender_suspended`, `invalid_message`."},"created_at":{"type":"string","format":"date-time","title":"Created At"},"sent_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Sent At"},"delivered_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Delivered At"},"read_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Read At"},"expires_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Expires At","description":"Solo si pasó por `pending_install`: fin de la espera (7 días)."}},"type":"object","required":["id","status","to_masked","sender_id","template","language","reference","created_at"],"title":"Message"},"MessageCreate":{"properties":{"to":{"type":"string","maxLength":32,"minLength":1,"title":"To","description":"Teléfono del destinatario en E.164 (`+573005550123`)."},"sender_id":{"type":"string","maxLength":32,"minLength":3,"title":"Sender Id","description":"Identificador del remitente (`business_id`, p. ej. `bancox`)."},"template":{"type":"string","maxLength":64,"minLength":2,"title":"Template","description":"Nombre de la plantilla del remitente (p. ej. `pedido_enviado`)."},"language":{"anyOf":[{"type":"string","enum":["es","en","de"]},{"type":"null"}],"title":"Language","description":"Idioma preferido. Si la plantilla no lo tiene se usa su idioma por defecto."},"variables":{"items":{"type":"string"},"type":"array","maxItems":20,"title":"Variables","description":"Valores de `{{1}}`..`{{n}}` en orden: texto plano, máx. 256 caracteres cada uno, sin enlaces ajenos al dominio del remitente."},"reference":{"anyOf":[{"type":"string","maxLength":64},{"type":"null"}],"title":"Reference","description":"Tu identificador del cliente (opcional). Se muestra en el panel y permite filtrar `GET /messages`."},"button_variables":{"anyOf":[{"additionalProperties":{"type":"string"},"type":"object"},{"type":"null"}],"title":"Button Variables","description":"Valor de la variable `{{1}}` de cada botón de enlace de la plantilla, por índice del botón (`{\"0\": \"TE-48213\"}`). Obligatorio para cada botón `url` con `{{1}}` y solo para esos; máx. 200 caracteres, texto plano (xYago lo codifica para la URL)."},"buttons":{"anyOf":[{"items":{"type":"object"},"type":"array"},{"type":"null"}],"title":"Buttons","description":"Obsoleto: los botones se definen en la plantilla (panel). Solo se admite `[]` u omitirlo."}},"additionalProperties":false,"type":"object","required":["to","sender_id","template"],"title":"MessageCreate","description":"Envio de una plantilla aprobada a un telefono. Campos extra -> 422."},"MessageList":{"properties":{"results":{"items":{"$ref":"#/components/schemas/Message"},"type":"array","title":"Results"},"next_cursor":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Next Cursor","description":"Cursor de la siguiente página; null si no hay más."}},"type":"object","required":["results"],"title":"MessageList"},"Sender":{"properties":{"id":{"type":"string","title":"Id","description":"`business_id` del remitente (se usa en `sender_id`)."},"display_name":{"type":"string","title":"Display Name"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"},"logo_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Logo Url"},"website":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Website"},"category":{"type":"string","title":"Category"},"status":{"type":"string","enum":["draft","pending_review","approved","rejected","suspended"],"title":"Status"},"verified":{"type":"boolean","title":"Verified","description":"True si xYago lo aprobó (insignia en la app)."},"rejection_reason":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Rejection Reason"},"new_recipients_paused":{"type":"boolean","title":"New Recipients Paused","description":"True si xYago pausó sus envíos a destinatarios nuevos."},"invite_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Invite Url","description":"Enlace de invitación a xYago (solo si está aprobado)."},"created_at":{"type":"string","format":"date-time","title":"Created At"},"updated_at":{"type":"string","format":"date-time","title":"Updated At"}},"type":"object","required":["id","display_name","description","logo_url","website","category","status","verified","new_recipients_paused","created_at","updated_at"],"title":"Sender"},"SenderList":{"properties":{"results":{"items":{"$ref":"#/components/schemas/Sender"},"type":"array","title":"Results"}},"type":"object","required":["results"],"title":"SenderList"},"Template":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"sender_id":{"type":"string","title":"Sender Id"},"name":{"type":"string","title":"Name"},"category":{"type":"string","enum":["utility"],"const":"utility","title":"Category"},"default_language":{"type":"string","enum":["es","en","de"],"title":"Default Language"},"languages":{"additionalProperties":{"type":"string"},"type":"object","title":"Languages","description":"Texto por idioma con variables `{{1}}`..`{{n}}`."},"variables_count":{"type":"integer","title":"Variables Count"},"buttons":{"items":{"$ref":"#/components/schemas/TemplateButton"},"type":"array","title":"Buttons","description":"Hasta 3 botones, en este orden."},"version":{"type":"integer","title":"Version","description":"Versión de la plantilla. Editar una aprobada crea la siguiente (mismo `name`) en revisión; la aprobada se sigue usando hasta aprobar la nueva, que deja la anterior en `superseded`.","default":1},"status":{"type":"string","enum":["draft","pending_review","approved","rejected","paused","superseded"],"title":"Status"},"review_flags":{"items":{"type":"string","enum":["marketing_words","url_shortener","too_many_variables","variables_adjacent","starts_or_ends_with_variable","only_variables"]},"type":"array","title":"Review Flags","description":"Avisos automáticos al enviarla a revisión (no la bloquean): xYago los tiene en cuenta al revisarla."},"rejection_reason":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Rejection Reason"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"updated_at":{"type":"string","format":"date-time","title":"Updated At"}},"type":"object","required":["id","sender_id","name","category","default_language","languages","variables_count","status","created_at","updated_at"],"title":"Template"},"TemplateButton":{"properties":{"type":{"type":"string","enum":["quick_reply","url"],"title":"Type","description":"`quick_reply` (respuesta rápida: el usuario la pulsa y recibes `message.button_clicked`) o `url` (abre el enlace)."},"texts":{"additionalProperties":{"type":"string"},"type":"object","title":"Texts","description":"Texto del botón por idioma, con la misma forma que `languages` (un texto por cada idioma de la plantilla; máx. 20, sin variables). Se muestra en el mismo idioma que el cuerpo del mensaje."},"payload":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Payload","description":"Solo `quick_reply`: tu valor fijo (máx. 128), vuelve en el webhook. Nunca llega al dispositivo."},"url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Url","description":"Solo `url`: enlace https; puede llevar un `{{1}}` en la ruta o la query que se rellena con `button_variables` en cada envío."}},"type":"object","required":["type","texts"],"title":"TemplateButton"},"TemplateList":{"properties":{"results":{"items":{"$ref":"#/components/schemas/Template"},"type":"array","title":"Results"}},"type":"object","required":["results"],"title":"TemplateList"},"TimeEntry":{"properties":{"id":{"type":"string","format":"uuid","title":"Id","description":"Identificador estable del tramo (deterministico a partir del registro interno)."},"employee_id":{"type":"string","format":"uuid","title":"Employee Id"},"started_at":{"type":"string","format":"date-time","title":"Started At"},"ended_at":{"type":"string","format":"date-time","title":"Ended At"},"duration_minutes":{"type":"integer","minimum":0.0,"title":"Duration Minutes"},"source":{"type":"string","enum":["api","app","manual"],"title":"Source","description":"api = creado por esta API; manual = registro manual en la app; app = cronometro de la app."},"note":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Note"},"created_at":{"type":"string","format":"date-time","title":"Created At"}},"type":"object","required":["id","employee_id","started_at","ended_at","duration_minutes","source","created_at"],"title":"TimeEntry"},"TimeEntryCreate":{"properties":{"employee_id":{"type":"string","format":"uuid","title":"Employee Id"},"started_at":{"type":"string","format":"date-time","title":"Started At"},"ended_at":{"type":"string","format":"date-time","title":"Ended At"},"note":{"anyOf":[{"type":"string","maxLength":300},{"type":"null"}],"title":"Note"}},"additionalProperties":false,"type":"object","required":["employee_id","started_at","ended_at"],"title":"TimeEntryCreate","description":"Un tramo trabajado ya terminado. Fechas ISO 8601; sin zona se interpretan en UTC.\nCampos extra -> 422 (la empresa sale de la API key)."},"TimeEntryList":{"properties":{"results":{"items":{"$ref":"#/components/schemas/TimeEntry"},"type":"array","title":"Results"},"next_cursor":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Next Cursor","description":"Cursor de la siguiente pagina; null si no hay mas."}},"type":"object","required":["results"],"title":"TimeEntryList"},"Vacation":{"properties":{"id":{"type":"string","pattern":"^[0-9a-f]{24}$","title":"Id","description":"Identificador de la solicitud."},"employee_id":{"type":"string","format":"uuid","title":"Employee Id"},"start_date":{"type":"string","format":"date","title":"Start Date"},"end_date":{"type":"string","format":"date","title":"End Date","description":"Ultimo dia de vacaciones (incluido)."},"days":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Days","description":"Dias laborables segun el calendario del empleado; null en solicitudes de horas (dentro de un mismo dia)."},"status":{"type":"string","enum":["pending","approved","rejected","cancelled"],"title":"Status","description":"pending | approved | rejected. `cancelled` esta reservado: hoy xYago no tiene estado de cancelacion."},"reason":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Reason","description":"Motivo indicado al solicitarla."},"created_at":{"type":"string","format":"date-time","title":"Created At"},"decided_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Decided At","description":"Fecha de la ultima actualizacion si ya esta decidida."}},"type":"object","required":["id","employee_id","start_date","end_date","status","created_at"],"title":"Vacation"},"VacationBalance":{"properties":{"employee_id":{"type":"string","format":"uuid","title":"Employee Id"},"year":{"type":"integer","title":"Year"},"total_days":{"type":"number","title":"Total Days","description":"Dias que corresponden en el ano (prorrateados)."},"used_days":{"type":"number","title":"Used Days","description":"Dias de solicitudes aprobadas que empiezan ese ano."},"available_days":{"type":"number","title":"Available Days"}},"type":"object","required":["employee_id","year","total_days","used_days","available_days"],"title":"VacationBalance"},"VacationCreate":{"properties":{"employee_id":{"type":"string","format":"uuid","title":"Employee Id"},"start_date":{"type":"string","format":"date","title":"Start Date"},"end_date":{"type":"string","format":"date","title":"End Date"},"reason":{"anyOf":[{"type":"string","maxLength":500},{"type":"null"}],"title":"Reason"}},"additionalProperties":false,"type":"object","required":["employee_id","start_date","end_date"],"title":"VacationCreate","description":"Solicitud de dias completos. Campos extra -> 422."},"VacationList":{"properties":{"results":{"items":{"$ref":"#/components/schemas/Vacation"},"type":"array","title":"Results"},"next_cursor":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Next Cursor","description":"Cursor de la siguiente pagina; null si no hay mas."}},"type":"object","required":["results"],"title":"VacationList"},"Verification":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"object":{"type":"string","enum":["verification"],"const":"verification","title":"Object","default":"verification"},"sender_id":{"type":"string","title":"Sender Id"},"to_masked":{"type":"string","title":"To Masked","description":"Teléfono enmascarado (`+57 ••• ••• 0123`)."},"channel":{"type":"string","enum":["xyago_chat"],"const":"xyago_chat","title":"Channel","description":"Canal por el que se envió el código (hoy siempre el chat)."},"language":{"anyOf":[{"type":"string","enum":["es","en","de"]},{"type":"null"}],"title":"Language"},"reference":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Reference"},"status":{"type":"string","enum":["pending","verified","expired","too_many_attempts","canceled","failed"],"title":"Status","description":"`pending` → `verified`; o `expired` (10 min), `too_many_attempts` (5 códigos erróneos), `canceled` (sustituida por otra verificación al mismo teléfono) o `failed` (no se pudo enviar, ver `failure_code`)."},"delivery_status":{"anyOf":[{"type":"string","enum":["sent","delivered","read","failed"]},{"type":"null"}],"title":"Delivery Status","description":"Entrega del mensaje con el código: `sent` → `delivered` → `read`, o `failed`. `null` mientras no llegó al chat."},"failure_code":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Failure Code","description":"Solo si `failed`: `recipient_not_on_xyago`, `undeliverable`, `sender_suspended`, `invalid_message`."},"attempts":{"type":"integer","title":"Attempts","description":"Códigos comprobados hasta ahora."},"max_attempts":{"type":"integer","title":"Max Attempts","description":"Intentos permitidos (5)."},"expires_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Expires At","description":"Fin de la validez del código (10 minutos)."},"verified_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Verified At"},"created_at":{"type":"string","format":"date-time","title":"Created At"}},"type":"object","required":["id","sender_id","to_masked","channel","language","reference","status","attempts","max_attempts","created_at"],"title":"Verification"},"VerificationCheck":{"properties":{"verification_id":{"type":"string","format":"uuid","title":"Verification Id","description":"`id` devuelto por `POST /verify/start`."},"code":{"type":"string","pattern":"^[0-9]{6}$","title":"Code","description":"Código de 6 dígitos que escribió el usuario."}},"additionalProperties":false,"type":"object","required":["verification_id","code"],"title":"VerificationCheck"},"VerificationCheckResult":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"valid":{"type":"boolean","title":"Valid","description":"True si el código es correcto (un solo uso)."},"status":{"type":"string","enum":["pending","verified","expired","too_many_attempts","canceled","failed"],"title":"Status","description":"`verified` si es correcto; `pending` si aún quedan intentos; `too_many_attempts` tras el último código erróneo."},"attempts_remaining":{"type":"integer","title":"Attempts Remaining","description":"Intentos que quedan (0 si ya no)."}},"type":"object","required":["id","valid","status","attempts_remaining"],"title":"VerificationCheckResult"},"VerificationStart":{"properties":{"to":{"type":"string","maxLength":32,"minLength":1,"title":"To","description":"Teléfono del usuario en E.164 (`+573005550123`)."},"sender_id":{"type":"string","maxLength":32,"minLength":3,"title":"Sender Id","description":"Remitente (`business_id`) en cuyo nombre se envía el código."},"language":{"anyOf":[{"type":"string","enum":["es","en","de"]},{"type":"null"}],"title":"Language","description":"Idioma del mensaje (`es`, `en`, `de`). Por defecto, el del país de tu cuenta: español en países hispanohablantes, alemán en DE/AT/CH/LI, inglés en el resto."},"reference":{"anyOf":[{"type":"string","maxLength":64},{"type":"null"}],"title":"Reference","description":"Tu identificador del usuario o de la operación (opcional). Se muestra en el panel."}},"additionalProperties":false,"type":"object","required":["to","sender_id"],"title":"VerificationStart","description":"Envio de un codigo de verificacion a un telefono. Campos extra -> 422."},"WorkShift":{"properties":{"day":{"type":"string","enum":["Monday","Tuesday","Wednesday","Thursday","Friday","Saturday","Sunday"],"title":"Day"},"start":{"type":"string","pattern":"^([01][0-9]|2[0-3]):[0-5][0-9]$","title":"Start","description":"HH:MM"},"end":{"type":"string","pattern":"^([01][0-9]|2[0-3]):[0-5][0-9]$","title":"End","description":"HH:MM"}},"additionalProperties":false,"type":"object","required":["day","start","end"],"title":"WorkShift"}},"securitySchemes":{"ApiKeyBearer":{"type":"http","scheme":"bearer","bearerFormat":"xy_live_… | xy_test_…","description":"API key en `Authorization: Bearer <api key>`. Las claves `xy_live_` operan sobre los datos reales; las `xy_test_` (sandbox) devuelven datos de ejemplo. Se crean en el panel de xYago (API Developer → Credenciales)."},"ApiKeyHeader":{"type":"apiKey","in":"header","name":"X-Api-Key","description":"Alternativa: la API key en la cabecera `X-Api-Key`."}},"headers":{"X-Request-Id":{"description":"Identificador de la petición: el que envió el cliente si es seguro (`[A-Za-z0-9._:-]{8,64}`); si no, uno generado (`req_<hex>`). Inclúyelo al contactar con soporte.","schema":{"type":"string"},"example":"req_7f3a9c2e5b1d4f60"},"X-RateLimit-Limit":{"description":"Peticiones permitidas por minuto para esta API key y producto.","schema":{"type":"integer"},"example":300},"X-RateLimit-Remaining":{"description":"Peticiones restantes en la ventana actual.","schema":{"type":"integer"},"example":297},"X-RateLimit-Reset":{"description":"Fin de la ventana actual (epoch Unix en segundos).","schema":{"type":"integer"},"example":1772463660},"Retry-After":{"description":"Segundos que hay que esperar antes de reintentar.","schema":{"type":"integer"},"example":42}},"parameters":{"XRequestId":{"name":"X-Request-Id","in":"header","required":false,"description":"Opcional. Identificador propio de la petición (8-64 caracteres `[A-Za-z0-9._:-]`); se devuelve en la respuesta y aparece en el log del panel.","schema":{"type":"string","pattern":"^[A-Za-z0-9._:-]{8,64}$"},"example":"siesa-sync-20260302-0001"}}},"security":[{"ApiKeyBearer":[]},{"ApiKeyHeader":[]}],"tags":[{"name":"areas","x-displayName":"Áreas","description":"Áreas (departamentos, sedes, secciones) de la empresa. Scopes `areas:read` y `areas:write`."},{"name":"employees","x-displayName":"Empleados","description":"Empleados de la empresa: consulta, alta, edición, desactivación e importación masiva con upsert por `external_id`. Scopes `employees:read` y `employees:write`."},{"name":"vacations","x-displayName":"Vacaciones","description":"Solicitudes de vacaciones (alta, aprobación, rechazo) y saldo anual. Scopes `vacations:read` y `vacations:write`."},{"name":"time_entries","x-displayName":"Registro de horas","description":"Tramos de trabajo cerrados de los empleados. Scopes `time:read` y `time:write`."},{"name":"messages","x-displayName":"Mensajes","description":"Notificaciones con plantilla a usuarios de xYago por el chat de la app, desde un remitente verificado. Si el destinatario aún no tiene xYago el mensaje espera hasta 7 días (`pending_install`). Scopes `messages:write` y `messages:read`. Guía: [Mensajes](https://api.xyago.com/v1/docs/es/messages)."},{"name":"senders","x-displayName":"Remitentes","description":"Remitentes de la cuenta (perfil verificado en la app) y su estado de revisión. Se crean en el panel. Scope `messages:read`."},{"name":"templates","x-displayName":"Plantillas","description":"Plantillas de notificación (`utility`) y su estado de aprobación. Se crean en el panel y las aprueba xYago. Scope `messages:read`."},{"name":"verify","x-displayName":"Verify","description":"Códigos de verificación de un solo uso (6 dígitos, 10 minutos, 5 intentos) por el chat de xYago, para inicio de sesión, registro o confirmación de operaciones. Si el teléfono no usa xYago se responde al momento para que envíes el código por SMS. Scopes `verify:write` y `verify:read`. Guía: [Verify](https://api.xyago.com/v1/docs/es/verify)."}],"externalDocs":{"description":"Guías de integración (primeros pasos, webhooks, errores...)","url":"https://api.xyago.com/v1/docs/es/"}}