Skip to main content
POST
Upload a PDF or a Word document and get a template with its fields already placed.

Authorizations

Authorization
string
header
required

Authorization: Bearer vsk_live_…. Chosen over a bespoke X-API-KEY header because every client, proxy and log-redaction rule already knows this one. What a key may DO is its scopes — see x-scopes at the root of this document and x-required-scope on each operation. The scope list is not written here because OpenAPI reserves a requirement’s scope array for oauth2 and openIdConnect and requires it to be empty for an http scheme.

Headers

Idempotency-Key
string
required

Any string that identifies this request; a UUID is the usual choice, and at most 255 characters. ⚠️ REQUIRED, because an upload is the request most likely to lose its answer — the body is the largest this API accepts and the reply comes after the document has been parsed twice. Retrying with the same key returns the same template rather than authoring a second one from the same document.

⚠️ AND IT IS ANSWERED CONSERVATIVELY IN ONE CASE. If a previous request under this key stopped partway and this API cannot establish whether it created a template, you are told so with request_in_progress and asked to look — it will not guess, because a wrong guess is a second template. Call GET /api/v1/templates; if it is not there, retry with a NEW key. The caller’s own request identifier.

Example:

"01960000-0000-4000-8000-00000000ffff"

Body

application/json

The document, base64-encoded, and who signs it.

The document and its roles.

file
string
required

⚠️ THE WHOLE DOCUMENT, BASE64-ENCODED, AND THE EXAMPLE ABOVE IS A TRUNCATED STUB — replace it with your own document’s bytes or the request is refused as unreadable. A PDF or a Word .docx; the BYTES decide which, so there is no content_type field to get wrong, and a .docx is converted to PDF on our side. Standard alphabet only: no data: prefix, no whitespace, no line wrapping, and not the URL-safe -/_ variant. Base64 is about a third larger than the file it carries, and the whole request body is bounded at 8388608 bytes — so roughly 6291456 bytes of document, whatever its format or page count.

Example:

"JVBERi0xLjcKJeLjz9MKMSAwIG9iago8PC9UeXBlL0NhdGFsb2c+PgplbmRvYmoK"

roles
string[]
required

Who signs it, in order, at least one and at most six. ⚠️ THE ORDER IS LOAD-BEARING: it is a role’s position in the recipient list and its colour in the editor, and — when signing_mode is sequential — the order people are actually asked. Every field detected in the document is allocated to the first role that is not a cc, because a page cannot say which of three signers writes in a box; reassign them in the editor afterwards. Each comes back with an id, but nothing accepts it as input today — see Field.id.

One role’s name.

filename
string

Optional. Used only to NAME the template when name is absent, exactly as the upload box suggests a name from the file you dropped on it. It never reaches storage — the object key is the content hash.

Example:

"employment-contract.pdf"

name
string

Optional. What to call the template. Absent or blank falls back to filename, and then to “Untitled document”. ⚠️ A name that is SENT and cannot be stored is refused, while a name that is not sent is not — a template’s name is editable afterwards, and refusing an upload over one would be a wall in front of the thing this endpoint exists to make free.

Example:

"Employment contract"

routing_types
enum<string>[]

Optional. One entry per role, in the same order. ⚠️ IF YOU SEND IT AT ALL IT MUST HAVE EXACTLY ONE ENTRY PER ROLE — a short list is refused rather than padded, because from an API caller a short list is far more likely to be an off-by-one than an intention. Omit it entirely and every role signs. A cc is never asked to fill anything in, which is why detected fields skip past one. ⚠️ IT IS SENT THE FINISHED COPY ONLY WHEN WE SEND THE COMPLETION NOTICES — completion_delivery: vumasign, the default for an envelope sent from the dashboard. Under integrator, the default for an envelope sent with an API key, nobody is sent anything at completion, a cc included: telling them is yours.

What this role is asked to do.

Available options:
sign,
approve,
cc
signing_mode
enum<string>

Optional, default parallel — everybody is asked at once. sequential asks them in the order roles is written, each invitation going out only when the one before it is finished.

Available options:
parallel,
sequential
Example:

"parallel"

subjects
object[]

Optional. The people this document asks about, so that a subject(...) tag can address a field to one of them. Omitted means none, and every subject(...) tag is then refused. At most 200. Two subjects with the same key, or with labels that differ only in case, are refused.

Maximum array length: 200

Response

The template, in exactly the shape GET /api/v1/templates/{templateId} returns — same fields, same ids, same everything, because it is read back through the same function rather than assembled separately. Idempotency-Replayed says whether this request authored it (false) or is being shown an earlier one’s result (true); the status is 201 either way, deliberately, so a client branching on it behaves identically on a retry.

The template, with every field’s id.

id
string<uuid>
required

The template’s id. A bare uuid, no prefix.

name
string
required

What the sender called it.

created_at
string<date-time>
required

ISO 8601, UTC. Also the order this list is in — newest first.

documents
object[]
required

The pack, in the order it is stacked.

pages
object[]
required

⚠️ THE DENOMINATOR FOR EVERY Field.rect, and a client doing field matching cannot skip it: a rectangle of fractions is dimensionless without the page it is a fraction of. Join on (document_id, page), the same pair fields is joined by.

Every page of every document of the pack, in the pack’s order and then by page number — the order a signer scrolls.

roles
object[]
required

The signing roles, in routing order.

subjects
object[]
required

The people whose data this template collects.

questions
object[]
required

The tickbox questions and how many answers each takes.

fields
object[]
required

⚠️ A FLAT LIST, NOT A MAP KEYED BY ADDRESS. Several fields sharing one (subject, data_key) pair is NORMAL — real forms ask for an ID number on the application and again on the declaration, and initials in the footer of all nine pages. A value supplied for an address means it for every box that asks.