Constancias
Emitir constancia NOM-151
POST /constancias
Emite una Constancia de Conservación NOM-151 a partir del hash SHA-256 de tu documento. Tu documento nunca sale de tu infraestructura.
No es una restricción nuestra: la NOM-151-SCFI-2016 lo ordena. Apéndice Normativo A, numeral A.2.4 — «el Prestador de Servicios de Certificación únicamente recibirá la huella digital electrónica del mensaje de datos». Usa este endpoint si ya firmas por tu cuenta (e.firma del SAT, tu propio flujo) y solo necesitas la prueba legal del momento en que el documento existió.
El hash va en hexadecimal minúsculas de 64 caracteres — exactamente lo que produce sha256sum archivo.pdf. Se aceptan mayúsculas y se normalizan; no se acepta base64 ni prefijos tipo sha256:.
Idempotency-Key es obligatoria. Este endpoint cobra y llama a un PSC externo: sin la key, un timeout de red te deja sin forma segura de reintentar. Reintentar con la misma key devuelve la primera respuesta, nunca emite dos veces.
externalId es tu propia referencia (el folio de tu expediente). Te sirve para recuperar la constancia después sin haber guardado nuestro id, y como segunda red contra el doble cobro. Reusar un externalId con el mismo hash devuelve la constancia ya emitida; reusarlo con otro hash devuelve 409 — la referencia ya está tomada por otro documento, y devolverte la constancia equivocada sería peor que fallar.
Facturación y entornos. En live consume créditos y llama al PSC. Con una API key test o dev no cobra, no contacta al PSC y devuelve un artefacto sin validez legal, marcado con sandbox: true y livemode: false.
Requiere contrato. El addon se activa por tenant tras firmar el contrato de prestación de servicio; sin él, live responde 403 CONTRACT_REQUIRED.
Cuerpo de la petición
Ejemplo (cURL)
# El hash lo calculas TÚ; el documento no viaja.
HASH=$(sha256sum contrato.pdf | cut -d" " -f1)
curl "https://api.allsign.io/v3/constancias" \
-H "Authorization: Bearer allsign_live_sk_..." \
-H "Content-Type: application/json" \
-H "Idempotency-Key: 9f3c1a2e-8d4b-4c6a-9e21-5b7f0d8a3c14" \
-d "{\"hashSha256\":\"$HASH\",\"externalId\":\"expediente-2026-0412\",\"label\":\"Contrato de arrendamiento\"}"
Respuestas
201 Constancia emitida. El artefacto viene inline en base64 en constancia. — ConstanciaResponse
Recuperar una constancia
GET /constancias/{constancia_id}
Devuelve una constancia que emitimos, con el artefacto inline en base64 — así que basta con haber guardado el id (o tu externalId) para recuperarla meses después.
Archiva constancia, no downloadUrl. La URL de descarga es una conveniencia con caducidad (downloadUrlExpiresAt); si guardas el JSON con la URL y vuelves después, te queda un enlace muerto. El base64 es la fuente de verdad y no expira.
Los campos sealedAt, serialNumber, policyOid y tsaName se extraen del interior del token, donde están cubiertos por la firma — no del JSON que envuelve la respuesta del PSC. sealedAt es el genTime del sello: el instante con valor legal.
Con Accept: application/pkcs7-mime la misma ruta devuelve el DER crudo en vez del JSON, listo para pasárselo a openssl sin decodificar nada.
Parámetros
constancia_id path · requerido |
string |
Ejemplo (cURL)
curl "https://api.allsign.io/v3/constancias"/cst_1a2b3c4d5e6f708192a3b4c5d6e7f809 \
-H "Authorization: Bearer allsign_live_sk_..."
Respuestas
200 La constancia, con el artefacto inline. — ConstanciaResponse
Listar constancias
GET /constancias
Lista las constancias del tenant, más recientes primero, con paginación por cursor.
El listado NO trae el artefacto. Devolver el base64 de hasta 100 constancias por página significaría descargarlas todas de nuestro almacenamiento en cada petición. Para el artefacto usa GET /v3/constancias/{constanciaId}, que trae uno.
Sirve para conciliar: filtra por tu externalId para encontrar el expediente que buscas, o pagina el periodo completo para cuadrar lo emitido contra lo cobrado.
Paginación: manda startingAfter con el nextCursor de la respuesta anterior. hasMore te dice si vale la pena pedir otra página.
El listado está acotado al entorno de tu API key: una key test no ve las constancias live del tenant, ni al revés.
Parámetros
limit query |
integer |
startingAfter query |
|
externalId query |
Ejemplo (cURL)
curl "https://api.allsign.io/v3/constancias"?externalId=expediente-2026-0412" \
-H "Authorization: Bearer allsign_live_sk_..."
Respuestas
200 Página de constancias (sin el artefacto — ver la nota arriba). — ConstanciaList
Verificar una constancia
POST /constancias/verify
Corre los cuatro checks criptográficos sobre una constancia y devuelve el resultado por criterio.
| Check | Qué prueba |
|---|---|
| integrity | El hash sellado dentro del token es el de tu documento |
| chainOfTrust | El certificado firmante encadena a la raíz de Secretaría de Economía |
| cmsSignature | La firma CMS del token es válida (RFC 5652) |
| certValidity | El certificado estaba vigente al momento del sellado |
Los cuatro son necesarios y ninguno implica a otro: un imprint correcto no dice nada de quién firmó, y una firma válida de un emisor cualquiera no acredita nada.
status es tri-estado, no un booleano (ETSI EN 319 102-1):
- VALID — los cuatro checks pasaron.
- INVALID — afirmación fuerte: el artefacto está mal (el documento no es el que se selló, o el emisor no es acreditado).
- INDETERMINATE — no pudimos concluir. Token ilegible, o un check que no se pudo completar. No significa que la constancia sea falsa.
Esa distinción importa: colapsar INDETERMINATE en INVALID te haría reportar «tu constancia es falsa» cuando el problema es de nuestro lado.
> Esto es una conveniencia, no una autoridad. AllSign no es PSC acreditado, y preguntarle al emisor si su propio artefacto es válido no constituye prueba ante un tercero. Para eso está la verificación independiente: baja la raíz en /v3/constancias/certchain y verifica con openssl por tu cuenta. El valor de este endpoint es que valida el certificado del PSC contra la raíz correcta, que es el paso que se suele equivocar.
Puedes mandar constancia (base64) + hashSha256, o solo constanciaId si la emitimos nosotros — en ese caso el hash sale de nuestro registro.
Cuerpo de la petición
Ejemplo (cURL)
curl "https://api.allsign.io/v3/constancias"/verify \
-H "Authorization: Bearer allsign_live_sk_..." \
-H "Content-Type: application/json" \
-d '{"constanciaId": "cst_1a2b3c4d5e6f708192a3b4c5d6e7f809"}'
# Revisa `status` Y `checks`. Un INDETERMINATE no dice "es falsa":
# dice "no se pudo concluir" — y `errors` explica qué faltó.
Respuestas
200 Resultado de la verificación. Revisa status y checks; un INDETERMINATE con errors te dice qué no se pudo comprobar. — VerifyResponse
Raíz de confianza para verificar offline
GET /constancias/certchain
Devuelve en PEM el certificado raíz de la Autoridad Certificadora Raíz Segunda de Secretaría de Economía — el -CAfile que necesitas para verificar una constancia sin depender de AllSign.
⚠️ openssl ts -verify rechaza una constancia NOM-151 con la invocación normal. No es que la constancia esté mal: el certificado firmante no lleva el Extended Key Usage Time Stamping (solo Digital Signature, Non Repudiation), porque una constancia de conservación no es un sello de tiempo puro. Sin el flag correcto verás unsuitable certificate purpose y concluirás, equivocadamente, que la constancia es inválida.
El flag es -purpose any. Y como SeguriData entrega un token CMS pelado (no un TimeStampResp), hace falta además -token_in.
Si tu versión de openssl aun así se niega, openssl cms -verify -purpose any comprueba firma y cadena — pero no compara el message imprint, así que en ese camino tienes que extraer el TSTInfo y comparar el hash tú mismo.
> Servimos esta raíz por conveniencia, no como autoridad: no somos PSC acreditado. La fuente autoritativa es la Secretaría de Economía; si tu proceso requiere rigor, bájala de ahí y compara. El archivo que servimos se valida contra su fingerprint SHA-256 antes de entregarse.
Ejemplo (cURL)
curl -s https://api.allsign.io/v3/constancias/certchain -o economia_root.pem
# Comprueba que es la raíz que esperas antes de confiar en ella:
openssl x509 -in economia_root.pem -noout -subject -fingerprint -sha256
Respuestas
200 El certificado raíz en PEM.