Collect electronic signatures on any DocType with eSign
Install the eSign app, turn any DocType into a signing page with a web form, and get a signed PDF, SHA-256 hash and audit trail.
On this page
eSign is an open-source Frappe app from Avunu that lets a customer or supplier sign a document in their browser without logging in. Use it when a quotation, contract, work order or any other DocType needs a signature and you want the signed PDF and a record of who signed attached to the document automatically.
The app works through standard Frappe Web Forms. If a signer needs a paper copy instead, see Mailing Printed Letters with PostGrid. You enable eSign on a Web Form, add a Signature field, and send the signer an expiring link.
How it works
You enable eSign on a published Web Form for a DocType.
You send the signer a link built from a Document Share Key. The link opens a two-pane page: the document preview and a "Fill and Sign" form.
The signer draws, types or uploads a signature and submits.
A background job renders the document to PDF, stores it as a private file, computes a SHA-256 hash, and records an audit entry on the document timeline.
The signer never needs a user account. Access is granted by the share key in the link.
Install the app
Run these from the bench directory (frappe-bench):
bench get-app Avunu/esign
bench --site <SITE_NAME> install-app esignInstalling adds an eSign option to the Communication type field (used for the audit trail) and adds the eSign fields to the Web Form and Email Template DocTypes. The app's hooks contain a separate code path for Frappe v16 and for other versions. We read the app against a v16 checkout and did not test v15.
Note
The signed PDF is generated by a background job on the short queue. If your bench workers are not running, signatures are saved but the PDF and audit entry are never created.
Prepare the DocType and print format
Your DocType needs a field of type Signature for the signer to fill in. Add one in Customize Form if it does not exist.
The signed PDF is rendered from a print format after the signature is saved, so choose a print format that prints the signature field. If the signature is not on the print format, the PDF will not show it.
Create the Web Form
Open Web Form and create a new one for your DocType.
Check Published. Only published eSign forms appear when you send a request.
Add the Signature field to the form fields table. Add any other fields the signer should fill in, such as an email address or printed name.
Check Enable eSign. The eSign Settings section appears.
Set Print Format to the format used for the preview and the signed PDF. If you leave it empty, the standard format is used.
Save.
Warning
On submit, eSign writes every field listed in the Web Form onto the document, and it does so with permission checks skipped because the share key already proved access. List only the fields the signer should change. Anything else on the document should stay out of the Web Form.
Saving an eSign Web Form forces Allow Edit on, because signing is an update to an existing document.
eSign settings
| Field | What it does |
|---|---|
| Enable eSign | Turns the Web Form into a signing page. |
| Submit on Response | Submits the document after signing. It only applies to a submittable DocType whose document is still a draft. |
| Only allow Signing on Mobile | On desktop, hides the form and shows a QR code to scan with a phone. The form stays available on small screens. |
| Set Property After Response | A field on the DocType to update when the document is signed, such as status. |
| Value To Be Set | The value written to that field. |
| Send Notification on Response | Sends a notification email when signing completes. |
| Notification | The Notification to send. It must be enabled, use the Custom event, use the Email channel, and be for the same DocType. |
Workflow field updates
Set Set Property After Response to a field and Value To Be Set to the value. After a signature, eSign writes that value as part of the same save. For example, you can set status to Signed so the document moves out of an "awaiting signature" state without anyone touching it.
The field list in the dropdown shows the DocType's value fields. At save time, eSign only applies the update to fields of type Select, Link, Data or Text and silently skips other types.
If the document is submitted and the signature field has no Allow on Submit, Frappe's normal rule about editing submitted documents still applies. We did not test signing against submitted documents.
Create a signature request
From the document form
On any DocType with a published eSign Web Form, a Send for eSign button appears on the document form.
Create an Email Template for the request and check eSign Request. Only templates with that box checked appear in the dialog.
Open the document and click Send for eSign.
Pick From (an Email Account with outgoing enabled), To, the Email Template and the eSign Web Form. Optionally set CC, BCC, a print format and Send me a copy.
Check the link preview, edit the subject and message if needed, and click Send eSign Request.
The email is sent immediately and a Communication is linked to the document. If the message body does not already contain the link, eSign appends a "Sign Document" button.
Note
Save the document first. The button warns you if the document is unsaved.
Build a link yourself
The link has this shape:
https://<SITE_NAME>/<WEB_FORM_ROUTE>/<DOCNAME>/edit?key=<SHARE_KEY>&format=<PRINT_FORMAT>&email=<SIGNER_EMAIL>The format and email parameters are optional. To generate one in a console session:
from esign.esign.custom.web_form import get_esign_link
doc = frappe.get_doc("Quotation", "<DOCNAME>")
get_esign_link(doc, "<WEB_FORM_NAME>", "", "jane@example.com")Run it with bench --site <SITE_NAME> console. The function returns an empty string if the Web Form is not eSign-enabled or belongs to a different DocType. It is also registered as a Jinja method, so you can call get_esign_link inside your own email templates.
Note
The function joins the site address and the Web Form's Route directly, without adding a slash between them. We did not test the generated link on a live site, so open the link preview in the Send for eSign dialog, or print the console result, and check it before you send anything to a signer.
Passing the signer's email matters. The signer's address is pre-filled into an email field on the form and recorded in the audit trail. The Send for eSign dialog does not add the email parameter, so use your own template if you want it.
Link expiry
Each link carries a Frappe Document Share Key. Frappe stamps an expiry date on the key when it creates it, using the Document Share Key Expiry (in Days) setting in System Settings (the field defaults to 30 days). Frappe's code falls back to 90 days if the setting is empty or zero.
Changing the setting affects keys created afterwards, not links already sent.
A key that does not belong to the document stops the page with "Invalid or expired document access key". A key past its expiry date stops it with Frappe's Link Expired error. After a document is signed, the same link still works until it expires, and shows the signed document with a download button.
The public endpoints are rate limited per client:
| Endpoint | Limit |
|---|---|
Submit signature (accept) | 10 requests per 60 seconds |
Preview HTML (get_print_html) | 30 requests per 60 seconds |
Signed PDF download (download_signed_pdf) | 20 requests per 60 seconds |
What the signer sees
The page shows the document rendered with your print format, scaled to fit, and a Fill and Sign form. The signer can also download the unsigned PDF from the header.
The Signature control has three modes:
Draw on a canvas, which works with touch.
Type a name and render it in one of four bundled fonts: Pacifico, Dancing Script, Great Vibes or Caveat.
Upload an image of a signature.
When the signer submits, the form is replaced by a success card and the preview refreshes to show the signature. If a success URL is set on the Web Form, the page redirects after a five second countdown.
The page counts as completed when every Signature field on the Web Form has a value. Completed pages show the signed PDF instead of the form.
The signed PDF and its hash
After each successful submit, a background job renders the PDF using the print format. The format comes from the link's format parameter, then the Web Form's Print Format, then standard.
The job then:
Names the file
<DOCNAME>_signed_<YYYYMMDD_HHMMSS>.pdf.Computes the SHA-256 hash of the PDF bytes.
Attaches the file privately to the document.
Creates a Communication of type
eSignwhose content holds the audit record as JSON, and attaches a copy of the PDF to it.
Guests download the PDF through an endpoint that checks the share key, so the file never has to be made public. Each download writes an Access Log entry.
To verify a PDF later, hash the downloaded file and compare it with the SHA-256 value shown on the timeline:
sha256sum <DOCNAME>_signed_<TIMESTAMP>.pdfNote
The hash is stored next to the file in the same system. It proves the PDF has not changed since signing only if you trust the stored hash. If you need stronger assurance, enable the notification, which emails the hash to people outside the system, and keep that email.
The audit trail
Each signing creates one audit record. The document timeline shows it as a card with a green Signed pill, open and download links for the PDF, and these details:
| Detail | Source |
|---|---|
| Timestamp | Server time when the signature was submitted. |
| IP address | First of CF-Connecting-IP, X-Real-IP, X-Forwarded-For (first address), otherwise the connection's remote address. |
| User agent | The User-Agent request header. |
| Signer email | The email URL parameter, else a Data field with Options set to Email on the Web Form, else the logged-in user. |
| Signer name | The full name of the User whose email matches. Guests without a User record have no name. |
| Web Form and print format | The Web Form title and the format used for the PDF. |
| Signed fields | The labels of the Signature fields that have values. |
| PDF SHA-256 | The hash of the stored PDF. |
Warning
The IP address comes from proxy headers. Make sure your reverse proxy overwrites those headers instead of passing through whatever the client sends, or the recorded address can be spoofed.
Notification after signing
If you enabled Send Notification on Response, eSign sends the chosen Notification after the PDF is stored. It attaches the PDF it just created instead of rendering a new one, and appends a table with the signer, signed time, IP address, user agent, signed fields and SHA-256 hash to the email body.
If the Notification is for another DocType, is not set to the Custom event, is disabled or uses a channel other than Email, eSign skips it and writes to the Error Log. If the Notification defines a property to set after sending, eSign applies it.
Troubleshooting
The Send for eSign button is missing: check that a published Web Form with Enable eSign exists for the DocType. A custom app can also control the button per DocType with a
can_esignhook, a mapping of DocType to a list of method paths. Each method receivesdocand every one must return true. For a DocType that has such a hook, the hook replaces the Web Form check.The signer sees "Invalid or expired document access key": the key in the link does not match the document, usually because the link was copied incompletely. Generate a new link. If the signer sees a Link Expired error instead, the key is past its expiry date and a new link fixes that too.
No signed PDF appears: check that bench workers are running and look in the Error Log.
The signature is missing from the PDF: the print format does not print the Signature field.
Sources
This article is in the public domain (CC0 1.0), code samples included. Use it however helps you.