# The Tally Sign API — for your own systems

> Send documents for e-signature from your own systems: create from a template, send, track signers, download the signed PDF, and get webhooks.

Create documents from your templates, send them, follow every signer and download the signed PDF, over a plain JSON REST API. Webhooks tell your systems the moment something is signed. Included on every plan, with no limit on documents.

[Get started](https://www.tallysign.com/signup) Quick start

## Quick start — signed in five calls

From an API key to a signed PDF. Everything answers JSON at `https://app.tallysign.com/api/v1`, and the full description is in the [OpenAPI document](https://app.tallysign.com/api/v1/openapi.json).

### 1. Create an API key

In Tally Sign, an owner or admin opens Settings → AI & API and chooses Create key. Copy it then: it's shown once.

```
export TALLY_API_KEY="tsk_…"
```

### 2. Check it works

The answer names your company and whose key it is.

```
curl https://app.tallysign.com/api/v1/me \
  -H "Authorization: Bearer $TALLY_API_KEY"
```

### 3. Pick a template

Each template lists the roles that sign and the variables it needs. Note the id, the client's role and any variable marked required.

```
curl https://app.tallysign.com/api/v1/templates \
  -H "Authorization: Bearer $TALLY_API_KEY"
```

### 4. Create and send it

Name who signs for each role and fill in the variables. With send set to true it goes out at once; your own side fills from your profile.

```
curl -X POST "https://app.tallysign.com/api/v1/documents" \
  -H "Authorization: Bearer $TALLY_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
  "templateId": "tpl_7Qm2xKp4Rv",
  "title": "Mutual NDA — Brightline Logistics",
  "variables": {
    "nda.effectiveDate": "October 1, 2026"
  },
  "recipients": [
    {
      "role": "client",
      "name": "Dana Whitfield",
      "email": "dana@brightline.example",
      "title": "VP of Operations",
      "company": "Brightline Logistics"
    }
  ],
  "message": "Here is the NDA we discussed. Thanks, Avery",
  "send": true
}'
```

### 5. Follow it, then download it

Each signer's status, when they viewed and signed, what they filled in, and the audit trail. Once it's completed, the PDF is the signed copy with its Certificate of Completion.

```
curl https://app.tallysign.com/api/v1/documents/$DOCUMENT_ID \
  -H "Authorization: Bearer $TALLY_API_KEY"

curl -L https://app.tallysign.com/api/v1/documents/$DOCUMENT_ID/pdf \
  -H "Authorization: Bearer $TALLY_API_KEY" -o signed.pdf
```

## Authentication — an API key per integration

Send the key as a bearer token on every request. Give each integration its own key, so you can see when each was last used and revoke one without touching the others.

```
Authorization: Bearer tsk_…
```

### One company per key

A key reaches only its own company's templates and documents, the same ones its person sees in the app.

### It acts as its person

Documents it sends come from the person who created the key, and the audit trail names them. Their own signature goes only to them.

### The same rules as the app

Sending needs an active account; a suspended company or a person whose access was turned off can't use their keys.

### Stored as a hash

Tally Sign keeps only a SHA-256 hash of each key. Lost one? Revoke it and create another.

### Revoke any time

Settings → AI & API lists every key with when it was last used. Revoking one stops it, and its webhooks, at once.

### HTTPS only

Send the key in the Authorization header over HTTPS. Never put it in a web page or a mobile app.

## Reference — every endpoint

### Account

The key itself.

GET `/api/v1/me`

### Check your API key

Which company the key works for and whose key it is. A good first call, and the test Zapier runs when you connect.

Request

```
curl "https://app.tallysign.com/api/v1/me" \
  -H "Authorization: Bearer $TALLY_API_KEY"
```

Response · 200

```
{
  "object": "api_key",
  "key": {
    "name": "Zapier",
    "prefix": "tsk_9fQ2xa"
  },
  "company": {
    "id": "org_Nb4kq2",
    "name": "Northbeam Software"
  },
  "user": {
    "id": "usr_Ab12cd",
    "name": "Avery Brooks",
    "email": "avery@northbeam.example"
  },
  "rateLimit": {
    "limit": 300,
    "windowSeconds": 60
  }
}
```

### Templates

Documents start from a template: your wording, layout and signature blocks. Make them in Tally Sign.

GET `/api/v1/templates`

### List templates

Your company's templates, oldest first, each with the roles it expects and every {{variable}} it uses.

Request

```
curl "https://app.tallysign.com/api/v1/templates" \
  -H "Authorization: Bearer $TALLY_API_KEY"
```

Response · 200

```
{
  "object": "list",
  "data": [
    {
      "id": "tpl_7Qm2xKp4Rv",
      "object": "template",
      "title": "Mutual Non-Disclosure Agreement",
      "description": "Two-way confidentiality agreement with a deal summary, plain-English intent and 15 standard sections.",
      "category": "nda",
      "createdAt": "2026-09-02T16:20:11.000Z",
      "updatedAt": "2026-09-18T09:41:37.000Z",
      "roles": [
        {
          "role": "sender",
          "label": "Your company",
          "type": "signer",
          "isSender": true,
          "order": 0,
          "name": "",
          "email": ""
        },
        {
          "role": "client",
          "label": "Counterparty",
          "type": "signer",
          "isSender": false,
          "order": 1,
          "name": "",
          "email": ""
        }
      ],
      "variables": [
        {
          "key": "client.company",
          "label": "Client company",
          "required": true,
          "kind": "recipient"
        },
        {
          "key": "client.name",
          "label": "Client name",
          "required": true,
          "kind": "recipient"
        },
        {
          "key": "nda.effectiveDate",
          "label": "NDA effective date",
          "default": "",
          "required": true,
          "kind": "variable"
        },
        {
          "key": "nda.purpose",
          "label": "NDA purpose",
          "default": "Evaluating, negotiating, and/or performing a potential business relationship between the parties.",
          "required": false,
          "kind": "variable"
        },
        {
          "key": "nda.termYears",
          "label": "NDA term years",
          "default": "three (3)",
          "required": false,
          "kind": "variable"
        },
        {
          "key": "org.legalName",
          "label": "Company legal name",
          "required": false,
          "kind": "company"
        },
        {
          "key": "today",
          "label": "Today",
          "required": false,
          "kind": "builtin"
        }
      ]
    }
  ],
  "hasMore": false,
  "nextCursor": null
}
```

GET `/api/v1/templates/{id}`

### Get a template

One template, with its roles and variables: what to pass when you create a document from it.

Path

| `id` required string |  |
| --- | --- |

Errors: `not_found`.

Request

```
curl "https://app.tallysign.com/api/v1/templates/$ID" \
  -H "Authorization: Bearer $TALLY_API_KEY"
```

Response · 200

```
{
  "id": "tpl_7Qm2xKp4Rv",
  "object": "template",
  "title": "Mutual Non-Disclosure Agreement",
  "description": "Two-way confidentiality agreement with a deal summary, plain-English intent and 15 standard sections.",
  "category": "nda",
  "createdAt": "2026-09-02T16:20:11.000Z",
  "updatedAt": "2026-09-18T09:41:37.000Z",
  "roles": [
    {
      "role": "sender",
      "label": "Your company",
      "type": "signer",
      "isSender": true,
      "order": 0,
      "name": "",
      "email": ""
    },
    {
      "role": "client",
      "label": "Counterparty",
      "type": "signer",
      "isSender": false,
      "order": 1,
      "name": "",
      "email": ""
    }
  ],
  "variables": [
    {
      "key": "client.company",
      "label": "Client company",
      "required": true,
      "kind": "recipient"
    },
    {
      "key": "client.name",
      "label": "Client name",
      "required": true,
      "kind": "recipient"
    },
    {
      "key": "nda.effectiveDate",
      "label": "NDA effective date",
      "default": "",
      "required": true,
      "kind": "variable"
    },
    {
      "key": "nda.purpose",
      "label": "NDA purpose",
      "default": "Evaluating, negotiating, and/or performing a potential business relationship between the parties.",
      "required": false,
      "kind": "variable"
    },
    {
      "key": "nda.termYears",
      "label": "NDA term years",
      "default": "three (3)",
      "required": false,
      "kind": "variable"
    },
    {
      "key": "org.legalName",
      "label": "Company legal name",
      "required": false,
      "kind": "company"
    },
    {
      "key": "today",
      "label": "Today",
      "required": false,
      "kind": "builtin"
    }
  ]
}
```

### Documents

Create from a template, send, remind, void, follow and download.

GET `/api/v1/documents`

### List documents

Documents, most recently changed first. Filter by status, change time, template or a search, and page with `cursor`.

Query

| `status` string | Only these statuses, comma-separated: `sent,completed`. |
| --- | --- |
| `updatedSince` date-time | Only documents changed at or after this time (ISO 8601). |
| `q` string | Search titles and descriptions. |
| `templateId` string | Only documents made from this template. |
| `limit` integer | Page size, 1 to 100 (default 25). |
| `cursor` string | `nextCursor` from the previous page. |

Errors: `invalid_request`.

Request

```
curl "https://app.tallysign.com/api/v1/documents?status=sent,completed&updatedSince=2026-10-01T00:00:00Z&limit=10" \
  -H "Authorization: Bearer $TALLY_API_KEY"
```

Response · 200

```
{
  "object": "list",
  "data": [
    {
      "id": "doc_3Hn8wLc2Tz",
      "object": "document",
      "title": "Mutual NDA — Brightline Logistics",
      "status": "sent",
      "category": "nda",
      "templateId": "tpl_7Qm2xKp4Rv",
      "createdAt": "2026-10-01T15:02:44.000Z",
      "updatedAt": "2026-10-01T15:02:47.000Z",
      "sentAt": "2026-10-01T15:02:47.000Z",
      "completedAt": null,
      "expiresAt": "2026-11-30T15:02:47.000Z",
      "url": "https://app.tallysign.com/documents/doc_3Hn8wLc2Tz",
      "recipients": [
        {
          "id": "rcp_Ns1aV0bQ",
          "role": "sender",
          "roleLabel": "Your company",
          "type": "signer",
          "name": "Avery Brooks",
          "email": "avery@northbeam.example",
          "company": "Northbeam Software, Inc.",
          "isSender": true,
          "status": "sent"
        },
        {
          "id": "rcp_Bl9dWh2K",
          "role": "client",
          "roleLabel": "Counterparty",
          "type": "signer",
          "name": "Dana Whitfield",
          "email": "dana@brightline.example",
          "company": "Brightline Logistics",
          "isSender": false,
          "status": "sent"
        }
      ]
    }
  ],
  "hasMore": true,
  "nextCursor": "eyJ1IjoiMjAyNi0xMC0wMVQxNTowMjo0Ny4wMDBaIiwiaSI6ImRvY18zSG44d0xjMlR6In0"
}
```

POST `/api/v1/documents`

### Create a document from a template

Makes a draft from a template: name who signs for each role, fill in variables, and choose how it's sent. With `send: true` it goes out for signature at once; if anything is missing, nothing is created and the answer (`not_ready`) lists what. Your own side fills from the API key's person and company profile.

Body

| `templateId` required string | The template to start from (GET /templates). |
| --- | --- |
| `title` string | Document title (default: the template's). |
| `variables` object | Values for the template's {{variables}}, by key. |
| `recipients` RecipientInput[] |  |
| `signingOrder` "parallel" \| "sequential" | `sequential` asks signers one after another by `order`. Default: everyone at once. |
| `expiresInDays` integer | Days until the signing links stop working (default 60). 1 to 365. |
| `subject` string | Email subject of the signing request. |
| `message` string | Personal note in the signing request email. |
| `send` boolean | Send it for signature right away. If anything is missing, nothing is created and the answer lists what. |

Each recipient

| `role` required string | A role from the template (`roles[].role`), or a new one to add a signer. |
| --- | --- |
| `name` string |  |
| `email` string |  |
| `title` string |  |
| `company` string |  |
| `order` integer | Signing order when `signingOrder` is `sequential`. 0 to 100. |

Takes an `Idempotency-Key` header.

Errors: `invalid_request`, `not_found`, `not_ready`, `billing_paused`.

Request

```
curl -X POST "https://app.tallysign.com/api/v1/documents" \
  -H "Authorization: Bearer $TALLY_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
  "templateId": "tpl_7Qm2xKp4Rv",
  "title": "Mutual NDA — Brightline Logistics",
  "variables": {
    "nda.effectiveDate": "October 1, 2026"
  },
  "recipients": [
    {
      "role": "client",
      "name": "Dana Whitfield",
      "email": "dana@brightline.example",
      "title": "VP of Operations",
      "company": "Brightline Logistics"
    }
  ],
  "message": "Here is the NDA we discussed. Thanks, Avery",
  "send": true
}'
```

Response · 201

```
{
  "id": "doc_3Hn8wLc2Tz",
  "object": "document",
  "title": "Mutual NDA — Brightline Logistics",
  "status": "sent",
  "category": "nda",
  "templateId": "tpl_7Qm2xKp4Rv",
  "createdAt": "2026-10-01T15:02:44.000Z",
  "updatedAt": "2026-10-01T15:02:47.000Z",
  "sentAt": "2026-10-01T15:02:47.000Z",
  "completedAt": null,
  "expiresAt": "2026-11-30T15:02:47.000Z",
  "url": "https://app.tallysign.com/documents/doc_3Hn8wLc2Tz",
  "recipients": [
    {
      "id": "rcp_Ns1aV0bQ",
      "role": "sender",
      "roleLabel": "Your company",
      "type": "signer",
      "name": "Avery Brooks",
      "email": "avery@northbeam.example",
      "company": "Northbeam Software, Inc.",
      "isSender": true,
      "status": "sent",
      "title": "COO",
      "order": 0,
      "viewedAt": null,
      "signedAt": null,
      "declinedReason": null,
      "fields": [],
      "fieldValues": {}
    },
    {
      "id": "rcp_Bl9dWh2K",
      "role": "client",
      "roleLabel": "Counterparty",
      "type": "signer",
      "name": "Dana Whitfield",
      "email": "dana@brightline.example",
      "company": "Brightline Logistics",
      "isSender": false,
      "status": "sent",
      "title": "VP of Operations",
      "order": 1,
      "viewedAt": null,
      "signedAt": null,
      "declinedReason": null,
      "fields": [],
      "fieldValues": {}
    }
  ],
  "signingOrder": "parallel",
  "subject": null,
  "message": "Here is the NDA we discussed. Thanks, Avery",
  "variables": {
    "nda.effectiveDate": "October 1, 2026",
    "nda.purpose": "Evaluating, negotiating, and/or performing a potential business relationship between the parties.",
    "nda.termYears": "three (3)"
  },
  "fieldValues": {},
  "hasSignedPdf": false,
  "pdfUrl": "https://app.tallysign.com/api/v1/documents/doc_3Hn8wLc2Tz/pdf",
  "events": [
    {
      "type": "email_sent",
      "detail": "Signing request emailed to Dana Whitfield <dana@brightline.example>",
      "at": "2026-10-01T15:02:48.000Z",
      "recipientId": "rcp_Bl9dWh2K"
    },
    {
      "type": "email_sent",
      "detail": "Signing request emailed to Avery Brooks <avery@northbeam.example>",
      "at": "2026-10-01T15:02:48.000Z",
      "recipientId": "rcp_Ns1aV0bQ"
    },
    {
      "type": "sent",
      "detail": "Sent for signature to Avery Brooks, Dana Whitfield",
      "at": "2026-10-01T15:02:47.000Z",
      "recipientId": null
    },
    {
      "type": "created",
      "detail": "Created through the API",
      "at": "2026-10-01T15:02:44.000Z",
      "recipientId": null
    }
  ]
}
```

GET `/api/v1/documents/{id}`

### Get a document

Status, every recipient with when they viewed and signed, what each signer filled in, and the audit trail. A signer's `fields` and `fieldValues` fill in once they sign (until then they're empty: entries before signing aren't agreed to), and `fieldValues` on the document merges every signer's. Files signers uploaded are listed with a link to download them with your API key.

Path

| `id` required string |  |
| --- | --- |

Errors: `not_found`.

Request

```
curl "https://app.tallysign.com/api/v1/documents/$ID" \
  -H "Authorization: Bearer $TALLY_API_KEY"
```

Response · 200

```
{
  "id": "doc_3Hn8wLc2Tz",
  "object": "document",
  "title": "Mutual NDA — Brightline Logistics",
  "status": "sent",
  "category": "nda",
  "templateId": "tpl_7Qm2xKp4Rv",
  "createdAt": "2026-10-01T15:02:44.000Z",
  "updatedAt": "2026-10-01T17:44:09.000Z",
  "sentAt": "2026-10-01T15:02:47.000Z",
  "completedAt": null,
  "expiresAt": "2026-11-30T15:02:47.000Z",
  "url": "https://app.tallysign.com/documents/doc_3Hn8wLc2Tz",
  "recipients": [
    {
      "id": "rcp_Ns1aV0bQ",
      "role": "sender",
      "roleLabel": "Your company",
      "type": "signer",
      "name": "Avery Brooks",
      "email": "avery@northbeam.example",
      "company": "Northbeam Software, Inc.",
      "isSender": true,
      "status": "sent",
      "title": "COO",
      "order": 0,
      "viewedAt": null,
      "signedAt": null,
      "declinedReason": null,
      "fields": [],
      "fieldValues": {}
    },
    {
      "id": "rcp_Bl9dWh2K",
      "role": "client",
      "roleLabel": "Counterparty",
      "type": "signer",
      "name": "Dana Whitfield",
      "email": "dana@brightline.example",
      "company": "Brightline Logistics",
      "isSender": false,
      "status": "signed",
      "title": "VP of Operations",
      "order": 1,
      "viewedAt": "2026-10-01T17:42:51.000Z",
      "signedAt": "2026-10-01T17:44:09.000Z",
      "declinedReason": null,
      "fields": [
        {
          "id": "f_Nm3kQ8",
          "type": "name",
          "label": "Full name",
          "key": "Full name",
          "value": "Dana Whitfield"
        },
        {
          "id": "f_Tt7wL2",
          "type": "title",
          "label": "Title",
          "key": "Title",
          "value": "VP of Operations"
        },
        {
          "id": "f_Ds4pR9",
          "type": "date",
          "label": "Date signed",
          "key": "Date signed",
          "value": "2026-10-01"
        },
        {
          "id": "f_Ne1xC5",
          "type": "text",
          "label": "Email for notices",
          "key": "Email for notices",
          "value": "legal@brightline.example"
        },
        {
          "id": "f_Em6hB0",
          "type": "number",
          "label": "Number of employees",
          "key": "Number of employees",
          "value": 240
        },
        {
          "id": "f_Rg2vJ7",
          "type": "dropdown",
          "label": "Region",
          "key": "Region",
          "value": "North America"
        },
        {
          "id": "f_Pd8sK3",
          "type": "dateInput",
          "label": "Disclosures start",
          "key": "Disclosures start",
          "value": "2026-10-15"
        },
        {
          "id": "f_Cb5mT1",
          "type": "checkbox",
          "label": "Share with affiliates",
          "key": "Share with affiliates",
          "value": false
        },
        {
          "id": "f_Fl9qW4",
          "type": "file",
          "label": "Certificate of insurance",
          "key": "Certificate of insurance",
          "value": "Brightline COI 2026.pdf",
          "files": [
            {
              "id": "sf_Hq2cV8xWm4Lp0aTz9Ke",
              "filename": "Brightline COI 2026.pdf",
              "size": 186412,
              "contentType": "application/pdf",
              "sha256": "9b2f4c0e7a1d3e5f8c6b9a0d2e4f6a8c1b3d5e7f9a0c2e4b6d8f0a1c3e5b7d9f",
              "uploadedAt": "2026-10-01T17:43:30.000Z",
              "url": "https://app.tallysign.com/api/v1/documents/doc_3Hn8wLc2Tz/files/sf_Hq2cV8xWm4Lp0aTz9Ke"
            }
          ]
        }
      ],
      "fieldValues": {
        "Full name": "Dana Whitfield",
        "Title": "VP of Operations",
        "Date signed": "2026-10-01",
        "Email for notices": "legal@brightline.example",
        "Number of employees": 240,
        "Region": "North America",
        "Disclosures start": "2026-10-15",
        "Share with affiliates": false,
        "Certificate of insurance": "Brightline COI 2026.pdf"
      }
    }
  ],
  "signingOrder": "parallel",
  "subject": null,
  "message": "Here is the NDA we discussed. Thanks, Avery",
  "variables": {
    "nda.effectiveDate": "October 1, 2026",
    "nda.purpose": "Evaluating, negotiating, and/or performing a potential business relationship between the parties.",
    "nda.termYears": "three (3)"
  },
  "fieldValues": {
    "Full name": "Dana Whitfield",
    "Title": "VP of Operations",
    "Date signed": "2026-10-01",
    "Email for notices": "legal@brightline.example",
    "Number of employees": 240,
    "Region": "North America",
    "Disclosures start": "2026-10-15",
    "Share with affiliates": false,
    "Certificate of insurance": "Brightline COI 2026.pdf"
  },
  "hasSignedPdf": false,
  "pdfUrl": "https://app.tallysign.com/api/v1/documents/doc_3Hn8wLc2Tz/pdf",
  "events": [
    {
      "type": "signed",
      "detail": "Signed by Dana Whitfield <dana@brightline.example>",
      "at": "2026-10-01T17:44:09.000Z",
      "recipientId": "rcp_Bl9dWh2K"
    },
    {
      "type": "file_uploaded",
      "detail": "Dana Whitfield <dana@brightline.example> uploaded “Brightline COI 2026.pdf” (182 KB, SHA-256 9b2f4c0e…) for “Certificate of insurance”",
      "at": "2026-10-01T17:44:09.000Z",
      "recipientId": "rcp_Bl9dWh2K"
    },
    {
      "type": "email_sent",
      "detail": "Signing request emailed to Dana Whitfield <dana@brightline.example>",
      "at": "2026-10-01T15:02:48.000Z",
      "recipientId": "rcp_Bl9dWh2K"
    },
    {
      "type": "email_sent",
      "detail": "Signing request emailed to Avery Brooks <avery@northbeam.example>",
      "at": "2026-10-01T15:02:48.000Z",
      "recipientId": "rcp_Ns1aV0bQ"
    },
    {
      "type": "sent",
      "detail": "Sent for signature to Avery Brooks, Dana Whitfield",
      "at": "2026-10-01T15:02:47.000Z",
      "recipientId": null
    },
    {
      "type": "created",
      "detail": "Created through the API",
      "at": "2026-10-01T15:02:44.000Z",
      "recipientId": null
    }
  ]
}
```

DELETE `/api/v1/documents/{id}`

### Delete a draft or voided document

Sent, completed and declined documents are part of the signing record and can't be deleted. Void a sent one instead.

Path

| `id` required string |  |
| --- | --- |

Errors: `not_found`, `not_deletable`.

Request

```
curl -X DELETE "https://app.tallysign.com/api/v1/documents/$ID" \
  -H "Authorization: Bearer $TALLY_API_KEY"
```

Response · 200

```
{
  "id": "doc_3Hn8wLc2Tz",
  "object": "document",
  "deleted": true
}
```

POST `/api/v1/documents/{id}/send`

### Send a draft for signature

Emails each signer their own signing link (the first ones only, when signing in order). Answers `not_ready` with the list of what's missing when it can't be sent yet.

Path

| `id` required string |  |
| --- | --- |

Body

| `signingOrder` "parallel" \| "sequential" | `sequential` asks signers one after another by `order`. Default: everyone at once. |
| --- | --- |
| `expiresInDays` integer | Days until the signing links stop working (default 60). 1 to 365. |
| `subject` string | Email subject of the signing request. |
| `message` string | Personal note in the signing request email. |

Takes an `Idempotency-Key` header.

Errors: `not_found`, `not_ready`, `conflict`, `billing_paused`.

Request

```
curl -X POST "https://app.tallysign.com/api/v1/documents/$ID/send" \
  -H "Authorization: Bearer $TALLY_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
  "signingOrder": "sequential",
  "expiresInDays": 30,
  "message": "Here is the NDA we discussed. Thanks, Avery"
}'
```

Response · 200

```
{
  "document": {
    "id": "doc_3Hn8wLc2Tz",
    "object": "document",
    "title": "Mutual NDA — Brightline Logistics",
    "status": "sent",
    "category": "nda",
    "templateId": "tpl_7Qm2xKp4Rv",
    "createdAt": "2026-10-01T15:02:44.000Z",
    "updatedAt": "2026-10-01T15:02:47.000Z",
    "sentAt": "2026-10-01T15:02:47.000Z",
    "completedAt": null,
    "expiresAt": "2026-11-30T15:02:47.000Z",
    "url": "https://app.tallysign.com/documents/doc_3Hn8wLc2Tz",
    "recipients": [
      {
        "id": "rcp_Ns1aV0bQ",
        "role": "sender",
        "roleLabel": "Your company",
        "type": "signer",
        "name": "Avery Brooks",
        "email": "avery@northbeam.example",
        "company": "Northbeam Software, Inc.",
        "isSender": true,
        "status": "sent",
        "title": "COO",
        "order": 0,
        "viewedAt": null,
        "signedAt": null,
        "declinedReason": null,
        "fields": [],
        "fieldValues": {}
      },
      {
        "id": "rcp_Bl9dWh2K",
        "role": "client",
        "roleLabel": "Counterparty",
        "type": "signer",
        "name": "Dana Whitfield",
        "email": "dana@brightline.example",
        "company": "Brightline Logistics",
        "isSender": false,
        "status": "sent",
        "title": "VP of Operations",
        "order": 1,
        "viewedAt": null,
        "signedAt": null,
        "declinedReason": null,
        "fields": [],
        "fieldValues": {}
      }
    ],
    "signingOrder": "parallel",
    "subject": null,
    "message": "Here is the NDA we discussed. Thanks, Avery",
    "variables": {
      "nda.effectiveDate": "October 1, 2026",
      "nda.purpose": "Evaluating, negotiating, and/or performing a potential business relationship between the parties.",
      "nda.termYears": "three (3)"
    },
    "fieldValues": {},
    "hasSignedPdf": false,
    "pdfUrl": "https://app.tallysign.com/api/v1/documents/doc_3Hn8wLc2Tz/pdf",
    "events": [
      {
        "type": "email_sent",
        "detail": "Signing request emailed to Dana Whitfield <dana@brightline.example>",
        "at": "2026-10-01T15:02:48.000Z",
        "recipientId": "rcp_Bl9dWh2K"
      },
      {
        "type": "email_sent",
        "detail": "Signing request emailed to Avery Brooks <avery@northbeam.example>",
        "at": "2026-10-01T15:02:48.000Z",
        "recipientId": "rcp_Ns1aV0bQ"
      },
      {
        "type": "sent",
        "detail": "Sent for signature to Avery Brooks, Dana Whitfield",
        "at": "2026-10-01T15:02:47.000Z",
        "recipientId": null
      },
      {
        "type": "created",
        "detail": "Created through the API",
        "at": "2026-10-01T15:02:44.000Z",
        "recipientId": null
      }
    ]
  },
  "deliveries": [
    {
      "name": "Avery Brooks",
      "email": "avery@northbeam.example",
      "delivered": true
    },
    {
      "name": "Dana Whitfield",
      "email": "dana@brightline.example",
      "delivered": true
    }
  ]
}
```

POST `/api/v1/documents/{id}/remind`

### Remind signers

Emails a reminder to everyone still waiting to sign, or to one signer. Each signer is reminded at most once an hour.

Path

| `id` required string |  |
| --- | --- |

Body

| `recipientId` string | Remind only this signer. |
| --- | --- |
| `role` string | Remind only the signer with this role. |
| `message` string | A note in the reminder. |

Takes an `Idempotency-Key` header.

Errors: `not_found`, `invalid_request`, `too_soon`.

Request

```
curl -X POST "https://app.tallysign.com/api/v1/documents/$ID/remind" \
  -H "Authorization: Bearer $TALLY_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
  "role": "client",
  "message": "A reminder about the NDA. Thanks!"
}'
```

Response · 200

```
{
  "documentId": "doc_3Hn8wLc2Tz",
  "reminded": [
    {
      "name": "Dana Whitfield",
      "email": "dana@brightline.example",
      "delivered": true
    }
  ]
}
```

POST `/api/v1/documents/{id}/void`

### Void a document

Cancels a document that's awaiting signatures. Its signing links stop working, and signers who had it are told (unless your company turned that email off).

Path

| `id` required string |  |
| --- | --- |

Body

| `reason` string | Recorded in the audit trail and included in the void notice to signers. |
| --- | --- |

Takes an `Idempotency-Key` header.

Errors: `not_found`, `conflict`.

Request

```
curl -X POST "https://app.tallysign.com/api/v1/documents/$ID/void" \
  -H "Authorization: Bearer $TALLY_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
  "reason": "Replaced by the revised NDA."
}'
```

Response · 200

```
{
  "id": "doc_3Hn8wLc2Tz",
  "object": "document",
  "title": "Mutual NDA — Brightline Logistics",
  "status": "voided",
  "category": "nda",
  "templateId": "tpl_7Qm2xKp4Rv",
  "createdAt": "2026-10-01T15:02:44.000Z",
  "updatedAt": "2026-10-01T15:02:47.000Z",
  "sentAt": "2026-10-01T15:02:47.000Z",
  "completedAt": null,
  "expiresAt": "2026-11-30T15:02:47.000Z",
  "url": "https://app.tallysign.com/documents/doc_3Hn8wLc2Tz",
  "recipients": [
    {
      "id": "rcp_Ns1aV0bQ",
      "role": "sender",
      "roleLabel": "Your company",
      "type": "signer",
      "name": "Avery Brooks",
      "email": "avery@northbeam.example",
      "company": "Northbeam Software, Inc.",
      "isSender": true,
      "status": "sent",
      "title": "COO",
      "order": 0,
      "viewedAt": null,
      "signedAt": null,
      "declinedReason": null,
      "fields": [],
      "fieldValues": {}
    },
    {
      "id": "rcp_Bl9dWh2K",
      "role": "client",
      "roleLabel": "Counterparty",
      "type": "signer",
      "name": "Dana Whitfield",
      "email": "dana@brightline.example",
      "company": "Brightline Logistics",
      "isSender": false,
      "status": "sent",
      "title": "VP of Operations",
      "order": 1,
      "viewedAt": null,
      "signedAt": null,
      "declinedReason": null,
      "fields": [],
      "fieldValues": {}
    }
  ],
  "signingOrder": "parallel",
  "subject": null,
  "message": "Here is the NDA we discussed. Thanks, Avery",
  "variables": {
    "nda.effectiveDate": "October 1, 2026",
    "nda.purpose": "Evaluating, negotiating, and/or performing a potential business relationship between the parties.",
    "nda.termYears": "three (3)"
  },
  "fieldValues": {},
  "hasSignedPdf": false,
  "pdfUrl": "https://app.tallysign.com/api/v1/documents/doc_3Hn8wLc2Tz/pdf",
  "events": [
    {
      "type": "email_sent",
      "detail": "Signing request emailed to Dana Whitfield <dana@brightline.example>",
      "at": "2026-10-01T15:02:48.000Z",
      "recipientId": "rcp_Bl9dWh2K"
    },
    {
      "type": "email_sent",
      "detail": "Signing request emailed to Avery Brooks <avery@northbeam.example>",
      "at": "2026-10-01T15:02:48.000Z",
      "recipientId": "rcp_Ns1aV0bQ"
    },
    {
      "type": "sent",
      "detail": "Sent for signature to Avery Brooks, Dana Whitfield",
      "at": "2026-10-01T15:02:47.000Z",
      "recipientId": null
    },
    {
      "type": "created",
      "detail": "Created through the API",
      "at": "2026-10-01T15:02:44.000Z",
      "recipientId": null
    }
  ]
}
```

GET `/api/v1/documents/{id}/pdf`

### Download the PDF

A completed document's signed PDF: every page as signed, its exhibits, and the Certificate of Completion with the audit trail at the end. Before it's completed, a preview of the document as it stands (add `preview=true` to always get the preview). The answer may be a redirect to a short-lived file link: follow it (`curl -L`).

Path

| `id` required string |  |
| --- | --- |

Errors: `not_found`.

Request

```
curl -L "https://app.tallysign.com/api/v1/documents/$ID/pdf" \
  -H "Authorization: Bearer $TALLY_API_KEY" \
  -o signed.pdf
```

Response · 200: The PDF.

GET `/api/v1/documents/{id}/files/{fileId}`

### Download a signer's file

A file a signer uploaded into a File upload field, once they've signed with it: a PDF, or a JPEG or PNG image, under the name it had. Its id and `url` are in the signer's `fields` (`files`). The answer may be a redirect to a short-lived file link: follow it (`curl -L`).

Path

| `id` required string |  |
| --- | --- |
| `fileId` required string |  |

Errors: `not_found`.

Request

```
curl -L "https://app.tallysign.com/api/v1/documents/$ID/files/$FILE_ID" \
  -H "Authorization: Bearer $TALLY_API_KEY" \
  -OJ
```

Response · 200: The file.

POST `/api/v1/documents/{id}/recipients/{recipientId}/signing-link`

### Get a signer's signing link

For signing inside your own product or in person: the link a signer would get by email. It works only while the document is out for signature and it's that signer's turn. Your company's own signer's link goes only to the API key's person when they are that signer. Each link handed out is recorded in the audit trail.

Path

| `id` required string |  |
| --- | --- |
| `recipientId` required string |  |

Body

| `inPerson` boolean | Hand your device to the signer: the link opens the in-person hand-off, valid 2 hours, and the certificate records the API key's person as host. |
| --- | --- |

Errors: `not_found`, `conflict`, `forbidden`.

Request

```
curl -X POST "https://app.tallysign.com/api/v1/documents/$ID/recipients/$RECIPIENT_ID/signing-link" \
  -H "Authorization: Bearer $TALLY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{}'
```

Response · 200

```
{
  "recipientId": "rcp_Bl9dWh2K",
  "name": "Dana Whitfield",
  "email": "dana@brightline.example",
  "url": "https://app.tallysign.com/s/q9Xc…",
  "expiresAt": "2026-11-30T15:02:47.000Z"
}
```

### Webhook subscriptions

Have Tally Sign post events to your URL. What arrives is described under Webhooks below.

GET `/api/v1/webhooks`

### List webhook subscriptions

The subscriptions this API key made.

Request

```
curl "https://app.tallysign.com/api/v1/webhooks" \
  -H "Authorization: Bearer $TALLY_API_KEY"
```

Response · 200

```
{
  "object": "list",
  "data": [
    {
      "id": "whk_5Jr0pYt8Gs",
      "object": "webhook",
      "url": "https://hooks.zapier.com/hooks/standard/1234567/abcdef/",
      "events": [
        "document.completed"
      ],
      "description": "Zapier: completed NDAs to the shared drive",
      "createdAt": "2026-10-02T10:15:00.000Z",
      "lastDeliveryAt": null,
      "lastError": null
    }
  ],
  "hasMore": false,
  "nextCursor": null
}
```

POST `/api/v1/webhooks`

### Subscribe to events

Tally Sign posts each event you pick to your URL as JSON, signed with the subscription's secret. Failed deliveries are retried for about a day. Answering 410 Gone removes the subscription. Only company owners' and admins' keys can subscribe.

Body

| `url` required string | Public https URL that receives the events. |
| --- | --- |
| `events` required ("document.sent" \| "recipient.viewed" \| "recipient.signed" \| "document.completed" \| "document.declined" \| "document.voided" \| "document.expired")[] | Which events to send. |
| `description` string | A note so you can tell subscriptions apart, e.g. `Zapier: signed NDAs to Slack`. |

Takes an `Idempotency-Key` header.

Errors: `invalid_request`, `forbidden`.

Request

```
curl -X POST "https://app.tallysign.com/api/v1/webhooks" \
  -H "Authorization: Bearer $TALLY_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
  "url": "https://hooks.zapier.com/hooks/standard/1234567/abcdef/",
  "events": [
    "document.completed"
  ],
  "description": "Zapier: completed NDAs to the shared drive"
}'
```

Response · 201

```
{
  "id": "whk_5Jr0pYt8Gs",
  "object": "webhook",
  "url": "https://hooks.zapier.com/hooks/standard/1234567/abcdef/",
  "events": [
    "document.completed"
  ],
  "description": "Zapier: completed NDAs to the shared drive",
  "createdAt": "2026-10-02T10:15:00.000Z",
  "lastDeliveryAt": null,
  "lastError": null,
  "secret": "whsec_4mZq9Tn2bW7yLc0aRf5uXe1k"
}
```

DELETE `/api/v1/webhooks/{id}`

### Unsubscribe

Stops the subscription. Deliveries still waiting are dropped.

Path

| `id` required string |  |
| --- | --- |

Errors: `not_found`.

Request

```
curl -X DELETE "https://app.tallysign.com/api/v1/webhooks/$ID" \
  -H "Authorization: Bearer $TALLY_API_KEY"
```

Response · 200

```
{
  "id": "whk_5Jr0pYt8Gs",
  "object": "webhook",
  "deleted": true
}
```

## What signers filled in — typed values, ready for a spreadsheet

Every recipient on a document, and every webhook about one, carries what that signer entered: each field with its label and typed value, the same as one flat object for no-code tools, and the files they uploaded.

- A signer's `fields` and `fieldValues` fill in once they sign. Until then they're empty: what someone types before signing isn't agreed to.
- Values are typed: a number as a number, a checkbox as true or false, a dropdown or radio as the option chosen, a date they picked and the date they signed as YYYY-MM-DD, anything else as text. Left empty: null.
- Signatures and initials aren't listed (`signedAt` says they signed), nor conditional fields that stayed hidden.
- `fieldValues` keys are the field's label as you wrote it in the template, or its type (`Text`) when it has none. A label used twice by one signer becomes `Label (2)`, `Label (3)` in page order.
- The document's `fieldValues` merges every signer's. A key two signers share is prefixed by the role label: `Counterparty: Company`. Keys come from the document as sent, so they stay the same as more people sign. Read values by key: the order of an object's keys isn't guaranteed (`fields` is in page order).
- File uploads give the file's name as the value, and the file under `files` with its size, type and SHA-256. Download it with your API key from `url`.

Each field

| `id` required string | The field's id in the document. |
| --- | --- |
| `type` required "name" \| "title" \| "company" \| "email" \| "date" \| "text" \| "checkbox" \| "dropdown" \| "radio" \| "number" \| "dateInput" \| "file" | `date` is the date they signed; `dateInput` a date they picked. |
| `label` required string | The field's label as the sender wrote it; its type (e.g. `Text`) when it has none. |
| `key` required string | Its key in `fieldValues`: the label, made unique per signer as `Label (2)`, `Label (3)` in page order. |
| `value` required string \| number \| boolean \| null | Typed: a number as a number, a checkbox as true or false, a dropdown or radio as the option chosen, `date` and `dateInput` as YYYY-MM-DD, a file upload as the file's name, anything else as text. null when left empty. |
| `files` SignerFile[] | File upload fields: the file they signed with. |

Each file

| `id` required string |  |
| --- | --- |
| `filename` required string | The name the signer's file had. |
| `size` required integer | Bytes. -9007199254740991 to 9007199254740991. |
| `contentType` required string | `application/pdf`, `image/jpeg` or `image/png` (checked from the file itself). |
| `sha256` required string | SHA-256 of the file, hex: the fingerprint on the Certificate of Completion. |
| `uploadedAt` required date-time | When the signer uploaded it. |
| `url` required string | Download it with your API key (GET /documents/{id}/files/{fileId}). Not a public link. |

A signer who has signed (part of their fields, and all of fieldValues)

```
{
  "fields": [
    {
      "id": "f_Em6hB0",
      "type": "number",
      "label": "Number of employees",
      "key": "Number of employees",
      "value": 240
    },
    {
      "id": "f_Rg2vJ7",
      "type": "dropdown",
      "label": "Region",
      "key": "Region",
      "value": "North America"
    },
    {
      "id": "f_Pd8sK3",
      "type": "dateInput",
      "label": "Disclosures start",
      "key": "Disclosures start",
      "value": "2026-10-15"
    },
    {
      "id": "f_Cb5mT1",
      "type": "checkbox",
      "label": "Share with affiliates",
      "key": "Share with affiliates",
      "value": false
    },
    {
      "id": "f_Fl9qW4",
      "type": "file",
      "label": "Certificate of insurance",
      "key": "Certificate of insurance",
      "value": "Brightline COI 2026.pdf",
      "files": [
        {
          "id": "sf_Hq2cV8xWm4Lp0aTz9Ke",
          "filename": "Brightline COI 2026.pdf",
          "size": 186412,
          "contentType": "application/pdf",
          "sha256": "9b2f4c0e7a1d3e5f8c6b9a0d2e4f6a8c1b3d5e7f9a0c2e4b6d8f0a1c3e5b7d9f",
          "uploadedAt": "2026-10-01T17:43:30.000Z",
          "url": "https://app.tallysign.com/api/v1/documents/doc_3Hn8wLc2Tz/files/sf_Hq2cV8xWm4Lp0aTz9Ke"
        }
      ]
    }
  ],
  "fieldValues": {
    "Full name": "Dana Whitfield",
    "Title": "VP of Operations",
    "Date signed": "2026-10-01",
    "Email for notices": "legal@brightline.example",
    "Number of employees": 240,
    "Region": "North America",
    "Disclosures start": "2026-10-15",
    "Share with affiliates": false,
    "Certificate of insurance": "Brightline COI 2026.pdf"
  }
}
```

## Webhooks — told the moment it's signed

Subscribe a URL to the events you care about with POST /api/v1/webhooks. Tally Sign posts each one as JSON, signed with the subscription's secret. Automations can send the same payload to a URL too, without code.

### Events

| `document.sent` | A document was sent for signature (again, when an edited version is sent). |
| --- | --- |
| `recipient.viewed` | A signer opened the document and interacted with it for the first time (link scanners don't count). |
| `recipient.signed` | A signer signed. Sent for every signer, your own side included. |
| `document.completed` | Everyone has signed. The signed PDF is ready to download. |
| `document.declined` | A signer declined to sign. `recipient.declinedReason` says why. |
| `document.voided` | A document awaiting signatures was voided. |
| `document.expired` | The signing links expired before everyone signed. |

### Delivery

- Answer with any 2xx within 10 seconds. Do the slow work afterwards.
- Anything else is retried after 1 minute, 5 minutes, 30 minutes, 2 hours, 6 hours and 12 hours, then dropped. The id stays the same on every retry, so you can ignore repeats.
- Answering `410 Gone` deletes the subscription.
- Only public https addresses receive events. Redirects aren't followed.
- Headers: `X-Tally-Event`, `X-Tally-Delivery` and `X-Tally-Signature`.
- Admins see every subscription in Settings → AI & API and can remove any of them. Revoking a key removes its subscriptions.

What your URL receives (recipient.signed)

```
{
  "id": "whd_W2fK8nQx0aLm",
  "event": "recipient.signed",
  "at": "2026-10-01T17:44:09.512Z",
  "subscription": {
    "id": "whk_5Jr0pYt8Gs"
  },
  "document": {
    "id": "doc_3Hn8wLc2Tz",
    "title": "Mutual NDA — Brightline Logistics",
    "status": "sent",
    "url": "https://app.tallysign.com/documents/doc_3Hn8wLc2Tz",
    "total": 0,
    "currency": null,
    "sentAt": "2026-10-01T15:02:47.000Z",
    "completedAt": null,
    "expiresAt": "2026-11-30T15:02:47.000Z",
    "templateId": "tpl_7Qm2xKp4Rv",
    "signedPdfUrl": null,
    "salesforce": null,
    "fieldValues": {
      "Full name": "Dana Whitfield",
      "Title": "VP of Operations",
      "Date signed": "2026-10-01",
      "Email for notices": "legal@brightline.example",
      "Number of employees": 240,
      "Region": "North America",
      "Disclosures start": "2026-10-15",
      "Share with affiliates": false,
      "Certificate of insurance": "Brightline COI 2026.pdf"
    }
  },
  "recipients": [
    {
      "id": "rcp_Ns1aV0bQ",
      "role": "sender",
      "name": "Avery Brooks",
      "email": "avery@northbeam.example",
      "company": "Northbeam Software, Inc.",
      "type": "signer",
      "status": "sent",
      "signedAt": null,
      "isSender": true,
      "fields": [],
      "fieldValues": {}
    },
    {
      "id": "rcp_Bl9dWh2K",
      "role": "client",
      "name": "Dana Whitfield",
      "email": "dana@brightline.example",
      "company": "Brightline Logistics",
      "type": "signer",
      "status": "signed",
      "signedAt": "2026-10-01T17:44:09.000Z",
      "isSender": false,
      "fields": [
        {
          "id": "f_Nm3kQ8",
          "type": "name",
          "label": "Full name",
          "key": "Full name",
          "value": "Dana Whitfield"
        },
        {
          "id": "f_Tt7wL2",
          "type": "title",
          "label": "Title",
          "key": "Title",
          "value": "VP of Operations"
        },
        {
          "id": "f_Ds4pR9",
          "type": "date",
          "label": "Date signed",
          "key": "Date signed",
          "value": "2026-10-01"
        },
        {
          "id": "f_Ne1xC5",
          "type": "text",
          "label": "Email for notices",
          "key": "Email for notices",
          "value": "legal@brightline.example"
        },
        {
          "id": "f_Em6hB0",
          "type": "number",
          "label": "Number of employees",
          "key": "Number of employees",
          "value": 240
        },
        {
          "id": "f_Rg2vJ7",
          "type": "dropdown",
          "label": "Region",
          "key": "Region",
          "value": "North America"
        },
        {
          "id": "f_Pd8sK3",
          "type": "dateInput",
          "label": "Disclosures start",
          "key": "Disclosures start",
          "value": "2026-10-15"
        },
        {
          "id": "f_Cb5mT1",
          "type": "checkbox",
          "label": "Share with affiliates",
          "key": "Share with affiliates",
          "value": false
        },
        {
          "id": "f_Fl9qW4",
          "type": "file",
          "label": "Certificate of insurance",
          "key": "Certificate of insurance",
          "value": "Brightline COI 2026.pdf",
          "files": [
            {
              "id": "sf_Hq2cV8xWm4Lp0aTz9Ke",
              "filename": "Brightline COI 2026.pdf",
              "size": 186412,
              "contentType": "application/pdf",
              "sha256": "9b2f4c0e7a1d3e5f8c6b9a0d2e4f6a8c1b3d5e7f9a0c2e4b6d8f0a1c3e5b7d9f",
              "uploadedAt": "2026-10-01T17:43:30.000Z",
              "url": "https://app.tallysign.com/api/v1/documents/doc_3Hn8wLc2Tz/files/sf_Hq2cV8xWm4Lp0aTz9Ke"
            }
          ]
        }
      ],
      "fieldValues": {
        "Full name": "Dana Whitfield",
        "Title": "VP of Operations",
        "Date signed": "2026-10-01",
        "Email for notices": "legal@brightline.example",
        "Number of employees": 240,
        "Region": "North America",
        "Disclosures start": "2026-10-15",
        "Share with affiliates": false,
        "Certificate of insurance": "Brightline COI 2026.pdf"
      }
    }
  ],
  "recipient": {
    "id": "rcp_Bl9dWh2K",
    "role": "client",
    "name": "Dana Whitfield",
    "email": "dana@brightline.example",
    "company": "Brightline Logistics",
    "type": "signer",
    "status": "signed",
    "signedAt": "2026-10-01T17:44:09.000Z",
    "isSender": false,
    "declinedReason": null,
    "fields": [
      {
        "id": "f_Nm3kQ8",
        "type": "name",
        "label": "Full name",
        "key": "Full name",
        "value": "Dana Whitfield"
      },
      {
        "id": "f_Tt7wL2",
        "type": "title",
        "label": "Title",
        "key": "Title",
        "value": "VP of Operations"
      },
      {
        "id": "f_Ds4pR9",
        "type": "date",
        "label": "Date signed",
        "key": "Date signed",
        "value": "2026-10-01"
      },
      {
        "id": "f_Ne1xC5",
        "type": "text",
        "label": "Email for notices",
        "key": "Email for notices",
        "value": "legal@brightline.example"
      },
      {
        "id": "f_Em6hB0",
        "type": "number",
        "label": "Number of employees",
        "key": "Number of employees",
        "value": 240
      },
      {
        "id": "f_Rg2vJ7",
        "type": "dropdown",
        "label": "Region",
        "key": "Region",
        "value": "North America"
      },
      {
        "id": "f_Pd8sK3",
        "type": "dateInput",
        "label": "Disclosures start",
        "key": "Disclosures start",
        "value": "2026-10-15"
      },
      {
        "id": "f_Cb5mT1",
        "type": "checkbox",
        "label": "Share with affiliates",
        "key": "Share with affiliates",
        "value": false
      },
      {
        "id": "f_Fl9qW4",
        "type": "file",
        "label": "Certificate of insurance",
        "key": "Certificate of insurance",
        "value": "Brightline COI 2026.pdf",
        "files": [
          {
            "id": "sf_Hq2cV8xWm4Lp0aTz9Ke",
            "filename": "Brightline COI 2026.pdf",
            "size": 186412,
            "contentType": "application/pdf",
            "sha256": "9b2f4c0e7a1d3e5f8c6b9a0d2e4f6a8c1b3d5e7f9a0c2e4b6d8f0a1c3e5b7d9f",
            "uploadedAt": "2026-10-01T17:43:30.000Z",
            "url": "https://app.tallysign.com/api/v1/documents/doc_3Hn8wLc2Tz/files/sf_Hq2cV8xWm4Lp0aTz9Ke"
          }
        ]
      }
    ],
    "fieldValues": {
      "Full name": "Dana Whitfield",
      "Title": "VP of Operations",
      "Date signed": "2026-10-01",
      "Email for notices": "legal@brightline.example",
      "Number of employees": 240,
      "Region": "North America",
      "Disclosures start": "2026-10-15",
      "Share with affiliates": false,
      "Certificate of insurance": "Brightline COI 2026.pdf"
    }
  }
}
```

### Check the signature

X-Tally-Signature is `sha256=` followed by the HMAC-SHA256 of the raw body with your subscription's secret, in hex. Compute it over the bytes you received, before parsing, and compare in constant time.

Node.js

```
import { createHmac, timingSafeEqual } from "node:crypto";

// rawBody: the request body exactly as received (a Buffer or string), before JSON parsing.
function isFromTallySign(rawBody, signatureHeader, secret) {
  const expected = "sha256=" + createHmac("sha256", secret).update(rawBody).digest("hex");
  const got = Buffer.from(signatureHeader ?? "");
  return got.length === expected.length && timingSafeEqual(got, Buffer.from(expected));
}
```

Python

```
import hashlib, hmac

def is_from_tally_sign(raw_body: bytes, signature_header: str, secret: str) -> bool:
    expected = "sha256=" + hmac.new(secret.encode(), raw_body, hashlib.sha256).hexdigest()
    return hmac.compare_digest(expected, signature_header or "")
```

## Errors — one shape for all of them

The HTTP status says what kind of problem it is, and the body says which one, in words you can show a person.

```
{
  "error": {
    "code": "not_ready",
    "message": "It can't be sent yet: Dana Whitfield: add a valid email address.",
    "details": {
      "issues": [
        "Dana Whitfield: add a valid email address."
      ]
    }
  }
}
```

| Code | Status | Meaning |
| --- | --- | --- |
| `invalid_request` | 400 | The request isn't valid: a missing or wrong field (`details.fields` says which), bad JSON or an unknown filter. |
| `unauthorized` | 401 | No API key, or it isn't valid any more (revoked, or its person was removed). |
| `billing_paused` | 402 | Sending is paused for your company's account. Drafts can still be made; an admin can resolve it in Settings → Billing. |
| `forbidden` | 403 | The key can't do this (e.g. only owners' and admins' keys subscribe to webhooks), or the account is suspended. |
| `not_found` | 404 | No such document, template, signer or subscription in your company. |
| `conflict` | 409 | The document isn't in a state that allows it (e.g. voiding a completed document), or the same Idempotency-Key is still being worked on. |
| `not_deletable` | 409 | Only drafts and voided documents can be deleted. |
| `not_ready` | 422 | It can't be sent yet. `details.issues` lists what's missing (a signer's email, a variable). |
| `idempotency_key_reused` | 422 | This Idempotency-Key was already used for a different request. |
| `too_soon` | 429 | That signer was reminded within the last hour. |
| `rate_limited` | 429 | Over 300 requests a minute for this key. Wait for Retry-After seconds. |
| `server_error` | 500 | Something went wrong on our side. It's logged; try again. |

## Limits and conventions — so retries and paging just work

### 300 requests a minute

Per key. Every answer has X-RateLimit-Limit, X-RateLimit-Remaining and X-RateLimit-Reset; past the limit you get 429 with Retry-After.

### Pages with a cursor

Lists answer with data, hasMore and nextCursor. Pass nextCursor as cursor for the next page. Documents come most recently changed first.

### Safe retries

Send an Idempotency-Key header (a UUID) when creating, sending, reminding, voiding or subscribing. A retry within 24 hours gets the first answer back.

### JSON in, JSON out

Field names are camelCase. Times are ISO 8601 in UTC. Unknown fields may be added to answers, so don't fail on them.

### No limit on documents

Every plan sends as many documents as you need. The rate limit stops runaway loops, not real work.

### Versioned

Everything lives under /api/v1. Changes that could break your code come as a new version, never in this one.

No code

### Zapier and Make

Zapier and Make work with Tally Sign today through webhooks and the REST API: catch an event when a document is signed, or call the API to send one.

In Zapier, use Webhooks by Zapier: Catch Hook as the trigger, and subscribe its URL to the events you want. To send a document, call POST /api/v1/documents with Webhooks by Zapier: Custom Request and your API key.

AI apps

### ChatGPT, Claude and MCP

For AI apps there's a built-in MCP server at `/api/mcp`. ChatGPT and Claude connect with a sign-in; Claude Code, Cursor and other MCP clients use the same API keys as this API.

[Connect an AI app](https://www.tallysign.com/ai)

## Questions

### Does the API cost extra?

No. It's part of both plans, and API calls, documents and webhooks aren't metered.

### Can I make templates through the API?

Not today. Make and edit templates in Tally Sign, where you see the page as it will print, then create documents from them through the API. AI apps connected over MCP can draft documents block by block.

### Can my customers sign inside my own product?

Yes. Create and send the document, then ask for that signer's signing link and open it in their browser. Each link handed out is recorded in the audit trail. Keep the link to that person: whoever opens it can sign as them.

### Is a document sent through the API the same as one sent from the app?

Yes. The same checks, emails, reminders, automations, audit trail and Certificate of Completion.

### Where's the OpenAPI document?

At https://app.tallysign.com/api/v1/openapi.json. It's OpenAPI 3.1, generated from the same definitions the API checks requests with, so it's always current.

### How do I test without emailing real people?

Use your own addresses as the signers while you build. Every email and signature is real, so use documents you're happy to void, and delete drafts and voided ones when you're done.

## Step-by-step guides

Tally Sign Support walks through each part with screenshots of the real product.

[All guides](https://www.tallysign.com/support?view=guides)

- [Use the REST API](https://www.tallysign.com/support/ai/use-the-rest-api): AI · Create an API key, then create documents from your templates, send them, follow each signer, get what they filled in, download the signed PDF and get webhooks, from your own systems, Zapier or Make.
- [Use Tally Sign from Claude or ChatGPT](https://www.tallysign.com/support/ai/use-with-claude-or-chatgpt): AI · Connect your AI app once, then ask for NDAs, SOWs or the order form for a Salesforce quote in plain English.
- [Recipe: post to Slack when a deal is signed](https://www.tallysign.com/support/automation/slack-recipe): Automation · A record-triggered Flow on Envelope that uses Salesforce's Send Slack Message action.
- [Security and your data](https://www.tallysign.com/support/admin/security-and-your-data): Admin · Where your documents are stored, encryption, the audit trail, signer access codes, sign-in options and how long data is kept.

Source: https://www.tallysign.com/developers
