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:
| Variable | Description |
|---|---|
SIGNABLE_API_KEY | Your API key from the Signable dashboard |
SIGNABLE_WEBHOOK_SECRET | The 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
| Status | Meaning |
|---|---|
draft | Envelope created locally, not yet sent to Signable |
sent | Envelope sent; signatories are being notified |
partially_signed | At least one signatory has signed, but not all |
completed | All signatories have signed |
cancelled | Envelope was cancelled before completion |
expired | Envelope passed its expiry date without completion |
Signer Statuses
| Status | Meaning |
|---|---|
pending | Awaiting notification |
notified | Invitation email sent |
viewed | Signer opened the document |
signed | Signer has completed their signature |
declined | Signer 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 Event | Effect |
|---|---|
envelope.sent | Envelope status → sent |
envelope.viewed | Signer status → viewed; viewedAt timestamp set |
envelope.signed | Signer status → signed; envelope status → partially_signed |
envelope.completed | Envelope status → completed; signed PDF URL stored; tenancyTermDocument updated with signingMethod: eSigned; audit log created |
envelope.declined | Signer status → declined; decline reason stored; audit log created |
envelope.expired | Envelope 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:
| Column | Type | Description |
|---|---|---|
id | text | Local UUID primary key |
signable_envelope_id | text | Signable's external envelope ID |
tenancy_term_id | text | Associated tenancy term |
tenancy_term_document_id | text | Optional link to source document |
document_type | text | e.g. tenancy_agreement, guarantor_agreement |
status | text | Current envelope status |
total_signers | integer | Total number of signatories |
signed_count | integer | Number who have signed |
source_pdf_url | text | URL of the uploaded PDF |
signed_pdf_url | text | URL of the completed signed PDF |
replaces_envelope_id | text | ID of the envelope this one replaced |
last_webhook_payload | jsonb | Most recent webhook payload from Signable |
signable_signers
Key columns:
| Column | Type | Description |
|---|---|---|
id | text | Local UUID primary key |
envelope_id | text | Parent envelope |
signable_signer_id | text | Signable's external party ID |
name | text | Signer's full name |
email | text | Signer's email address |
role | text | tenant | landlord | guarantor | agent | witness |
signing_order | integer | Sequential signing position |
status | text | Current signer status |
signing_ip_address | text | IP address captured at signing time |
decline_reason | text | Populated if signer declined |