Skip to main content
All Docs
FeaturesPurple PepperUpdated April 7, 2026

eSigning with Signable

eSigning with Signable

The platform integrates with the Signable API to support end-to-end digital eSigning of tenancy agreements and compliance documents. Documents can be sent to multiple signatories in a defined order, with real-time status updates delivered via webhooks.

Prerequisites

You must configure three environment variables before the integration is active:

VariableDescription
SIGNABLE_API_KEYYour API key from the Signable dashboard
SIGNABLE_WEBHOOK_SECRETThe webhook signing secret for HMAC-SHA256 verification
SIGNABLE_API_URL(Optional) Override the default base URL (https://api.signable.co.uk/v1)

If SIGNABLE_API_KEY is not set, the integration degrades gracefully — eSigning.isConfigured will return false and no API calls will be made.

Workflow

1. Agent uploads PDF
       ↓
2. Call eSigning.sendForSigning with signatories
       ↓
3. Signable sends invitation emails in signing order
       ↓
4. Webhook events received → Inngest processes each
   (envelope.viewed, envelope.signed, envelope.declined, …)
       ↓
5. envelope.completed
   → Signed PDF URL stored
   → tenancyTermDocument updated with signingMethod: eSigned

If an applicant's email address changes after the envelope is sent, call eSigning.reissue — it will cancel the original envelope and create a new one, maintaining a re-issuance chain via replacesEnvelopeId.

Signing Statuses

Envelope Statuses

StatusMeaning
draftEnvelope created locally, not yet sent to Signable
sentEnvelope sent; signatories are being notified
partially_signedAt least one signatory has signed, but not all
completedAll signatories have signed
cancelledEnvelope was cancelled before completion
expiredEnvelope passed its expiry date without completion

Signer Statuses

StatusMeaning
pendingAwaiting notification
notifiedInvitation email sent
viewedSigner opened the document
signedSigner has completed their signature
declinedSigner refused to sign (decline reason recorded)

tRPC Procedures

All procedures are available under the eSigning namespace.

eSigning.isConfigured

Returns whether the Signable integration is configured and ready to use. Check this before attempting to send documents.

const { configured } = await trpc.eSigning.isConfigured.query();

eSigning.sendForSigning

Sends a document (PDF) to Signable with one or more signatories. Signatories receive invitation emails in signingOrder sequence.

await trpc.eSigning.sendForSigning.mutate({
  tenancyTermId: "...",
  tenancyTermDocumentId: "...",   // optional
  documentType: "tenancy_agreement",
  title: "Tenancy Agreement — 12 High Street",
  sourcePdfUrl: "https://...",
  signatories: [
    { name: "Jane Smith", email: "jane@example.com", role: "tenant", signingOrder: 1 },
    { name: "John Landlord", email: "john@example.com", role: "landlord", signingOrder: 2 },
  ],
});

Signer roles: tenant | landlord | guarantor | agent | witness

eSigning.cancel

Cancels an active envelope. Accepts an optional cancellation reason.

await trpc.eSigning.cancel.mutate({
  envelopeId: "...",
  reason: "Tenant requested amendment",
});

eSigning.reissue

Cancels the specified envelope and creates a replacement with updated signatory details. Use this when an applicant's email address changes after the envelope has been sent.

await trpc.eSigning.reissue.mutate({
  envelopeId: "...",
  signatories: [ /* updated list */ ],
});

eSigning.listByTenancyTerm

Returns all envelopes associated with a tenancy term.

const envelopes = await trpc.eSigning.listByTenancyTerm.query({
  tenancyTermId: "...",
});

eSigning.getById

Returns full envelope details including all signer statuses.

const envelope = await trpc.eSigning.getById.query({ envelopeId: "..." });
// envelope.signers includes per-signer status, timestamps, and decline reasons

eSigning.refreshStatus

Manually polls Signable for the latest envelope and signer statuses. Use this as a fallback if a webhook event was missed.

await trpc.eSigning.refreshStatus.mutate({ envelopeId: "..." });

Webhook Handler

The webhook endpoint is available at:

POST /api/webhooks/signable

This endpoint must be registered in your Signable dashboard as the webhook destination. It is publicly accessible (no authentication middleware) and verifies request authenticity using HMAC-SHA256 via the x-signable-signature header.

The endpoint always responds with 200 OK to prevent Signable from retrying events that failed during internal processing.

Verification ping: Signable may issue a GET /api/webhooks/signable request to confirm the endpoint is reachable — this is handled automatically.

Supported Webhook Events

Signable EventEffect
envelope.sentEnvelope status → sent
envelope.viewedSigner status → viewed; viewedAt timestamp set
envelope.signedSigner status → signed; envelope status → partially_signed
envelope.completedEnvelope status → completed; signed PDF URL stored; tenancyTermDocument updated with signingMethod: eSigned; audit log created
envelope.declinedSigner status → declined; decline reason stored; audit log created
envelope.expiredEnvelope status → expired; audit log created

Audit Trail

Audit log entries are automatically created for the following events:

  • Envelope completed (all signatories signed)
  • Signer declined (includes decline reason)
  • Envelope expired

Each signer record also captures the IP address at the time of signing for legal audit purposes.

Data Model

signable_envelopes

Key columns:

ColumnTypeDescription
idtextLocal UUID primary key
signable_envelope_idtextSignable's external envelope ID
tenancy_term_idtextAssociated tenancy term
tenancy_term_document_idtextOptional link to source document
document_typetexte.g. tenancy_agreement, guarantor_agreement
statustextCurrent envelope status
total_signersintegerTotal number of signatories
signed_countintegerNumber who have signed
source_pdf_urltextURL of the uploaded PDF
signed_pdf_urltextURL of the completed signed PDF
replaces_envelope_idtextID of the envelope this one replaced
last_webhook_payloadjsonbMost recent webhook payload from Signable

signable_signers

Key columns:

ColumnTypeDescription
idtextLocal UUID primary key
envelope_idtextParent envelope
signable_signer_idtextSignable's external party ID
nametextSigner's full name
emailtextSigner's email address
roletexttenant | landlord | guarantor | agent | witness
signing_orderintegerSequential signing position
statustextCurrent signer status
signing_ip_addresstextIP address captured at signing time
decline_reasontextPopulated if signer declined