Opbox Sign API
Opbox Sign is the native, in-tree e-signature provider. Envelopes carry a PDF source document, a typed field layout, and one or more recipient signing sessions. Signed output is flattened into a new PDF, paired with a programmatic audit certificate, and appended to a hash-chained workspace audit log that rolls up daily and is externally anchored via RFC 3161.
Opbox Sign is scoped to the Simple Electronic Signature (SES) tier with a defensible audit trail. Sender endpoints require an authenticated session with CSRF protection. Signer endpoints are public and are gated by a one-time, hashed, 32-byte token supplied in the email invite.
Envelopes
Authenticated sender routes. All mutations require CSRF and ADMIN / OWNER role on the workspace.
| Method | Endpoint | Description |
|---|---|---|
POST | /api/sign/envelopes | Create an envelope from a source PDF file. |
GET | /api/sign/envelopes | List envelopes for the active workspace. |
GET | /api/sign/envelopes/:id | Load a single envelope with fields, recipients, and sessions. |
PATCH | /api/sign/envelopes/:id | Update envelope metadata (subject, consent text, signing order, reminder cadence). |
DELETE | /api/sign/envelopes/:id | Void an envelope. Expires open sessions, notifies recipients, retains signed artefacts. |
PUT | /api/sign/envelopes/:id/recipients | Replace the recipient list (denormalised JSON cache on the envelope). |
POST | /api/sign/envelopes/:id/fields | Place a field on the PDF. Body carries type, page, normalised coordinates, role. |
GET | /api/sign/envelopes/:id/fields | List all fields for an envelope. |
PATCH | /api/sign/envelopes/:id/fields/:fieldId | Move, resize, or retype a field. |
DELETE | /api/sign/envelopes/:id/fields/:fieldId | Remove a field. |
POST | /api/sign/envelopes/:id/send | Mint signing tokens, create sessions, email invites, log send events. |
GET | /api/sign/envelopes/:id/audit-bundle | Export a verifiable JSON bundle (see Audit Log). |
Create envelope
POST /api/sign/envelopes
Content-Type: application/json
x-csrf-token: <token>
{
"subject": "Director Appointment - Acme Holdings Ltd",
"sourceFileId": "cln1a2b3c4d5e6f7g8h9",
"signingOrder": "SEQUENTIAL",
"consentText": "By signing this document electronically, I agree that my signature is as legally binding as a handwritten one..."
}
Response 201 Created:
{
"id": "cln1a2b3c4d5e6f7g8h9",
"status": "CREATED",
"provider": "OPBOX",
"sourceFileId": "cln1a2b3c4d5e6f7g8h9",
"recipients": [],
"fields": [],
"createdAt": "2026-04-16T09:21:34.000Z"
}
Recipient shape
{
"name": "Dr Aisha Rahman",
"email": "aisha@acme.example",
"role": "Director",
"signingOrder": 1
}
Field shape
Coordinates are stored as fractions of page width and height. This lets the signer render the PDF at any resolution and overlay fields by multiplication.
{
"id": "cln_field_a1b2c3",
"type": "SIGNATURE",
"page": 1,
"x": 0.12,
"y": 0.78,
"width": 0.3,
"height": 0.06,
"required": true,
"recipientRole": "Director",
"tabOrder": 1
}
Supported field types: SIGNATURE, INITIAL, TEXT, NAME, EMAIL, DATE, CHECKBOX, DROPDOWN.
Sessions
Public signer routes. The URL token is the credential. There is no session cookie. Every route hashes the token, looks up by tokenHash, and enforces the session state machine. Invalid tokens return a generic 404.
| Method | Endpoint | Description |
|---|---|---|
GET | /api/sign/sessions/:token | Load session state, envelope metadata, fields, and any existing instances. |
GET | /api/sign/sessions/:token/pdf | Stream the decrypted source PDF for rendering. |
GET | /api/sign/sessions/:token/auth-check | Verify re-auth status before submit. |
POST | /api/sign/sessions/:token/consent | Record consent. Stamps consentedAt, persists the exact consent text shown. |
POST | /api/sign/sessions/:token/otp | Request a 6-digit email OTP code. |
PUT | /api/sign/sessions/:token/otp | Verify an OTP code. Five attempts, 10-minute TTL. |
POST | /api/sign/sessions/:token/sign | Persist a single field value. SIGNATURE / INITIAL PNGs are stored encrypted. |
POST | /api/sign/sessions/:token/submit | Final submit. Triggers compositing, audit certificate, and completion emails. |
POST | /api/sign/sessions/:token/decline | Decline the envelope. A single decline cancels the whole envelope. |
GET | /api/sign/sessions/:token/download | Download the signed PDF (after completion). Checksum-verified. |
GET | /api/sign/sessions/:token/audit-cert | Download the audit certificate PDF. |
Session lifecycle
PENDING -> SENT -> OPENED -> CONSENTED -> IN_PROGRESS -> COMPLETED
-> DECLINED
-> EXPIRED
Tokens are single-use: once a session transitions to COMPLETED or DECLINED, the stored tokenHash is nulled within the same database transaction. A replayed link after completion returns 404.
Fields and signing flow
The sender drops fields in a visual designer. Each interaction posts a PATCH to the field route with updated coordinates. No save button. The signer flow walks through:
- Consent gate (
/consent) - required before any field interaction. - Initial access OTP (
/otp) - gates field entry. - Per-field writes (
/sign) - one POST per field value. - Signature authorisation OTP (
/otpagain) - grants a 10-minute submit window. - Final submit (
/submit) - validates all required fields, marks the session COMPLETED, triggers the compositor on the last signer.
SIGNATURE and INITIAL field values are PNG images produced client-side (draw, type, or upload), base64-uploaded, stored in the encrypted file store, and referenced by the signature instance. TEXT-class fields store their value inline. CHECKBOX stores a boolean.
Completion triggers an inline compositor that flattens signatures into the source PDF, embeds document metadata, computes a SHA-256 checksum, stores the signed PDF, generates the audit certificate, and sends completion emails to the sender and all recipients.
Audit Log
Every meaningful action emits a SigningEvent row (envelope-scoped timeline). On envelope completion, a chain entry is appended to SigningAuditLogEntry (per-workspace, hash-chained with a domain-separated SHA-256 prefix).
Each entry's hash includes the previous entry's hash, so modifying or dropping any row is detectable by recomputing the chain.
A daily cron builds two Merkle rollups:
- Workspace anchor: Merkle root over new chain entries for that workspace during the day.
- Organisation anchor: Merkle root over all workspace anchors for the org, externally timestamped via RFC 3161 and signed via AWS KMS.
The GET /api/sign/envelopes/:id/audit-bundle endpoint returns a verifiable JSON bundle containing the envelope metadata, the signed-file checksum, the chain entries touching the envelope, the workspace anchor root, the organisation Merkle proof, and the RFC 3161 + KMS proofs. Any third party can verify the bundle offline by recomputing hashes and checking the external timestamp.
The audit certificate PDF carries its own per-page hash chain footer, independent of the database, so a tampered cert is detectable even without access to the workspace chain.
Audit entries have a 7-year minimum retention window that overrides per-workspace retention tiers.
Example: end-to-end walkthrough
# 1. Create envelope
curl -X POST https://opbox.app/api/sign/envelopes \
-H "Content-Type: application/json" \
-H "x-csrf-token: $CSRF" \
-b "session=$SESSION" \
-d '{
"subject": "Engagement Letter - Q2 2026",
"sourceFileId": "cln_src_file_abc",
"signingOrder": "SEQUENTIAL"
}'
# 2. Add recipients
curl -X PUT https://opbox.app/api/sign/envelopes/cln_env_xyz/recipients \
-H "x-csrf-token: $CSRF" -b "session=$SESSION" \
-d '{"recipients":[{"name":"Jane Partner","email":"jane@example.com","role":"Partner","signingOrder":1}]}'
# 3. Place a signature field on page 1
curl -X POST https://opbox.app/api/sign/envelopes/cln_env_xyz/fields \
-H "x-csrf-token: $CSRF" -b "session=$SESSION" \
-d '{"type":"SIGNATURE","page":1,"x":0.1,"y":0.8,"width":0.3,"height":0.06,"required":true,"recipientRole":"Partner"}'
# 4. Send
curl -X POST https://opbox.app/api/sign/envelopes/cln_env_xyz/send \
-H "x-csrf-token: $CSRF" -b "session=$SESSION"
# 5. Poll status
curl https://opbox.app/api/sign/envelopes/cln_env_xyz \
-b "session=$SESSION"
Once the last recipient submits, the envelope reaches COMPLETED, signedFileId and auditCertFileId are populated, and completion emails ship with both PDFs attached.
Status Codes
| Status | Meaning |
|---|---|
200 OK | Successful read or update. |
201 Created | Envelope, field, or session created. |
204 No Content | Field or recipient deletion. |
400 Bad Request | Validation failure (coordinates out of bounds, required field missing, malformed body). |
401 Unauthorized | Sender route with no authenticated session. |
403 Forbidden | Workspace mismatch, wrong role, or consent missing before a field write. |
404 Not Found | Envelope, field, or session token hash does not exist. Invalid tokens always return 404. |
409 Conflict | Session state-machine violation (double-sign, submit before OTP, resend after completion). |
410 Gone | Session expired or envelope voided. |
429 Too Many Requests | Per-IP or per-session rate limit exceeded. |
500 Internal Server Error | Compositor failure or unexpected error. Compositor failures revert the envelope to IN_PROGRESS and emit a COMPOSITOR_FAILURE security event. |