Embed Integration Guide
Use this guide to choose the right embed flow, wire up the iframe, and handle signing events safely. For the exact API operations used to create documents or enable direct links, use the API Reference.
Choose the right embed flow
Papyrus supports two different embed patterns.
| Flow | Best for | How it starts |
|---|---|---|
| Signing token embed | Your app already knows who the signer is | Create a document from a template and use the returned embedUrl |
| Direct link embed | Self-serve public flows like waivers or onboarding packets | Enable a template direct link and embed /embed/d/TOKEN |
If you need multiple signers, custom recipient mapping, or deeper server-side control, use a signing token embed.
Flow 1: Signing token embeds
This is the most common API-driven integration.
Recommended sequence
- Build a template with the correct roles and optional
preFillKeyvalues - Create a document from that template with
sendEmails: false - Save the Papyrus
documentIdand your ownexternalId - Render the returned
embedUrlfor the signer - Listen for
postMessageevents from the iframe - Update your backend from webhooks or follow-up API reads
Minimal create request
preFillValues keys must match the template field's preFillKey, not the field
label shown in the editor.
curl -X POST "https://your-app.test/api/v1/acme-corp/templates/12/documents" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-d '{
"name": "Acme Contract",
"signingOrder": "sequential",
"recipients": [
{
"roleLabel": "Signer",
"name": "Jane Doe",
"email": "jane@example.com"
}
],
"preFillValues": {
"company_name": "Acme Inc."
},
"sendEmails": false,
"externalId": "order-456"
}'
The 201 Created response includes the signer-specific URLs you need:
{
"id": 42,
"status": "sent",
"externalId": "order-456",
"recipients": [
{
"id": 101,
"roleLabel": "Signer",
"type": "signer",
"status": "sent",
"signingUrl": "https://your-app.test/sign/abc123...",
"embedUrl": "https://your-app.test/embed/sign/abc123..."
}
]
}
Note: Save
signingUrlandembedUrlwhen you create the document, but if your process loses them you can fetch the document later withinclude=recipientsto recover the current recipient links. Insequentialflows, later signers will not receive a livesigningUrlorembedUrluntil Papyrus advances the document to their turn.
Flow 2: Direct link embeds
Direct links let a signer open a template-driven flow without you creating the document first.
Use this when:
- The same template is reused many times
- The signer enters their own name and email
- You want a shareable or embeddable self-serve experience
Typical sequence
- Create a template where the direct-link signer is the only signing role
- Enable a direct link from the template detail page or the API
- Share the standalone link or embed the
/embed/d/TOKENversion - Let Papyrus create a new document for each signing session
If your account includes embedded signing features, you can manage direct links through the API Reference as well. If that feature is not enabled for your team, direct-link API operations will not be available.
Iframe examples
Basic iframe
<iframe
src="https://your-app.test/embed/sign/TOKEN"
width="100%"
height="800"
allow="camera"
style="border: 0;"
></iframe>
Responsive container
<div style="position: relative; width: 100%; padding-bottom: 75%;">
<iframe
src="https://your-app.test/embed/sign/TOKEN"
allow="camera"
style="position: absolute; inset: 0; width: 100%; height: 100%; border: 0;"
></iframe>
</div>
Handling postMessage events
Papyrus sends embed events to the parent window using the
papyrus:signing.* namespace.
window.addEventListener('message', (event) => {
if (event.origin !== 'https://your-app.test') {
return;
}
switch (event.data.event) {
case 'papyrus:signing.ready':
console.log('Embed is ready');
break;
case 'papyrus:signing.completed':
console.log('Signed document', {
documentId: event.data.documentId,
documentName: event.data.documentName,
recipientId: event.data.recipientId,
signedAt: event.data.signedAt,
});
break;
case 'papyrus:signing.expired':
console.log('Signing link expired');
break;
case 'papyrus:signing.voided':
console.log('Document was voided');
break;
case 'papyrus:signing.error':
console.log('Signing error', event.data.error);
break;
}
});
Event reference
| Event | Description |
|---|---|
papyrus:signing.ready |
The signing UI loaded and is ready |
papyrus:signing.completed |
The recipient finished signing; payload includes documentId, recipientId, documentName, and signedAt |
papyrus:signing.expired |
The signing link can no longer be used |
papyrus:signing.voided |
The sender voided the document |
papyrus:signing.error |
The signer hit a validation or submission problem |
papyrus:signing.field.signed |
A field was filled or signed |
papyrus:signing.field.unsigned |
A field was cleared |
Use these events to update your frontend quickly, but treat your backend as the source of truth. For durable system updates, rely on Webhooks.
Useful URL parameters
Direct link embeds only
These parameters help you pre-fill or lock signer identity for /embed/d/TOKEN
flows:
| Parameter | Description |
|---|---|
name |
Pre-fill the signer's name |
email |
Pre-fill the signer's email |
lockName=true |
Make the name field read-only |
lockEmail=true |
Make the email field read-only |
externalId |
Attach your own reference when the document is created |
Both embed types
| Parameter | Description |
|---|---|
redirectUrl |
Redirect after signing completes. Must match an allowed embed origin |
theme_primary |
Override the primary accent color |
theme_background |
Override the background color |
theme_radius |
Override border radius values |
darkMode=true |
Force dark mode |
darkMode=false |
Force light mode |
If a value contains #, URL-encode it. For example, #4f46e5 becomes
%234f46e5.
For embedded flows, Papyrus only honors redirectUrl when its origin matches
one of your configured allowed embed origins.
Production checklist
Configure allowed origins
Papyrus uses Content-Security-Policy: frame-ancestors to control which sites
can embed signing pages. In production, add each allowed domain in
Settings > Team > Embedded Signing.
If you leave the allowed origins list empty, Papyrus only allows same-origin embedding. Add every external app origin explicitly before going live.
Examples:
https://myapp.com
https://staging.myapp.com
https://localhost:3000
Treat embed URLs as sensitive
- Signing token URLs are unique per recipient
- Direct-link URLs stay active until the direct link is disabled
- Do not log embed tokens in browser consoles or analytics tools
- Avoid storing raw embed links where unauthorized users can retrieve them
Use webhooks for server-side state
postMessage is ideal for frontend UX. Webhooks are better for:
- Marking an order or workflow step as complete
- Sending your own follow-up emails or receipts
- Reconciling state if the browser closes before your UI updates
Where to go next
- API Authentication - auth, team scoping, and common API flows
- Templates - designing reusable roles and pre-fill keys
- Webhooks - reacting to signed and completed events
- API Reference - exact create-from-template and direct-link operations
