Sign a PDF with React
Embed signing in a React app, with the API key where it belongs: on your server.
Drop your document here and sign it now. PDF, Word and Excel all work: a .docx or .xlsx is laid out in your browser exactly as it was written, so there is nothing to convert first. Add a signature, a date, a company stamp or a watermark, then download it sealed. Free to start, no account, and the file is never uploaded to open it.
Create a document and get a signing link
React runs in the browser, so this half belongs on your server. What the browser receives is a signing URL, never the API key.
// Your server creates the document and returns only the signing URL.
// The key never reaches the browser.
const { signingUrl } = await fetch("/api/agreements", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ clientId }),
}).then((r) => r.json());
// Then embed it.
export function SignPane({ signingUrl }) {
return (
<iframe
src={signingUrl}
title="Sign the agreement"
style={{ width: "100%", height: "80vh", border: 0 }}
allow="camera"
/>
);
}What this ecosystem gets wrong first
- There is no version of this where the API key belongs in the React app. Anything shipped to the browser is readable, and a bundler environment variable is not a secret: NEXT_PUBLIC_ and VITE_ prefixed values are compiled into the JavaScript your users download.
- The route that creates the document is a thin server endpoint that checks the user is allowed to sign this thing, then calls the API. That check is the actual security boundary, not the key.
- allow="camera" is only needed when the signing flow includes an ID check with a selfie. Leave it off otherwise, because asking for a permission you do not use is a reason for somebody to close the tab.
Handling the webhook
When a signer completes, we POST the document id, the completion time, the signer record and the SHA-256 fingerprint of the sealed file to your webhook_url. Verify the signature header against the raw request body before you trust any of it, and respond 2xx quickly: do the slow work afterwards, because a webhook that takes ten seconds to answer is a webhook that gets retried.
The fingerprint in that payload is the same value the public verification page checks against, so you can store it and let anybody confirm a document you hold is the one that was signed.
What you get back
- A sealed PDF, with the signature part of the page rather than an annotation some viewers skip
- An audit certificate naming each signer, the time, the address and which checks they passed
- A SHA-256 fingerprint, and a public verification URL that needs no account to open
- Optional company stamps with serial numbers, applied in the same call
The REST API is included on the Business plan at $15 a month rather than sold as an add-on, which is the part worth comparing: several of the platforms a developer evaluates price the API separately and considerably higher.