Signing sessions & links
Recipients sign on Bona Fide’s signing page: either the hosted page at https://sign.bonafidesign.com/s/…, or
the same page embedded in your product with <bf-signing>. Recipients do not need
an account. What lets them in is a signing session.
Signing sessions
Section titled “Signing sessions”Email delivery creates sessions for you (the link in the email). For embedded or self-delivered signing, mint one from your backend:
curl https://bonafide-api.republic.global/v1/envelopes/env_8Gm2kQ4rT1/sessions \ -H "Authorization: Bearer $BONA_FIDE_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: $(uuidgen)" \ -d '{ "recipient": "rcp_5Jw0pLs9Xe", "mode": "embedded", "allowedOrigins": ["https://app.example.com"], "ttlSeconds": 600 }'{ "id": "ses_2Hq8vNw3Kd", "token": "bfs_…", "url": "https://bonafide-app.republic.global/s/?embed=1#session=bfs_…", "expiresAt": "2026-10-13T14:08:04.512Z"}| Field | Notes |
|---|---|
recipient |
A recipient of the envelope who can still sign (not CC, not finished) |
mode |
embedded (default) or hosted. Hosted urls are …/s/<token>, to redirect to or send yourself |
allowedOrigins |
Up to 10 origins that may frame the embedded page |
returnUrl |
Where the hosted page sends the signer when they are done |
ttlSeconds |
60 to 3600, default 600 |
Sessions are single-use: once the recipient submits, the session is spent, and a used or expired session answers
410 Gone. Mint a fresh one each time your page renders. Sessions can only be created while the envelope is sent,
and voiding the envelope ends them all.
What the signer goes through
Section titled “What the signer goes through”The signing page (and any custom UI you build on the same API) follows these steps:
- Exchange:
POST /v1/signing/exchange { "token": "bfs_…" }returns a short-lived access token (bfa_…) kept in memory. No cookies are involved, so third-party cookie blocking does not matter in an iframe. - Load:
GET /v1/signing/sessionreturns the signer view (envelope title, sender, the recipient’s role and fields, prefilled values, the consumer disclosure) and recordsrecipient.viewedthe first time.GET /v1/signing/documentis the PDF to display. - Verify: with
email_otp,POST /v1/signing/otpemails a six-digit code (10 minutes) andPOST /v1/signing/otp/verifychecks it. A wrong code returnsverified: falsewith the attempts left. - Submit:
POST /v1/signing/submitwith the fieldvalues(signatures drawn, typed or uploaded), consent to the disclosure, andviewedDocumentHash: the SHA-256 of the exact PDF that was shown. If the document changed since, the submit is refused (409). - Or decline:
POST /v1/signing/decline { "reason": "…" }ends the envelope (envelope.declined).
It is the recipient’s turn only when every earlier routing group is done; before that the view has
canSign: false, and a submit answers 409 not-your-turn.
Always-on signing links
Section titled “Always-on signing links”A signing link is a permanent URL for contracts that many people sign the same way: a waiver, a contractor agreement, terms for a pilot. Every visitor who completes it gets their own envelope.
curl https://bonafide-api.republic.global/v1/signing-links \ -H "Authorization: Bearer $BONA_FIDE_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "template": "tpl_…", "visitorRole": "contractor", "countersign": { "role": "company", "mode": "auto" }, "limits": { "perEmailPerDay": 3, "perIpPerHour": 20, "dailyCap": 200, "allowedEmailDomains": ["example.com"] } }'visitorRoleis the signer role the visitor takes. Links support one visitor and at most one countersigner:mode: "auto"countersigns automatically with the Bona Fide platform seal,mode: "person"routes the envelope to the givenemail. Every other non-CC role would have no recipient, so such templates are refused.- The visitor enters their name, email and the template’s variables, then confirms an email code before anything
is created (
POST /v1/links/{id}/start, thenPOST /v1/links/{id}/verify). limitscaps use per email per day (default 3), per IP per hour (default 20), per day overall, and by email domain. Withdedupe(default on), an email address that already signed gets409 already-signed.PATCH /v1/signing-links/{id}pauses (active: false), resumes or moves the link to a newer version; deleting a link keeps the envelopes it created. Each use emitssigning_link.used.