Esquema de color
Are you an LLM? You can read better optimized documentation at /services/registro-civil/actas.md for this page in Markdown format
📜 Servicio de Actas del Registro Civil (asíncrono)
Endpoint asíncrono para obtener actas del registro civil —nacimiento, matrimonio, defunción y divorcio, en variante estándar o foliada— a partir de una CURP. El cliente envía la solicitud y elige cómo recibir el resultado:
- Modo webhook: provee
webhookUrly entregamos el PDF del acta a esa URL cuando esté lista (firmado con HMAC-SHA256). - Modo polling-only: omite
webhookUrly consulta el resultado porGET /v3/actas/{uuid}cuando lo necesites.
Ambos modos cuestan los mismos 4 créditos por submit; lo único que cambia es cómo te enteras del resultado.
Si ya integraste Semanas Cotizadas Premium, este servicio usa el mismo patrón: mismo esquema de firma, mismos headers
X-CU-*, mismouuid, mismos nombres deerrorCode. Reutilizas tu validador de webhook y tu diccionario de errores tal cual. Las diferencias están resumidas en §6 Preguntas frecuentes.
- 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/actas - Webhook de entrega —
POSTa tuwebhookUrl - Consultar resultado —
GET /v3/actas/{uuid} - Reintentar entrega —
POST /v3/actas/{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": "El servicio de actas está disponible de 7:30 a 22:00 (hora del centro de México), favor de intentar dentro de ese horario",
"description": "Las actas solo pueden solicitarse dentro del horario de atención del día; vuelve a intentarlo dentro de ese horario.",
"errorCode": "outside_business_hours"
}| 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. - Los
errorCodeson los mismos que en Semanas Cotizadas Premium cuando la causa es la misma (duplicate_in_progress,recent_query,invalid_curp,request_not_found,replay_*,invalid_webhook_url,concurrent_idempotent_request). Mantén un solo diccionario para ambos servicios. 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.- El único error que trae una llave extra es la validación de payload, que agrega
errors(ver 409).
1. Submit — POST /v3/actas
Envía una solicitud de acta para una CURP e indica a dónde entregar el resultado.
Request — modo webhook (push)
http
POST /v3/actas HTTP/1.1
Host: api.consultaunica.mx
X-API-Key: <tu_api_key>
Content-Type: application/json
{
"curp": "CASE020722HTSRNDA8",
"actaType": "nacimiento",
"conFolio": false,
"webhookUrl": "https://tu-dominio.com/webhook/actas"
}Request — modo polling-only (pull)
http
POST /v3/actas HTTP/1.1
Host: api.consultaunica.mx
X-API-Key: <tu_api_key>
Content-Type: application/json
{
"curp": "CASE020722HTSRNDA8",
"actaType": "nacimiento"
}Campos
| Campo | Tipo | Requerido | Descripción |
|---|---|---|---|
curp | string | sí | CURP de 18 caracteres, se normaliza a mayúsculas y se valida contra regex. |
actaType | string | sí | Tipo de acta: nacimiento, matrimonio, defuncion o divorcio. Sin acentos y en minúsculas. |
conFolio | boolean | no | Default false. Con true solicitamos la variante foliada del acta. Cuesta lo mismo. No aplica a divorcio — ver abajo. |
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/actas/{uuid}. Ver requisitos. |
Solo se busca por CURP. La consulta por cadena o folio electrónico no está soportada en esta versión.
El acta de divorcio no tiene variante foliada. El registro civil solo la emite sin folio. Si envías
actaType: "divorcio"conconFolio: true, no rechazamos la solicitud: la tratamos comoconFolio: falsey te devolvemosconFolio: falseen la respuesta, para que el eco del submit te diga qué se pidió realmente. Consecuencia práctica: en divorcio las dos formas de pedir la misma acta caen en la misma ventana de duplicados, así que no se cobra dos veces.
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.
Ventana horaria y capacidad
Dos condiciones propias de este servicio que no existen en los demás endpoints de la API. Ambas se evalúan antes de cobrar, así que un rechazo por cualquiera de las dos no consume créditos.
| Condición | Regla | Respuesta si no se cumple |
|---|---|---|
| Ventana horaria | Las actas solo pueden solicitarse de 07:30 a 22:00, hora del centro de México. | 403 outside_business_hours |
| Capacidad | Hay un límite global de 5 solicitudes de acta en curso simultáneas. | 503 system_at_capacity |
No hay encolamiento: en ambos casos la solicitud se rechaza y te toca reintentar.
- Ante
outside_business_hours, reintenta dentro de la ventana. Si programas trabajos por lotes, agéndalos dentro del horario. - Ante
system_at_capacity, reintenta con backoff (empieza en 30–60 s). Es una condición transitoria, no un problema de tu cuenta.
Costo
Cada solicitud exitosa consume 4 créditos de tu saldo, descontados al momento del submit. La variante foliada cuesta lo mismo.
Si la consulta termina en failed, revertimos el cobro. No pagas por un acta que no se pudo entregar.
Rate limit
Máximo 3 requests cada 2 segundos por X-API-Key en este endpoint. Al rebasarlo devolvemos 429 rate_limit_exceeded con header Retry-After. Es independiente del límite global de la cuenta y del rate limit de otros servicios.
Consultas duplicadas
Tratamos como duplicado la misma combinación (tu cuenta, curp, actaType, conFolio) dentro de la ventana del día (hasta la medianoche del centro de México):
| Situación | Respuesta |
|---|---|
| Ya hay una solicitud de esa combinación en curso | 422 duplicate_in_progress |
| Ya se completó | 200 — te re-entregamos el acta, sin cobrar |
Dos precisiones importantes:
- El scope incluye el tipo y la variante. El mismo CURP sí puede tener en curso un acta de
nacimientoy otra dedefuncion, o la foliada y la no-foliada del mismo tipo, en paralelo. Endivorciola variante no distingue nada —siempre esfalse—, así que ahí solo cuenta el tipo. - El alcance es cross-canal. Una solicitud iniciada desde el portal web también cuenta como duplicado por API, y viceversa.
Un duplicate_in_progress no se re-entrega porque todavía no hay resultado que servir: espera el desenlace de la solicitud original.
Re-entrega del mismo día
Si reenvías un submit de algo que ya completaste hoy, en vez de rechazarlo te servimos el acta que ya obtuviste. No volvemos a consultar al registro civil, así que:
- No consume créditos. El header
x-remaining-requestsreporta tu saldo sin descuento. - No aplica la ventana horaria. Funciona a cualquier hora, también fuera de 07:30–22:00.
- No consume capacidad. No depende del límite de solicitudes simultáneas.
- 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(y elmessagecambia a"Consulta del día disponible"). Es informativo: el camino programático es idéntico al de una consulta normal. Si necesitas distinguirlas —para no volver a cobrarle a tu usuario, por ejemplo— usa el booleano, no el texto delmessage.
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 acta sin pagarla dos veces.
Si el archivo de la solicitud original ya no es recuperable, el submit responde 422 recent_query, que era el comportamiento anterior. 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: hasta la medianoche del centro de México, con un piso de 1 hora. Después de eso, la misma key vuelve a tratarse como consulta nueva.
- Scope: por usuario y por servicio. La misma key en Semanas Cotizadas y en Actas son dos consultas distintas, y dos usuarios 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.
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/actas \
-H "X-API-Key: $CU_API_KEY" \
-H "Idempotency-Key: orden-12345-reintento-1" \
-H "Content-Type: application/json" \
-d '{"curp":"CASE020722HTSRNDA8","actaType":"nacimiento","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
{
"message": "Solicitud recibida, pendiente de resultado",
"uuid": "9f2b7c1a-4d3e-4a5f-8b0c-2e1d0f9a8b77",
"curp": "CASE020722HTSRNDA8",
"actaType": "nacimiento",
"conFolio": false,
"redelivered": false
}| Campo | Tipo | Descripción |
|---|---|---|
message | string | Mensaje descriptivo del estado. |
uuid | string | Identificador único de la solicitud. Guárdalo: llegará idéntico en el webhook y es la llave del polling y del replay. |
curp | string | CURP confirmado. |
actaType | string | Tipo de acta solicitado. |
conFolio | boolean | Variante foliada solicitada. |
redelivered | boolean | true si es una re-entrega del mismo día (sin cargo); false en una solicitud 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/actas \
-H "X-API-Key: $CU_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"curp": "CASE020722HTSRNDA8",
"actaType": "defuncion",
"conFolio": true,
"webhookUrl": "https://tu-dominio.com/webhook/actas"
}'❌ Respuestas de error
Todas siguen el formato de tres campos. Programa contra errorCode.
400 — Webhook inválido o header mal formado
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 | Caso |
|---|---|
invalid_webhook_url | La URL es sintácticamente válida pero no resuelve a una IP pública. |
invalid_idempotency_key | El header Idempotency-Key no cumple el formato. |
401 — Autenticación
http
HTTP/1.1 401 UnauthorizederrorCode | 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 — Fuera de horario o sin compra previa
http
HTTP/1.1 403 Forbiddenjson
{
"message": "El servicio de actas está disponible de 7:30 a 22:00 (hora del centro de México), favor de intentar dentro de ese horario",
"description": "Las actas solo pueden solicitarse dentro del horario de atención del día; vuelve a intentarlo dentro de ese horario.",
"errorCode": "outside_business_hours"
}errorCode | Caso |
|---|---|
outside_business_hours | Submit fuera de 07:30–22:00 CDMX. Ver Ventana horaria y capacidad. |
api_key_requires_purchase | Tu cuenta necesita al menos un paquete comprado para usar la API. |
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.actaType",
"error": "Input should be 'nacimiento', 'matrimonio', 'defuncion' or 'divorcio'"
}
]
}Casos que disparan bad_request:
curpfaltante, con longitud distinta de 18, o fuera del patrón.actaTypefaltante o fuera de{nacimiento, matrimonio, defuncion, divorcio}.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 acta o consulta duplicada
http
HTTP/1.1 422 Unprocessable EntityerrorCode | message | Caso |
|---|---|---|
invalid_curp | La CURP ingresada no es válida, favor de verificar | La CURP fue rechazada al crear la solicitud. |
no_data_found | No se encontró el acta solicitada para la CURP ingresada | CURP válida pero sin acta de ese tipo. |
duplicate_in_progress | Ya existe una consulta en curso para esta CURP y tipo de acta, espere a que termine antes de reintentar | Hay una solicitud en vuelo para la misma combinación. |
recent_query | Ya cuentas con una consulta reciente para esta CURP y tipo de acta, espera unos minutos antes de reintentar | Ya la solicitaste hoy pero el archivo no es recuperable. No se cobra. Caso degradado: lo normal es la re-entrega 200. |
429 — Sin créditos o rate limit
http
HTTP/1.1 429 Too Many RequestserrorCode | Caso |
|---|---|
too_many_requests | Saldo insuficiente: necesitas al menos 4 créditos disponibles. |
rate_limit_exceeded | Rebasaste 3 requests / 2 s en este endpoint. Respeta Retry-After. |
502 — Falla al crear la solicitud
http
HTTP/1.1 502 Bad GatewayerrorCode | message | Caso |
|---|---|---|
insufficient_provider_credits | El servicio no se encuentra disponible, favor de consultar más tarde | El servicio no pudo atender la solicitud. |
unknown | Ocurrió un error inesperado, favor de reintentar | Falla no clasificada al crear la solicitud. |
Reintenta con backoff exponencial empezando en 60 s.
503 — Sistema saturado
http
HTTP/1.1 503 Service Unavailablejson
{
"message": "El sistema se encuentra a máxima capacidad, favor de esperar unos minutos antes de reintentar",
"description": "Hay mucha demanda en este momento y el servicio está saturado; reintenta en unos minutos.",
"errorCode": "system_at_capacity"
}Se alcanzó el límite de 5 solicitudes simultáneas. Ver Ventana horaria y capacidad.
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_idempotency_key | No — corrige el header. |
| 401 | missing_api_key | No. |
| 401 | unauthorized_user | No. |
| 401 | user_not_found | No. |
| 403 | outside_business_hours | Sí — dentro de 07:30–22:00 CDMX (no aplica a una re-entrega). |
| 403 | api_key_requires_purchase | No — requiere compra. |
| 409 | bad_request | No — corrige el payload. |
| 409 | concurrent_idempotent_request | Sí, tras unos segundos. |
| 422 | invalid_curp | No — corrige la CURP. |
| 422 | no_data_found | No. |
| 422 | duplicate_in_progress | Sí, cuando termine la consulta en curso. |
| 422 | recent_query | No hoy — recupera el resultado con el uuid original. |
| 429 | too_many_requests | Sólo tras comprar créditos. |
| 429 | rate_limit_exceeded | Sí — respeta Retry-After. |
| 502 | insufficient_provider_credits | Sí, con backoff. |
| 502 | unknown | Sí, una vez. |
| 502 | duplicate_in_progress | Raro — el servicio reporta un duplicado propio. |
| 503 | system_at_capacity | Sí, con backoff. |
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 acta está lista (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. |
Son exactamente los mismos headers y el mismo esquema de firma que Semanas Cotizadas Premium. Si ya validas ese webhook, el mismo código sirve aquí sin cambios.
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 acta codificado en base64 estándar. Decodifícalo directamente a bytes y guárdalo / muéstralo.
Este servicio no ofrece el PDF estructurado. A diferencia de Semanas Cotizadas Premium, no existe
parsePdfnidocumentData: el acta viaja únicamente como PDF enpdfBase64.
Body — entrega fallida
json
{
"version": 1,
"uuid": "9f2b7c1a-4d3e-4a5f-8b0c-2e1d0f9a8b77",
"status": "failed",
"pdfBase64": null,
"errorMessage": "No se localizó la CURP en RENAPO, revise que esté bien escrita"
}Toda entrega failed implica que el cobro de los 4 créditos se revirtió.
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 | Acta lista, pdfBase64 presente. | No, es terminal. |
failed | No se pudo obtener o entregar el acta. | No, es terminal. |
Catálogo de errorMessage en status: "failed"
Máximo 100 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 |
|---|---|---|
No se localizó la CURP en RENAPO, revise que esté bien escrita | renapo | La CURP no existe en RENAPO. |
No se encontró el acta solicitada para esta CURP | not_found | No hay acta de ese tipo para esa CURP. |
No fue posible recuperar el acta en este intento, favor de reintentar la consulta | pdf_download | El acta se generó pero no pudimos recuperar el PDF. |
No fue posible recuperar el acta del reporte, favor de reintentar la consulta | pdf_download_failed | El proceso terminó sin un PDF descargable. |
La consulta fue cancelada por exceder el tiempo de espera, favor de reintentar | cancelled_by_timeout | La solicitud excedió el tiempo máximo de espera. |
No fue posible completar la consulta, favor de reintentar más tarde | unknown | Error no clasificado durante la generación. |
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.
Python
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==.
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.
- 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: recupera el resultado con GET /v3/actas/{uuid} o pide un replay. Ninguno de los dos cobra.
Además revalidamos que tu URL siga resolviendo a una IP pública antes de cada intento; si deja de hacerlo, ese intento no se envía.
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/actas")
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 "", 2023. Consultar resultado — GET /v3/actas/{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 acta que se perdió en los intentos de entrega.
Request
bash
curl https://api.consultaunica.mx/v3/actas/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 solicitud aún se procesa:
json
// pending — solicitud en curso; vuelve a consultar en unos segundos
{ "version": 1, "uuid": "...", "status": "pending", "pdfBase64": null, "errorMessage": null }json
// completed — acta lista
{ "version": 1, "uuid": "...", "status": "completed", "pdfBase64": "JVBE...", "errorMessage": null }json
// failed — no se pudo obtener
{ "version": 1, "uuid": "...", "status": "failed", "pdfBase64": null, "errorMessage": "..." }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 acta.
- Recibiste un
422 recent_queryy necesitas el resultado de la solicitud original. - Recibiste una re-entrega del mismo día en modo polling-only: consulta con el
uuidnuevo, que ya viene resuelto.
Frecuencia recomendada: no más de 1 request cada 5 segundos por uuid. Empieza polleando 30 s después del submit.
4. Reintentar entrega — POST /v3/actas/{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/actas/{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/actas/9f2b7c1a-4d3e-4a5f-8b0c-2e1d0f9a8b77/replay \
-H "X-API-Key: $CU_API_KEY"Respuesta — 202 Accepted
json
{ "uuid": "9f2b7c1a-...", "scheduled": true }El 202 confirma que la entrega quedó agendada, no que tu endpoint ya haya respondido.
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 solicitud 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 /v3/actas/{uuid}→status=completed(ofailed). - Llamas
POST /v3/actas/{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/actas │
│ (X-API-Key, curp, actaType, webhookUrl) │
│──────────────────────────────────────────────────────>│
│ │
│ 200 OK { uuid, message, curp, actaType, conFolio } │
│<──────────────────────────────────────────────────────│
│ │
│ ... trámite del acta ... │
│ │
│ POST <webhookUrl> │
│ X-CU-Timestamp: <unix_ts> │
│ X-CU-Signature: sha256=... │
│ X-CU-Uuid: <uuid> │
│ { version, uuid, status, pdfBase64, errorMessage } │
│<──────────────────────────────────────────────────────│
│ │
│ 200 / 201 / 202 / 204 │
│──────────────────────────────────────────────────────>│Modo polling-only (pull)
┌─────────┐ ┌──────────────────┐
│ Cliente │ │ Consulta Única │
└────┬────┘ └────────┬─────────┘
│ POST /v3/actas │
│ (X-API-Key, curp, actaType) ← sin webhookUrl │
│──────────────────────────────────────────────────────>│
│ │
│ 200 OK { uuid, message, curp, actaType, conFolio } │
│<──────────────────────────────────────────────────────│
│ │
│ ... trámite del acta ... │
│ │
│ GET /v3/actas/{uuid} (cada ~30 s) │
│──────────────────────────────────────────────────────>│
│ 200 OK { status: "pending", ... } │
│<──────────────────────────────────────────────────────│
│ ... │
│ GET /v3/actas/{uuid} │
│──────────────────────────────────────────────────────>│
│ 200 OK { status: "completed", pdfBase64: "JVB..." } │
│<──────────────────────────────────────────────────────│6. Preguntas frecuentes
Ya integré Semanas Cotizadas Premium. ¿Qué cambia? El flujo, la firma, los errorCode, el rate limit del endpoint (3 req / 2 s en ambos) y el trato de una consulta repetida el mismo día (re-entrega sin cobrar en ambos) son idénticos. Las diferencias:
| Semanas Cotizadas Premium | Actas | |
|---|---|---|
| Campos del submit | curp, parsePdf | curp, actaType, conFolio |
| Campos de la respuesta | nss, fullname | actaType, conFolio, redelivered |
parsePdf / documentData | Sí | No existe |
| Ventana horaria | No | 07:30–22:00 CDMX |
| Límite de concurrencia | No | 5 simultáneas |
| Costo | 3 créditos | 4 créditos |
| Modo mock | Sí | Sí |
¿Puedo pedir la variante foliada y la normal del mismo CURP? Sí, son solicitudes independientes: cada una tiene su uuid, su cobro y su llave de duplicados. La excepción es divorcio, que no tiene variante foliada: ahí ambas peticiones son la misma solicitud.
¿Por qué recibo 403 outside_business_hours si mi clave es válida? Porque las actas solo se tramitan de 07:30 a 22:00 hora del centro de México. No es un problema de tu cuenta. Ver Ventana horaria y capacidad. Excepción: si esa misma acta ya la obtuviste hoy, el submit funciona a cualquier hora porque se resuelve por re-entrega.
¿Me cobran dos veces si pido la misma acta el mismo día? No. El segundo submit se resuelve por re-entrega: 200, uuid nuevo y saldo intacto. Igual que en Semanas Cotizadas Premium.
Perdí el uuid de mi solicitud. ¿Cómo recupero el acta? Reenvía el mismo submit. Al ser un duplicado del día te devolvemos el acta con un uuid nuevo, sin cobrarte.
Recibí 422 recent_query. ¿Cómo obtengo el acta? Es el caso degradado: ya la solicitaste hoy pero el archivo no quedó recuperable. Usa el uuid de la solicitud original con GET /v3/actas/{uuid}. No se cobra.
¿Me cobran si el acta no se pudo obtener? No. Si la solicitud termina en failed, revertimos los 4 créditos.
¿Qué pasa si mi servidor está caído cuando entregamos? Hacemos 3 intentos (0 s / 30 s / 120 s). Si todos fallan, recupera el acta con GET /v3/actas/{uuid} o pide un replay. Ninguno cobra.
Mi solicitud se canceló por tiempo de espera y después el acta apareció. ¿Me la entregan? No. Cuando una solicitud excede el tiempo máximo la cerramos, te notificamos failed y te revertimos el cobro. Si el resultado llega después, ya no se entrega: la solicitud está cerrada de tu lado. Vuelve a solicitarla si la necesitas.
¿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. También tienes el modo mock, que simula el contrato completo sin consumir créditos y sin requerir compra previa: puedes dejar tu integración terminada antes de contratar un paquete.
¿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.
7. Modo Mock
Endpoints con prefijo /mock que simulan el contrato completo —submit, webhook firmado, polling y replay— sin tramitar el acta ante el registro civil.
Sirven para integrar y para pruebas automatizadas: no consumen créditos y no requieren compra previa. Basta con tener la cuenta confirmada y tu X-API-Key. Es la forma de tener tu integración terminada antes de contratar un paquete.
Rutas
| Productivo | Mock equivalente |
|---|---|
POST /v3/actas | POST /v3/mock/actas |
GET /v3/actas/{uuid} | GET /v3/mock/actas/{uuid} |
POST /v3/actas/{uuid}/replay | POST /v3/mock/actas/{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 sin cambiarlo. Los únicos códigos exclusivos del mock soninvalid_mock_modeymock_daily_quota_exceeded, que en producción no existen.
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. ElGETfunciona idéntico a producción.
El PDF del mock es un documento dummy, no un acta. Sirve para que ejercites la decodificación de
pdfBase64y el guardado del archivo con un payload de tamaño realista, no para validar el contenido de un acta.
⚠️ Lo que el mock no simula
El mock reproduce el contrato, no las reglas de negocio del servicio. Estas tres las encontrarás solo en producción:
| Regla de producción | En el mock |
|---|---|
| Ventana horaria 07:30–22:00 CDMX | Responde a cualquier hora. Nunca verás outside_business_hours. |
| Límite de 5 consultas simultáneas | Sin límite. Nunca verás system_at_capacity. |
| Duplicados y re-entrega del mismo día | Cada submit crea un uuid nuevo. Nunca verás duplicate_in_progress ni recent_query, y redelivered siempre llega en false. |
Son justo los tres caminos que más conviene tener contemplados en tu código, así que revísalos en §1 Submit aunque no puedas dispararlos desde el mock.
Comportamiento
- Los
uuidgenerados viven 1 hora; luego expiran yGET/replayregresan404 request_not_found. - El webhook se dispara al
webhookUrlprovisto, firmado con tuapi_key(mismos headersX-CU-Signature,X-CU-Timestamp,X-CU-Uuidque producción). Un único intento, sin reintentos: si tu endpoint falla, recupera el resultado porGEToreplay. - El
webhookUrlse valida igual que en producción: HTTPS y host público. Localhost y túneles HTTP siguen sin pasar. - La respuesta del submit te devuelve
actaTypeyconFoliotal como los mandaste, igual que producción — incluida la normalización deconFolioafalseendivorcio. Idempotency-Keyse acepta por compatibilidad pero se ignora: dos submits con la misma clave devuelvenuuiddistinto.replaymantiene el cooldown de 60 s poruuidy responde409si el modo espending.- El submit no descuenta créditos, y la respuesta sí incluye el header
X-Remaining-Requestscon tu saldo actual (sin deducción). Útil para ejercitar tu lógica de alertas de saldo. - Rate limit: 3 req / 2 s por
X-API-KeyenPOST /v3/mock/actas— la misma cuota que producción, en un bucket independiente. - Cuota diaria: 50 operaciones con webhook saliente (submit + replay) por cuenta y por servicio. Al agotarse responde
429 mock_daily_quota_exceededcon headerRetry-After. ElGETde polling no consume cuota, así que puedes consultar en bucle sin gastarla. La cuota de actas es independiente de la de Semanas Cotizadas Premium: agotar una no afecta a la otra.
Ejemplos
Submit success:
bash
curl -X POST https://api.consultaunica.mx/v3/mock/actas \
-H "X-API-Key: $CU_API_KEY" \
-H "Content-Type: application/json" \
-H "X-Mock-Mode: success" \
-d '{"curp":"PEGJ800101HDFRRN09","actaType":"nacimiento","webhookUrl":"https://tuapp.com/cb"}'Submit que simula un acta no obtenida:
bash
curl -X POST https://api.consultaunica.mx/v3/mock/actas \
-H "X-API-Key: $CU_API_KEY" \
-H "Content-Type: application/json" \
-H "X-Mock-Mode: failed" \
-d '{"curp":"PEGJ800101HDFRRN09","actaType":"defuncion","webhookUrl":"https://tuapp.com/cb"}'Submit pending (no dispara webhook), variante foliada:
bash
curl -X POST https://api.consultaunica.mx/v3/mock/actas \
-H "X-API-Key: $CU_API_KEY" \
-H "Content-Type: application/json" \
-H "X-Mock-Mode: pending" \
-d '{"curp":"PEGJ800101HDFRRN09","actaType":"matrimonio","conFolio":true,"webhookUrl":"https://tuapp.com/cb"}'Submit de acta de divorcio (conFolio no aplica):
bash
curl -X POST https://api.consultaunica.mx/v3/mock/actas \
-H "X-API-Key: $CU_API_KEY" \
-H "Content-Type: application/json" \
-H "X-Mock-Mode: success" \
-d '{"curp":"PEGJ800101HDFRRN09","actaType":"divorcio","webhookUrl":"https://tuapp.com/cb"}'Submit polling-only (sin webhookUrl):
bash
curl -X POST https://api.consultaunica.mx/v3/mock/actas \
-H "X-API-Key: $CU_API_KEY" \
-H "Content-Type: application/json" \
-d '{"curp":"PEGJ800101HDFRRN09","actaType":"nacimiento"}'Polling y replay:
bash
curl https://api.consultaunica.mx/v3/mock/actas/<uuid> \
-H "X-API-Key: $CU_API_KEY"
curl -X POST https://api.consultaunica.mx/v3/mock/actas/<uuid>/replay \
-H "X-API-Key: $CU_API_KEY"8. Changelog
- v1.4 — acta de divorcio.
actaTypeacepta un cuarto valor,divorcio, con el mismo costo, la misma ventana horaria y el mismo flujo que los otros tipos. Única particularidad: el registro civil no emite acta de divorcio foliada, así queconFoliose normaliza afalseen ese tipo —la solicitud no se rechaza— y la respuesta te lo devuelve así. ComoconFoliodeja de distinguir, las dos formas de pedir el mismo divorcio comparten ventana de duplicados. Cambio aditivo: ninguna integración existente cambia de comportamiento. - v1.3 — la respuesta del submit incluye
redelivered(booleano):truecuando se trata de una re-entrega del mismo día —sin cargo—,falseen una solicitud nueva. Antes la única marca era el texto delmessage, que no es contrato estable. Cambio aditivo: si ignoras el campo, nada cambia para tu integración. - v1.2 — modo mock: endpoints
/v3/mock/actas*con headerX-Mock-Mode(success/failed/pending) para integrar y probar sin consumir créditos y sin compra previa. Mismo contrato, misma firma y mismoserrorCodeque producción; lo que no simula son las reglas de negocio del servicio —ventana horaria, límite de concurrencia y duplicados del día—, que solo verás en el endpoint real. Ver §7 Modo Mock. - v1.1 — re-entrega del mismo día. Un submit de una combinación que ya completaste hoy deja de responder
422 recent_queryy pasa a responder200con unuuidnuevo, sirviéndote el acta ya obtenida sin cobrar créditos. La ventana horaria y el límite de concurrencia no aplican a ese camino.recent_queryse conserva para el caso degradado en que el archivo ya no es recuperable. Cierra la última diferencia de comportamiento con Semanas Cotizadas Premium. - v1.0 — primer release del endpoint público
actas: submit conactaTypeyconFolio, modo webhook y polling-only,Idempotency-Key, consulta poruuidy replay. Webhook firmado conversion: 1.