Signing inside your product
The person is already in your app. Do not send them to their inbox.
Three ways to put signing in front of somebody, from least work to most:
- Send them to our page. The
urlin the send's answer, in a new tab or as a redirect; setreturn_urlon the document and the signed page offers the way back. - Frame our page on your own site. This page.
- Build your own page on the signing routes (
/v1/sign/{token}), which take no key and answer a browser on any site. The Node client'sSignSealerSigningis that client.
1. Send without sending
Give the signer delivery: "embedded". Nothing is emailed or
texted to them, and the automatic reminders leave them alone: you are showing
them the page yourself. They still need an email address or a mobile number,
because the certificate records how they could be reached and the completed
copy goes to them.
const { links } = await signsealer.send(documentId, [
{ email: "dana@example.com", full_name: "Dana Reyes", delivery: "embedded" },
]);
2. A fresh link when the page is shown
Do not keep the link from the send in your database: it is a bearer credential. Mint one when the person opens the screen that shows it. The link they had stops working, and the trail records that one was shown.
const { url, frame_origins } = await signsealer.embedLink(documentId, signerId);
// POST /v1/documents/{id}/signers/{signer}/embed
3. Put it in a frame, on a site the business has proven
The signing page may be framed only on sites the business has proven are
its own, and the browser enforces it (frame-ancestors): anywhere
else the frame stays empty. A business proves a site in one of three ways, the
same three that let a signed page send somebody back to it:
- a signing domain it verified (Settings, under Your own domains), and every host under it;
- a company sign-in domain it proved (Settings → Single sign-on), and every host under it;
- the site of an app it connected, such as a WordPress site running our plugin: that one host.
frame_origins on the embed answer, and on GET /v1/account,
lists them. Empty means nowhere yet. The business's public forms
(/f/{business}/{form}) may be framed on the same sites.
<iframe src="{url}" title="Sign your rental agreement"
style="width:100%;border:0;min-height:640px"
allow="camera; geolocation"></iframe>
allow="camera" lets a template that asks for a photograph use it;
geolocation, one that asks where the signer is.
4. Hear what happened
Inside a frame the page posts a message to the page around it:
{ source: "signsealer", event, document_id, signer_id, height }
| event | When |
|---|---|
| ready | Every page, as it loads. |
| resize | The page's height changed: set the frame's height to height and there is no inner scrollbar. |
| signed | They have signed. Other signers may still be to come; signing.completed on your webhook is when everyone has. |
| declined | They declined. |
window.addEventListener("message", (e) => {
if (e.origin !== new URL(url).origin) return; // only the signing page
const m = e.data;
if (!m || m.source !== "signsealer") return;
if (m.event === "resize" || m.event === "ready") frame.style.height = m.height + "px";
if (m.event === "signed") showThanks();
if (m.event === "declined") showDeclined();
});
Treat the message as a hint to update the screen, not as proof: anything
that matters — releasing an order, unlocking a booking — waits for
the webhook, which is signed, or for GET /v1/documents/{id}.
What the page does and does not do in a frame
- It sets no cookie, so a browser that blocks third-party cookies changes nothing.
- It carries one script in a frame, pinned by its hash in the page's own policy, and none at all when opened in a tab. The message is the only thing it does.
- The way back on the signed page (
return_url) leaves the frame for the whole window. - A frame on a site that is not proven shows nothing, and that is the browser refusing on our instruction, not an error to work around.
Ready to build? An API key takes a minute in the portal, and the free plan covers the first 25 agreements a month.
Get an API key