Opbox

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.

MethodEndpointDescription
POST/api/sign/envelopesCreate an envelope from a source PDF file.
GET/api/sign/envelopesList envelopes for the active workspace.
GET/api/sign/envelopes/:idLoad a single envelope with fields, recipients, and sessions.
PATCH/api/sign/envelopes/:idUpdate envelope metadata (subject, consent text, signing order, reminder cadence).
DELETE/api/sign/envelopes/:idVoid an envelope. Expires open sessions, notifies recipients, retains signed artefacts.
PUT/api/sign/envelopes/:id/recipientsReplace the recipient list (denormalised JSON cache on the envelope).
POST/api/sign/envelopes/:id/fieldsPlace a field on the PDF. Body carries type, page, normalised coordinates, role.
GET/api/sign/envelopes/:id/fieldsList all fields for an envelope.
PATCH/api/sign/envelopes/:id/fields/:fieldIdMove, resize, or retype a field.
DELETE/api/sign/envelopes/:id/fields/:fieldIdRemove a field.
POST/api/sign/envelopes/:id/sendMint signing tokens, create sessions, email invites, log send events.
GET/api/sign/envelopes/:id/audit-bundleExport 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.

MethodEndpointDescription
GET/api/sign/sessions/:tokenLoad session state, envelope metadata, fields, and any existing instances.
GET/api/sign/sessions/:token/pdfStream the decrypted source PDF for rendering.
GET/api/sign/sessions/:token/auth-checkVerify re-auth status before submit.
POST/api/sign/sessions/:token/consentRecord consent. Stamps consentedAt, persists the exact consent text shown.
POST/api/sign/sessions/:token/otpRequest a 6-digit email OTP code.
PUT/api/sign/sessions/:token/otpVerify an OTP code. Five attempts, 10-minute TTL.
POST/api/sign/sessions/:token/signPersist a single field value. SIGNATURE / INITIAL PNGs are stored encrypted.
POST/api/sign/sessions/:token/submitFinal submit. Triggers compositing, audit certificate, and completion emails.
POST/api/sign/sessions/:token/declineDecline the envelope. A single decline cancels the whole envelope.
GET/api/sign/sessions/:token/downloadDownload the signed PDF (after completion). Checksum-verified.
GET/api/sign/sessions/:token/audit-certDownload 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:

  1. Consent gate (/consent) - required before any field interaction.
  2. Initial access OTP (/otp) - gates field entry.
  3. Per-field writes (/sign) - one POST per field value.
  4. Signature authorisation OTP (/otp again) - grants a 10-minute submit window.
  5. 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

StatusMeaning
200 OKSuccessful read or update.
201 CreatedEnvelope, field, or session created.
204 No ContentField or recipient deletion.
400 Bad RequestValidation failure (coordinates out of bounds, required field missing, malformed body).
401 UnauthorizedSender route with no authenticated session.
403 ForbiddenWorkspace mismatch, wrong role, or consent missing before a field write.
404 Not FoundEnvelope, field, or session token hash does not exist. Invalid tokens always return 404.
409 ConflictSession state-machine violation (double-sign, submit before OTP, resend after completion).
410 GoneSession expired or envelope voided.
429 Too Many RequestsPer-IP or per-session rate limit exceeded.
500 Internal Server ErrorCompositor failure or unexpected error. Compositor failures revert the envelope to IN_PROGRESS and emit a COMPOSITOR_FAILURE security event.