API Authentication
Use this guide for authentication, team-scoped URLs, and the most common API flows. For endpoint-by-endpoint request bodies, response schemas, and live examples, use the API Reference.
Create an API token
- Go to Settings > Developer
- Enter a descriptive token name such as
Production APIorCI - Click Create Token
- Copy the token immediately
Important: Tokens are only shown once. Store them securely and never commit them to source control.
Papyrus currently allows up to 25 active API tokens per user.
Understand the base URL
Every API request uses both:
- A
Bearertoken in theAuthorizationheader - Your team slug in the URL path
If your dashboard URL is https://your-app.test/acme-corp/dashboard, your team
slug is acme-corp and your API base URL is:
https://your-app.test/api/v1/acme-corp/
If a user belongs to multiple teams, change the slug to work inside a different team context.
Make your first request
This example lists sent documents, includes recipients, and sorts newest first:
curl -G "https://your-app.test/api/v1/acme-corp/documents" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Accept: application/json" \
--data-urlencode "filter[status]=sent" \
--data-urlencode "include=recipients" \
--data-urlencode "sort=-createdAt"
Papyrus uses a consistent query style for read endpoints:
filter[...]for filters such asfilter[status]includefor related data such asinclude=recipientssortfor sorting such assort=-createdAtpageandperPagefor pagination
Use the API Reference to see the exact filters, includes, and sorts supported by each endpoint.
Common integration flow
Most integrations follow the same path:
- Look up the template you want to use
- Inspect its roles and field
labelvalues - Create a document from that template — sent immediately, or as a draft
- Decide whether Papyrus emails signers or your app embeds signing
- Track progress with webhooks, if available for your team, and follow-up API calls
1. Inspect the template first
Templates define the roles and fields your request must satisfy. A common first
step is fetching the template with its fields (include=fields) so you know:
- Which
roleLabelvalues to send for each signer - Which field
labelvalues can be used in thevaluespayload - Which fields are prefill fields (
isPrefill: true) and which are recipient fields (isPrefill: false), sincevaluesfills prefill fields with their final, read-only value and gives recipient fields an editable default - Which fields are required (
isRequired: true) and whether they already have adefaultValue, since every required prefill field must be satisfied byvaluesor a default before a document can be sent - Whether the template is a good fit for email sending, embedded signing, direct links, or kiosk mode
If you plan to manage direct links over the API, make sure embedded signing features are enabled for your team first.
If you plan to manage kiosk mode over the API, make sure kiosk mode is enabled for your team's plan. Kiosk API operations are separate from direct-link API operations: enabling or disabling one does not change the other.
See the API Reference for the templates endpoints, and the Templates guide for the workflow behind them.
2. Create a document from a template
POST /api/v1/{team}/templates/{template}/documents is the main automation entry
point. A typical request looks like this:
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": "Thompson Plea Agreement",
"signingOrder": "sequential",
"recipients": [
{
"roleLabel": "Attorney",
"name": "Peter Hibbard",
"email": "peter@example.com"
},
{
"roleLabel": "Prosecutor",
"name": "Grant County Prosecutor",
"email": "prosecutor@example.com"
}
],
"values": {
"defendant_name": "James Robert Thompson",
"charge_number": "T00087521",
"charge": "46.16A.030 - Expired Vehicle Registration"
},
"externalId": "order-456",
"sendEmails": true
}'
Keep these rules in mind:
recipientsmust map one-to-one onto the template's signer roles: send exactly one recipient per role. An unknown role label (including a casing mismatch such asattorneyvsAttorney) and a role label that appears more than once are each rejected with a422keyedrecipients.{index}.roleLabel, and a template signer role with no recipient is rejected with a422keyedrecipientsnaming the missing role labelsvaluesproperty names match the template field'slabelafter trimming outer whitespace and ignoring letter casevaluesapplies to both kinds of fields: a prefill field receives its final, read-only value; a recipient field receives an editable defaultsigningOrderacceptsparallelorsequential; if omitted, Papyrus usesparallelsendcontrols whether the document is sent immediately (true, the default) or created as a draft (false)sendEmailscontrols whether Papyrus sends the invitations when the document is sentexternalIdis useful for mapping Papyrus documents back to your systemexpiresInDaysis optional and supports values from1to365. For drafts (send: false) it is converted to an absolute deadline at creation time: a draft created withexpiresInDays: 30and sent 10 days later gives signers the remaining 20 days, and a stored deadline that has already passed by send time is replaced with your team's default signing window- For standard email signing, you can omit recipient auth fields entirely
- Only send recipient auth fields when you need
access_codeorsmsverification
In sequential flows, only the active signer gets a live signing link at first.
Later signers stay pending until their turn, so their signingUrl and
embedUrl remain null until Papyrus advances the workflow.
How field values resolve
Each field's initial value is resolved as: the payload's values[label] if the
label is present, otherwise the template field's defaultValue, otherwise
empty. Checkbox values are normalized — true, "true", and "1" check the
box; false, "false", "0", "", and null leave it unchecked.
Strict values validation
Papyrus validates the values payload before creating anything, so a failed
request never leaves a stray draft behind. Each problem is reported in a 422
keyed values.{label}:
- An unknown label — no field on the template has it — is rejected
- Two differently cased or spaced properties that resolve to one label are
accepted when their normalized values agree; conflicting values produce a
422on both supplied paths - A value targeting an auto-populated field (
date_signed,email,name) is rejected; those fields are filled automatically at signing time - A value targeting a
signatureorinitialsfield is rejected; those must be completed by the signer - A dropdown value must exactly match one of the field's
options - A
datefield value must use the ISOYYYY-MM-DDformat (for example2026-08-25) — other formats such as08/25/2026are rejected
The payload itself is also bounded: values accepts at most 100 entries,
each value is capped at 5,000 characters, and every value must be a scalar
string, number, boolean, or null — arrays and objects are rejected with a
422. The same limits apply to PATCH /documents/{document}/fields.
Create-and-send must be satisfiable
When send is true (the default), every required prefill field must be
satisfied by values or by its template defaultValue; a required prefill
checkbox must resolve to checked. Failures are reported per label as
values.{label}. If a required prefill field somehow has no label, the request
fails with a general values error. Drafts (send: false) skip this check —
they stay editable until you send them.
One exception to the no-stray-drafts rule: the document quota check runs
at send time, after the document row exists. If your team is out of documents,
a create-and-send request fails with a 422 keyed document and leaves the
created document behind as a draft — it appears in the dashboard and in
GET /documents. Send it later once quota frees up, or void it; don't
blindly retry the create call, or you will accumulate duplicate drafts.
3. Work with drafts (optional)
Pass send: false to create the document as a draft: no signing tokens are
issued and no emails go out. Two endpoints operate on drafts:
Update field values
curl -X PATCH "https://your-app.test/api/v1/acme-corp/documents/42/fields" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-d '{
"values": {
"defendant_name": "James Robert Thompson",
"charge_number": "T00087521"
}
}'
Values are addressed by field label with the same semantics and strict
validation as creation: prefill fields receive their final value, recipient
fields receive an editable default. Labels absent from the payload are left
untouched, and a blank value clears the stored value. The response includes
the document with its fields. Only drafts can be edited — a 409 is
returned otherwise.
Send the draft
curl -X POST "https://your-app.test/api/v1/acme-corp/documents/42/send" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Accept: application/json"
The endpoint takes no body. Papyrus runs the same send validation as the web
editor — every required prefill field filled, every recipient field assigned,
every dropdown field with options — then transitions the draft to sent,
issues signing tokens, and dispatches emails (honoring the sendEmails and
email settings stored at creation). A signing deadline stored at creation via
expiresInDays is anchored to the creation clock: it is kept only while
still in the future, and a deadline that has already passed is replaced with
your team's default signing window. Sending a non-draft returns a 409.
Validation failures return a 422 whose errors use five path shapes, so
parse all of them:
fields.{label}— a required prefill field is not filled (falls back to the field id if the field has no label)fields— aggregate structural problems: fields not assigned to any recipient, or dropdown fields with no optionsrecipients.{index}— recipient problems: a missing name or email, or a signer with no assigned fieldsrecipients— the document has no signer recipients at alldocument— the send is blocked by your team's document quota
The same shapes apply to any send: POST /documents/{document}/send and
create-with-send (send: true, where value problems are additionally keyed
values.{label}).
Read field values back
GET /documents/{document} and GET /documents accept include=fields. Each
field exposes id, label, type, geometry, recipientId, isPrefill,
isRequired, value, and options — including values submitted by signers
once they have signed, addressed by the same stable labels.
4. Choose email delivery or embedded signing
After document creation, you usually branch into one of two flows.
Send email invitations
- Keep
sendEmailsset totrue - Papyrus emails signer recipients for you
- In
sequentialmode, only the first signer is emailed immediately - Use Webhooks to react to sent, signed, and completed events if webhooks are enabled for your team
Embed signing in your app
- Set
sendEmailstofalse - Use the
signingUrlorembedUrlreturned in the 201 Created response - If you need them again later, fetch the document with
include=recipients
For full iframe guidance, event handling, and security recommendations, see Embed Integration.
5. Operate on sent documents
Once a document is in progress, the most common follow-up actions are:
- Resend an invitation to a signer
- Update mutable metadata such as
emailSubject,emailMessage, orexternalId - Void a sent document
- Download the finalized PDF
- Fetch the document again with
include=recipientsorinclude=fields
Those actions are all covered in the API Reference. The Sending for Signature guide explains the business flow behind them.
Signer authentication
When you create a document from a template, you can require additional signer verification per recipient:
nonefor no extra challengeaccess_codefor a passphrasesmsfor a one-time code sent to a phone number in E.164 format
For the most common email-only flow, omit authMethod, accessCode, and
authPhone completely.
Use signer authentication when you need more confidence that the recipient is
the intended signer. Share access codes out-of-band, and keep phone numbers in
international format such as +15551234567.
Working with responses
Papyrus uses standard JSON error responses. The most common status codes are:
401when the token is missing or invalid403when the token owner cannot access that team or resource404when the resource does not exist in the current team context422when request validation fails429when you hit the rate limit
For full request and response schemas, use the API Reference.
Revoke a token
To revoke a token:
- Go to Settings > Developer
- Find the token in the list
- Click Delete
The token is invalid immediately.
Security checklist
- Create separate tokens for local, staging, and production systems
- Store tokens in environment variables or a secrets manager
- Rotate tokens when ownership or deployment scope changes
- Always send requests over HTTPS
- Use
externalIdand Webhooks so your backend can reconcile state - Treat returned
signingUrl,embedUrl, direct-link URLs, and kiosk URLs as sensitive
Where to go next
- API Reference - exact endpoints, schemas, and query parameters
- Templates - how roles, field labels, and default values are modeled
- Embed Integration - embedding signer flows in your app
- Kiosk Mode - shared-device signing flows
- Webhooks - reacting to document lifecycle events