How to add e-signatures to an AI-built app
The drawn squiggle is decoration. What makes a signature hold up is intent, attribution, integrity and an audit trail. The fields to store, and when to buy instead.
A signature pad that captures a scribble on a canvas is not an e-signature. It is a picture. What makes a signature hold up is not the image, it is the record around it: evidence that a specific person intended to sign a specific document at a specific time, and that the document has not changed since. Get that record right and a drawn squiggle is optional. Get it wrong and the prettiest signature widget is worthless.
This walks the four properties your record needs, the fields to store, and the point at which you should stop building and buy.
The four properties that matter
Both the US ESIGN Act and the EU's eIDAS regulation treat electronic signatures as broadly valid, and neither requires a drawn image. What they care about reduces to four things:
Property | The question it answers | How you satisfy it |
|---|---|---|
Intent | Did they mean to sign? | An explicit, separate action with clear wording. Not a pre-ticked box, not a side effect of saving a form |
Attribution | Was it them? | Authentication before signing, plus a record of how they authenticated |
Integrity | Is this the document they signed? | A hash of the exact document bytes, stored at signing time |
Audit trail | Can you prove the above later? | An append-only record of the events, with timestamps you did not generate on the client |
Integrity is the one that is usually missing and the only one you cannot retrofit. If you did not hash the document at signing time, you have no way to show a year later which version was signed.
The data model
Three tables. Documents are immutable once sent, which is the design decision everything else rests on.
documents
id, title, file_path, content_sha256, created_by, created_at
-- never updated after a signature request is sent.
-- a change means a new document row and a new request.
signature_requests
id, document_id, signer_email (lowercased), signer_name,
token_hash, expires_at, status, created_at
signatures
id, request_id, document_id,
document_sha256_at_signing, -- copied, not joined
signed_at, -- server clock
signer_user_id, auth_method,
ip_address, user_agent,
consent_text, -- the exact wording shown
typed_name, drawn_image_path -- both optional
Two of those fields carry more weight than their size suggests.
document_sha256_at_signing. Copied into the signature row rather than read through the join. If somebody later replaces the file, the mismatch is visible instead of silently resolving to the new version.
consent_text. Store the sentence the signer actually saw, not a reference to the current version of it. You will reword that sentence at some point, and old signatures must keep the wording they were given.
The signing flow
Generate the document and hash it. Hash the exact bytes you will serve, after any rendering. Hashing the template and then generating a PDF produces a hash of something nobody signed. PDF generation in an AI-built app covers getting a stable output, which matters here because a PDF that embeds a generation timestamp hashes differently every time.
Send a link with a random token, hashed in your database as with any credential.
Authenticate the signer before showing the document. Email possession via the link is weak but defensible for low-value agreements. Anything meaningful should require a sign-in or a one-time code.
Show the document in full. Not a summary, not a scroll-locked preview with the terms behind a link. The signer has to be able to read what they are signing, and a court asks whether they could.
Capture the signature as a deliberate act. A separate button with explicit wording, after the document, with the consent sentence visible next to it.
Write the signature row in a transaction with the request status change, using the server clock. Never trust a client timestamp.
Send both parties a copy immediately, with the hash in the email. A countersigned copy in two inboxes is strong practical evidence and costs you one send.
What to do about the drawn signature
If you want one, the Canvas API gets you there in very little code: capture pointer events, draw a path, export a PNG. Store it alongside the record.
Just be clear with yourself about what it is for. The image is reassurance, not evidence. A typed name next to a checkbox and a solid audit trail is legally stronger than a beautiful drawn signature with no hash. Many products offer both and let the signer pick, which is the sensible answer because it costs nothing.
When to stop building and buy
Rolling your own is reasonable for internal approvals, contractor agreements, waivers, consent forms and anything where both parties are cooperative and the amounts are modest. The honest cases for a dedicated provider:
You need qualified or advanced electronic signatures under eIDAS. These involve certificate-based identity and you cannot produce them yourself.
The agreement is high value or likely to be litigated. Providers sell evidence packages and expert testimony, which is most of what you are buying.
You are in a regulated sector with specific requirements, such as some healthcare and financial use cases.
You need multi-party signing in sequence, with reminders and delegation. The state machine gets genuinely complicated and is not interesting work.
The useful framing: you are not comparing signature widgets, you are deciding who carries the evidentiary burden if a signature is disputed. If that burden sitting with you is unacceptable, buy.
The forwarded link problem
A tokenised link is a bearer credential, which means whoever holds it can sign. Signers forward them routinely, usually to an assistant or a business partner, and usually with good intentions. Your record will then say that the right email address signed, when somebody else did.
You cannot prevent this with a link alone, so handle it in one of two ways depending on what the document is worth. For low-value agreements, accept it and make the record honest: store the authentication method as email link possession so the strength of the evidence is explicit rather than implied. For anything meaningful, add a second factor that is not the inbox, such as a code sent to a phone number you captured earlier, or a sign-in. The extra step costs you some completion rate and buys you attribution.
Either way, record the signer name the person typed as well as the address the invitation went to. A mismatch between the two is the clearest signal available that the document was signed by somebody other than the invitee, and it is free to capture.
Reminders, and knowing when to stop
Unsigned documents are the normal state, not an error. Two reminders, a few days apart, recover most of what will ever be recovered, and a third mostly annoys people. Expire the request after a set period rather than leaving it open, because an indefinitely valid signing link is the same permanent credential problem as an invite that never expires.
Put reminders on a scheduled job rather than a timer in your request handler. Scheduled tasks in an AI-built app covers the mechanics, and the useful detail is that the job should read state at run time rather than trusting a queued payload, so a document signed an hour ago does not get a reminder.
Mistakes worth avoiding
Four that show up repeatedly in self-built flows:
Letting the document change after sending. If a document row is mutable, every signature on it is questionable. Make a new row.
Using a client timestamp. The signer's clock is not evidence of anything.
Pre-ticking the consent box. It defeats intent, which is the easiest property to get right and the most damaging to lose.
Keeping no record of failed or abandoned attempts. A gap in the audit trail is harder to explain than a record of somebody opening and not signing.
Also worth doing early: an audit log that captures the signing events, and a check that your file uploads are not publicly listable, because signed agreements are exactly the documents you do not want enumerable.
Common questions
Are e-signatures legally binding?
In most cases yes. The ESIGN Act in the US and eIDAS in the EU both give electronic signatures broad validity, with carve-outs for things like wills and some property transactions. Validity depends on the record, not on whether a drawn image exists. Check local rules for your document type.
Do I need a drawn signature image?
No. A typed name with a clear consent action, authentication and a document hash is stronger than a drawn image with none of those. Offer the drawing if users expect it.
What should I hash, the template or the final file?
The final bytes you serve to the signer. Make your generation deterministic first, because a PDF that embeds a build timestamp produces a different hash on every render and the hash becomes meaningless.
Can I let people sign without creating an account?
Yes, with a tokenised link, and it is appropriate for low-value agreements. Record that the authentication method was email link possession so the strength of the evidence is visible in the record.
Is self-built good enough for customer contracts?
For routine, low-dispute agreements with cooperative counterparties, usually. For high-value contracts, regulated sectors, or anything needing eIDAS advanced or qualified signatures, use a provider. Before any of this goes live, check whether your app exposes user data, since signed documents are the worst thing to leak, and what to do if an AI tool leaks your data covers the response side.
How did this land?
About the author

Developer Advocate
Steve builds something with Swarmz every week and writes up what worked, what broke, and what he'd do differently. Tutorials and hands-on guides are his lane.


