Send a document for signature from code, embed the form, and check the seal The developer behind Docustay, an AGPL-3.0 e-signature server with MIT-licensed SDKs, published a walkthrough showing how to send documents for signature programmatically, embed the signing form in an existing app, and verify the resulting seal. The guide covers the JavaScript SDK's idempotency-key handling and rate-limit backoff, test keys that watermark pages and route email to the sender only, and a one-hour embedded signing session generated server-side so the API key never reaches the browser. Docustay also ships a Python client, a CLI, and an MCP server that lets an AI assistant send and track documents. Signing a document is a short story with four parts. Your software decides a document is needed. Someone signs it. You get told. You keep a copy you can prove has not changed. This post does all four with the Docustay API, from a test key, so nothing real is sent and nothing is billed. Docustay https://docustay.app https://docustay.app is an e-signature server under the AGPL-3.0, with SDKs under MIT. You can use the hosted service or run your own. I make it, so read what follows with that in mind. I will say plainly what is not finished. Want to look before you read? The demo needs no account and no card: https://docustay.app/demo https://docustay.app/demo . You get a small property-management workspace where you can send a document, sign it yourself and ask Scribe, the built-in AI assistant, to draft one. Nothing is emailed, and the demo is deleted after 24 hours. Two things matter if you are a developer or you run your own servers. Pro is one flat price for the whole workspace, with no per-document or per-call fees. And the code is open, so the same product runs on your own server. documents:read and documents:write . A key that starts dsk test only makes practice runs: the document is emailed to you alone, and every page carries a TEST watermark. Install the SDK with npm install @docustay2026/sdk . Its code is in the sdk/js folder of https://github.com/Docustay/docustay https://github.com/Docustay/docustay . There is a Python client too pip install docustay , a command line tool npx docustay , and an MCP server @docustay2026/mcp that lets an AI assistant send documents for signature and follow them. The SDKs are listed at https://docustay.app/developers/sdks https://docustay.app/developers/sdks . js import { Docustay } from "@docustay2026/sdk"; const docustay = new Docustay process.env.DOCUSTAY API KEY, process.env.DOCUSTAY URL ? { baseUrl: process.env.DOCUSTAY URL } : {} ; const doc = await docustay.sendDocument { templateId: process.env.DOCUSTAY TEMPLATE ID, parties: { seat: "signer 1", name: "Pat Example", email: "pat@example.com" } , } ; console.log doc.id, doc.state ; A seat is the template's name for a signer: the first role in the document is signer 1 , the next signer 2 . The client makes an Idempotency-Key for the call and reuses it on every retry, so a network retry never sends the document twice. Over the rate limit 600 requests a minute per key it waits for Retry-After . Run it with a test key and look in your own inbox, not Pat's. The email is addressed to you and its subject has TEST in it. doc.state moves from sent through partially signed to executed . An emailed link is fine for a stranger. If the signer is already in your app, you can keep them there. Three steps. First, list your website under Developers, then Embedding. http://localhost:3000 is allowed while you build. Second, send with the seat marked as embedded, so that nobody is emailed for it: js import { Docustay } from "@docustay2026/sdk"; const docustay = = new Docustay process.env.DOCUSTAY API KEY, process.env.DOCUSTAY URL ? { baseUrl: process.env.DOCUSTAY URL } : {} ; // POST { name, email } → sends the template to that person, to be signed inside your page. export async function POST req { const { name, email } = await req.json ; try { const doc = await docustay .sendDocument { templateId: process.env.DOCUSTAY TEMPLATE ID, embedded: "signer 1" , parties: { seat: "signer 1", name, email } } ; return Response.json { id: doc.id } ; } catch e { return Response.json { error: e.message }, { status: e.status ?? 500 } ; } } Third, on your server, ask for a session. It is a link that works for one hour, so make a new one each time the page loads. The key never reaches the browser: python import { Docustay } from "@docustay2026/sdk"; import Form from "./form"; // A server component: the one-hour link is made on the server, so the API key never reaches the browser. export default async function Page { searchParams } { const { id } = await searchParams; const docustay = new Docustay process.env.DOCUSTAY API KEY, process.env.DOCUSTAY URL ? { baseUrl: process.env.DOCUSTAY URL } : {} ; const { url } = await docustay.embedSession id, { seat: "signer 1", origin: process.env.PUBLIC ORIGIN ?? "http://localhost:3000" } ; return