Skip to content

Signing certificates

The SRI only accepts documents signed (XAdES-BES) with the issuer’s electronic signature: a .p12 (PKCS#12) file issued by a certification authority accredited in Ecuador. Flashpi keeps it encrypted, checks it with its certification authority and signs every document of the issuer with it.

The shared demo issuer signs with Flashpi’s own test certificate, so you only need yours to issue with your own RUC.

There are two ways to upload a certificate:

  • The onboarding from the certificate (POST /v1/issuer-onboardings): creates the issuer from its .p12, or renews the certificate of the issuer that already has that RUC. The certificate serves test and live (allModes: true). It is what the console does. See Create an issuer from its certificate and Renew a certificate.
  • The upload to an issuer (POST /v1/issuers/{issuerId}/certificates, below): the certificate serves the environment of the key that uploads it.

Upload the file and its password as multipart/form-data. It needs an account-scoped read_write key.

Terminal window
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: the .p12 file, at most 1 MiB (1,048,576 bytes; 413 payload_too_large beyond it).
  • password: its password, at most 1024 bytes.
  • The key’s environment matters. A certificate uploaded here with a test key signs only test documents; one uploaded with a live key signs only live documents. For one certificate that serves both environments, use the onboarding from the certificate.

The answer is 202 Accepted with the certificate in status 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 encrypts the file and the password as soon as they arrive. The password is never stored in clear and never returned. GET /v1/issuers/{issuerId}/certificates lists an issuer’s certificates (newest first) and GET /v1/issuers/{issuerId}/certificates/{certificateId} reads one.

allModes says whether the certificate serves test and live (uploaded through the onboarding from the certificate). A key reads the certificates of its environment and those of both, and on a certificate of both environments environment is the environment of the key that reads it.

To renew an issuer’s signature with one certificate for both environments, start an onboarding from the certificate with the new .p12. Since your account already has an issuer with that RUC, the ready onboarding carries existingIssuer, and you complete it with an empty body:

Terminal window
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 '{}'

The new certificate is added to the issuer (allModes: true, processing) without changing its profile. In the console, Upload certificate on an issuer does the same: you choose no environment and confirm with your authenticator code or your password. When the .p12 belongs to another RUC, the console takes you to New issuer with the same file. Members who write the certificates of some issuers without managing issuers upload one certificate per environment instead.

An isolated service opens the file and checks the password, that it holds exactly one signing key with its certificate, its validity dates, its chain up to an accredited Ecuadorian certification authority, and its revocation status with that authority. It usually takes a few seconds. Read the certificate again until its status is no longer processing:

  • accepted: it can sign. The certificate now shows its subject, issuedBy, serialNumber, validFrom, validUntil, sha256Fingerprint, the ruc written in it (when its authority writes one) and its revocation state. Flashpi raises certificate.verified, or certificate.pending when the revocation status could not be checked yet.
  • rejected: rejection.code and rejection.message say why. A rejected upload raises no webhook event today.
rejection.code Meaning
invalid_password The password does not open the file.
invalid_pkcs12 The file is not a valid .p12.
pkcs12_mac_missing The file has no password integrity check. Export it again with a password.
no_signing_certificate No private key with a signing certificate in the file.
ambiguous_signing_certificate Several signing certificates in the file. Upload a file with only one.
certificate_not_yet_valid, certificate_expired Outside its validity dates.
untrusted_certificate_chain Not issued by a certification authority accredited in Ecuador.
certificate_revoked Its certification authority revoked it.
revocation_status_unknown Its revocation status could not be determined.
certificate_unsupported A format Flashpi cannot use.
upload_unreadable, upload_too_large The upload could not be read, or is too large.

A certificate can be revoked by its authority at any time, for example when its owner reports it lost. Flashpi checks every accepted certificate again: daily while its status is good, hourly while it is pending or blocked. The revocation object shows the result:

revocation.status Signs? What it means
good Yes Verified not revoked. checkedAt and nextCheckAt say when it was and will be checked.
pending Yes, until blockAt The authority’s revocation service did not answer, so the status could not be verified. Flashpi keeps trying every hour.
blocked No Still unverified at blockAt (blockedReason: "revocation_status_unverified"), or its chain is no longer trusted (blockedReason: "untrusted_certificate_chain").
revoked Never again Its certification authority revoked it.

How a certificate moves between them:

  1. When the status cannot be verified, the certificate becomes pending with a blockAt 7 days later (certificate.pending). It keeps signing meanwhile.
  2. Reminders are raised on days 3 and 6, as certificate.pending again.
  3. Verified good before blockAt: back to good (certificate.verified).
  4. Still unverified at blockAt: blocked (certificate.blocked). It stops signing at blockAt even if a check runs late. Once a check verifies it good, it is reactivated and signs again (certificate.reactivated).
  5. A good certificate that cannot be verified on a later check goes back to pending, with a new 7-day window.
  6. If its chain is no longer trusted (the authority or an intermediate certificate changed), it is blocked at once. Upload a new certificate.
  7. If the authority reports it revoked, it becomes revoked (certificate.revoked) and never signs again. A revoked certificate is also refused at upload.
  • The certificate itself: usable says whether it signs right now (accepted, within its validity dates, and not blocked or revoked; a pending certificate is usable until its blockAt).
  • The console shows a banner on the issuer’s certificates when one is pending (with the date it stops signing), blocked, revoked, expired or about to expire.
  • Webhooks: subscribe to certificate.pending, certificate.blocked, certificate.revoked, certificate.reactivated and certificate.verified to alert your team. Their data is the certificate. The events of a certificate of both environments are recorded once per environment, so the test endpoints and the live ones both get them. See Webhooks.
  • Documents: a document issued while the issuer has no usable certificate for its environment is accepted, then ends failed with failure.code certificate_required, certificate_blocked, certificate_revoked, certificate_expired or certificate_not_yet_valid. It uses up its sequential, so issue it again once a certificate signs.

Documents are signed with the newest usable certificate that serves their environment: one uploaded for that environment, or one of both environments (allModes). To renew a signature, upload the new .p12 before the old one expires (Renew a certificate): once accepted, it takes over, and the older one only signs if the newer one stops being usable.

  • The file and the password are encrypted on arrival with a key managed by AWS KMS, in a dedicated storage area. An upload to an issuer is deleted once checked. The upload of an onboarding from the certificate waits for you to complete the onboarding, which deletes it; one never completed (a rejected or expired one included) is deleted automatically about 7 days after it was uploaded.
  • Only the isolated signing service can decrypt the certificate, in memory, to sign your documents. The API that receives your requests cannot read it.
  • See Security.