Esquema de color
Are you an LLM? You can read better optimized documentation at /services/imss/sc_premium.md for this page in Markdown format
🏥 Servicio Premium de Semanas Cotizadas del Seguro Social (asíncrono)
Endpoint asíncrono para obtener el reporte Premium de Semanas Cotizadas del Seguro Social. El cliente envía la solicitud y elige cómo recibir el resultado:
- Modo webhook: provee
webhookUrly entregamos el PDF a esa URL cuando el reporte esté listo (firmado con HMAC-SHA256). - Modo polling-only: omite
webhookUrly consulta el resultado porGET /v3/sc_premium/{uuid}cuando lo necesites.
Ambos modos cuestan los mismos 3 créditos; lo único que cambia es cómo te enteras del resultado.
- Base URL:
https://api.consultaunica.mx - Autenticación: header
X-API-Key - Formato: JSON (
Content-Type: application/json) - Compra de paquetes: https://consultaunica.mx/paquetes
📑 Contenido
- Submit —
POST /v3/sc_premium - Webhook de entrega —
POSTa tuwebhookUrl - Consultar resultado —
GET /v3/sc_premium/{uuid} - Reintentar entrega —
POST /v3/sc_premium/{uuid}/replay - Flujo completo
- Preguntas frecuentes
- Modo Mock
- Changelog
Formato de las respuestas de error
Todos los endpoints de esta API responden los errores con el mismo objeto de tres campos, sin importar el status HTTP:
json
{
"message": "Cuenta no válida, favor de verificar su clave de consulta",
"description": "La clave no corresponde a una cuenta activa: puede ser incorrecta, o la cuenta está bloqueada, sin confirmar o desactivada.",
"errorCode": "unauthorized_user"
}| Campo | Tipo | Descripción |
|---|---|---|
message | string | Mensaje corto de la causa, en español y apto para mostrar al usuario final. |
description | string | Explicación no técnica de por qué ocurrió, pensada para tu UI. Puede venir vacía (""). |
errorCode | string | Slug estable en inglés. Es el campo contra el que debes programar. Puede venir vacío (""). |
Reglas para integrar:
- Haz match por
errorCode, no pormessage. ElerrorCodees contrato estable; los textos demessageydescriptionpueden reescribirse para mejorar la redacción sin aviso. errorCodepuede llegar vacío en errores no clasificados. Todos los errores documentados aquí traen código; los que no lo traen son fallas genéricas de infraestructura y el404de una ruta inexistente. Trata""como error no clasificado y cae a tu manejo genérico por status HTTP.- Nunca recibes el campo
detailde FastAPI. Un handler central reescribe toda excepción a esta forma. - El único error que trae una llave extra es la validación de payload, que agrega
errors(ver 409).
1. Submit — POST /v3/sc_premium
Envía una consulta para una CURP e indica a dónde entregar el resultado.
Request — modo webhook (push)
http
POST /v3/sc_premium HTTP/1.1
Host: api.consultaunica.mx
X-API-Key: <tu_api_key>
Content-Type: application/json
{
"curp": "AAAA800101HDFBBB01",
"webhookUrl": "https://tu-dominio.com/webhook/sc-premium",
"parsePdf": false
}Request — modo polling-only (pull)
Si tu infraestructura no puede recibir webhooks (corres detrás de NAT, no tienes endpoint HTTPS público, etc.), omite webhookUrl y consulta el resultado por GET /v3/sc_premium/{uuid}.
http
POST /v3/sc_premium HTTP/1.1
Host: api.consultaunica.mx
X-API-Key: <tu_api_key>
Content-Type: application/json
{
"curp": "AAAA800101HDFBBB01"
}Campos
| Campo | Tipo | Requerido | Descripción |
|---|---|---|---|
curp | string | sí | CURP de 18 caracteres, se normaliza a mayúsculas y se valida contra regex. |
webhookUrl | string | no | URL HTTPS pública a la que te entregaremos el resultado. Si lo omites o envías null, no enviaremos webhook y deberás consultar el estado por GET /v3/sc_premium/{uuid}. Ver requisitos. |
parsePdf | boolean | no | Default false. Si es true, el webhook de entrega incluye además documentData: el contenido de la constancia ya estructurado en JSON. Ver documentData: el PDF estructurado. |
parsePdfen el submit sólo afecta al webhook. El webhook es asíncrono: cuando lo entregamos no hay una request tuya donde poner una opción, así que el submit es el único momento en que puedes pedirlo. En modo polling-only el flag se decide en cada lectura con?parsePdf=truesobre elGET, aunque no lo hayas enviado aquí — ver §3.
Requisitos del webhookUrl
Aplican sólo si envías
webhookUrl. En modo polling-only no hay validación de red ni resolución DNS.
Rechazamos la solicitud si la URL no cumple:
- Esquema HTTPS (no
http://). - Sin credenciales embebidas (
https://user:pass@...no se acepta). - Debe resolver vía DNS a una IP pública. Rechazamos loopback (127.0.0.0/8,
::1), rangos privados (10/8, 172.16/12, 192.168/16, fc00::/7), link-local (169.254/16, fe80::/10), reservados, multicast y0.0.0.0. - Timeout de resolución DNS: 2 segundos. Si tu dominio tarda más, la solicitud se rechaza.
Costo
Cada consulta cuesta 3 créditos, que se aplican en dos momentos:
- Retención — al aceptar el submit apartamos los 3 créditos de tu saldo.
- Cobro — se consuman cuando el Seguro Social nos entrega el PDF del reporte.
Una consulta que nunca llega a producir un PDF no convierte la retención en cobro. Es la razón por la que pdf_download_failed no te cuesta nada, y por la que tampoco consumen saldo la re-entrega del mismo día ni el 422 recent_query.
Rate limit
Máximo 3 requests cada 2 segundos por X-API-Key en este endpoint. Al rebasarlo devolvemos 429 Too Many Requests con el mensaje de la tabla de errores más abajo. El rate limit es independiente del límite global de la cuenta.
Es un token bucket, no una ventana fija: acumulas hasta 3 peticiones de ráfaga y se repone una cada ~0.67 s, así que el ritmo sostenido es de 90 req/min. Si vas a procesar lotes, ese es el número con el que debes dimensionar.
Re-entrega del mismo día
Si reenvías un submit de una CURP que ya completaste hoy, en vez de rechazarlo te servimos el reporte que ya obtuviste. No volvemos a consultar al Seguro Social, así que:
- No consume créditos. El header
X-Remaining-Requestsreporta tu saldo sin descuento. - Recibes un
uuidnuevo, no el original. El resultado queda disponible por eseuuiden polling y replay, y si mandastewebhookUrlte llega el push firmado igual que en una consulta fresca. - La respuesta trae
redelivered: true. Elmessagees idéntico al de una consulta nueva, así que ese booleano es el único discriminante: si necesitas distinguir los dos casos —para no volver a cobrarle a tu usuario, por ejemplo— es el campo que debes leer.
Es la red de seguridad para cuando pierdes el uuid original o tu proceso reintenta a ciegas: reenvías el mismo submit y recuperas el reporte sin pagarlo dos veces.
Si el PDF de la solicitud original ya no es recuperable, el submit responde 422 recent_query. Es el caso degradado, no el habitual.
Idempotency-Key (opcional)
Para protegerte de cobros duplicados si tu cliente reintenta el submit por un error de red, envía el header Idempotency-Key. Reenviar con la misma key devuelve la respuesta de la consulta original sin volver a consumir créditos.
- Formato:
^[A-Za-z0-9_-]{1,64}$(64 chars máx, alfanuméricos +-+_). - TTL: 24 horas. Después de eso, la misma key vuelve a tratarse como consulta nueva.
- Scope: por usuario. Dos usuarios distintos pueden usar la misma key sin colisión.
- Respuesta de replay:
- Body idéntico al de la consulta original.
- Header
X-Idempotent-Replay: true. - Header
X-Remaining-Requests: refleja el saldo inmediatamente después del submit original, no el saldo vivo actual. Si necesitas el saldo al día, consúltalo desde tu dashboard.
Errores:
| HTTP | Caso | errorCode | message |
|---|---|---|---|
| 400 | Key con formato inválido | invalid_idempotency_key | El encabezado Idempotency-Key no tiene un formato válido |
| 409 | Key ya en uso por otra request concurrente que aún no termina | concurrent_idempotent_request | Ya se está procesando una consulta idéntica, espera un momento antes de reintentar |
Ejemplo curl con idempotency:
bash
curl -X POST https://api.consultaunica.mx/v3/sc_premium \
-H "X-API-Key: $CU_API_KEY" \
-H "Idempotency-Key: order-12345-retry-1" \
-H "Content-Type: application/json" \
-d '{"curp":"AAAA800101HDFBBB01","webhookUrl":"https://tu-dominio.com/hook"}'Genera un valor único por cada submit lógico de tu lado (por ejemplo, el ID de la orden interna + contador de reintento).
✅ Respuesta exitosa — 200 OK
json
{
"uuid": "9f2b7c1a-4d3e-4a5f-8b0c-2e1d0f9a8b77",
"message": "Solicitud recibida, pendiente de resultado",
"curp": "AAAA800101HDFBBB01",
"nss": "12345678901",
"fullname": "Juan Pérez López",
"redelivered": false
}| Campo | Tipo | Descripción |
|---|---|---|
uuid | string | Identificador único de la solicitud. Guárdalo: llegará idéntico en el webhook y te sirve para deduplicar. |
message | string | Mensaje descriptivo del estado. |
curp | string | CURP confirmado. |
nss | string | Número de Seguridad Social asociado al CURP. Útil para prellenar otros flujos. |
fullname | string | Nombre completo del titular. |
redelivered | boolean | true si es una re-entrega del mismo día (sin cargo); false en una consulta nueva. |
Headers de respuesta:
X-Remaining-Requests: créditos restantes después de este consumo.
Ejemplo curl
bash
curl -X POST https://api.consultaunica.mx/v3/sc_premium \
-H "X-API-Key: $CU_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"curp": "AAAA800101HDFBBB01",
"webhookUrl": "https://tu-dominio.com/webhook/sc-premium"
}'❌ Respuestas de error
Todas siguen el formato de tres campos. Programa contra errorCode.
400 — Webhook inválido y datos rechazados en origen
http
HTTP/1.1 400 Bad Requestjson
{
"message": "webhookUrl debe ser https y apuntar a un host público accesible",
"description": "La dirección web (URL) que configuraste para recibir el resultado no es válida.",
"errorCode": "invalid_webhook_url"
}errorCode | message | Caso |
|---|---|---|
invalid_webhook_url | webhookUrl debe ser https y apuntar a un host público accesible | La URL no pasó las validaciones de red. |
invalid_curp | La CURP ingresada no es válida, favor de verificar | CURP con formato válido pero rechazada al consultarla. |
imss_data_inconsistency | El Seguro Social reportó inconsistencias en los datos de esta CURP | Datos incongruentes en la fuente (típicamente más de un NSS o más de una CURP registrada). |
invalid_idempotency_key | El encabezado Idempotency-Key no tiene un formato válido | Idempotency-Key fuera del patrón permitido. |
401 — Autenticación
http
HTTP/1.1 401 Unauthorizedjson
{
"message": "Cuenta no válida, favor de verificar su clave de consulta",
"description": "La clave no corresponde a una cuenta activa: puede ser incorrecta, o la cuenta está bloqueada, sin confirmar o desactivada.",
"errorCode": "unauthorized_user"
}errorCode | message | Caso |
|---|---|---|
missing_api_key | No se encontró la Clave para realizar consultas | Header X-API-Key ausente o vacío. |
unauthorized_user | Cuenta no válida, favor de verificar su clave de consulta | La clave no corresponde a una cuenta activa. |
user_not_found | Usuario no encontrado | Cuenta sin registro post-autenticación (caso raro). |
403 — Se requiere una compra previa
El uso de la API por clave está restringido a cuentas con al menos un paquete comprado. Una cuenta que sólo tiene créditos bonificados recibe este error aunque su saldo sea mayor a cero.
http
HTTP/1.1 403 Forbiddenjson
{
"message": "Tu clave de consulta requiere una compra activa para usar la API, adquiere un paquete en https://dash.consultaunica.mx/compras/nueva",
"description": "Tu cuenta necesita al menos un paquete de consultas comprado para usar este servicio.",
"errorCode": "api_key_requires_purchase"
}409 — Validación del payload
Cuando algún campo del cuerpo no pasa la validación, el status es 409 Conflict (no 422) y la respuesta agrega la llave errors con un elemento por campo inválido:
http
HTTP/1.1 409 Conflictjson
{
"message": "Datos inválidos, favor de verificar la información ingresada",
"description": "Alguno de los datos que enviaste tiene un formato incorrecto o falta información obligatoria.",
"errorCode": "bad_request",
"errors": [
{
"field": "body.curp",
"error": "String should match pattern '^[A-Z][AEIOUX][A-Z]{2}[0-9]{2}(0[1-9]|1[012])(0[1-9]|[12][0-9]|3[01])[MHX]([ABCMTZ]S|[BCJMOT]C|[CNPST]L|[GNQ]T|[GQS]R|C[MH]|[MY]N|[DH]G|NE|VZ|DF|SP)[BCDFGHJ-NP-TV-Z]{3}[0-9A-Z][0-9]$'"
}
]
}Campo de errors[] | Descripción |
|---|---|
field | Ruta del campo con puntos, incluyendo el origen (body.curp). |
error | Mensaje del validador, en inglés. |
Casos que disparan bad_request:
curpfaltante, con longitud distinta de 18, o fuera del patrón.webhookUrlcon esquema distinto ahttpso formato inválido (sólo si lo envías; omitirlo es válido y activa modo polling-only).- JSON malformado.
409 — Idempotencia concurrente
json
{
"message": "Ya se está procesando una consulta idéntica, espera un momento antes de reintentar",
"description": "Enviaste dos peticiones idénticas casi al mismo tiempo; una ya se está procesando.",
"errorCode": "concurrent_idempotent_request"
}422 — CURP sin información o consulta duplicada
http
HTTP/1.1 422 Unprocessable EntityerrorCode | message | Caso |
|---|---|---|
no_data_found | No se encontró información en el Seguro Social para la CURP ingresada | CURP válida pero sin registros de semanas cotizadas. |
duplicate_in_progress | Ya existe una consulta en curso para esta CURP, espere a que termine antes de reintentar | Hay una consulta en vuelo para la misma CURP. |
recent_query | Ya cuentas con una consulta reciente para esta CURP, espera unos minutos antes de reintentar | Caso degradado de la re-entrega del mismo día: ya consultaste esa CURP hoy y su PDF ya no es recuperable. Lo habitual es el 200 con redelivered: true. No se cobra. |
429 — Sin créditos, rate limit o saturación
http
HTTP/1.1 429 Too Many RequestserrorCode | message | Caso |
|---|---|---|
too_many_requests | Has alcanzado el límite de consultas diarias, visita nuestros paquetes para más: … | Saldo agotado. Compra un paquete. |
rate_limit_exceeded | Debe esperar {N} segundos para volver a consultar, evitemos congestionar el servicio | Rebasaste 3 req / 2 s en este endpoint. Trae header Retry-After. |
system_at_capacity | El sistema se encuentra a máxima capacidad, favor de esperar unos minutos antes de reintentar | Demanda alta en el origen; reintenta en unos minutos. |
El número de segundos de rate_limit_exceeded es dinámico: usa el header Retry-After en vez de parsear el texto.
503 — Servicio no disponible
http
HTTP/1.1 503 Service UnavailableerrorCode | message |
|---|---|
service_unavailable | El servicio no se encuentra disponible, favor de consultar más tarde |
system_paused | El servicio de semanas cotizadas detalladas no está disponible, favor de intentar más tarde |
Recomendación: reintenta con backoff exponencial empezando en 60 s.
500 — Error interno
http
HTTP/1.1 500 Internal Server Errorjson
{
"message": "Ocurrió un error inesperado, favor de reintentar",
"description": "Ocurrió un error inesperado al procesar tu consulta.",
"errorCode": "unknown"
}Ciertas fallas de red contra el origen entregan
errorCode: "system_paused"con status500en vez de503. Trata ambos status igual para ese código: es una condición transitoria, reintentable con backoff.
Catálogo de errorCode para armar tu diccionario
Estos son los códigos que puede devolver el submit. El errorCode es el contrato estable; message y description pueden reescribirse sin bump de versión.
| HTTP | errorCode | Reintentable |
|---|---|---|
| 400 | invalid_webhook_url | No — corrige la URL. |
| 400 | invalid_curp | No — corrige la CURP. |
| 400 | imss_data_inconsistency | No — requiere trámite del titular en el Seguro Social. |
| 400 | invalid_idempotency_key | No — corrige el header. |
| 401 | missing_api_key | No. |
| 401 | unauthorized_user | No. |
| 401 | user_not_found | No. |
| 403 | api_key_requires_purchase | No — requiere compra. |
| 409 | bad_request | No — corrige el payload. |
| 409 | concurrent_idempotent_request | Sí, tras unos segundos. |
| 422 | no_data_found | No. |
| 422 | duplicate_in_progress | Sí, cuando termine la consulta en curso. |
| 422 | recent_query | Sí, tras unos minutos. |
| 429 | too_many_requests | Sólo tras comprar créditos. |
| 429 | rate_limit_exceeded | Sí — respeta Retry-After. |
| 429 | system_at_capacity | Sí, con backoff. |
| 503 | service_unavailable | Sí, con backoff. |
| 503 | system_paused | Sí, con backoff. |
| 500 | unknown | Sí, una vez. |
2. Webhook de entrega — POST a tu webhookUrl
Aplica sólo si enviaste
webhookUrlen el submit. En modo polling-only no enviamos webhook — salta a §3 Consultar resultado.
Cuando el reporte está listo (o falla), hacemos POST a la URL que nos diste.
Headers
| Header | Ejemplo | Descripción |
|---|---|---|
Content-Type | application/json | Siempre JSON. |
User-Agent | consultaunica-webhook/1.0 | Identifica nuestro tráfico. |
X-CU-Uuid | 9f2b7c1a-4d3e-4a5f-8b0c-2e1d0f9a8b77 | Mismo uuid que devolvió el submit. |
X-CU-Timestamp | 1745336400 | Unix epoch (segundos) en el que se generó la firma. Nuevo en cada reintento. |
X-CU-Signature | sha256=a1b2c3... | HMAC-SHA256 de "{X-CU-Timestamp}." + <cuerpo bruto>, firmado con tu X-API-Key. |
Body — entrega exitosa
json
{
"version": 1,
"uuid": "9f2b7c1a-4d3e-4a5f-8b0c-2e1d0f9a8b77",
"status": "completed",
"pdfBase64": "JVBERi0xLjQKJ...",
"errorMessage": null
}versiones la versión del contrato del webhook (entero). Ver Versionado del contrato abajo.pdfBase64es el PDF del reporte codificado en base64 estándar. Decodifícalo directamente a bytes y guárdalo / muéstralo.documentDatasólo aparece si enviaste"parsePdf": trueen el submit. Ver abajo.
Body — entrega fallida
json
{
"version": 1,
"uuid": "9f2b7c1a-4d3e-4a5f-8b0c-2e1d0f9a8b77",
"status": "failed",
"pdfBase64": null,
"errorMessage": "La CURP ingresada no es válida, favor de verificar"
}Versionado del contrato
El campo version del body te permite detectar cambios de esquema sin parsear headers.
- Estamos en
version: 1. - Cambios aditivos (campos nuevos opcionales) → mantenemos la misma versión.
documentDataes uno de ellos: sólo aparece si tú lo pediste, así que no hay nada de lo que enterarse por la versión. - Cambios breaking (renames, removals, cambios de tipo) → bump a
version: 2y damos aviso con anticipación. - La versión va dentro del body firmado, así que un atacante no puede cambiarla sin invalidar la firma.
Recomendación: en tu validador, rechaza versiones distintas a las que conoces en vez de silenciosamente aceptarlas.
Estados posibles
status | Significado | ¿Habrá más callbacks? |
|---|---|---|
completed | Reporte listo, pdfBase64 presente. | No, es terminal. |
failed | No se pudo generar o entregar el reporte. | No, es terminal. |
Catálogo de errorMessage en status: "failed"
Son los mismos mensajes que devuelve el submit síncrono cuando aplican durante la generación asíncrona, más tres específicos del flujo de entrega. Máximo 500 caracteres.
El body del webhook no lleva
errorCode. El objeto de tres campos aplica a las respuestas HTTP de error de la API, no al payload que te entregamos: aquí sólo viaja el texto enerrorMessage. La columna "código sugerido" de abajo es una recomendación para tu propio diccionario, no un valor que enviemos.
errorMessage exacto | Código sugerido | Caso |
|---|---|---|
La CURP ingresada no es válida, favor de verificar | invalid_curp | CURP rechazado durante el procesamiento. |
No se encontró información en el Seguro Social para la CURP ingresada | no_data_found | CURP válido pero sin registros. |
El Seguro Social reportó inconsistencias en los datos de esta CURP | imss_data_inconsistency | Datos incongruentes en la fuente. |
El servicio no se encuentra disponible, favor de consultar más tarde | service_unavailable | Falla transitoria del sistema durante la generación. |
No fue posible recuperar el PDF del reporte, favor de reintentar la consulta | pdf_download_failed | El reporte se generó pero no pudimos recuperar el PDF para entregártelo. No se cobró el PDF. |
Ocurrió un error inesperado, favor de reintentar | unknown | Error interno no especificado. |
Validación de la firma (obligatorio)
Antes de confiar en un callback debes realizar dos verificaciones:
- Tolerancia de tiempo. El
X-CU-Timestamp(unix epoch en segundos) debe estar dentro de una ventana cercana a tu reloj actual. Recomendamos ±300 segundos (5 min). Esto protege contra replay. - Firma HMAC. Recomputas
HMAC-SHA256sobre los bytes"{X-CU-Timestamp}." + <cuerpo_crudo>usando tuX-API-Keycomo secreto. Comparas contraX-CU-Signatureen tiempo constante.
Importante: firmamos los bytes exactos del cuerpo recibido. No parsees ni re-serialices el JSON antes de validar — los separadores/espacios cambiarían la firma.
Pseudocódigo
secret = TU_API_KEY # misma que usas en X-API-Key
raw_body = bytes_del_request # NO parsear ni re-serializar
timestamp = request.headers["X-CU-Timestamp"] # string de dígitos
signature = request.headers["X-CU-Signature"] # "sha256=<hex>"
if abs(now_unix() - int(timestamp)) > 300:
return 401 # fuera de ventana
signed = (timestamp + ".").encode() + raw_body
expected = "sha256=" + hex(hmac_sha256(secret, signed))
if not constant_time_equals(expected, signature):
return 401Python
python
import hmac, hashlib, time
secret = os.environ["CU_API_KEY"].encode()
raw = request.get_data() # Flask: raw bytes
ts = request.headers.get("X-CU-Timestamp", "")
sig = request.headers.get("X-CU-Signature", "")
if not ts.isdigit() or abs(int(time.time()) - int(ts)) > 300:
abort(401)
signed = f"{ts}.".encode() + raw
digest = hmac.new(secret, signed, hashlib.sha256).hexdigest()
if not hmac.compare_digest(f"sha256={digest}", sig):
abort(401)Node.js
javascript
const crypto = require('crypto');
function verify(rawBody, headers, apiKey) {
const ts = headers['x-cu-timestamp'] || '';
const sig = headers['x-cu-signature'] || '';
if (!/^\d+$/.test(ts) || Math.abs(Math.floor(Date.now() / 1000) - Number(ts)) > 300) {
return false;
}
const signed = Buffer.concat([Buffer.from(`${ts}.`), rawBody]);
const digest = crypto.createHmac('sha256', apiKey).update(signed).digest('hex');
const expected = `sha256=${digest}`;
return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(sig));
}Importante: usa comparación en tiempo constante (
hmac.compare_digest,crypto.timingSafeEqual). Nunca==.
Bash / curl (debug)
bash
TS=1745336400
printf '%s.%s' "$TS" '<cuerpo_crudo>' | openssl dgst -sha256 -hmac "$CU_API_KEY"Qué debe responder tu webhook
- Respuesta exitosa aceptada: HTTP
200,201,202o204. Cualquier otra cosa (incluido299) se considera fallida y la reintentamos. - Sin cuerpo de respuesta requerido.
- Timeout: 15 segundos por intento. Si no respondemos a tiempo, cuenta como fallo de ese intento.
- Responde lo más rápido posible (idealmente
<1s): encola el trabajo y responde202.
Reintentos
Si tu endpoint no responde con un status aceptado:
| Intento | Delay desde el anterior |
|---|---|
| 1 | 0 s |
| 2 | 30 s |
| 3 | 120 s |
Después del 3er intento dejamos de intentar. No hay cola persistente: si perdiste los 3 intentos tienes que re-emitir la consulta manualmente o usar el endpoint de replay.
Idempotencia y deduplicación
- Reenviamos el mismo
uuiden cada reintento. Tu endpoint debe ser idempotente: si ves unuuidque ya procesaste, responde 200 sin volver a hacer el trabajo. - Cada reintento trae un
X-CU-Timestampnuevo y firma nueva. La ventana de tolerancia (±5 min) cierra replays viejos; el chequeo deuuidcierra duplicados dentro de la ventana. - Recomendación: persiste los
uuidpor al menos 24 horas.
Ejemplo mínimo de receptor (Flask)
python
import base64, hmac, hashlib, os, json, time
from flask import Flask, request, abort
app = Flask(__name__)
SECRET = os.environ["CU_API_KEY"].encode()
PROCESSED = set() # usa redis/db en prod
@app.post("/webhook/sc-premium")
def webhook():
raw = request.get_data()
ts = request.headers.get("X-CU-Timestamp", "")
sig = request.headers.get("X-CU-Signature", "")
if not ts.isdigit() or abs(int(time.time()) - int(ts)) > 300:
abort(401)
signed = f"{ts}.".encode() + raw
digest = hmac.new(SECRET, signed, hashlib.sha256).hexdigest()
if not hmac.compare_digest(f"sha256={digest}", sig):
abort(401)
payload = json.loads(raw)
uid = payload["uuid"]
if uid in PROCESSED:
return "", 200
PROCESSED.add(uid)
if payload["status"] == "completed":
pdf = base64.b64decode(payload["pdfBase64"])
with open(f"/tmp/{uid}.pdf", "wb") as f:
f.write(pdf)
else:
# log payload["errorMessage"]
pass
return "", 2022.1 documentData: el PDF estructurado
Si enviaste "parsePdf": true, la entrega completed trae una llave adicional con el contenido de la constancia ya extraído, para que no tengas que parsear el PDF por tu cuenta:
json
{
"version": 1,
"uuid": "9f2b7c1a-4d3e-4a5f-8b0c-2e1d0f9a8b77",
"status": "completed",
"pdfBase64": "JVBERi0xLjQKJ...",
"errorMessage": null,
"documentData": {
"clientData": {
"fullName": "JUAN MOCK PEREZ",
"firstName": "JUAN",
"paternalName": "MOCK",
"maternalName": "PEREZ",
"curp": "MOPJ800101HDFRRN09",
"nss": "12345678901"
},
"weeksDetails": {
"totalWeeks": 726,
"contributedWeeks": 762,
"discountedWeeks": 36,
"reintegratedWeeks": 0,
"summaryChips": [
{ "label": "Semanas Cotizadas", "value": "762" },
{ "label": "Semanas Descontadas", "value": "36" }
]
},
"records": [
{
"employerName": "AGROINDUSTRIAS MOCK S DE RL DE CV",
"employerRegistry": "Y454322213",
"employerEntity": "AGUASCALIENTES",
"startDate": "2022-12-19",
"endDate": "2023-04-03",
"salary": 277.1,
"movements": [
{ "movementType": "BAJA", "movementDate": "2023-04-03", "salary": 277.1 },
{ "movementType": "MODIFICACION DE SALARIO", "movementDate": "2023-01-01", "salary": 247.36 },
{ "movementType": "REINGRESO", "movementDate": "2022-12-19", "salary": 246.38 }
]
}
],
"metadata": {
"issuedDate": "2026-08-10",
"originalChain": "|||Origen de Trámite:Internet (I)|Tipo Trámite:CONSTANCIA DE SEMANAS COTIZADAS|Fecha:10 de agosto 2026, 07:54:44|Apellido Paterno:MOCK|...||",
"inquiryOrigin": "Internet (I)",
"inquiryType": "CONSTANCIA DE SEMANAS COTIZADAS",
"inquiryDate": "10 de agosto 2026, 07:54:44",
"documentFolio": "10000000000000000000001",
"digitalSign": "TW9ja0RpZ2l0YWxTaWduYXR1cmU...==",
"notarialSequence": "00000000-0000-4000-8000-000000000001",
"serialNumber": "00000000000000000001"
}
}
}Bloques
| Bloque | Contenido |
|---|---|
clientData | Identidad del titular: nombre desglosado, CURP y NSS. |
weeksDetails | Semanas totales, cotizadas, descontadas y reintegradas. Cualquiera puede venir null si no aparece en el documento. summaryChips es derivado: sólo incluye descontadas y reintegradas si son mayores a cero. |
records | Un elemento por patrón, con fechas de alta y baja, salario base y sus movements. endDate: null significa patrón vigente. |
metadata | Datos del documento: folio, fecha de emisión, cadena original, sello digital y número de serie. |
Detalles del formato
Conviene no adivinarlos:
- Orden descendente. Tanto
recordscomomovementsvan del más reciente al más antiguo: el primer movimiento de un patrón cerrado es suBAJAy el último suALTAoREINGRESO. salaryes el Salario Base de Cotización diario, no un sueldo mensual: decenas o cientos de pesos. Los registros antiguos traen cifras de uno o dos dígitos.- Tipos de movimiento observados:
ALTA,REINGRESO,MODIFICACION DE SALARIO,BAJA. - Fechas en ISO
YYYY-MM-DD, salvometadata.inquiryDate, que conserva el texto largo en español con hora tal como aparece en la cadena original del Seguro Social ("10 de agosto 2026, 07:54:44"). - Casi todo
metadatapuede venirnullsi el documento no lo trae; sólooriginalChaines siempre una cadena (vacía si no se pudo extraer).
⚠️ documentData es best-effort; pdfBase64 no
Si el reporte llega en un formato que nuestro extractor no reconoce, entregamos "documentData": null conservando el pdfBase64. Nunca perderás el PDF por un fallo de extracción, así que tu receptor debe tratar el null como un caso normal y caer de vuelta al PDF.
⚠️ Tamaño del payload
Con parsePdf: true el body crece: un historial laboral largo puede sumar decenas de KB de JSON sobre un pdfBase64 que ya son cientos. Si tu endpoint tiene un límite de tamaño de body (client_max_body_size en nginx, límites de API Gateway, etc.), súbelo antes de activar el flag o las entregas fallarán con un status que contaremos como intento fallido.
3. Consultar resultado — GET /v3/sc_premium/{uuid}
Consulta el estado actual de una solicitud por uuid. Autenticado con X-API-Key. No consume créditos.
Es la única vía para obtener el resultado en modo polling-only (cuando no enviaste webhookUrl), y también funciona como complemento al webhook si necesitas recuperar un PDF que se perdió en el primer intento.
Request
bash
curl https://api.consultaunica.mx/v3/sc_premium/9f2b7c1a-4d3e-4a5f-8b0c-2e1d0f9a8b77 \
-H "X-API-Key: $CU_API_KEY"Query params
| Param | Tipo | Default | Descripción |
|---|---|---|---|
parsePdf | boolean | false | Si es true, la respuesta incluye documentData. Ver §2.1. |
El parsePdf del GET es independiente del parsePdf del submit. Puedes pedir el documentData al leer aunque hayas enviado la solicitud sin el flag, y viceversa. Es lo que hace que el modo polling-only tenga acceso completo a la función. Sin el parámetro, la llave documentData no aparece en absoluto y el PDF no se parsea.
bash
curl "https://api.consultaunica.mx/v3/sc_premium/9f2b7c1a-4d3e-4a5f-8b0c-2e1d0f9a8b77?parsePdf=true" \
-H "X-API-Key: $CU_API_KEY"Respuesta
El shape es idéntico al cuerpo del webhook, con un estado adicional pending mientras la consulta aún se genera:
json
// pending — generación en curso; vuelve a consultar en unos segundos
{ "version": 1, "uuid": "...", "status": "pending", "pdfBase64": null, "errorMessage": null }json
// completed — reporte listo
{ "version": 1, "uuid": "...", "status": "completed", "pdfBase64": "JVBE...", "errorMessage": null }json
// failed — no se pudo generar
{ "version": 1, "uuid": "...", "status": "failed", "pdfBase64": null, "errorMessage": "..." }Con ?parsePdf=true los tres llevan además documentData: el objeto de §2.1 cuando el estado es completed, y null en pending y failed. Un PDF que no parsea también devuelve "documentData": null con pdfBase64 intacto y HTTP 200 — nunca un error.
Errores
| HTTP | Caso | errorCode | message |
|---|---|---|---|
| 404 | El uuid no existe o no pertenece a tu cuenta | request_not_found | No encontramos la consulta solicitada, favor de verificar el identificador |
| 401 | X-API-Key ausente, inválida o revocada | ver 401 — Autenticación |
request_not_foundno es lo mismo que un 404 de ruta inexistente. Si escribes mal la URL recibirás el 404 genérico de la API ({"message": "Resource not found", "errorCode": ""}). El códigorequest_not_foundconfirma que la ruta era correcta y que eluuidfue el que no se encontró.
Cuándo usarlo
- Modo polling-only: enviaste el submit sin
webhookUrl. Consulta hasta questatusdeje de serpending. - Tu webhook estuvo caído y quieres recuperar el reporte.
- Necesitas verificar el estado antes de tomar una decisión en tu UI.
Frecuencia recomendada: son dos tiempos distintos. Espera 30 s después del submit antes de la primera consulta —el tiempo típico de generación arranca ahí, así que consultar antes sólo devuelve pending— y a partir de entonces consulta cada ~10 segundos. En ningún caso hagas más de 1 request cada 5 segundos por uuid. El resultado típico tarda 30 s – 3 min y hasta 10 min en escenarios degradados.
4. Reintentar entrega — POST /v3/sc_premium/{uuid}/replay
Aplica sólo a solicitudes que se enviaron con
webhookUrl. Solicitudes en modo polling-only no tienen URL a la cual reenviar y devuelven422— usaGET /v3/sc_premium/{uuid}para obtener el resultado.
Si las 3 entregas automáticas a tu webhook fallaron (o quieres una copia adicional), reintenta manualmente. Autenticado con X-API-Key. No consume créditos.
Request
bash
curl -X POST https://api.consultaunica.mx/v3/sc_premium/9f2b7c1a-4d3e-4a5f-8b0c-2e1d0f9a8b77/replay \
-H "X-API-Key: $CU_API_KEY"Respuesta — 202 Accepted
json
{ "uuid": "9f2b7c1a-...", "scheduled": true }Disparamos una nueva entrega al webhookUrl original en background, con los mismos 3 intentos y backoff que una entrega regular.
parsePdf en el replay
El replay no acepta un parsePdf propio: reentrega con la forma que pidió el submit original. Si tu solicitud llevaba "parsePdf": true, la reentrega incluye documentData; si no, no lo incluye. Un ?parsePdf=true en la URL del replay se ignora.
Es deliberado: si la reentrega tuviera una forma distinta a la entrega original, tu endpoint recibiría dos contratos distintos para el mismo uuid — justo lo que el replay existe para evitar. Si lo que quieres es el documentData de una solicitud que no lo pidió, obtenlo por GET /v3/sc_premium/{uuid}?parsePdf=true, que sí lo permite en cualquier momento.
Cooldown
Solo un replay por uuid cada 60 segundos. El segundo request dentro de esa ventana recibe 429:
http
HTTP/1.1 429 Too Many Requests
Retry-After: 47json
{
"message": "Debes esperar antes de volver a solicitar la entrega de esta consulta",
"description": "Ya solicitaste el reenvío de esta consulta hace menos de un minuto; espera un momento antes de pedir otro.",
"errorCode": "replay_cooldown_active"
}Retry-After indica los segundos restantes del cooldown.
Errores
| HTTP | Caso | errorCode |
|---|---|---|
| 404 | uuid no existe o no pertenece a tu cuenta | request_not_found |
| 409 | La consulta aún está en status=pending (no hay nada que reenviar) | replay_not_available_while_pending |
| 422 | La solicitud original se envió en modo polling-only (sin webhookUrl) | replay_unavailable_polling_only |
| 429 | Cooldown activo por uuid | replay_cooldown_active + Retry-After |
| 500 | No se pudo leer la configuración de entrega de la solicitud | replay_metadata_failed |
| 401 | Auth | ver 401 — Autenticación |
Flujo típico de recuperación
- Consultas
GET /sc_premium/{uuid}→status=completed(ofailed). - Llamas
POST /sc_premium/{uuid}/replay→ 202. - Tu endpoint webhook recibe el mismo body que habría recibido originalmente (mismo
uuid, firma fresca con nuevoX-CU-Timestamp).
5. Flujo completo
Entregamos el resultado a tu URL en cuanto está listo. No tienes que preguntar por él.
9 pasos
Secuencia del modo Webhook (push):
- De Tu backend a Consulta Única: POST /v3/sc_premium, X-API-Key · curp · webhookUrl.
- De Consulta Única a Seguro Social: Validación de la CURP.
- De Seguro Social a Consulta Única: 200 OK, { nss, fullname }.
- De Consulta Única a Tu backend: 200 OK · retiene 3 créditos, { uuid, nss, fullname }.
- De Consulta Única a Seguro Social: Consulta de semanas cotizadas.
- Espera — generación: 30 s – 3 min.
- De Seguro Social a Consulta Única: Constancia en PDF, aquí se cobran los 3 créditos.
- De Consulta Única a Tu backend: POST <webhookUrl>, firmado · { uuid, status, pdfBase64 }.
- De Tu backend a Consulta Única: 202 Accepted, valida la firma antes de confiar.
Tiempos típicos de generación: 30 s – 3 min entre submit y resultado. Diseña tu UX asumiendo hasta 10 minutos en casos degradados.
Ver los dos flujos en texto
Modo webhook (push)
Tu backend Consulta Única Seguro Social
│ │ │
│ POST /v3/sc_premium │ │
│ X-API-Key · curp · webhookUrl │ │
│────────────────────────────────────────>│ │
│ │ │
│ │ Validación de la CURP │
│ │────────────────────────────────────────>│
│ │ │
│ │ 200 OK │
│ │ { nss, fullname } │
│ │<────────────────────────────────────────│
│ │ │
│ 200 OK · retiene 3 créditos │ │
│ { uuid, nss, fullname } │ │
│<────────────────────────────────────────│ │
│ │ │
│ │ Consulta de semanas cotizadas │
│ │────────────────────────────────────────>│
│ │ │
│ │ ... generación: 30 s – 3 min ... │
│ │ │
│ │ Constancia en PDF │
│ │ aquí se cobran los 3 créditos │
│ │<────────────────────────────────────────│
│ │ │
│ POST <webhookUrl> │ │
│ firmado · { uuid, status, pdfBase64 } │ │
│<────────────────────────────────────────│ │
│ │ │
│ 202 Accepted │ │
│ valida la firma antes de confiar │ │
│────────────────────────────────────────>│ │
│ │ │Modo polling-only (pull)
Tu backend Consulta Única Seguro Social
│ │ │
│ POST /v3/sc_premium │ │
│ X-API-Key · curp — sin webhookUrl │ │
│────────────────────────────────────────>│ │
│ │ │
│ │ Validación de la CURP │
│ │────────────────────────────────────────>│
│ │ │
│ │ 200 OK │
│ │ { nss, fullname } │
│ │<────────────────────────────────────────│
│ │ │
│ 200 OK · retiene 3 créditos │ │
│ { uuid, nss, fullname } │ │
│<────────────────────────────────────────│ │
│ │ │
│ │ Consulta de semanas cotizadas │
│ │────────────────────────────────────────>│
│ │ │
│ │ ... generación: 30 s – 3 min ... │
│ │ │
│ GET /v3/sc_premium/{uuid} │ │
│ cada ~10 s · no consume créditos │ │
│────────────────────────────────────────>│ │
│ │ │
│ 200 OK │ │
│ { status: "pending" } │ │
│<────────────────────────────────────────│ │
│ │ │
│ │ Constancia en PDF │
│ │ aquí se cobran los 3 créditos │
│ │<────────────────────────────────────────│
│ │ │
│ GET /v3/sc_premium/{uuid} │ │
│────────────────────────────────────────>│ │
│ │ │
│ 200 OK │ │
│ { status: "completed", pdfBase64: … } │ │
│<────────────────────────────────────────│ │
│ │ │6. Preguntas frecuentes
¿Cuándo conviene usar polling-only y cuándo webhook?
- Webhook (push) es la opción recomendada si tu backend tiene un endpoint HTTPS público: te enteras del resultado en cuanto está listo, sin polling extra.
- Polling-only (pull) es ideal si corres en infraestructura sin endpoint público (jobs cron, máquinas detrás de NAT, scripts locales) o si quieres simplicidad operativa sin manejar firma HMAC ni reintentos en tu lado.
¿Puedo cambiar el webhookUrl después de enviar la solicitud? No. La URL se captura al momento del submit. Si necesitas otra, emite una nueva solicitud. Tampoco puedes pasar de polling-only a webhook (ni viceversa) en una solicitud existente.
¿Qué pasa si mi servidor está caído cuando entregamos? Hacemos 3 intentos (0s / 30s / 120s). Si todos fallan, puedes recuperarte sin pagar de nuevo: consulta GET /v3/sc_premium/{uuid} para ver el estado + descargar el PDF, o llama POST /v3/sc_premium/{uuid}/replay para reintentar la entrega (cooldown 60s).
¿Qué hago si mi cliente pierde la respuesta del submit por un error de red? Reintenta el mismo submit con un header Idempotency-Key estable. Si el primero sí llegó y se procesó, el retry devuelve la misma respuesta sin cobrar de nuevo. Ver Idempotency-Key (opcional).
¿Cómo pruebo en desarrollo? Usa un servicio tipo webhook.site, ngrok o smee.io para exponer tu endpoint local en HTTPS con IP pública. Localhost y túneles HTTP no pasan la validación. Si no quieres montar nada de eso, usa modo polling-only: omite webhookUrl y consulta el resultado por GET.
¿Cuántos créditos cuesta? 3 por cada submit exitoso. Los webhooks de entrega no cobran adicional.
¿El uuid es el mismo que guardan internamente? Sí. Úsalo como referencia si necesitas abrir soporte.
7. Modo Mock
Para pruebas de integración del lado del cliente, hay endpoints mock con prefijo /mock que simulan el contrato completo sin ejecutar la consulta real.
Rutas
| Productivo | Mock equivalente |
|---|---|
POST /v3/sc_premium | POST /v3/mock/sc_premium |
GET /v3/sc_premium/{uuid} | GET /v3/mock/sc_premium/{uuid} |
POST /v3/sc_premium/{uuid}/replay | POST /v3/mock/sc_premium/{uuid}/replay |
Migración: elimina /mock del path. El header X-Mock-Mode será ignorado en producción.
Header X-Mock-Mode
| Valor | Comportamiento |
|---|---|
success | Tras ~1 s, webhook con status=completed y un PDF dummy en pdfBase64. |
failed | Tras ~1 s, webhook con status=failed y errorMessage="mock_service_error". |
pending | No dispara webhook. GET siempre regresa status=pending. |
| (ausente) | Default success. |
Valores no permitidos → 400 con errorCode: "invalid_mock_mode".
El mock devuelve los mismos
errorCodeque producción. Unuuidinexistente responderequest_not_founden ambos, el replay bloqueado respondereplay_cooldown_activeen ambos, etc. Puedes construir tu diccionario de códigos contra el mock y migrar a producción sin cambiarlo. El único código exclusivo del mock esinvalid_mock_mode, que en producción no existe.
Polling-only en mock: el mock también soporta omitir
webhookUrl. Si lo omites, no dispararemos webhook (independiente del modo) yreplayregresará422 replay_unavailable_polling_only. ElGETsigue funcionando idéntico a producción.
El
documentDatadel mock es un fixture aparte, no el parseo de su PDF. ElpdfBase64del mock es un PDF dummy del que no se puede extraer nada, así que parsearlo de verdad devolveríanullsiempre y no podrías ejercitar tu código de consumo. Es la única parte del mock donde el PDF y los datos no corresponden al mismo documento. La estructura del fixture sí está calcada de una constancia real: mismo orden descendente, mismas magnitudes de SBC diario, los cuatro tipos de movimiento y un patrón vigente (endDate: null).
Comportamiento
- Los uuids generados viven 1 hora en cache (Redis); luego expiran y
GET/replayregresan 404. - El webhook se dispara al
webhook_urlprovisto, firmado con tuapi_key(mismos headersX-CU-Signature,X-CU-Timestamp,X-CU-Uuidque producción). Un único intento, sin reintentos. En polling-only no se dispara. Idempotency-Keyse acepta por compatibilidad pero se ignora en mock.- Datos mock fijos en la respuesta del POST:
nss="12345678901",fullname="JUAN MOCK PEREZ". GET /v3/mock/sc_premium/{uuid}?parsePdf=truedevuelve undocumentDatafixture fijo con tres patrones y sus movimientos, con la misma forma que producción. En modopendingofaileddevuelve"documentData": null.replaymantiene el cooldown de 60 s y regresa 409 si el modo espending.- Mock no consume créditos. Pruébalo todas las veces que quieras.
- Rate limit: 3 req / 2 s por
X-API-KeyenPOST /mock/sc_premium— la misma cuota que producción, en un bucket independiente. Lo que acota el volumen del mock es su cuota diaria (mock_daily_quota_exceeded), no este limitador. - La respuesta del POST sí incluye el header
X-Remaining-Requestscon tu saldo actual (sin deducción, ya que el mock no cobra). Útil para que tu cliente ejercite la lógica de alertas igual que en producción.
Ejemplos
Submit success:
bash
curl -X POST https://api.consultaunica.mx/v3/mock/sc_premium \
-H "X-API-Key: $CU_API_KEY" \
-H "Content-Type: application/json" \
-H "X-Mock-Mode: success" \
-d '{"curp":"PEGJ800101HDFRRN09","webhookUrl":"https://tuapp.com/cb"}'Submit que simula error del servicio:
bash
curl -X POST https://api.consultaunica.mx/v3/mock/sc_premium \
-H "X-API-Key: $CU_API_KEY" \
-H "Content-Type: application/json" \
-H "X-Mock-Mode: failed" \
-d '{"curp":"PEGJ800101HDFRRN09","webhookUrl":"https://tuapp.com/cb"}'Submit pending (no dispara webhook):
bash
curl -X POST https://api.consultaunica.mx/v3/mock/sc_premium \
-H "X-API-Key: $CU_API_KEY" \
-H "Content-Type: application/json" \
-H "X-Mock-Mode: pending" \
-d '{"curp":"PEGJ800101HDFRRN09","webhookUrl":"https://tuapp.com/cb"}'Submit pidiendo el PDF estructurado en el webhook:
bash
curl -X POST https://api.consultaunica.mx/v3/mock/sc_premium \
-H "X-API-Key: $CU_API_KEY" \
-H "Content-Type: application/json" \
-d '{"curp":"PEGJ800101HDFRRN09","webhookUrl":"https://tuapp.com/cb","parsePdf":true}'Polling y replay:
bash
curl https://api.consultaunica.mx/v3/mock/sc_premium/<uuid> \
-H "X-API-Key: $CU_API_KEY"
# Con el PDF estructurado
curl "https://api.consultaunica.mx/v3/mock/sc_premium/<uuid>?parsePdf=true" \
-H "X-API-Key: $CU_API_KEY"
curl -X POST https://api.consultaunica.mx/v3/mock/sc_premium/<uuid>/replay \
-H "X-API-Key: $CU_API_KEY"8. Changelog
- v1.9 (corrección de documentación, sin cambio de contrato) — se documenta que los 3 créditos se retienen al aceptar el submit y que el cargo se consuma cuando el Seguro Social nos entrega el PDF. La página decía sólo que «cada solicitud exitosa consume 3 créditos», lo que dejaba sin explicar por qué
pdf_download_failed, la re-entrega del mismo día yrecent_queryno cuestan nada: en ninguno de los tres llega a haber PDF que consuma la retención. Si concilias tu saldo contra tus propios registros, la entrega del PDF es el momento que debes tomar como cobro. También baja la frecuencia de polling recomendada, de 30 s a ~10 s entre consultas; la espera antes de la primera consulta sigue siendo de 30 s. Nada de esto cambia el comportamiento de la API: se corrige lo que la documentación describía. - v1.8 — la respuesta del submit incluye
redelivered(booleano):truecuando se trata de una re-entrega del mismo día —sin cargo—,falseen una consulta nueva. Ese caso existía desde antes pero era indistinguible desde la respuesta: elmessagees el mismo que el de una consulta nueva. Cambio aditivo: si ignoras el campo, nada cambia para tu integración. El mismo campo, con la misma semántica, existe en Actas. - v1.7 — el rate limit del submit no es 5 req/min: desde el 2 de julio de 2026 es 3 req / 2 s y la documentación se había quedado con el número viejo. La corrección es a tu favor: al ser un token bucket, la ráfaga baja de 5 a 3 pero el ritmo sostenido sube de 5 a 90 req/min, así que si dimensionaste tus lotes con lo que decíamos, puedes subirlos. (Aquí no cambia nada del contrato: solo se documenta lo que ya corría.) Sí cambia el endpoint
/mock, que tenía una cuota propia de 5 req/min y ahora usa la misma que producción — más permisiva, así que nada que ya funcionara deja de hacerlo. El objetivo es que lo que calibres contra el ambiente de pruebas sea lo que encuentres en producción. El volumen del mock lo sigue acotando su cuota diaria. - v1.6 — todos los errores de
GET /v3/sc_premium/{uuid},POST /{uuid}/replay, los endpoints/mocky la validación deIdempotency-Keyemiten ahoraerrorCodeydescription, igual que el submit. Cambio enmessage: donde antes viajaba el slug técnico ("message": "replay_cooldown_active") ahora va un texto en español y el slug pasa aerrorCode. Si tu integración compara el valor demessage, cámbiala aerrorCode— es el campo estable. Ningúnstatus_codecambia. Eluuidinexistente estrenarequest_not_found, que lo distingue del404de una ruta mal escrita. El mock emite los mismos códigos que producción. - v1.5 (corrección de documentación, sin cambio de contrato) — se documenta el formato real del body de error:
message+description+errorCode, y nodetail. Se corrigen los status que estaban mal descritos: la validación de payload responde 409 (no 422) y agrega la llaveerrors;invalid_curpeimss_data_inconsistencysalen con 400 (no 422). Se agregan el403 api_key_requires_purchase, el422 recent_queryy el500 replay_metadata_failed, y se marcan los endpoints que todavía responden conerrorCodevacío (GET,replay,/mock, y todos los 404). - v1.4 —
parsePdfopcional: campo del submit para recibirdocumentData(la constancia ya estructurada en JSON) en el webhook, y query param delGETy del mock para pedirlo en cada lectura. Aditivo y opt-in:versionsigue en1y quien no lo pida recibe exactamente el mismo payload de antes. Un PDF que no parsea devuelvedocumentData: nullconservando elpdfBase64. - v1.3 —
webhookUrlahora es opcional. Omítelo para usar modo polling-only y consultar el resultado porGET /v3/sc_premium/{uuid}. Replay sobre solicitudes polling-only devuelve422 replay_unavailable_polling_only. Mismo comportamiento en/mock. - v1.2 — endpoints mock
/v3/mock/sc_premium*con headerX-Mock-Mode(success/failed/pending) para pruebas de integración sin consumir créditos. - v1.1 —
Idempotency-Keyopcional en submit, rate limit específico de 5 req/min, endpoints de consulta y replay, webhook body con campoversion: 1firmado. - v1.0 — primer release del endpoint público
sc_premium.