Sign a PDF with Next.js
Create the signing session in a Next.js route handler and render it in a client component.
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
One POST creates the document, places the fields and returns a signing URL for each signer. The key stays in the environment.
// app/api/agreements/route.ts (server: the key lives here)
export async function POST(req: Request) {
const { clientId } = await req.json();
// ...check this user may sign this agreement...
const res = await fetch("https://api.pdfverified.com/v1/documents", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.PDFVERIFIED_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
name: "Services agreement",
file_url: await agreementUrlFor(clientId),
signers: [await signerFor(clientId)],
webhook_url: process.env.APP_URL + "/api/hooks/pdfverified",
}),
cache: "no-store",
});
const doc = await res.json();
return Response.json({ signingUrl: doc.signing_urls.client });
}What this ecosystem gets wrong first
- cache: "no-store" matters here. Next extends fetch with caching, and a cached POST to a document-creation endpoint is a bug that only appears in production, because caching behaves differently in dev.
- PDFVERIFIED_API_KEY without the NEXT_PUBLIC_ prefix stays on the server. With the prefix it is compiled into the client bundle, which is the most common way an API key leaks from a Next app.
- The webhook route needs the raw body to verify the signature, so read it with await req.text() and parse afterwards rather than calling req.json() first.
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.