01Overview
All endpoints are served over HTTPS from https://validature.app/api/v1. Requests and responses use JSON (UTF-8), except the OAuth token endpoints, which take form-encoded bodies, and file downloads. Identifiers are UUIDs and dates are ISO 8601 timestamps in UTC.
API access, including OAuth, is part of the Business Pro plan. On other plans, every call returns 409 with the message commercial_feature_unavailable.
Questions or client registration: contact@validature.com. A machine-readable description is available as OpenAPI 3.1.
02Authentication
The API uses OAuth 2.0 with the authorization code grant, so users never share their password with your application. Each OAuth client is registered by Validature: write to us with your application name and redirect URIs to receive a client ID and secret.
- Send the user to
GET /oauth/authorizewith yourclient_id,redirect_uri, thescopeyou need and a randomstate. - The user signs in and approves the listed permissions. Validature redirects to your
redirect_uriwith acode. - Exchange the code at
POST /oauth/tokenfor an access token and a refresh token. - Call the API with
Authorization: Bearer <access_token>. When a call returns 401, or beforeexpires_inruns out, get a new access token with the refresh token.
curl -X POST https://validature.app/api/v1/oauth/token \ -u "$CLIENT_ID:$CLIENT_SECRET" \ -d grant_type=authorization_code \ -d code="$CODE" \ -d redirect_uri="https://example.com/oauth/callback" curl https://validature.app/api/v1/auth/me \ -H "Authorization: Bearer $ACCESS_TOKEN"
Tokens act for the user who approved them, with that user's role: owners and administrators reach the whole organization, other members reach what they created.
03Scopes
Ask only for the scopes you need: the consent screen lists them in plain words.
| contacts:read | Read the address book. |
| contacts:write | Create and update contacts. |
| requests:read | Read templates and signature requests, and download completed files. |
| requests:write | Create, send, remind and cancel signature requests. |
| documents:read | Receive download links of signed documents in request.completed events. |
| webhooks:manage | Subscribe to and unsubscribe from events. |
04Errors and limits
Errors return an HTTP status and a JSON body. code, when present, is stable and meant for programs:
{
"statusCode": 400,
"error": "Bad Request",
"code": "zapier_template_recipient_incomplete",
"message": "Add the email address, first name and last name of \"Employee\".",
"timestamp": "2026-09-24T10:30:00.000Z",
"requestId": "6fd4f8a7-cebe-48c8-a726-c3ce89cae49a"
}| 400 | The request is invalid: check message and code. |
| 401 | The access token is missing, expired or revoked: refresh it. |
| 403 | The token lacks a scope (error: invalid_scope, with required_scopes), or the user lacks the permission. |
| 404 | Not found, or not visible to the connected user. |
| 409 | The resource is not in a state that allows the action, or the plan does not include API access (commercial_feature_unavailable). |
| 429 | Too many requests: wait, then retry. The token endpoint accepts 20 requests per minute. |
| 5xx | Temporary server error: retry with exponential backoff. |
Include the requestId of the response when you contact support.
05OAuth 2.0
Validature uses the OAuth 2.0 authorization code grant. Clients are registered by Validature: contact us to receive a client ID and secret for your integration.
Ask the user for access
GET /oauth/authorize
Opened in the user's browser
Redirect the user's browser here. Validature signs the user in if needed, shows a consent screen listing the requested permissions, then redirects to redirect_uri with code and state (or error=access_denied).
Query parameters
| response_typerequired | string | Must be code.One of: code |
| client_idrequired | string | Your client ID. |
| redirect_urirequired | string | One of the redirect URIs registered for your client. |
| staterequired | string | Opaque value returned unchanged; use it to prevent CSRF. |
| scope | string | Space-separated scopes. Defaults to the scopes allowed for your client. |
| code_challenge | string | PKCE challenge (optional for confidential clients). |
| code_challenge_method | string | S256 when code_challenge is sent.One of: S256 |
Responses
302 Redirect to redirect_uri?code=…&state=…. The code expires after a few minutes and can be used once.
Get or refresh an access token
POST /oauth/token
OAuth client credentials
Exchange an authorization code, or a refresh token, for an access token. Authenticate the client with HTTP Basic (client_secret_basic) or with client_id and client_secret in the body (client_secret_post). Access tokens are short-lived: read expires_in and refresh before it runs out, or when a call returns 401. Refresh tokens are long-lived and rotate: each refresh returns a new refresh token and invalidates the previous one; reusing an old one revokes the whole chain.
Body (application/x-www-form-urlencoded)
| grant_typerequired | string | authorization_code or refresh_token.One of: authorization_code, refresh_token |
| code | string | The authorization code (authorization_code grant). |
| redirect_uri | string | The same redirect_uri as in the authorization request (authorization_code grant). |
| refresh_token | string | The refresh token (refresh_token grant). |
| client_id | string | Your client ID, unless sent with HTTP Basic. |
| client_secret | string | Your client secret, unless sent with HTTP Basic. |
| code_verifier | string | PKCE verifier, when a code_challenge was sent. |
Responses
200 Tokens issued.
{
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9…",
"token_type": "Bearer",
"expires_in": 3600,
"refresh_token": "vrt_2b9c…",
"scope": "contacts:read contacts:write requests:read requests:write documents:read webhooks:manage"
}400 invalid_grant (expired, used or revoked code or refresh token) or invalid_request.
{
"error": "invalid_grant",
"error_description": "Refresh token revoked"
}401 invalid_client: unknown client or wrong secret.
{
"error": "invalid_client"
}Revoke a token
POST /oauth/revoke
OAuth client credentials
Revoke a refresh token (and the access it grants). Authenticate the client as for /oauth/token.
Body (application/x-www-form-urlencoded)
| tokenrequired | string | The token to revoke. |
| token_type_hint | string | refresh_token or access_token. |
Responses
200 Revoked (also returned for unknown tokens).
{
"revoked": true
}Authorization server metadata
GET /oauth/.well-known/oauth-authorization-server
No authentication
RFC 8414 metadata: endpoints, supported grants, scopes and client authentication methods.
Responses
200 Metadata document.
06Account
Get the connected user
GET /auth/me
Bearer access token
Returns the user the access token acts for. Use it to test a connection and to label it.
Responses
200 The user.
{
"id": "daa9e01b-c7ef-4dde-b72e-b001c3646f32",
"email": "olivia.owner@example.com",
"firstName": "Olivia",
"lastName": "Owner",
"phone": null,
"language": "en",
"role": "owner",
"organizationId": "b7e1d2c3-4a5b-4c6d-8e9f-0a1b2c3d4e5f"
}07Contacts
The address book of the connected user. Owners and administrators see the whole organization's contacts; other members see their own.
List or search contacts
GET /contacts
Bearer access token · Scopes: contacts:read
Returns contacts matching search (name or email), 100 at most per page. Entries whose id starts with user: are members of the organization and cannot be edited.
Query parameters
| search | string | Text to find in the name or email (100 characters max). |
| limit | integer | Page size, 1 to 100 (default 100). |
| offset | integer | Number of contacts to skip, 0 to 10,000 (default 0). |
Responses
200 A page of contacts.
{
"data": [
{
"id": "4c6e8a0b-2d4f-4e6a-8c0e-2a4c6e8a0c2e",
"firstName": "Camille",
"lastName": "Martin",
"email": "camille.martin@example.com",
"phone": "+33 6 12 34 56 78",
"language": "fr",
"createdAt": "2026-09-24T10:30:00.000Z",
"updatedAt": "2026-09-24T10:30:00.000Z"
}
],
"total": 1,
"limit": 100,
"offset": 0,
"hasMore": false
}Create or update a contact
POST /contacts
Bearer access token · Scopes: contacts:write
Creates a contact, or updates the contact that already uses this email address. Fields you omit are left unchanged.
Body (application/json)
| emailrequired | string | Email address (254 characters max). |
| firstName | string | First name (100 characters max). |
| lastName | string | Last name (100 characters max). |
| phone | string | Phone number in international format, for example +33 6 12 34 56 78. |
| language | string | Preferred language for emails, for example fr or en. |
{
"email": "camille.martin@example.com",
"firstName": "Camille",
"lastName": "Martin",
"language": "fr"
}Responses
201 The contact, created or updated.
{
"id": "4c6e8a0b-2d4f-4e6a-8c0e-2a4c6e8a0c2e",
"firstName": "Camille",
"lastName": "Martin",
"email": "camille.martin@example.com",
"phone": null,
"language": "fr",
"createdAt": "2026-09-24T10:30:00.000Z",
"updatedAt": "2026-09-24T10:30:00.000Z"
}409 contact_email_unavailable: the email belongs to a contact you cannot access.
08Templates
Reusable documents with signature fields and recipient roles. A role is either fixed (its recipient is stored in the template, and can be overridden) or variable (you provide the recipient when sending).
List templates
GET /zapier/templates
Bearer access token · Scopes: requests:read
Returns the templates the connected user can use, optionally filtered by name.
Query parameters
| search | string | Text to find in the template name. |
Responses
200 Templates.
[
{
"id": "3f8a2c1d-7e6b-4d5c-9a8b-1c2d3e4f5a6b",
"name": "Employment contract",
"description": "Standard employment contract",
"signingOrder": "sequential",
"createdAt": "2026-09-01T08:00:00.000Z",
"updatedAt": "2026-09-20T08:00:00.000Z",
"recipients": [
{
"roleKey": "hr",
"label": "HR manager",
"role": "signer",
"fixed": true,
"email": "hr@example.com",
"firstName": "Hana",
"lastName": "Rossi",
"phone": null,
"language": "en"
},
{
"roleKey": "employee",
"label": "Employee",
"role": "signer",
"fixed": false,
"email": null,
"firstName": null,
"lastName": null,
"phone": null,
"language": null
}
]
}
]Get a template and its recipient roles
GET /zapier/templates/{templateId}
Bearer access token · Scopes: requests:read
Path parameters
| templateIdrequired | string (uuid) | Template identifier. |
Responses
200 The template.
{
"id": "3f8a2c1d-7e6b-4d5c-9a8b-1c2d3e4f5a6b",
"name": "Employment contract",
"description": "Standard employment contract",
"signingOrder": "sequential",
"createdAt": "2026-09-01T08:00:00.000Z",
"updatedAt": "2026-09-20T08:00:00.000Z",
"recipients": [
{
"roleKey": "hr",
"label": "HR manager",
"role": "signer",
"fixed": true,
"email": "hr@example.com",
"firstName": "Hana",
"lastName": "Rossi",
"phone": null,
"language": "en"
},
{
"roleKey": "employee",
"label": "Employee",
"role": "signer",
"fixed": false,
"email": null,
"firstName": null,
"lastName": null,
"phone": null,
"language": null
}
]
}09Signature requests
A signature request sends documents to recipients (signers, approvers, observers). Its status is one of: draft, prepared, sent, ongoing, completed, declined, expired, canceled. A request past its deadline is reported expired, and one a recipient declined is reported declined. Owners and administrators access every request of the organization; other members access the requests they created. Zero-access (Vault) requests are created in the Validature app only, because their documents are encrypted in the sender's browser: the API reports their status and recipients, with the title Document sécurisé and no documents.
Create a signature request from a template
POST /zapier/signature-requests/from-template
Bearer access token · Scopes: requests:write
Creates a request from a template, fills its recipient roles and sends it (or keeps it as a draft with send: false). Each role needs an email address; variable roles also need a first and last name. Two recipients cannot share an email address.
Headers
| Idempotency-Key | string (uuid) | Optional. Repeat the same key to retry safely: the request is performed once. |
Body (application/json)
| templateIdrequired | string (uuid) | Template to use. |
| recipientsrequired | array | One entry per role you fill: roleKey (required), email, firstName, lastName, phone, language. Fixed roles you omit keep the recipient stored in the template. |
| title | string | Request title (255 characters max). Defaults to the template name. |
| message | string | Message shown to recipients (5,000 characters max). |
| emailSubject | string | Subject of the invitation email. |
| signatureLevel | string | SES (simple) or AES (advanced, with identity verification).One of: SES, AES |
| signingOrder | string | parallel or sequential.One of: parallel, sequential |
| expirationDays | integer | Days before the request expires, 1 to 365. |
| reminderFrequency | string | Automatic reminders: daily, weekly or monthly.One of: daily, weekly, monthly |
| reminderMaxCount | integer | Maximum number of automatic reminders, 0 to 12. |
| securityCodeType | string | One-time code before signing: none, email or sms.One of: none, email, sms |
| ccEmails | array of strings | Email addresses that receive a copy of the signed documents (50 max). |
| externalId | string | Your own reference for the request, for example a CRM deal ID (255 characters max). It must be unique: it also becomes the request reference shown in Validature. |
| send | boolean | true (default) sends the request now; false keeps it as a draft. |
{
"templateId": "3f8a2c1d-7e6b-4d5c-9a8b-1c2d3e4f5a6b",
"recipients": [
{
"roleKey": "employee",
"email": "camille.martin@example.com",
"firstName": "Camille",
"lastName": "Martin"
}
],
"title": "Employment contract - Camille Martin",
"externalId": "hire-208",
"send": true
}Responses
201 The signature request. dispatch is queued when sending has started, draft otherwise.
{
"id": "5a7c9e1b-3d5f-4b7a-9c1e-3f5a7c9e1b3d",
"title": "Service agreement - Acme",
"status": "sent",
"externalId": "deal-1042",
"signatureLevel": "SES",
"signingOrder": "parallel",
"createdAt": "2026-09-23T16:20:00.000Z",
"sentAt": "2026-09-23T16:25:00.000Z",
"completedAt": null,
"expiresAt": "2026-10-23T21:59:59.999Z",
"url": "https://validature.app/requests/5a7c9e1b-3d5f-4b7a-9c1e-3f5a7c9e1b3d",
"senderEmail": "olivia.owner@example.com",
"senderName": "Olivia Owner",
"recipients": [
{
"id": "9d2f4b6a-1c3e-4a5b-8d7f-2e4c6a8b0d1f",
"email": "camille.martin@example.com",
"firstName": "Camille",
"lastName": "Martin",
"role": "signer",
"status": "signed",
"order": 1,
"notifiedAt": "2026-09-23T16:25:00.000Z",
"viewedAt": "2026-09-24T09:12:00.000Z",
"signedAt": "2026-09-24T09:15:00.000Z",
"declinedAt": null,
"declineReason": null
},
{
"id": "1e3a5c7d-9b2f-4d6e-8a1c-3e5f7a9b1d2c",
"email": "lea.dubois@example.com",
"firstName": "Léa",
"lastName": "Dubois",
"role": "signer",
"status": "notified",
"order": 2,
"notifiedAt": "2026-09-23T16:25:00.000Z",
"viewedAt": null,
"signedAt": null,
"declinedAt": null,
"declineReason": null
}
],
"documents": [
{
"id": "8c1d2e3f-4a5b-4c6d-9e8f-7a6b5c4d3e2f",
"name": "service-agreement.pdf",
"version": "original"
}
],
"dispatch": "queued"
}400 Invalid input, for example zapier_template_recipient_incomplete or zapier_template_recipient_duplicate_email.
{
"statusCode": 400,
"error": "Bad Request",
"code": "zapier_template_recipient_incomplete",
"message": "Add the email address, first name and last name of \"Employee\"."
}409 The externalId is already used by another request (zapier_external_reference_taken). Nothing was created or sent.
{
"statusCode": 409,
"error": "Conflict",
"code": "zapier_external_reference_taken",
"message": "This external reference is already used by another signature request. Use a unique reference, or leave it empty.",
"timestamp": "2026-09-24T10:30:00.000Z",
"requestId": "6fd4f8a7-cebe-48c8-a726-c3ce89cae49a"
}Find signature requests
GET /zapier/signature-requests
Bearer access token · Scopes: requests:read
Returns the most recent requests matching every criterion given, newest first.
Query parameters
| externalId | string | Your reference, exact match. |
| recipientEmail | string | A recipient's email address (case-insensitive). |
| search | string | Text to find in the title. |
| status | string | Only requests in this status. One of: draft, prepared, sent, ongoing, completed, declined, expired, canceled |
| limit | integer | 1 to 100 (default 10). |
Responses
200 Signature requests.
[
{
"id": "5a7c9e1b-3d5f-4b7a-9c1e-3f5a7c9e1b3d",
"title": "Service agreement - Acme",
"status": "ongoing",
"externalId": "deal-1042",
"signatureLevel": "SES",
"signingOrder": "parallel",
"createdAt": "2026-09-23T16:20:00.000Z",
"sentAt": "2026-09-23T16:25:00.000Z",
"completedAt": null,
"expiresAt": "2026-10-23T21:59:59.999Z",
"url": "https://validature.app/requests/5a7c9e1b-3d5f-4b7a-9c1e-3f5a7c9e1b3d",
"senderEmail": "olivia.owner@example.com",
"senderName": "Olivia Owner",
"recipients": [
{
"id": "9d2f4b6a-1c3e-4a5b-8d7f-2e4c6a8b0d1f",
"email": "camille.martin@example.com",
"firstName": "Camille",
"lastName": "Martin",
"role": "signer",
"status": "signed",
"order": 1,
"notifiedAt": "2026-09-23T16:25:00.000Z",
"viewedAt": "2026-09-24T09:12:00.000Z",
"signedAt": "2026-09-24T09:15:00.000Z",
"declinedAt": null,
"declineReason": null
},
{
"id": "1e3a5c7d-9b2f-4d6e-8a1c-3e5f7a9b1d2c",
"email": "lea.dubois@example.com",
"firstName": "Léa",
"lastName": "Dubois",
"role": "signer",
"status": "notified",
"order": 2,
"notifiedAt": "2026-09-23T16:25:00.000Z",
"viewedAt": null,
"signedAt": null,
"declinedAt": null,
"declineReason": null
}
],
"documents": [
{
"id": "8c1d2e3f-4a5b-4c6d-9e8f-7a6b5c4d3e2f",
"name": "service-agreement.pdf",
"version": "original"
}
]
}
]Get a signature request
GET /zapier/signature-requests/{requestId}
Bearer access token · Scopes: requests:read
Path parameters
| requestIdrequired | string (uuid) | Signature request identifier. |
Responses
200 The signature request.
{
"id": "5a7c9e1b-3d5f-4b7a-9c1e-3f5a7c9e1b3d",
"title": "Service agreement - Acme",
"status": "ongoing",
"externalId": "deal-1042",
"signatureLevel": "SES",
"signingOrder": "parallel",
"createdAt": "2026-09-23T16:20:00.000Z",
"sentAt": "2026-09-23T16:25:00.000Z",
"completedAt": null,
"expiresAt": "2026-10-23T21:59:59.999Z",
"url": "https://validature.app/requests/5a7c9e1b-3d5f-4b7a-9c1e-3f5a7c9e1b3d",
"senderEmail": "olivia.owner@example.com",
"senderName": "Olivia Owner",
"recipients": [
{
"id": "9d2f4b6a-1c3e-4a5b-8d7f-2e4c6a8b0d1f",
"email": "camille.martin@example.com",
"firstName": "Camille",
"lastName": "Martin",
"role": "signer",
"status": "signed",
"order": 1,
"notifiedAt": "2026-09-23T16:25:00.000Z",
"viewedAt": "2026-09-24T09:12:00.000Z",
"signedAt": "2026-09-24T09:15:00.000Z",
"declinedAt": null,
"declineReason": null
},
{
"id": "1e3a5c7d-9b2f-4d6e-8a1c-3e5f7a9b1d2c",
"email": "lea.dubois@example.com",
"firstName": "Léa",
"lastName": "Dubois",
"role": "signer",
"status": "notified",
"order": 2,
"notifiedAt": "2026-09-23T16:25:00.000Z",
"viewedAt": null,
"signedAt": null,
"declinedAt": null,
"declineReason": null
}
],
"documents": [
{
"id": "8c1d2e3f-4a5b-4c6d-9e8f-7a6b5c4d3e2f",
"name": "service-agreement.pdf",
"version": "original"
}
]
}Send a draft
POST /zapier/signature-requests/{requestId}/send
Bearer access token · Scopes: requests:write
Sends a request created as a draft. Fails with 409 if the request is no longer a draft.
Path parameters
| requestIdrequired | string (uuid) | Signature request identifier. |
Headers
| Idempotency-Key | string (uuid) | Optional. Repeat the same key to retry safely: the request is performed once. |
Responses
201 The signature request, now sent.
{
"id": "5a7c9e1b-3d5f-4b7a-9c1e-3f5a7c9e1b3d",
"title": "Service agreement - Acme",
"status": "sent",
"externalId": "deal-1042",
"signatureLevel": "SES",
"signingOrder": "parallel",
"createdAt": "2026-09-23T16:20:00.000Z",
"sentAt": "2026-09-23T16:25:00.000Z",
"completedAt": null,
"expiresAt": "2026-10-23T21:59:59.999Z",
"url": "https://validature.app/requests/5a7c9e1b-3d5f-4b7a-9c1e-3f5a7c9e1b3d",
"senderEmail": "olivia.owner@example.com",
"senderName": "Olivia Owner",
"recipients": [
{
"id": "9d2f4b6a-1c3e-4a5b-8d7f-2e4c6a8b0d1f",
"email": "camille.martin@example.com",
"firstName": "Camille",
"lastName": "Martin",
"role": "signer",
"status": "signed",
"order": 1,
"notifiedAt": "2026-09-23T16:25:00.000Z",
"viewedAt": "2026-09-24T09:12:00.000Z",
"signedAt": "2026-09-24T09:15:00.000Z",
"declinedAt": null,
"declineReason": null
},
{
"id": "1e3a5c7d-9b2f-4d6e-8a1c-3e5f7a9b1d2c",
"email": "lea.dubois@example.com",
"firstName": "Léa",
"lastName": "Dubois",
"role": "signer",
"status": "notified",
"order": 2,
"notifiedAt": "2026-09-23T16:25:00.000Z",
"viewedAt": null,
"signedAt": null,
"declinedAt": null,
"declineReason": null
}
],
"documents": [
{
"id": "8c1d2e3f-4a5b-4c6d-9e8f-7a6b5c4d3e2f",
"name": "service-agreement.pdf",
"version": "original"
}
]
}Remind recipients
POST /zapier/signature-requests/{requestId}/remind
Bearer access token · Scopes: requests:write
Sends a reminder to every recipient who still has to sign or approve. Recipients who could not be reminded are listed in skipped with a reason. Fails with 409 when nobody is waiting (zapier_reminder_nobody_waiting) or when no reminder could be sent (zapier_reminder_not_sent).
Path parameters
| requestIdrequired | string (uuid) | Signature request identifier. |
Headers
| Idempotency-Key | string (uuid) | Optional. Repeat the same key to retry safely: the request is performed once. |
Responses
201 Who was reminded.
{
"requestId": "5a7c9e1b-3d5f-4b7a-9c1e-3f5a7c9e1b3d",
"reminded": [
{
"recipientId": "1e3a5c7d-9b2f-4d6e-8a1c-3e5f7a9b1d2c",
"email": "lea.dubois@example.com",
"status": "queued"
}
],
"skipped": []
}Cancel a signature request
POST /zapier/signature-requests/{requestId}/cancel
Bearer access token · Scopes: requests:write
Cancels the request; recipients can no longer sign it.
Path parameters
| requestIdrequired | string (uuid) | Signature request identifier. |
Responses
201 The signature request, canceled.
{
"id": "5a7c9e1b-3d5f-4b7a-9c1e-3f5a7c9e1b3d",
"title": "Service agreement - Acme",
"status": "canceled",
"externalId": "deal-1042",
"signatureLevel": "SES",
"signingOrder": "parallel",
"createdAt": "2026-09-23T16:20:00.000Z",
"sentAt": "2026-09-23T16:25:00.000Z",
"completedAt": null,
"expiresAt": "2026-10-23T21:59:59.999Z",
"url": "https://validature.app/requests/5a7c9e1b-3d5f-4b7a-9c1e-3f5a7c9e1b3d",
"senderEmail": "olivia.owner@example.com",
"senderName": "Olivia Owner",
"recipients": [
{
"id": "9d2f4b6a-1c3e-4a5b-8d7f-2e4c6a8b0d1f",
"email": "camille.martin@example.com",
"firstName": "Camille",
"lastName": "Martin",
"role": "signer",
"status": "signed",
"order": 1,
"notifiedAt": "2026-09-23T16:25:00.000Z",
"viewedAt": "2026-09-24T09:12:00.000Z",
"signedAt": "2026-09-24T09:15:00.000Z",
"declinedAt": null,
"declineReason": null
},
{
"id": "1e3a5c7d-9b2f-4d6e-8a1c-3e5f7a9b1d2c",
"email": "lea.dubois@example.com",
"firstName": "Léa",
"lastName": "Dubois",
"role": "signer",
"status": "notified",
"order": 2,
"notifiedAt": "2026-09-23T16:25:00.000Z",
"viewedAt": null,
"signedAt": null,
"declinedAt": null,
"declineReason": null
}
],
"documents": [
{
"id": "8c1d2e3f-4a5b-4c6d-9e8f-7a6b5c4d3e2f",
"name": "service-agreement.pdf",
"version": "original"
}
]
}10Signed files
Downloads of a completed request. Zero-access (Vault) documents stay encrypted and cannot be downloaded through the API.
Download the signed package
GET /requests/{requestId}/download-bundle
Bearer access token · Scopes: requests:read
A ZIP archive with the signed documents and the signature certificate. Available once the request is completed.
Path parameters
| requestIdrequired | string (uuid) | Signature request identifier. |
Responses
200 application/zip file.
400 The request is not completed.
Download one document
GET /requests/{requestId}/documents/{documentId}/download
Bearer access token · Scopes: requests:read
The signed version once the request is completed (see documents[].version).
Path parameters
| requestIdrequired | string (uuid) | Signature request identifier. |
| documentIdrequired | string (uuid) | Document identifier, from documents[].id. |
Responses
200 The file, usually application/pdf.
Download the signature certificate
GET /requests/{requestId}/certificate
Bearer access token · Scopes: requests:read
The PDF audit trail: recipients, authentication, timestamps and events.
Path parameters
| requestIdrequired | string (uuid) | Signature request identifier. |
Query parameters
| download | string | Set to 1 to receive the file as an attachment. |
Responses
200 application/pdf file.
11Webhooks
Subscribe a URL to receive events as they happen. Subscriptions belong to the connected user: they receive the events of the contacts and requests this user can access, and stop when the user loses access.
- Validature sends each event as
POSTwith a JSON body (see Events) and the headersX-Validature-Event(the event name) andX-Validature-Delivery-Id. Answer with any 2xx status within 10 seconds; redirects are not followed. 408,429and5xxanswers, and timeouts, are retried up to 6 times with an exponential backoff starting at 30 seconds.404and410answers disable the subscription; other4xxanswers are not retried.- A delivery can be repeated: use the event
idto ignore duplicates. - Events are delivered within seconds; transitions read from the audit trail (sent, viewed, declined, canceled, expired, status changed) within about 15 seconds.
- Target URLs must be public HTTPS addresses. Webhook requests are not signed: use a secret, hard-to-guess URL and treat the payload as a notification to verify with the API when it matters.
Subscribe to an event
POST /zapier/hooks/{hook}/subscribe
Bearer access token · Scopes: webhooks:manage · Plus the scopes of the event (see Events).
Validature will POST each new event to targetUrl. Subscribing the same URL again reactivates it.
Path parameters
| hookrequired | string | The event to subscribe to (see Events). One of: contact-created, signer-done, request-done, request-sent, request-status-changed, signer-viewed, signer-declined, request-canceled, request-expired |
Body (application/json)
| targetUrlrequired | string | Public HTTPS URL that receives the events. |
{
"targetUrl": "https://hooks.example.com/validature/7d1f…"
}Responses
201 The subscription.
{
"id": "0b6e1c2d-3f4a-4b5c-8d9e-0f1a2b3c4d5e",
"event": "request.sent",
"targetUrl": "https://hooks.example.com/validature/7d1f…",
"active": true
}Unsubscribe
POST /zapier/hooks/{hook}/unsubscribe
Bearer access token · Scopes: webhooks:manage · Plus the scopes of the event (see Events).
Path parameters
| hookrequired | string | The event to subscribe to (see Events). One of: contact-created, signer-done, request-done, request-sent, request-status-changed, signer-viewed, signer-declined, request-canceled, request-expired |
Body (application/json)
| targetUrlrequired | string | The URL given when subscribing. |
Responses
201 Done (also returned when no such subscription exists).
{
"unsubscribed": true
}List recent events
GET /zapier/hooks/{hook}/poll
Bearer access token · The scopes of the event (see Events).
Returns the most recent events of this type, newest first, in the same shape as the webhook payloads. Use it to backfill or to test an integration.
Path parameters
| hookrequired | string | The event to subscribe to (see Events). One of: contact-created, signer-done, request-done, request-sent, request-status-changed, signer-viewed, signer-declined, request-canceled, request-expired |
Query parameters
| limit | integer | 1 to 100 (default 20). |
| createdAfter | signedAfter | completedAfter | occurredAfter | string (ISO 8601) | Only events after this time. The parameter depends on the event: createdAfter for contact-created, signedAfter for signer-done, completedAfter for request-done, occurredAfter for the others. |
| statuses | string | request-status-changed only: comma-separated transitions to keep (sent, completed, declined, canceled, expired). |
Responses
200 Events.
[
{
"id": "5a7c9e1b-3d5f-4b7a-9c1e-3f5a7c9e1b3d",
"eventType": "request.sent",
"occurredAt": "2026-09-24T10:30:00.000Z",
"eventStatus": "sent",
"requestId": "5a7c9e1b-3d5f-4b7a-9c1e-3f5a7c9e1b3d",
"organizationId": "b7e1d2c3-4a5b-4c6d-8e9f-0a1b2c3d4e5f",
"title": "Service agreement - Acme",
"status": "sent",
"externalId": "deal-1042",
"signatureLevel": "SES",
"signingOrder": "parallel",
"createdAt": "2026-09-23T16:20:00.000Z",
"sentAt": "2026-09-23T16:25:00.000Z",
"completedAt": null,
"expiresAt": "2026-10-23T21:59:59.999Z",
"url": "https://validature.app/requests/5a7c9e1b-3d5f-4b7a-9c1e-3f5a7c9e1b3d",
"senderEmail": "olivia.owner@example.com",
"senderName": "Olivia Owner",
"recipients": [
{
"id": "9d2f4b6a-1c3e-4a5b-8d7f-2e4c6a8b0d1f",
"email": "camille.martin@example.com",
"firstName": "Camille",
"lastName": "Martin",
"role": "signer",
"status": "signed",
"order": 1,
"notifiedAt": "2026-09-23T16:25:00.000Z",
"viewedAt": "2026-09-24T09:12:00.000Z",
"signedAt": "2026-09-24T09:15:00.000Z",
"declinedAt": null,
"declineReason": null
},
{
"id": "1e3a5c7d-9b2f-4d6e-8a1c-3e5f7a9b1d2c",
"email": "lea.dubois@example.com",
"firstName": "Léa",
"lastName": "Dubois",
"role": "signer",
"status": "notified",
"order": 2,
"notifiedAt": "2026-09-23T16:25:00.000Z",
"viewedAt": null,
"signedAt": null,
"declinedAt": null,
"declineReason": null
}
]
}
]12Events
Subscribe with POST /zapier/hooks/{hook}/subscribe, or list recent events with GET /zapier/hooks/{hook}/poll. Each payload has an id, an eventType and an occurredAt timestamp.
contact.created
Hook contact-created · Scopes: contacts:read · Poll cursor: createdAfter
A contact is added to the address book of the connected user.
{
"id": "4c6e8a0b-2d4f-4e6a-8c0e-2a4c6e8a0c2e",
"eventType": "contact.created",
"occurredAt": "2026-09-24T10:30:00.000Z",
"contactId": "4c6e8a0b-2d4f-4e6a-8c0e-2a4c6e8a0c2e",
"organizationId": "b7e1d2c3-4a5b-4c6d-8e9f-0a1b2c3d4e5f",
"firstName": "Camille",
"lastName": "Martin",
"email": "camille.martin@example.com",
"phone": "+33 6 12 34 56 78",
"language": "fr",
"createdAt": "2026-09-24T10:30:00.000Z",
"updatedAt": "2026-09-24T10:30:00.000Z"
}participant.signed
Hook signer-done · Scopes: requests:read · Poll cursor: signedAfter
A recipient signs a signature request. Not sent for zero-access (Vault) requests.
{
"id": "9d2f4b6a-1c3e-4a5b-8d7f-2e4c6a8b0d1f",
"eventType": "participant.signed",
"occurredAt": "2026-09-24T09:15:00.000Z",
"participantId": "9d2f4b6a-1c3e-4a5b-8d7f-2e4c6a8b0d1f",
"requestId": "5a7c9e1b-3d5f-4b7a-9c1e-3f5a7c9e1b3d",
"requestTitle": "Service agreement - Acme",
"organizationId": "b7e1d2c3-4a5b-4c6d-8e9f-0a1b2c3d4e5f",
"role": "signer",
"email": "camille.martin@example.com",
"firstName": "Camille",
"lastName": "Martin",
"signedAt": "2026-09-24T09:15:00.000Z",
"requestStatus": "ongoing",
"requestCompletedAt": null,
"requestExternalId": "deal-1042",
"requestUrl": "https://validature.app/requests/5a7c9e1b-3d5f-4b7a-9c1e-3f5a7c9e1b3d"
}request.completed
Hook request-done · Scopes: requests:read, documents:read · Poll cursor: completedAfter
Every recipient has signed or approved: the signed documents are ready. downloadUrl links are valid for 24 hours. Not sent for zero-access (Vault) requests.
{
"id": "5a7c9e1b-3d5f-4b7a-9c1e-3f5a7c9e1b3d",
"eventType": "request.completed",
"occurredAt": "2026-09-24T11:00:00.000Z",
"requestId": "5a7c9e1b-3d5f-4b7a-9c1e-3f5a7c9e1b3d",
"organizationId": "b7e1d2c3-4a5b-4c6d-8e9f-0a1b2c3d4e5f",
"title": "Service agreement - Acme",
"status": "completed",
"createdAt": "2026-09-23T16:20:00.000Z",
"sentAt": "2026-09-23T16:25:00.000Z",
"completedAt": "2026-09-24T11:00:00.000Z",
"externalId": "deal-1042",
"url": "https://validature.app/requests/5a7c9e1b-3d5f-4b7a-9c1e-3f5a7c9e1b3d",
"recipients": [
{
"id": "9d2f4b6a-1c3e-4a5b-8d7f-2e4c6a8b0d1f",
"email": "camille.martin@example.com",
"firstName": "Camille",
"lastName": "Martin",
"role": "signer",
"status": "signed",
"order": 1,
"notifiedAt": "2026-09-23T16:25:00.000Z",
"viewedAt": "2026-09-24T09:12:00.000Z",
"signedAt": "2026-09-24T09:15:00.000Z",
"declinedAt": null,
"declineReason": null
},
{
"id": "1e3a5c7d-9b2f-4d6e-8a1c-3e5f7a9b1d2c",
"email": "lea.dubois@example.com",
"firstName": "Léa",
"lastName": "Dubois",
"role": "signer",
"status": "signed",
"order": 2,
"notifiedAt": "2026-09-23T16:25:00.000Z",
"viewedAt": "2026-09-24T10:55:00.000Z",
"signedAt": "2026-09-24T11:00:00.000Z",
"declinedAt": null,
"declineReason": null
}
],
"signedDocuments": [
{
"id": "8c1d2e3f-4a5b-4c6d-9e8f-7a6b5c4d3e2f",
"name": "service-agreement.pdf",
"mimeType": "application/pdf",
"downloadUrl": "https://storage.example/signed/service-agreement.pdf?signature=…"
}
]
}request.sent
Hook request-sent · Scopes: requests:read · Poll cursor: occurredAfter
A signature request is sent to its recipients.
{
"id": "5a7c9e1b-3d5f-4b7a-9c1e-3f5a7c9e1b3d",
"eventType": "request.sent",
"occurredAt": "2026-09-24T10:30:00.000Z",
"eventStatus": "sent",
"requestId": "5a7c9e1b-3d5f-4b7a-9c1e-3f5a7c9e1b3d",
"organizationId": "b7e1d2c3-4a5b-4c6d-8e9f-0a1b2c3d4e5f",
"title": "Service agreement - Acme",
"status": "sent",
"externalId": "deal-1042",
"signatureLevel": "SES",
"signingOrder": "parallel",
"createdAt": "2026-09-23T16:20:00.000Z",
"sentAt": "2026-09-23T16:25:00.000Z",
"completedAt": null,
"expiresAt": "2026-10-23T21:59:59.999Z",
"url": "https://validature.app/requests/5a7c9e1b-3d5f-4b7a-9c1e-3f5a7c9e1b3d",
"senderEmail": "olivia.owner@example.com",
"senderName": "Olivia Owner",
"recipients": [
{
"id": "9d2f4b6a-1c3e-4a5b-8d7f-2e4c6a8b0d1f",
"email": "camille.martin@example.com",
"firstName": "Camille",
"lastName": "Martin",
"role": "signer",
"status": "signed",
"order": 1,
"notifiedAt": "2026-09-23T16:25:00.000Z",
"viewedAt": "2026-09-24T09:12:00.000Z",
"signedAt": "2026-09-24T09:15:00.000Z",
"declinedAt": null,
"declineReason": null
},
{
"id": "1e3a5c7d-9b2f-4d6e-8a1c-3e5f7a9b1d2c",
"email": "lea.dubois@example.com",
"firstName": "Léa",
"lastName": "Dubois",
"role": "signer",
"status": "notified",
"order": 2,
"notifiedAt": "2026-09-23T16:25:00.000Z",
"viewedAt": null,
"signedAt": null,
"declinedAt": null,
"declineReason": null
}
]
}request.status_changed
Hook request-status-changed · Scopes: requests:read · Poll cursor: occurredAfter
A signature request is sent, completed, declined, canceled or expires. eventStatus names the transition; filter it with the statuses query parameter when polling. The completed transition is not reported for zero-access (Vault) requests.
{
"id": "5a7c9e1b-3d5f-4b7a-9c1e-3f5a7c9e1b3d",
"eventType": "request.status_changed",
"occurredAt": "2026-09-24T10:30:00.000Z",
"eventStatus": "completed",
"requestId": "5a7c9e1b-3d5f-4b7a-9c1e-3f5a7c9e1b3d",
"organizationId": "b7e1d2c3-4a5b-4c6d-8e9f-0a1b2c3d4e5f",
"title": "Service agreement - Acme",
"status": "completed",
"externalId": "deal-1042",
"signatureLevel": "SES",
"signingOrder": "parallel",
"createdAt": "2026-09-23T16:20:00.000Z",
"sentAt": "2026-09-23T16:25:00.000Z",
"completedAt": "2026-09-24T11:00:00.000Z",
"expiresAt": "2026-10-23T21:59:59.999Z",
"url": "https://validature.app/requests/5a7c9e1b-3d5f-4b7a-9c1e-3f5a7c9e1b3d",
"senderEmail": "olivia.owner@example.com",
"senderName": "Olivia Owner",
"recipients": [
{
"id": "9d2f4b6a-1c3e-4a5b-8d7f-2e4c6a8b0d1f",
"email": "camille.martin@example.com",
"firstName": "Camille",
"lastName": "Martin",
"role": "signer",
"status": "signed",
"order": 1,
"notifiedAt": "2026-09-23T16:25:00.000Z",
"viewedAt": "2026-09-24T09:12:00.000Z",
"signedAt": "2026-09-24T09:15:00.000Z",
"declinedAt": null,
"declineReason": null
},
{
"id": "1e3a5c7d-9b2f-4d6e-8a1c-3e5f7a9b1d2c",
"email": "lea.dubois@example.com",
"firstName": "Léa",
"lastName": "Dubois",
"role": "signer",
"status": "notified",
"order": 2,
"notifiedAt": "2026-09-23T16:25:00.000Z",
"viewedAt": null,
"signedAt": null,
"declinedAt": null,
"declineReason": null
}
]
}participant.viewed
Hook signer-viewed · Scopes: requests:read · Poll cursor: occurredAfter
A recipient opens a signature request for the first time.
{
"id": "participant.viewed:1e3a5c7d-9b2f-4d6e-8a1c-3e5f7a9b1d2c",
"email": "lea.dubois@example.com",
"firstName": "Léa",
"lastName": "Dubois",
"role": "signer",
"status": "viewed",
"order": 2,
"notifiedAt": "2026-09-23T16:25:00.000Z",
"viewedAt": "2026-09-24T10:30:00.000Z",
"signedAt": null,
"declinedAt": null,
"declineReason": null,
"eventType": "participant.viewed",
"occurredAt": "2026-09-24T10:30:00.000Z",
"participantId": "1e3a5c7d-9b2f-4d6e-8a1c-3e5f7a9b1d2c",
"requestId": "5a7c9e1b-3d5f-4b7a-9c1e-3f5a7c9e1b3d",
"requestTitle": "Service agreement - Acme",
"requestStatus": "ongoing",
"requestExternalId": "deal-1042",
"requestUrl": "https://validature.app/requests/5a7c9e1b-3d5f-4b7a-9c1e-3f5a7c9e1b3d",
"organizationId": "b7e1d2c3-4a5b-4c6d-8e9f-0a1b2c3d4e5f"
}participant.declined
Hook signer-declined · Scopes: requests:read · Poll cursor: occurredAfter
A recipient declines to sign; the request stops there. declineReason is always null for zero-access (Vault) requests.
{
"id": "participant.declined:1e3a5c7d-9b2f-4d6e-8a1c-3e5f7a9b1d2c",
"email": "lea.dubois@example.com",
"firstName": "Léa",
"lastName": "Dubois",
"role": "signer",
"status": "declined",
"order": 2,
"notifiedAt": "2026-09-23T16:25:00.000Z",
"viewedAt": "2026-09-24T10:25:00.000Z",
"signedAt": null,
"declinedAt": "2026-09-24T10:30:00.000Z",
"declineReason": "Wrong price",
"eventType": "participant.declined",
"occurredAt": "2026-09-24T10:30:00.000Z",
"participantId": "1e3a5c7d-9b2f-4d6e-8a1c-3e5f7a9b1d2c",
"requestId": "5a7c9e1b-3d5f-4b7a-9c1e-3f5a7c9e1b3d",
"requestTitle": "Service agreement - Acme",
"requestStatus": "declined",
"requestExternalId": "deal-1042",
"requestUrl": "https://validature.app/requests/5a7c9e1b-3d5f-4b7a-9c1e-3f5a7c9e1b3d",
"organizationId": "b7e1d2c3-4a5b-4c6d-8e9f-0a1b2c3d4e5f"
}request.canceled
Hook request-canceled · Scopes: requests:read · Poll cursor: occurredAfter
The sender cancels a signature request.
{
"id": "5a7c9e1b-3d5f-4b7a-9c1e-3f5a7c9e1b3d",
"eventType": "request.canceled",
"occurredAt": "2026-09-24T10:30:00.000Z",
"eventStatus": "canceled",
"requestId": "5a7c9e1b-3d5f-4b7a-9c1e-3f5a7c9e1b3d",
"organizationId": "b7e1d2c3-4a5b-4c6d-8e9f-0a1b2c3d4e5f",
"title": "Service agreement - Acme",
"status": "canceled",
"externalId": "deal-1042",
"signatureLevel": "SES",
"signingOrder": "parallel",
"createdAt": "2026-09-23T16:20:00.000Z",
"sentAt": "2026-09-23T16:25:00.000Z",
"completedAt": null,
"expiresAt": "2026-10-23T21:59:59.999Z",
"url": "https://validature.app/requests/5a7c9e1b-3d5f-4b7a-9c1e-3f5a7c9e1b3d",
"senderEmail": "olivia.owner@example.com",
"senderName": "Olivia Owner",
"recipients": [
{
"id": "9d2f4b6a-1c3e-4a5b-8d7f-2e4c6a8b0d1f",
"email": "camille.martin@example.com",
"firstName": "Camille",
"lastName": "Martin",
"role": "signer",
"status": "signed",
"order": 1,
"notifiedAt": "2026-09-23T16:25:00.000Z",
"viewedAt": "2026-09-24T09:12:00.000Z",
"signedAt": "2026-09-24T09:15:00.000Z",
"declinedAt": null,
"declineReason": null
},
{
"id": "1e3a5c7d-9b2f-4d6e-8a1c-3e5f7a9b1d2c",
"email": "lea.dubois@example.com",
"firstName": "Léa",
"lastName": "Dubois",
"role": "signer",
"status": "notified",
"order": 2,
"notifiedAt": "2026-09-23T16:25:00.000Z",
"viewedAt": null,
"signedAt": null,
"declinedAt": null,
"declineReason": null
}
]
}request.expired
Hook request-expired · Scopes: requests:read · Poll cursor: occurredAfter
A signature request reaches its deadline before every recipient has acted.
{
"id": "5a7c9e1b-3d5f-4b7a-9c1e-3f5a7c9e1b3d",
"eventType": "request.expired",
"occurredAt": "2026-09-24T10:30:00.000Z",
"eventStatus": "expired",
"requestId": "5a7c9e1b-3d5f-4b7a-9c1e-3f5a7c9e1b3d",
"organizationId": "b7e1d2c3-4a5b-4c6d-8e9f-0a1b2c3d4e5f",
"title": "Service agreement - Acme",
"status": "expired",
"externalId": "deal-1042",
"signatureLevel": "SES",
"signingOrder": "parallel",
"createdAt": "2026-09-23T16:20:00.000Z",
"sentAt": "2026-09-23T16:25:00.000Z",
"completedAt": null,
"expiresAt": "2026-09-24T10:30:00.000Z",
"url": "https://validature.app/requests/5a7c9e1b-3d5f-4b7a-9c1e-3f5a7c9e1b3d",
"senderEmail": "olivia.owner@example.com",
"senderName": "Olivia Owner",
"recipients": [
{
"id": "9d2f4b6a-1c3e-4a5b-8d7f-2e4c6a8b0d1f",
"email": "camille.martin@example.com",
"firstName": "Camille",
"lastName": "Martin",
"role": "signer",
"status": "signed",
"order": 1,
"notifiedAt": "2026-09-23T16:25:00.000Z",
"viewedAt": "2026-09-24T09:12:00.000Z",
"signedAt": "2026-09-24T09:15:00.000Z",
"declinedAt": null,
"declineReason": null
},
{
"id": "1e3a5c7d-9b2f-4d6e-8a1c-3e5f7a9b1d2c",
"email": "lea.dubois@example.com",
"firstName": "Léa",
"lastName": "Dubois",
"role": "signer",
"status": "notified",
"order": 2,
"notifiedAt": "2026-09-23T16:25:00.000Z",
"viewedAt": null,
"signedAt": null,
"declinedAt": null,
"declineReason": null
}
]
}




