
Engineering
Add e-signatures to a document workflow
FastYoke Engineering · 6 min read · Jul 14, 2026
- Tutorial
- E-signature
- Workflow
Audience: power users (Pro+) · Recipe: Add a legally-binding e-signature step to a document workflow — without per-envelope fees.
What you'll build
By the end of this tutorial you'll have a document workflow with a real signing gate in the middle of it. A record moves from draft, an operator sends it out for signature, and the signer opens a token-gated public page, reviews the exact PDF they're agreeing to, and signs — typing or drawing their name. The moment they finish, the record advances to signed and the signed event lands in your append-only event log.
The signed document is sealed with an ed25519 cryptographic signature over a hash-chained audit trail. That makes it legally meaningful under ESIGN/UETA without a PKI, without a third-party e-sign vendor, and — this is the part that adds up — without a per-envelope fee. The signature, the audit chain, and the Certificate of Completion all live inside your own tenant data. Anyone can verify a sealed document later against a published public key, with no call back to FastYoke.
Before you start
- A Pro (or higher) tenant. E-signature capture and sealing is a Pro+ capability.
- A document to be signed. You have two starting points: a form that generates the PDF at submit time (an intake form, a consent form, a single-party agreement), or an existing PDF you upload (a contract, an NDA, a vendor agreement). This tutorial covers the uploaded-PDF path, then shows the form path as a variation.
- An entity or job that represents the thing being signed — the row your workflow moves through draft → out-for-signature → signed. If you don't have one yet, create a simple entity (for example,
Agreement) in your app first. - Admin access. Preparing and sending an envelope is an admin action; the signer needs no account at all.
Steps
1. Model the signing step as an FSM transition
Open your workflow in the FSM Designer and give the agreement three states: draft, out_for_signature, and signed. Add two forward transitions: send (draft → out_for_signature) and complete (out_for_signature → signed).
The reason to model signing as a real FSM step — rather than a status flag you flip by hand — is that the signed transition writes one row to the append-only event_log. That row is your durable record that the state changed and when. Because the ledger is append-only, nobody can quietly walk the agreement back to draft and pretend it was never signed; the history is the source of truth. Downstream automations (notify accounting, kick off fulfillment, start a retention clock) hang off the complete transition rather than off a mutable column.
2. Upload the document and place the signature fields
In the admin shell, go to Envelopes → New uploaded-PDF and drop your PDF in. FastYoke creates a draft envelope whose source_kind is uploaded and stores your file as an attachment. Under the hood this is the POST /esign/documents call, which returns an envelope_id.
Open the placement editor and drag a signature field (and optionally a date field) onto the page where the signer signs. Coordinates are normalized to a 0..1 range inside each page, so the field lands in the right spot no matter what DPI the document renders at. Assign each field to a signer slot — you'll define that slot next.
3. Add the signer and send
Still on the draft, add a signer slot: a role label (counterparty), a capture mode of send (an emailed link, versus inline self-sign), and an email source. If the agreement is counter-signed by your own company, add a second slot in inline capture mode and self-sign it before you send — your signature stamps into every field assigned to that slot.
Placements and the signer plan save together with a single PUT /esign/envelopes/:id/draft. Then click Send. FastYoke moves the envelope to in_progress and emails each send signer a one-time portal link. This is the natural place to fire your workflow's send transition, moving the agreement to out_for_signature and dropping the first event-log row.
4. The signer signs on the token-gated portal
The invitation email carries a signed, single-use URL that resolves to the public signer portal at /esign/sign/:token. The signer needs no FastYoke account — the token is their authorization, bound to this envelope and this specific signer slot.
On that page the signer sees, in order: the fully rendered PDF preview (exactly the bytes that will be sealed), a versioned consent disclosure explaining what they're agreeing to and their right to withdraw, and their email of record for confirmation. If you configured an access code, they enter it here; the platform checks it against a stored hash before it lets them sign.
They sign with the signature pad — type their name for a rendered signature, or draw it with mouse or finger. On Finish, FastYoke records the signed-at timestamp, the signer's IP, and their user-agent, and appends all of it to the hash-chained audit log. If any signer would rather not proceed, the Decline path captures a reason and moves the envelope to declined — a clean, recorded exit rather than a silent dead end.
5. Sealing and the signed event
When the last required signer finishes, FastYoke stamps every signature into the document, folds the audit chain into a canonical-JSON manifest, and signs that manifest with an ed25519 key. It then renders a Certificate of Completion — a PDF listing every signer, every audit event with timestamps, the document's SHA-256, and the signature — and publishes the sealed envelope.
That finalization is your cue to fire the workflow's complete transition: out_for_signature → signed. A guard on that transition (a JSONLogic predicate checking that the envelope reached its sealed state) keeps the FSM honest — the agreement can't reach signed unless a real sealed envelope backs it. The transition writes the signed row into the append-only event_log, and that ledger row — not a status column — is what any dashboard, report, or downstream automation should read.
6. Verify it — from outside FastYoke
Anyone holding the sealed PDF can confirm it independently. GET /api/v1/public/esign/verify/:verification_id returns the manifest bytes, the ed25519 signature, the audit chain, and the key id; GET /api/v1/public/esign/verify-keys returns the published public keys. A verifier checks the signature against the literal manifest bytes and recomputes the document's SHA-256 to confirm nothing changed — all with a pure-JavaScript ed25519 library and no call back to us at runtime. The evidence outlives any vendor relationship because it's math, not a login.
Take it further
- Multi-party, in order. The signer plan supports up to three signers with order tiers — tier 0 signs before tier 1 can start — and per-slot
required_whenconditions. A parent/minor consent form can require a second parent's signature only when a "sole guardian" box is unchecked, and skip that slot cleanly otherwise. - Sign a form's generated PDF instead of an upload. Set
requires_esign_ceremonyon a form and the respondent signs the form-generated PDF inline at submit, with no email round-trip — same sealing engine, same audit chain. - Feed the evidence vault. Sealed PDFs and their Certificates of Completion flow into the Compliance Yoke as audit-ready evidence, so a reviewer or auditor finds every signed agreement in one place.
Related
- E-signatures overview — the three signing modes and the one sealing engine behind them.
- Uploaded-PDF envelopes — the full field-placement and signer-plan reference.
- The signer experience — what the ceremony and portal pages look like to the person signing.
- Trust and verify — the audit chain, the seal, the legal posture, and offline-verification recipes.