# 🏥 Servicio Premium de Semanas Cotizadas del IMSS (asíncrono)
Endpoint asíncrono para obtener el reporte Premium de Semanas Cotizadas del IMSS. 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 por submit; 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/#prices (opens new window)
# 📑 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
# 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)
POST /v3/sc_premium HTTP/1.1
Host: consultaunica.mx
X-API-Key: <tu_api_key>
Content-Type: application/json
{
"curp": "AAAA800101HDFBBB01",
"webhookUrl": "https://tu-dominio.com/webhook/sc-premium"
}
# 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}.
POST /v3/sc_premium HTTP/1.1
Host: 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. |
# 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 solicitud exitosa consume 3 créditos de tu saldo.
# Rate limit
Máximo 5 requests por minuto 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.
# 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 | detail |
|---|---|---|
| 400 | Key con formato inválido | invalid_idempotency_key |
| 409 | Key ya en uso por otra request concurrente que aún no termina | concurrent_idempotent_request |
Ejemplo curl con idempotency:
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
{
"uuid": "9f2b7c1a-4d3e-4a5f-8b0c-2e1d0f9a8b77",
"message": "Solicitud recibida, pendiente de resultado",
"curp": "AAAA800101HDFBBB01",
"nss": "12345678901",
"fullname": "Juan Pérez López"
}
| 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. |
Headers de respuesta:
X-Remaining-Requests: créditos restantes después de este consumo.
# Ejemplo curl
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
Todos los errores respetan el formato estándar de nuestro servidor:
{ "detail": "<mensaje o arreglo>" }
A continuación la lista completa de respuestas que puede recibir tu cliente. Usa el mensaje exacto (columna detail) para armar tu catálogo; son cadenas estables entre versiones.
# 400 — Webhook inválido
HTTP/1.1 400 Bad Request
{
"detail": "webhookUrl debe ser https y apuntar a un host público accesible"
}
# 401 — Autenticación
HTTP/1.1 401 Unauthorized
{ "detail": "No se encontró la Clave para realizar consultas" }
{ "detail": "Cuenta no válida, favor de verificar su clave de consulta" }
{ "detail": "Usuario no encontrado" }
| Caso | detail |
|---|---|
Header X-API-Key ausente o vacío | No se encontró la Clave para realizar consultas |
| API key no corresponde a una cuenta activa | Cuenta no válida, favor de verificar su clave de consulta |
| Cuenta sin registro post-autenticación (caso raro) | Usuario no encontrado |
# 422 — Validación del payload
Nuestro servidor devuelve un arreglo describiendo cada campo inválido.
HTTP/1.1 422 Unprocessable Entity
{
"detail": [
{
"type": "string_pattern_mismatch",
"loc": ["body", "curp"],
"msg": "String should match pattern '^[A-Z]{4}[0-9]{6}[HM][A-Z]{5}[0-9A-Z][0-9]$'",
"input": "no-es-un-curp"
}
]
}
Campos que pueden disparar 422:
curpfaltante, 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.
# 422 — CURP no localizado o datos inconsistentes
HTTP/1.1 422 Unprocessable Entity
{ "detail": "La CURP ingresada no es válida, favor de verificar" }
{ "detail": "No se encontró información en el IMSS para la CURP ingresada" }
{ "detail": "El IMSS reportó inconsistencias en los datos de esta CURP" }
{ "detail": "Ya existe una consulta en curso para esta CURP, espere a que termine antes de reintentar" }
# 429 — Sin créditos
HTTP/1.1 429 Too Many Requests
{
"detail": "Has alcanzado el límite de consultas diarias, visita nuestros paquetes para más: https://consultaunica.mx/#prices"
}
# 429 — Rate limit
HTTP/1.1 429 Too Many Requests
{
"detail": "Debe esperar 60 segundos para volver a consultar, evitemos congestionar el servicio"
}
El número de segundos es dinámico; haz match por prefijo Debe esperar si necesitas detectarlo.
# 503 — Servicio no disponible
HTTP/1.1 503 Service Unavailable
{ "detail": "El servicio no se encuentra disponible, favor de consultar más tarde" }
{ "detail": "El sistema se encuentra a máxima capacidad, favor de esperar unos minutos antes de reintentar" }
{ "detail": "El servicio de semanas cotizadas premium no está disponible, favor de intentar más tarde" }
Recomendación: reintenta con backoff exponencial empezando en 60 s.
# 500 — Error interno
HTTP/1.1 500 Internal Server Error
{ "detail": "Ocurrió un error inesperado, favor de reintentar" }
# Catálogo de mensajes para armar tu diccionario
| HTTP | detail exacto | Código sugerido |
|---|---|---|
| 400 | webhookUrl debe ser https y apuntar a un host público accesible | invalid_webhook_url |
| 401 | No se encontró la Clave para realizar consultas | missing_api_key |
| 401 | Cuenta no válida, favor de verificar su clave de consulta | unauthorized_user |
| 401 | Usuario no encontrado | user_not_found |
| 422 | La CURP ingresada no es válida, favor de verificar | invalid_curp |
| 422 | No se encontró información en el IMSS para la CURP ingresada | no_data_found |
| 422 | El IMSS reportó inconsistencias en los datos de esta CURP | imss_data_inconsistency |
| 422 | Ya existe una consulta en curso para esta CURP, espere a que termine antes de reintentar | duplicate_in_progress |
| 422 | (arreglo del validador de solicitudes) | validation_error |
| 429 | Has alcanzado el límite de consultas diarias, visita nuestros paquetes para más: … | insufficient_credits |
| 429 | Debe esperar {N} segundos para volver a consultar, evitemos congestionar el servicio | rate_limit_exceeded |
| 503 | El servicio no se encuentra disponible, favor de consultar más tarde | service_unavailable |
| 503 | El sistema se encuentra a máxima capacidad, favor de esperar unos minutos antes de reintentar | system_at_capacity |
| 503 | El servicio de semanas cotizadas premium no está disponible, favor de intentar más tarde | system_paused |
| 500 | Ocurrió un error inesperado, favor de reintentar | unknown |
# 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
{
"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.
# Body — entrega fallida
{
"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.
- 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.
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 IMSS para la CURP ingresada | no_data_found | CURP válido pero sin registros. |
El IMSS 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 401
# 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
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)
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)
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 "", 202
# 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
curl https://api.consultaunica.mx/v3/sc_premium/9f2b7c1a-4d3e-4a5f-8b0c-2e1d0f9a8b77 \
-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:
// pending — generación en curso; vuelve a consultar en unos segundos
{ "version": 1, "uuid": "...", "status": "pending", "pdfBase64": null, "errorMessage": null }
// completed — reporte listo
{ "version": 1, "uuid": "...", "status": "completed", "pdfBase64": "JVBE...", "errorMessage": null }
// failed — no se pudo generar
{ "version": 1, "uuid": "...", "status": "failed", "pdfBase64": null, "errorMessage": "..." }
# Errores
| HTTP | Caso | detail |
|---|---|---|
| 404 | El uuid no existe o no pertenece a tu cuenta | not_found |
| 401 | X-API-Key ausente, inválida o revocada | ver 401 — Autenticación |
# 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: no más de 1 request cada 5 segundos por uuid. Consultas muy frecuentes pueden devolver siempre pending sin avance real. Empieza polleando 30 s después del submit; 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
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
{ "uuid": "9f2b7c1a-...", "scheduled": true }
Disparamos una nueva entrega al webhookUrl original en background, con los mismos 3 intentos y backoff que una entrega regular.
# Cooldown
Solo un replay por uuid cada 60 segundos. El segundo request dentro de esa ventana recibe 429:
HTTP/1.1 429 Too Many Requests
Retry-After: 47
{ "detail": "replay_cooldown_active" }
Retry-After indica los segundos restantes del cooldown.
# Errores
| HTTP | Caso | detail |
|---|---|---|
| 404 | uuid no existe o no pertenece a tu cuenta | 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 |
| 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
# Modo webhook (push)
┌─────────┐ ┌──────────────────┐
│ Cliente │ │ Consulta Única │
└────┬────┘ └────────┬─────────┘
│ POST /v3/sc_premium │
│ (X-API-Key, curp, webhookUrl) │
│──────────────────────────────────────────────────────>│
│ │
│ 200 OK { uuid, message, curp, nss, fullname } │
│<──────────────────────────────────────────────────────│
│ │
│ ... generación ... │
│ │
│ POST <webhookUrl> │
│ X-CU-Timestamp: <unix_ts> │
│ X-CU-Signature: sha256=... │
│ X-CU-Uuid: <uuid> │
│ { uuid, status, pdfBase64, errorMessage } │
│<──────────────────────────────────────────────────────│
│ │
│ 200 / 201 / 202 / 204 │
│──────────────────────────────────────────────────────>│
# Modo polling-only (pull)
┌─────────┐ ┌──────────────────┐
│ Cliente │ │ Consulta Única │
└────┬────┘ └────────┬─────────┘
│ POST /v3/sc_premium │
│ (X-API-Key, curp) ← sin webhookUrl │
│──────────────────────────────────────────────────────>│
│ │
│ 200 OK { uuid, message, curp, nss, fullname } │
│<──────────────────────────────────────────────────────│
│ │
│ ... generación ... │
│ │
│ GET /v3/sc_premium/{uuid} (cada ~30 s) │
│──────────────────────────────────────────────────────>│
│ 200 OK { status: "pending", ... } │
│<──────────────────────────────────────────────────────│
│ ... │
│ GET /v3/sc_premium/{uuid} │
│──────────────────────────────────────────────────────>│
│ 200 OK { status: "completed", pdfBase64: "JVB..." } │
│<──────────────────────────────────────────────────────│
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.
# 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 invalid_mock_mode.
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.
# 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". 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: 5 req/min por
X-API-KeyenPOST /mock/sc_premium, independiente del bucket de producción. - 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:
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:
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):
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"}'
Polling y replay:
curl https://api.consultaunica.mx/v3/mock/sc_premium/<uuid> \
-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.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.