Ir al contenido

Certificados de firma

El SRI solo acepta comprobantes firmados (XAdES-BES) con la firma electrónica del emisor: un archivo .p12 (PKCS#12) emitido por una entidad de certificación acreditada en Ecuador. Flashpi lo guarda cifrado, lo verifica con su entidad de certificación y firma con él cada comprobante del emisor.

El emisor demo compartido firma con el certificado de prueba de Flashpi, así que solo necesitas el tuyo para emitir con tu propio RUC.

Hay dos formas de subir un certificado:

  • El alta desde el certificado (POST /v1/issuer-onboardings): crea el emisor desde su .p12, o renueva el certificado del emisor que ya tiene ese RUC. El certificado sirve para pruebas y producción (allModes: true). Es lo que hace la consola. Consulta Crear un emisor desde su certificado y Renovar un certificado.
  • La carga a un emisor (POST /v1/issuers/{issuerId}/certificates, abajo): el certificado sirve para el ambiente de la llave que lo sube.

Sube el archivo y su contraseña como multipart/form-data. Requiere una llave read_write con alcance account.

Ventana de terminal
curl https://api.flashpi.com.ec/v1/issuers/01a0fd6a-d098-76ca-b492-1bcb6505a7bd/certificates \
-H "Authorization: Bearer $FLASHPI_API_KEY" \
-F "file=@signature.p12" \
-F "password=$P12_PASSWORD"
  • file: el archivo .p12, de máximo 1 MiB (1.048.576 bytes; 413 payload_too_large si lo supera).
  • password: su contraseña, de máximo 1024 bytes.
  • Importa el ambiente de la llave. Un certificado subido aquí con una llave de prueba solo firma comprobantes de prueba; uno subido con una llave de producción solo firma comprobantes de producción. Para un solo certificado que sirva a ambos ambientes, usa el alta desde el certificado.

La respuesta es 202 Accepted con el certificado en estado processing:

{
"id": "01a0fd6e-f718-7c9c-b50c-fafb236eb1a6",
"issuerId": "01a0fd6a-d098-76ca-b492-1bcb6505a7bd",
"environment": "test",
"allModes": false,
"status": "processing",
"usable": false,
"subject": null,
"issuedBy": null,
"serialNumber": null,
"validFrom": null,
"validUntil": null,
"sha256Fingerprint": null,
"ruc": null,
"revocation": null,
"rejection": null,
"createdAt": "2026-10-02T16:25:03.000Z",
"updatedAt": "2026-10-02T16:25:03.000Z"
}

Flashpi cifra el archivo y la contraseña apenas llegan. La contraseña nunca se guarda en claro ni se devuelve. GET /v1/issuers/{issuerId}/certificates lista los certificados de un emisor (del más nuevo al más antiguo) y GET /v1/issuers/{issuerId}/certificates/{certificateId} lee uno.

allModes dice si el certificado sirve para pruebas y producción (subido con el alta desde el certificado). Una llave lee los certificados de su ambiente y los de ambos ambientes, y en un certificado de ambos ambientes environment es el ambiente de la llave que lo lee.

Para renovar la firma de un emisor con un solo certificado para ambos ambientes, inicia un alta desde el certificado con el .p12 nuevo. Como tu cuenta ya tiene un emisor con ese RUC, el alta lista trae existingIssuer, y la completas con un cuerpo vacío:

Ventana de terminal
curl https://api.flashpi.com.ec/v1/issuer-onboardings/01a0fd70-2b4c-7d1e-9f3a-5c6b7d8e9f01/complete \
-H "Authorization: Bearer $FLASHPI_API_KEY" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{}'

El certificado nuevo se agrega al emisor (allModes: true, en processing) sin cambiar su perfil. En la consola, Subir certificado en un emisor hace lo mismo: no eliges ambiente y confirmas con el código de tu app de autenticación o con tu contraseña. Si el .p12 es de otro RUC, la consola te lleva a Nuevo emisor con el mismo archivo. Los miembros que escriben los certificados de algunos emisores sin administrar emisores suben, en cambio, un certificado por ambiente.

Un servicio aislado abre el archivo y verifica la contraseña, que contenga exactamente una clave de firma con su certificado, sus fechas de validez, su cadena hasta una entidad de certificación acreditada en Ecuador y su estado de revocación con esa entidad. Normalmente tarda unos segundos. Vuelve a leer el certificado hasta que su status deje de ser processing:

  • accepted: puede firmar. El certificado muestra ahora su subject, issuedBy, serialNumber, validFrom, validUntil, sha256Fingerprint, el ruc que trae (cuando su entidad lo escribe) y su estado de revocation. Flashpi genera certificate.verified, o certificate.pending cuando todavía no se pudo verificar el estado de revocación.
  • rejected: rejection.code y rejection.message dicen por qué. Hoy un certificado rechazado no genera un evento de webhook.
rejection.code Significado
invalid_password La contraseña no abre el archivo.
invalid_pkcs12 El archivo no es un .p12 válido.
pkcs12_mac_missing El archivo no tiene verificación de integridad con contraseña. Expórtalo de nuevo con una contraseña.
no_signing_certificate El archivo no tiene una clave privada con un certificado de firma.
ambiguous_signing_certificate El archivo tiene varios certificados de firma. Sube un archivo con uno solo.
certificate_not_yet_valid, certificate_expired Está fuera de sus fechas de validez.
untrusted_certificate_chain No lo emitió una entidad de certificación acreditada en Ecuador.
certificate_revoked Su entidad de certificación lo revocó.
revocation_status_unknown No se pudo determinar su estado de revocación.
certificate_unsupported Tiene un formato que Flashpi no puede usar.
upload_unreadable, upload_too_large No se pudo leer el archivo subido, o es demasiado grande.

Una entidad puede revocar un certificado en cualquier momento, por ejemplo cuando su titular reporta que lo perdió. Flashpi vuelve a revisar cada certificado aceptado: cada día mientras su estado es bueno y cada hora mientras está pendiente o bloqueado. El objeto revocation muestra el resultado:

revocation.status ¿Firma? Qué significa
good Sí Verificado como no revocado. checkedAt y nextCheckAt dicen cuándo se revisó y cuándo se revisará.
pending Sí, hasta blockAt El servicio de revocación de la entidad no respondió, así que no se pudo verificar el estado. Flashpi sigue intentando cada hora.
blocked No Sigue sin verificarse en blockAt (blockedReason: "revocation_status_unverified"), o su cadena ya no es confiable (blockedReason: "untrusted_certificate_chain").
revoked Nunca más Su entidad de certificación lo revocó.

Cómo pasa un certificado de un estado a otro:

  1. Cuando no se puede verificar el estado, el certificado pasa a pending con un blockAt 7 días después (certificate.pending). Mientras tanto sigue firmando.
  2. Se generan recordatorios los días 3 y 6, de nuevo como certificate.pending.
  3. Verificado como bueno antes de blockAt: vuelve a good (certificate.verified).
  4. Sin verificar todavía en blockAt: blocked (certificate.blocked). Deja de firmar en blockAt aunque una revisión llegue tarde. Cuando una revisión lo verifica como bueno, se reactiva y vuelve a firmar (certificate.reactivated).
  5. Un certificado bueno que no se puede verificar en una revisión posterior vuelve a pending, con una nueva ventana de 7 días.
  6. Si su cadena deja de ser confiable (cambió la entidad o un certificado intermedio), pasa a blocked de inmediato. Sube un certificado nuevo.
  7. Si la entidad lo reporta revocado, pasa a revoked (certificate.revoked) y nunca vuelve a firmar. Un certificado revocado también se rechaza al subirlo.
  • El propio certificado: usable dice si firma en este momento (aceptado, dentro de sus fechas de validez y ni bloqueado ni revocado; un certificado pendiente se puede usar hasta su blockAt).
  • La consola muestra un aviso en los certificados del emisor cuando uno está pendiente (con la fecha en que deja de firmar), bloqueado, revocado, vencido o por vencer.
  • Webhooks: suscríbete a certificate.pending, certificate.blocked, certificate.revoked, certificate.reactivated y certificate.verified para alertar a tu equipo. Su data es el certificado. Los eventos de un certificado de ambos ambientes se generan una vez por ambiente, así que los reciben los endpoints de pruebas y los de producción. Consulta Webhooks.
  • Comprobantes: un comprobante emitido mientras el emisor no tiene un certificado utilizable para su ambiente se acepta y luego termina failed con failure.code certificate_required, certificate_blocked, certificate_revoked, certificate_expired o certificate_not_yet_valid. Usa su secuencial, así que emítelo de nuevo cuando un certificado firme.

Los comprobantes se firman con el certificado utilizable más nuevo que sirve a su ambiente: uno subido para ese ambiente, o uno de ambos ambientes (allModes). Para renovar una firma, sube el nuevo .p12 antes de que venza el anterior (Renovar un certificado): cuando se acepta, toma el relevo, y el anterior solo firma si el más nuevo deja de ser utilizable.

  • El archivo y la contraseña se cifran al llegar con una llave administrada por AWS KMS, en un área de almacenamiento dedicada. El archivo de una carga a un emisor se elimina tras revisarlo. El de un alta desde el certificado espera a que completes el alta, que lo elimina; el de un alta que no se completa (también una rechazada o vencida) se elimina automáticamente unos 7 días después de subirlo.
  • Solo el servicio de firma aislado puede descifrar el certificado, en memoria, para firmar tus comprobantes. La API que recibe tus solicitudes no puede leerlo.
  • Consulta Seguridad.