Signature Requests
List signature requests
Section titled “List signature requests”GET /v1/signature_requestsReturns a paginated list of signature requests for your organization.
Query parameters:
| Parameter | Type | Description |
|---|---|---|
page | integer | Page number (default: 1) |
per_page | integer | Results per page (default: 20, max: 100) |
Response:
{ "signature_requests": [ { "id": "sr_123", "title": "Service Agreement", "status": "sent", "is_complete": false, "is_declined": false, "signing_order": "everyone", "test_mode": false, "metadata": {}, "files_url": "/v1/signature_requests/sr_123/files", "form_data": {}, "created_at": "2026-03-04T10:00:00Z", "expires_at": null, "signers": [ { "id": "sig_456", "name": "Jane Smith", "email": "jane@example.com", "status": "pending", "order": 0, "signed_at": null, "viewed_at": null } ] } ], "pagination": { "page": 1, "per_page": 20, "total_pages": 1, "total_count": 1 }}Signature request statuses:
| Status | Description |
|---|---|
draft | Created but not yet sent |
sent | Sent to signers, awaiting signatures |
completed | All signers have signed |
cancelled | Cancelled by the sender |
expired | Past the expires_at date without completion |
Get a signature request
Section titled “Get a signature request”GET /v1/signature_requests/:idReturns a single signature request with signer details.
Create a signature request
Section titled “Create a signature request”POST /v1/signature_requestsSend a document for signature. You can either upload a PDF file (multipart/form-data) or provide markdown content in the JSON body — see Signing a markdown document below.
Parameters:
| Parameter | Type | Description |
|---|---|---|
file | file | PDF file to sign (required unless data.content is provided) |
data | JSON | Request data (required, see below) |
Data fields:
| Field | Type | Default | Description |
|---|---|---|---|
title | string | "Untitled" | Document title |
content | string | — | Markdown source to render as the document (alternative to file) |
message | string | — | Message included in signing emails |
signers | array | required | Signer objects (see below) |
fields | array | — | Field placement objects (not needed when using content with inline tokens) |
import_fields | boolean | false | Auto-detect AcroForm fields from the uploaded PDF and assign them to the first signer |
signing_order | string | "everyone" | "everyone" or "one_at_a_time" |
expires_at | string | — | ISO 8601 expiration date |
test_mode | boolean | false | Non-binding test mode |
metadata | object | — | Arbitrary key-value data |
Signer object:
| Field | Type | Description |
|---|---|---|
name | string | Signer’s full name (required) |
email | string | Signer’s email address (required) |
phone_number | string | Signer’s phone number |
order | integer | Signing order (required for one_at_a_time) |
role | string | Role key used to match inline markdown tokens (e.g. signer1) |
Maximum signers per document: 10 for accounts less than 30 days old, 50 for established accounts.
Field object:
| Field | Type | Description |
|---|---|---|
type | string | signature, initials, date, text, or checkbox (required) |
signer_index | integer | Index into the signers array (required) |
page | integer | Page number, 1-based (required) |
x | number | X position in points (required) |
y | number | Y position in points (required) |
width | number | Width in points (required) |
height | number | Height in points (required) |
label | string | Label shown to signer |
mapping_key | string | Key for extracting the value into form_data |
required | boolean | Whether field must be filled (default: true) |
Example:
curl -X POST https://api.visisign.app/v1/signature_requests \ -H "Authorization: Bearer vsk_your_key_here" \ -F "file=@contract.pdf" \ -F 'data={ "title": "Service Agreement", "message": "Please review and sign this agreement.", "signers": [ {"name": "Jane Smith", "email": "jane@example.com"} ], "fields": [ { "type": "signature", "signer_index": 0, "page": 1, "x": 100, "y": 650, "width": 200, "height": 50 } ] }'Error responses:
| Status | Description |
|---|---|
402 | Insufficient wallet balance (API plan) |
422 | Validation error (e.g. missing required fields, max signers per document exceeded) |
429 | Hourly/daily send limit or recipient rate limit exceeded |
Response (201):
{ "signature_request": { "id": "sr_789", "title": "Service Agreement", "status": "sent", "is_complete": false, "is_declined": false, "message": "Please review and sign this agreement.", "signing_order": "everyone", "test_mode": false, "metadata": {}, "files_url": "/v1/signature_requests/sr_789/files", "form_data": {}, "created_at": "2026-03-04T12:00:00Z", "expires_at": null, "signers": [ { "id": "sig_101", "name": "Jane Smith", "email": "jane@example.com", "status": "pending", "order": 0, "signed_at": null, "viewed_at": null, "signing_url": "https://app.visisign.app/sign/tok_abc123", "embed_url": "https://app.visisign.app/embed/sign/tok_abc123" } ] }}The signing_url opens the standard signing page. The embed_url opens a streamlined view designed for iframe embedding — see Embedded Signing for details.
Signing a markdown document
Section titled “Signing a markdown document”Instead of uploading a PDF, you can send raw markdown as the document source. VisiSign renders the markdown to a PDF on the server and auto-places signature fields at inline tokens.
Use application/json (no multipart/form-data required) and pass the markdown in data.content.
Inline field tokens:
Embed {{type:key}} tokens directly in the markdown body. The key is matched against each signer’s role (or, as a fallback, their name) to decide who fills the field.
| Token | Field type | Rendered size |
|---|---|---|
{{signature:key}} | signature | 200 × 50 pt |
{{initials:key}} | initials | 150 × 50 pt |
{{date:key}} | date | 160 × 28 pt |
{{text:key}} | text | 200 × 28 pt |
{{checkbox:key}} | checkbox | 20 × 20 pt |
Page breaks:
Put {{pagebreak}} on its own line to start a new PDF page. Useful for splitting long agreements into clear sections (e.g. main agreement → addendum → guaranty). Whitespace inside the braces is ignored, so {{ pagebreak }} works too.
Supported markdown: #/##/### headings, paragraphs, -/*/numbered lists, horizontal rules (---), GFM pipe tables, and inline **bold**, *italic*, __underline__. If no signer’s role matches a token’s key, the field is assigned to the first signer.
The API rejects markdown sends that produce zero {{signature:*}} fields after rendering, so you’ll get a 422 if your template has no signature anchors.
Example:
curl -X POST https://api.visisign.app/v1/signature_requests \ -H "Authorization: Bearer vsk_your_key_here" \ -H "Content-Type: application/json" \ -d '{ "title": "NDA", "content": "# Mutual NDA\n\nThis agreement is between **Acme Inc** and the signer below.\n\n## Acknowledgement\n\nI have read and agree to the terms.\n\nSignature: {{signature:signer1}}\n\nDate: {{date:signer1}}\n", "signers": [ {"name": "Jane Smith", "email": "jane@example.com", "role": "signer1"} ] }'The response matches the PDF-upload variant — you get back a signature_request with status: "sent" and tokenized signing URLs emailed to each signer.
Send with template
Section titled “Send with template”POST /v1/signature_requests/send_with_templateCreate a signature request from a pre-configured template.
Body parameters:
| Field | Type | Default | Description |
|---|---|---|---|
template_id | string | required | Template ID (prefixed tmpl_) |
title | string | template name | Document title |
message | string | — | Message for signers |
signers | array | required | Signers mapped to template roles |
expires_at | string | — | ISO 8601 expiration |
test_mode | boolean | false | Non-binding test mode |
metadata | object | — | Arbitrary metadata |
custom_fields | object | — | Prefill fields by mapping_key (see below) |
Signer object (template):
| Field | Type | Description |
|---|---|---|
name | string | Signer’s full name (required) |
email | string | Signer’s email address (required) |
role | string | Template role name |
role_id | integer | Template role ID |
Provide either role or role_id for each signer. For roleless templates (e.g. W-9), all fields are automatically assigned to the first signer — you can omit role/role_id.
Error responses:
| Status | Description |
|---|---|
403 | Templates require a Team plan |
Prefilling fields with custom_fields:
Pass a custom_fields object to pre-populate field values by their mapping_key. Get available mapping keys from GET /v1/templates/:id.
{ "template_id": "tmpl_123", "signers": [ {"name": "Jane Smith", "email": "jane@example.com"} ], "custom_fields": { "taxpayer_name": "Jane Smith", "business_name": "Acme Inc", "address": "123 Main St, Springfield, IL 62701" }}Only fields with a non-null mapping_key matching a key in custom_fields are prefilled. Unmatched keys are ignored.
Cancel a signature request
Section titled “Cancel a signature request”POST /v1/signature_requests/:id/cancelCancels a signature request. Only requests that haven’t been completed can be cancelled.
Response (200):
Returns the updated signature request with status: "cancelled".
Send a reminder
Section titled “Send a reminder”POST /v1/signature_requests/:id/remindSend a reminder email to a signer who hasn’t signed yet.
Body parameters:
| Field | Type | Description |
|---|---|---|
email_address | string | Email of the signer to remind (required) |
Response (200):
{ "message": "Reminder sent"}Error responses:
| Status | Description |
|---|---|
422 | No active envelope |
404 | No pending signer with that email |
429 | Reminder cooldown — already sent recently |
Download signed files
Section titled “Download signed files”GET /v1/signature_requests/:id/filesReturns the signed PDF. With an Authorization header, streams the file directly as an attachment. Without an Authorization header, redirects to the file URL.
Only available after all signers have completed.
curl https://api.visisign.app/v1/signature_requests/sr_123/files \ -H "Authorization: Bearer vsk_your_key_here" \ -o signed.pdfError responses:
| Status | Description |
|---|---|
404 | Signed file not yet available |
500 | Integrity error — signed file hash mismatch |
The 404 response uses the standard error format:
{ "error": { "type": "not_found", "message": "Signed file not yet available" }}