Papyrus
Papyrus Doc
Docs
Sign in

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

  1. Go to Settings > Developer
  2. Enter a descriptive token name such as Production API or CI
  3. Click Create Token
  4. 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 Bearer token in the Authorization header
  • 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 as filter[status]
  • include for related data such as include=recipients
  • sort for sorting such as sort=-createdAt
  • page and perPage for 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:

  1. Look up the template you want to use
  2. Inspect its roles and field label values
  3. Create a document from that template — sent immediately, or as a draft
  4. Decide whether Papyrus emails signers or your app embeds signing
  5. 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 roleLabel values to send for each signer
  • Which field label values can be used in the values payload
  • Which fields are prefill fields (isPrefill: true) and which are recipient fields (isPrefill: false), since values fills 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 a defaultValue, since every required prefill field must be satisfied by values or 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:

  • recipients must 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 as attorney vs Attorney) and a role label that appears more than once are each rejected with a 422 keyed recipients.{index}.roleLabel, and a template signer role with no recipient is rejected with a 422 keyed recipients naming the missing role labels
  • values property names match the template field's label after trimming outer whitespace and ignoring letter case
  • values applies to both kinds of fields: a prefill field receives its final, read-only value; a recipient field receives an editable default
  • signingOrder accepts parallel or sequential; if omitted, Papyrus uses parallel
  • send controls whether the document is sent immediately (true, the default) or created as a draft (false)
  • sendEmails controls whether Papyrus sends the invitations when the document is sent
  • externalId is useful for mapping Papyrus documents back to your system
  • expiresInDays is optional and supports values from 1 to 365. For drafts (send: false) it is converted to an absolute deadline at creation time: a draft created with expiresInDays: 30 and 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_code or sms verification

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 422 on 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 signature or initials field is rejected; those must be completed by the signer
  • A dropdown value must exactly match one of the field's options
  • A date field value must use the ISO YYYY-MM-DD format (for example 2026-08-25) — other formats such as 08/25/2026 are 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 options
  • recipients.{index} — recipient problems: a missing name or email, or a signer with no assigned fields
  • recipients — the document has no signer recipients at all
  • document — 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 sendEmails set to true
  • Papyrus emails signer recipients for you
  • In sequential mode, 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 sendEmails to false
  • Use the signingUrl or embedUrl returned 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, or externalId
  • Void a sent document
  • Download the finalized PDF
  • Fetch the document again with include=recipients or include=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:

  • none for no extra challenge
  • access_code for a passphrase
  • sms for 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:

  • 401 when the token is missing or invalid
  • 403 when the token owner cannot access that team or resource
  • 404 when the resource does not exist in the current team context
  • 422 when request validation fails
  • 429 when you hit the rate limit

For full request and response schemas, use the API Reference.

Revoke a token

To revoke a token:

  1. Go to Settings > Developer
  2. Find the token in the list
  3. 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 externalId and Webhooks so your backend can reconcile state
  • Treat returned signingUrl, embedUrl, direct-link URLs, and kiosk URLs as sensitive

Where to go next

Last updated: October 14, 2026