Issue a per-user leaf certificate, sign the PDF ByteRange digest server-side, embed in a CMS PKCS#7 (PAdES-B-B, or PAdES-B-T when TSA_URL is configured)
After /sign and after the browser has APPENDED an incremental update to the locally stamped PDF (new /Sig + Widget + re-emitted Page/Catalog + new xref pointing at /Prev, with placeholder /ByteRange and /Contents <hex>), the browser POSTs the SHA-256 of the PDF's ByteRange plus the verified signer email. The Worker verifies the email matches the envelope-scoped HMAC stored at /sign time, generates an ephemeral ECDSA P-256 keypair, issues a 10-year leaf certificate under the FreeSign CA (Google Cloud HSM in production; the long validity is required because Adobe Reader re-validates the signer cert against the current wall clock when the chain is not AATL-trusted, the ephemeral signing key itself is still destroyed inside the same request) with Subject CN = the human signer's typed name and subjectAltName.rfc822Name = the verified email, signs SignedAttributes with the ephemeral key, and assembles a CMS SignedData with both the leaf and CA certs in the certs set. When env.TSA_URL is configured, the Worker additionally fetches an RFC 3161 TimeStampToken over the signature value and embeds it as the SignerInfo signatureTimeStampToken unsigned attribute (PAdES-B-T). The ephemeral private key never leaves the Worker. The browser embeds the resulting CMS bytes into the /Contents placeholder. PDF bytes are never accepted on this endpoint. The base PDF stays intact as revision 1, the signature is revision 2 — multi-signer documents stack additional revisions cleanly, each with its own per-user leaf cert. Idempotent: same byterange_sha256 on retry returns the cached CMS (and the previously-issued leaf cert); a different byterange_sha256 returns 409 already_sealed_with_different_byterange. Requires the four session-binding headers (see x-fsig-session-* component parameters). FreeSign also submits the byterange hash to OpenTimestamps for independent timestamp proof; after calendar upgrade this proof resolves to public block headers.
Request
- Base URL: https://free-sign.com
- URL: https://free-sign.com/api/envelopes/{envelope_id}/seal
- Auth: none declared. Required headers: x-fsig-session-signature, x-fsig-session-nonce, x-fsig-session-timestamp, x-fsig-session-action
Path parameters
Headers
Base64url ECDSA P-256 signature over canonicalJson({action, envelope_id, nonce, timestamp}). Produced by the non-extractable session keypair bound to this envelope at creation. See invariant #11 in CLAUDE.md.
Random 32-hex-char nonce, fresh per request.
ISO-8601 UTC timestamp of the request. Accepted within +/-5 minutes of server clock.
Action name matching the route. Must equal the action field in the signed canonical payload. The webauthn.* actions gate the browser-only passkey endpoints.
Request body
Response
Per-user CMS produced (or returned from cache if idempotent). Response includes the base64 CMS, the leaf cert + serial + validity, and audit hashes.
Changes
No recorded changes to this operation across all 1 revision of this API.