SignEnvoy docs
Sign in Start in the sandbox

Guides

Templates

A template is a PDF with roles ("Client", "Manager") and fields placed once. Using it starts a normal signature request: you name the real people for each role, optionally prefill fields, and the envelope goes out like any other. The dashboard's visual editor and POST /v1/templates write the same definition, so what you build in one you can read, diff, and push from the other.

Create one

In the dashboard: Templates, New template, upload the PDF, then place fields on the page (drag, or keyboard: arrow keys move a selected field by 0.5% of the page, Shift by 2%, Alt resizes). Every field belongs to a role; a template with an unassigned field cannot be saved, and the error names the field and page.

Over the API, the PDF travels base64-encoded and the definition is JSON:

curl -s -X POST https://app-staging.signenvoy.com/v1/templates \
  -H "Authorization: Bearer $SENV_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Consulting agreement",
    "pdf_base64": "JVBERi0xLjQK...",
    "recipients": [
      {"name": "Client", "email": "client@placeholders.example", "role": "signer", "signing_order": 1},
      {"name": "Manager", "email": "manager@placeholders.example", "role": "approver", "signing_order": 2}
    ],
    "fields": [
      {"type": "signature", "recipient_index": 0, "page": 1, "x": 0.1, "y": 0.75, "w": 0.3, "h": 0.05, "name": "client_sig"},
      {"type": "text", "recipient_index": 1, "page": 2, "x": 0.1, "y": 0.2, "w": 0.3, "h": 0.03,
       "name": "po_number", "required": false, "settings": {"max_length": 20}}
    ]
  }'
{
  "id": "tpl_...",
  "name": "Consulting agreement",
  "version": 1,
  "updated_at": null,
  "recipients": [...],
  "fields": [...],
  "sha256": "..."
}

recipients are placeholder identities, bound by position: the first real recipient you pass at use time becomes role 0, the second role 1, and so on. Their role and signing order come from the template. The placeholder email is never mailed. Every signer needs at least one required signature or initials field before the envelope can be sent; without one, send answers 422 validation-failed naming the signer. An approver, viewer or cc needs no fields. A template can be saved before its fields are placed.

Fields

  • type: signature, initials, name, email, date, text, number, checkbox, radio, dropdown.
  • recipient_index: which role fills it in (0-based position in recipients).
  • page, x, y, w, h: where it sits. Coordinates are fractions of the page in [0, 1], origin at the top-left corner, y measured down from the top, relative to the page as stored (before any rotation the PDF asks viewers to apply). Renderers apply the rotation; the numbers do not change. A rect of x: 0.25, y: 0.75, w: 0.5, h: 0.05 is a wide box low on the page. The rectangle must stay on the page (x + w and y + h at most 1) and page must exist in the PDF; saving, using or finalizing with a field that does not fit answers 422 validation-failed naming the field. When the document is sealed, each signed value is stamped into the PDF inside its rectangle.
  • name: optional, letters, digits, dots, dashes, underscores, unique within the template. It is how form_values refers to the field.
  • required, read_only, prefill_value, and settings (max_length, pattern, min_value, max_value, input_modes for signatures, options for radio and dropdown).

Use one

curl -s -X POST https://app-staging.signenvoy.com/v1/templates/tpl_.../use \
  -H "Authorization: Bearer $SENV_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "title": "Consulting agreement: Acme",
    "recipients": [
      {"name": "Ada Lovelace", "email": "ada@acme.example"},
      {"name": "Bob Byrne", "email": "bob@acme.example"}
    ],
    "form_values": {"po_number": "PO-88213"},
    "display_timezone": "Europe/London"
  }'

The response is a draft envelope (doc_..., status draft) with the template's fields copied in. Send it with POST /v1/documents/{id}/send.

form_values keys resolve by field name first, then by field index as a string ("0", "1", in the template's field order). An unknown key fails the whole request:

HTTP/1.1 422 Unprocessable Content
Content-Type: application/problem+json

{
  "type": "/problems/validation-failed",
  "title": "Validation failed",
  "status": 422,
  "detail": "form_values: unknown field key(s) 'po_numbre'",
  "docs_url": "https://staging.signenvoy-docs-b4g.pages.dev/errors/validation-failed"
}

Silently dropping a prefill would be the worst outcome (a envelope goes out with a blank where a value was meant to be), so a typo is refused rather than ignored. See validation-failed.

display_timezone, date_format, and redirect_url on the use request set the envelope's display preferences. Leave them out and the workspace's team defaults apply, the same as for any other new envelope.

Change one: versions

curl -s -X PATCH https://app-staging.signenvoy.com/v1/templates/tpl_... \
  -H "Authorization: Bearer $SENV_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"fields": [ ... the full field list ... ]}'

PATCH replaces the parts you send (name, recipients, fields) and bumps version; updated_at moves with it. Envelopes already created from the template carry their own copy of the definition and are unaffected. The next use takes the new version. The dashboard says the same thing on every save.

  • DELETE /v1/templates/{id} (204) hides the template from listing, reading, and use. Envelopes created from it are unaffected.
  • POST /v1/templates/{id}/duplicate (201) copies the PDF and the definition under a new id at version 1, in the same folder; pass {"name": "..."} or get "(copy)" appended.
  • A PATCH that carries only folder_id or "clear_folder": true moves the template between template folders and does not bump version. See Folders and search.
  • POST /v1/documents/{id}/duplicate (201) does the same for an envelope: a fresh draft with the same PDF, people, fields, and display preferences, and no signing links.

Templates as code

GET /v1/templates/{id} returns the complete definition, fields and recipients included, exactly as stored. The dashboard editor reads and writes the same shape: a field placed in the editor and a field pushed over the API are the same field, and a setting the editor cannot display is kept rather than dropped.

senv template pull <id> writes that definition to a file, and senv template update <id> template.yaml sends it back as a PATCH (a new version); senv template push creates a new template from a file instead. senv template use <id> --var po_number=PO-88213 prefills by name.

Who can use templates

Templates are shared across the team: every member sees the team's templates, and GET /v1/templates lists them.