RESTful API for eSignatures, payments, and customer management. Authenticate with your API key and start building.
All API requests require an x-api-key header. Your API key follows the format:
Generate API keys from Admin → API Settings in your Sign-n-Pay dashboard.
All request bodies should be JSON with the Content-Type: application/json header.
All responses follow a standard structure:
{
"success": true,
"message": "Operation completed successfully",
"data": { ... }
}200/201Success400Bad request — invalid parameters401Unauthorized — invalid or missing API key404Resource not found409Conflict — resource already exists429Rate limit exceeded500Server errorAPI requests are limited to 100 requests per minute per API key.
/v1/external/customersCreate a new customer. Returns 409 if a customer with the same email already exists (with existingCustomerId in the response).
| Name | Type | Required | Description |
|---|---|---|---|
name | string | Yes | Customer's full name |
email | string | Yes | Customer's email address |
phoneNumber | string | No | Customer's phone number |
{
"name": "John Doe",
"email": "john@example.com",
"phoneNumber": "555-123-4567"
}{
"success": true,
"message": "Customer created successfully",
"data": {
"id": "507f1f77bcf86cd799439011",
"name": "John Doe",
"email": "john@example.com",
"phoneNumber": "555-123-4567",
"internalCustomerId": "CUS-abc123"
}
}/v1/external/customersList all customers with optional filtering.
| Name | Type | Required | Description |
|---|---|---|---|
limit | number | No | Max results (default: 100, max: 500) |
offset | number | No | Skip this many results |
search | string | No | Search by name or email |
{
"success": true,
"message": "Found 25 customers",
"data": {
"customers": [...],
"pagination": { "limit": 100, "offset": 0, "count": 25 }
}
}/v1/external/customers/:idGet customer details by ID, including card-on-file status (requires the payments-api entitlement to be populated).
{
"success": true,
"message": "Customer found",
"data": {
"id": "507f1f77bcf86cd799439011",
"name": "John Doe",
"email": "john@example.com",
"emails": ["john@example.com"],
"phoneNumber": "555-123-4567",
"phoneNumbers": ["555-123-4567"],
"internalCustomerId": "CUS-abc123",
"hasCardOnFile": true,
"cardOnFile": {
"hasCardOnFile": true,
"brand": "Visa",
"last4": "4242",
"expiration": "12/2028",
"consentCaptured": true
}
}
}Packages must be configured in the Sign-n-Pay admin UI before they can be sent via API. This includes setting up pricing, templates, and subscription terms.
/v1/external/packagesList all available packages configured for your tenant.
{
"success": true,
"message": "Packages retrieved",
"data": {
"packages": [
{
"id": "pkg123",
"title": "Monthly Subscription",
"description": "Standard monthly plan",
"subscriptionCost": 99.00,
"setupCost": 0
}
]
}
}/v1/external/packages/:packageId/sendSend a package to a customer. Creates a pending subscription, invoice, and sends the e-sign agreement. The subscription starts in 'pending' status until payment is received.
| Name | Type | Required | Description |
|---|---|---|---|
customerId | string | Yes | Customer ID to send the package to |
emailInvoice | boolean | No | Send email notification (default: true) |
ccEmails | string[] | No | Additional email addresses to CC |
customMergeTagValues | object | No | Custom values for merge tags in the template |
{
"customerId": "507f1f77bcf86cd799439011",
"emailInvoice": true,
"ccEmails": ["manager@company.com"],
"customMergeTagValues": {
"companyName": "Acme Corp",
"contractDate": "01/20/2026"
}
}{
"success": true,
"message": "Package sent successfully",
"data": {
"subscriptionId": "sub123",
"invoiceId": "inv456",
"invoiceUrl": "https://sign-n-pay.com/tenant/cust123/invoice/inv456",
"signingRequestId": "req789",
"signingUrl": "https://sign-n-pay.com/sign?token=abc123",
"customerId": "507f1f77bcf86cd799439011",
"packageId": "pkg123"
}
}Create and manage e-sign templates via API or the Sign-n-Pay admin UI. Templates define the document, signature fields, and signer roles.
/v1/external/esign/templatesList all available e-sign templates with their configured signers and merge tags.
{
"success": true,
"message": "Templates retrieved",
"data": {
"templates": [
{
"id": "template123",
"name": "Standard NDA",
"description": "Non-disclosure agreement",
"signers": [
{ "id": "signer1", "role": "customer", "label": "Client", "signingOrder": 1 }
],
"mergeTags": [
{ "tagName": "customer_name" },
{ "tagName": "contract_date" }
]
}
]
}
}Upload documents via presigned S3 URLs. Supports PDF and DOCX (auto-converted to PDF). Create templates programmatically with signers and fields.
/v1/external/esign/templatesCreate a template and get a presigned S3 URL to upload the file (valid 15 minutes). For multi-file uploads, pass a files array.
| Name | Type | Required | Description |
|---|---|---|---|
name | string | Yes | Template name |
fileName | string | Yes | Original file name |
fileSize | number | Yes | Size in bytes (max 300MB) |
fileType | string | Yes | "pdf" or "docx" |
isOneTime | boolean | No | If true, hidden from template list |
{
"name": "My Contract",
"fileName": "contract.pdf",
"fileSize": 245000,
"fileType": "pdf"
}{
"success": true,
"data": {
"templateId": "abc123",
"uploadUrl": "https://s3.amazonaws.com/...",
"s3Key": "templates/tenant/abc123/original.pdf"
}
}/v1/external/esign/templates/:id/finalize-uploadFinalize after uploading to S3. Converts DOCX to PDF if needed. For multi-file, pass fileKeys to merge. Returns previewUrl for rendering the merged PDF in a field placement UI.
{
"success": true,
"data": {
"id": "abc123",
"name": "My Contract",
"fileType": "pdf",
"isActive": true,
"previewUrl": "https://s3.amazonaws.com/...",
"pageCount": 4,
"signers": [...],
"nextSteps": {
"addSigners": "POST /v1/external/esign/templates/abc123/signers",
"addFields": "POST /v1/external/esign/templates/abc123/fields",
"createRequest": "POST /v1/external/esign/requests"
}
}
}/v1/external/esign/templates/:id/reorder-pagesReorder pages in the template PDF. Updates all field and merge-tag page numbers. Returns a new previewUrl so the user can preview the reordered document before sending.
| Name | Type | Required | Description |
|---|---|---|---|
pageOrder | number[] | Yes | Array of 1-based page numbers in desired order, e.g. [3, 1, 2]. Must include every page exactly once. |
{
"success": true,
"data": {
"id": "abc123",
"pageCount": 3,
"previewUrl": "https://s3.amazonaws.com/...",
"fields": [{ "id": "f1", "fieldType": "signature", "pageNumber": 2, ... }],
"signers": [{ "id": "s1", "label": "Signer 1", ... }]
}
}/v1/external/esign/templates/:id/signersAdd a signer role to a template (e.g., Buyer, Seller).
| Name | Type | Required | Description |
|---|---|---|---|
role | string | Yes | Role identifier |
label | string | Yes | Display name |
order | number | No | Signing order |
color | string | No | Hex color for UI |
/v1/external/esign/templates/:id/fieldsPlace a signature, text, date, or checkbox field on a template page.
| Name | Type | Required | Description |
|---|---|---|---|
signerId | string | Yes | Template signer ID |
fieldType | string | Yes | signature, initials, date, text, checkbox, merge_tag |
pageNumber | number | Yes | 1-based page number |
xPercent | number | Yes | X position (0-100%) |
yPercent | number | Yes | Y position (0-100%) |
widthPercent | number | Yes | Width (0-100%) |
heightPercent | number | Yes | Height (0-100%) |
/v1/external/esign/requestsCreate a signing request from a template. All signers receive links immediately (parallel signing). Only the first signer is required; additional signers are optional.
| Name | Type | Required | Description |
|---|---|---|---|
templateId | string | Yes | Template to use |
name | string | Yes | Name for this signing request |
signers | array | Yes | Map real people to template signer roles |
mergeTagValues | object | No | Values for merge tags |
customerId | string | No | Associate with a customer |
sendNotification | boolean | No | Send email to all signers (default: true) |
expiresAt | string | No | ISO date for expiration (default: 30 days) |
{
"templateId": "template123",
"name": "NDA - Acme Corp",
"signers": [
{ "templateSignerId": "signer_1", "name": "Alice", "email": "alice@acme.com" },
{ "templateSignerId": "signer_2", "name": "Bob", "email": "bob@vendor.com" }
],
"mergeTagValues": { "customer_name": "Acme Corp" },
"sendNotification": true
}{
"success": true,
"data": {
"signingRequestId": "req123",
"status": "pending",
"signerLinks": [
{ "signerId": "inst_1", "name": "Alice", "email": "alice@acme.com", "signingUrl": "https://...", "status": "pending" },
{ "signerId": "inst_2", "name": "Bob", "email": "bob@vendor.com", "signingUrl": "https://...", "status": "pending" }
]
}
}/v1/external/esign/sendSend an e-sign agreement to one or more signers. Creates a signing request and optionally sends email notifications.
| Name | Type | Required | Description |
|---|---|---|---|
templateId | string | Yes | ID of the e-sign template |
name | string | Yes | Name for this signing request |
signers | array | Yes | Array of signer mappings (see example) |
mergeTagValues | object | No | Values for merge tags in the template |
customerId | string | No | Associate with a customer |
sendNotification | boolean | No | Send email notification (default: true) |
{
"templateId": "template123",
"name": "NDA - John Doe",
"signers": [
{
"templateSignerId": "signer1",
"name": "John Doe",
"email": "john@example.com"
}
],
"mergeTagValues": {
"customer_name": "John Doe",
"contract_date": "January 20, 2026"
},
"customerId": "507f1f77bcf86cd799439011",
"sendNotification": true
}{
"success": true,
"message": "E-sign agreement sent successfully",
"data": {
"signingRequestId": "req123",
"name": "NDA - John Doe",
"status": "pending",
"signerLinks": [
{
"signerId": "signer456",
"name": "John Doe",
"email": "john@example.com",
"signingUrl": "https://sign-n-pay.com/sign?token=xyz789",
"status": "pending"
}
],
"customerId": "507f1f77bcf86cd799439011",
"expiresAt": "2026-02-20T00:00:00.000Z",
"createdAt": "2026-01-21T00:00:00.000Z"
}
}/v1/external/esign/requests/:idGet the status of a signing request, including signer completion status and download URL.
{
"success": true,
"message": "Signing request retrieved",
"data": {
"signingRequest": {
"id": "req123",
"name": "NDA - John Doe",
"status": "completed",
"completedAt": "2026-01-20T15:30:00Z",
"signers": [
{
"name": "John Doe",
"email": "john@example.com",
"status": "completed",
"signedAt": "2026-01-20T15:30:00Z"
}
],
"documentUrl": "https://sign-n-pay.com/download/doc123"
}
}
}/v1/external/esign/send-documentUpload a PDF and send for signature in one call. Creates a one-time document (not saved as template). Ideal for documents that don't need to be reused.
| Name | Type | Required | Description |
|---|---|---|---|
document | string | Yes | Base64-encoded PDF file (max 50MB) |
fileName | string | Yes | Original file name (must end in .pdf) |
name | string | Yes | Name for this signing request |
signers | array | Yes | Array of signers with name and email |
customerId | string | No | Associate with a customer |
sendNotification | boolean | No | Send email notification (default: true) |
expiresInDays | number | No | Days until expiration (default: 30, max: 90) |
signaturePosition | object | No | Custom signature field position |
{
"document": "JVBERi0xLjQK...base64...",
"fileName": "contract.pdf",
"name": "Service Agreement - John Doe",
"signers": [
{ "name": "John Doe", "email": "john@example.com" }
],
"customerId": "507f1f77bcf86cd799439011",
"sendNotification": true
}{
"success": true,
"message": "Document sent for signature successfully",
"data": {
"signingRequestId": "req123",
"name": "Service Agreement - John Doe",
"status": "pending",
"signerLinks": [
{
"signerId": "signer456",
"name": "John Doe",
"email": "john@example.com",
"signingUrl": "https://sign-n-pay.com/sign?token=xyz789",
"status": "pending"
}
],
"isOneTimeDocument": true,
"expiresAt": "2026-02-20T00:00:00.000Z"
}
}/v1/external/esign/requests/:id/downloadGet a fresh download URL for the signed document. Can be called multiple times to generate new URLs.
| Name | Type | Required | Description |
|---|---|---|---|
expiresIn | number | No | URL expiration in seconds (default: 3600, max: 604800) |
{
"success": true,
"message": "Download URL generated successfully",
"data": {
"signingRequestId": "req123",
"name": "NDA - John Doe",
"status": "completed",
"completedAt": "2026-01-20T15:30:00Z",
"downloadUrl": "https://s3.amazonaws.com/...",
"expiresIn": 3600,
"expiresAt": "2026-01-21T16:30:00Z"
}
}Use this workflow when you want to place signature fields from your own UI. Upload a document, add fields with precise positioning, then send for signature.
/v1/external/esign/documentsUpload a PDF document for field placement. Creates a draft document that can have fields added before sending.
| Name | Type | Required | Description |
|---|---|---|---|
document | string | Yes | Base64-encoded PDF file (max 50MB) |
fileName | string | Yes | Original file name (must end in .pdf) |
name | string | Yes | Name for this document |
signers | array | No | Define signers (can add emails later when sending) |
{
"document": "JVBERi0xLjQK...base64...",
"fileName": "contract.pdf",
"name": "Service Agreement",
"signers": [
{ "name": "Client" },
{ "name": "Company Representative" }
]
}{
"success": true,
"message": "Document uploaded successfully. Add fields and then send.",
"data": {
"documentId": "doc123",
"name": "Service Agreement",
"status": "draft",
"pageCount": 5,
"signers": [
{ "id": "signer1", "index": 0, "name": "Client", "color": "#3B82F6" },
{ "id": "signer2", "index": 1, "name": "Company Representative", "color": "#10B981" }
],
"previewUrl": "https://s3.amazonaws.com/...",
"expiresAt": "2026-01-22T00:00:00Z",
"nextSteps": {
"addFields": "POST /v1/external/esign/documents/doc123/fields",
"send": "POST /v1/external/esign/documents/doc123/send"
}
}
}/v1/external/esign/documents/:idGet document details including current fields and signers.
{
"success": true,
"message": "Document retrieved",
"data": {
"documentId": "doc123",
"name": "Service Agreement",
"status": "draft",
"fileName": "contract.pdf",
"fileSize": 245789,
"createdAt": "2026-01-21T00:00:00Z",
"signers": [
{ "id": "signer1", "name": "Client", "role": "Signer 1", "color": "#3B82F6", "signingOrder": 1 }
],
"fields": [
{ "id": "field1", "type": "signature", "signerId": "signer1", "pageNumber": 1, "x": 55, "y": 75, "width": 25, "height": 6 }
],
"fieldsCount": 1,
"previewUrl": "https://s3.amazonaws.com/..."
}
}/v1/external/esign/documents/:idDelete a draft document. Cannot delete documents that have already been sent.
{
"success": true,
"message": "Document deleted",
"data": { "documentId": "doc123" }
}/v1/external/esign/documents/:id/fieldsGet all fields currently placed on the document.
{
"success": true,
"message": "Found 3 fields",
"data": {
"documentId": "doc123",
"fields": [
{ "id": "field1", "type": "signature", "signerId": "signer1", "pageNumber": 1, "x": 55, "y": 75, "width": 25, "height": 6 }
],
"signers": [
{ "id": "signer1", "name": "Client", "color": "#3B82F6", "signingOrder": 1 }
]
}
}/v1/external/esign/documents/:id/fieldsAdd fields to the document. Fields are appended to existing fields.
| Name | Type | Required | Description |
|---|---|---|---|
fields | array | Yes | Array of field objects to add |
{
"fields": [
{
"type": "signature",
"signerId": "signer1",
"pageNumber": 1,
"x": 55,
"y": 75,
"width": 25,
"height": 6,
"label": "Client Signature",
"required": true
},
{
"type": "date",
"signerId": "signer1",
"pageNumber": 1,
"x": 82,
"y": 75,
"width": 15,
"height": 6
}
]
}{
"success": true,
"message": "Added 2 fields",
"data": {
"documentId": "doc123",
"fieldsAdded": 2,
"totalFields": 2,
"fields": [...],
"nextStep": "POST /v1/external/esign/documents/doc123/send"
}
}/v1/external/esign/documents/:id/fieldsReplace all fields on the document with a new set of fields.
| Name | Type | Required | Description |
|---|---|---|---|
fields | array | Yes | Array of field objects (replaces existing) |
{
"fields": [
{
"type": "signature",
"signerId": "signer1",
"pageNumber": 1,
"x": 55,
"y": 80,
"width": 25,
"height": 6
}
]
}{
"success": true,
"message": "Replaced with 1 fields",
"data": {
"documentId": "doc123",
"fieldsAdded": 1,
"totalFields": 1,
"fields": [...]
}
}/v1/external/esign/documents/:id/fieldsRemove all fields from the document.
{
"success": true,
"message": "All fields removed",
"data": { "documentId": "doc123", "fieldsRemoved": 3 }
}/v1/external/esign/documents/:id/sendSend the prepared document for signature. All signers must have email addresses assigned.
| Name | Type | Required | Description |
|---|---|---|---|
signers | array | Yes | Map signer IDs to email addresses |
customerId | string | No | Associate with a customer |
sendNotification | boolean | No | Send email notification (default: true) |
expiresInDays | number | No | Days until expiration (default: 30, max: 90) |
{
"signers": [
{ "signerId": "signer1", "email": "client@example.com" },
{ "signerId": "signer2", "email": "rep@company.com" }
],
"customerId": "cust123",
"sendNotification": true,
"expiresInDays": 30
}{
"success": true,
"message": "Document sent for signature",
"data": {
"signingRequestId": "req123",
"name": "Service Agreement",
"status": "pending",
"signerLinks": [
{
"signerId": "signer456",
"name": "Client",
"email": "client@example.com",
"signingUrl": "https://sign-n-pay.com/sign?token=...",
"status": "pending"
}
],
"customerId": "cust123",
"expiresAt": "2026-02-20T00:00:00Z"
}
}signature — Signature capture fieldinitials — Initials fielddate — Auto-filled date fieldtext — Text input fieldcheckbox — Checkbox field/v1/external/invoicesCreate an invoice for a customer. The invoice is created in NOT_PAID status. By default, an email with the payment link is sent to the customer.
| Name | Type | Required | Description |
|---|---|---|---|
customerId | string | Yes | Customer ID to invoice |
name | string | Yes | Invoice line item name/description |
amount | number | Yes | Total invoice amount |
description | string | No | Additional description |
isOneTimePayment | boolean | No | One-time vs recurring (default: true) |
lineItems | array | No | Detailed line items |
sendEmail | boolean | No | Send invoice email to customer (default: true) |
autoCharge | boolean | No | Create and immediately charge the card on file in one call (default: false). Requires the payments-api entitlement, plus an existing card on file and captured consent — see Payments below. |
{
"customerId": "507f1f77bcf86cd799439011",
"name": "Consulting Services - January",
"amount": 500.00,
"description": "Professional consulting services",
"isOneTimePayment": true,
"sendEmail": true,
"lineItems": [
{
"name": "Consulting",
"description": "10 hours at $50/hr",
"quantity": 10,
"unitPrice": 50.00
}
]
}{
"success": true,
"message": "Invoice created and email sent",
"data": {
"id": "inv123",
"refId": "INV-A1B2C3",
"name": "Consulting Services - January",
"amount": 500.00,
"status": "NOT_PAID",
"customerId": "507f1f77bcf86cd799439011",
"customerName": "John Doe",
"customerEmail": "john@example.com",
"invoiceUrl": "https://sign-n-pay.com/tenant/cust123/invoice/inv123",
"emailSent": true,
"createdAt": "2026-01-21T00:00:00.000Z"
}
}/v1/external/invoicesList all invoices with optional filtering.
| Name | Type | Required | Description |
|---|---|---|---|
limit | number | No | Max results (default: 100) |
offset | number | No | Skip this many results |
customerId | string | No | Filter by customer |
status | string | No | Filter by status (NOT_PAID, PAID, FAILED) |
{
"success": true,
"message": "Found 15 invoices",
"data": {
"invoices": [...],
"pagination": { "limit": 100, "offset": 0, "count": 15 }
}
}/v1/external/invoices/:idGet invoice details by ID.
{
"success": true,
"message": "Invoice retrieved",
"data": {
"invoice": {
"id": "inv123",
"refId": "INV-A1B2C3",
"name": "Consulting Services - January",
"amount": 500.00,
"status": "PAID",
"paidDate": "2026-01-20T12:00:00Z",
"authorizeTransactionId": "txn123",
"customer": {
"id": "cust123",
"name": "John Doe"
}
}
}
}/v1/external/subscriptionsCreate a recurring subscription for a customer. The subscription starts in 'pending' status until the customer makes their first payment. An initial invoice is automatically created.
| Name | Type | Required | Description |
|---|---|---|---|
customerId | string | Yes | Customer ID |
name | string | Yes | Subscription name |
amount | number | Yes | Recurring amount |
cadence | number | No | Billing frequency (default: 30) |
cadenceUnit | string | No | 'days' or 'months' (default: 'days') |
startDate | string | No | First billing date (ISO format) |
packageId | string | No | Associate with a package |
{
"customerId": "507f1f77bcf86cd799439011",
"name": "Monthly Retainer",
"amount": 299.00,
"cadence": 30,
"cadenceUnit": "days",
"startDate": "2026-02-01"
}{
"success": true,
"message": "Subscription created successfully (pending payment)",
"data": {
"id": "sub123",
"refId": "SUB-X1Y2Z3",
"name": "Monthly Retainer",
"amount": 299.00,
"cadence": 30,
"cadenceUnit": "days",
"status": "pending",
"isActive": false,
"customerId": "507f1f77bcf86cd799439011",
"customerName": "John Doe",
"nextPaymentDate": "2026-02-01T00:00:00.000Z",
"billingDayOfMonth": 1,
"invoiceId": "inv456",
"invoiceUrl": "https://sign-n-pay.com/tenant/cust123/invoice/inv456",
"createdAt": "2026-01-21T00:00:00.000Z"
}
}/v1/external/subscriptionsList all subscriptions with optional filtering.
| Name | Type | Required | Description |
|---|---|---|---|
limit | number | No | Max results (default: 100) |
offset | number | No | Skip this many results |
customerId | string | No | Filter by customer |
status | string | No | Filter by status (active, pending, cancelled) |
isActive | boolean | No | Filter by active status |
{
"success": true,
"message": "Found 10 subscriptions",
"data": {
"subscriptions": [...],
"pagination": { "limit": 100, "offset": 0, "count": 10 }
}
}/v1/external/subscriptions/:idGet subscription details by ID.
{
"success": true,
"message": "Subscription retrieved",
"data": {
"subscription": {
"id": "sub123",
"refId": "SUB-X1Y2Z3",
"name": "Monthly Retainer",
"amount": 299.00,
"status": "active",
"isActive": true,
"nextPaymentDate": "2026-02-01T12:00:00Z",
"lastPaymentDate": "2026-01-01T12:00:00Z",
"customer": {
"id": "cust123",
"name": "John Doe"
}
}
}
}/v1/external/subscriptions/:idUpdate a subscription. Use this to modify amount, billing day, or deactivate.
| Name | Type | Required | Description |
|---|---|---|---|
name | string | No | Updated subscription name |
amount | number | No | Updated recurring amount |
isActive | boolean | No | Activate/deactivate subscription |
{
"amount": 349.00,
"name": "Premium Monthly Retainer"
}{
"success": true,
"message": "Subscription updated",
"data": {
"subscription": {
"id": "sub123",
"name": "Premium Monthly Retainer",
"amount": 349.00,
"status": "active"
}
}
}/v1/external/subscriptions/:idCancel a subscription. This stops future billing but does not issue refunds.
{
"success": true,
"message": "Subscription canceled",
"data": {
"subscription": {
"id": "sub123",
"status": "canceled",
"isActive": false,
"canceledAt": "2026-01-20T15:00:00Z"
}
}
}Save a card on file on a hosted page, then charge it from your side — as a one-off charge, against an existing invoice, or automatically when creating a new invoice.
403 until it's enabled — contact support to turn it on for your tenant.Card entry happens entirely on our hosted page, so your servers never receive or store the card number.
/v1/external/customers/:id/card-setup-linkGenerate a signed, expiring link to our hosted page where the customer can securely save a card on file.
| Name | Type | Required | Description |
|---|---|---|---|
expiresInDays | number | No | Link lifetime in days (1–30, default: 7) |
{
"expiresInDays": 7
}{
"success": true,
"message": "Card-setup link created",
"data": {
"setupUrl": "https://sign-n-pay.com/acme-corp/507f1f77bcf86cd799439011/update-card?setup_token=...",
"customerId": "507f1f77bcf86cd799439011",
"expiresAt": "2026-01-28T00:00:00.000Z"
}
}Once saved, GET /v1/external/customers/:id (see Customers above) returns a cardOnFile object with hasCardOnFile, brand, last4, expiration, and consentCaptured.
Charges are merchant-initiated. The customer must already have a card on file with consent captured, or these return 409.
/v1/external/customers/:id/chargeCharge the customer's card on file for an arbitrary amount. Creates a one-time invoice and charges it in the same call.
| Name | Type | Required | Description |
|---|---|---|---|
amount | number | Yes | Amount to charge (must be greater than 0) |
description | string | No | Shown on the invoice created for this charge |
reference | string | No | Your own reference ID, echoed back in the response and webhook |
{
"amount": 150.00,
"description": "Change order — extra hours",
"reference": "order-4471"
}{
"success": true,
"message": "Payment succeeded.",
"data": {
"invoiceId": "inv789",
"transactionId": "txn123",
"status": "PAID",
"amount": 150.00,
"livemode": true,
"reference": "order-4471",
"customerId": "507f1f77bcf86cd799439011",
"invoiceUrl": "https://sign-n-pay.com/tenant/cust123/invoice/inv789"
}
}/v1/external/invoices/:id/chargeSettle an existing invoice by charging the customer's card on file. Rejects already-paid invoices and guards against a concurrent double-charge on the same invoice.
{
"success": true,
"message": "Invoice paid.",
"data": {
"invoiceId": "inv456",
"transactionId": "txn124",
"status": "PAID",
"amount": 500.00,
"livemode": true,
"reference": null,
"customerId": "507f1f77bcf86cd799439011"
}
}autoCharge: true to POST /v1/external/invoices (see Invoices above) to create and charge an invoice in one call. It fails fast with 409 before creating the invoice if there's no chargeable card on file or captured consent.Send an Idempotency-Key header on any charge request to make retries safe:
ReplaySame key + same request body — the original response is replayed exactly, no second charge409Same key while the original request is still processing (concurrent duplicate)422Same key reused with a different request bodyGenerate a separate TEST API key from Admin → API Settings (visible once payments is enabled) — format {tenant-slug}--test--{api-key}. Requests authenticated with a TEST key run charges in simulate-only mode: no gateway call is made, no money moves, and the response comes back with livemode: false. Use it to dry-run card setup and charging before going live.
Configure a webhook URL to receive real-time notifications when signing or payment events occur. All payloads include an X-Webhook-Signature header (HMAC-SHA256) for verification.
/v1/external/webhook-configGet current webhook configuration.
{
"success": true,
"data": {
"webhookUrl": "https://your-app.com/webhooks/esign",
"hasSecret": true,
"events": [
"document.sent",
"document.viewed",
"signer.completed",
"document.completed",
"document.declined",
"document.expired",
"charge.succeeded",
"charge.failed",
"invoice.paid",
"card.saved"
]
}
}/v1/external/webhook-configConfigure webhook URL and secret for receiving e-sign and payment event notifications.
| Name | Type | Required | Description |
|---|---|---|---|
webhookUrl | string | No | URL to receive webhooks (null to disable) |
regenerateSecret | boolean | No | Generate new webhook signing secret |
{
"webhookUrl": "https://your-app.com/webhooks/esign",
"regenerateSecret": true
}{
"success": true,
"data": {
"webhookUrl": "https://your-app.com/webhooks/esign",
"hasSecret": true,
"secret": "abc123...newSecret...",
"events": ["document.sent", "signer.completed", "document.completed", "charge.succeeded", "invoice.paid", ...]
}
}document.sentSigning request created and sentdocument.viewedSigner viewed the documentsigner.completedA signer completed their signaturedocument.completedAll signers completed (includes signedDocumentUrl)document.declinedA signer declined to signdocument.expiredSigning request expiredcharge.succeededA card-on-file charge succeededcharge.failedA card-on-file charge failedinvoice.paidAn invoice was marked paidcard.savedA customer saved a card on file{
"event": "charge.succeeded",
"timestamp": "2026-01-20T15:30:00Z",
"data": {
"invoiceId": "inv789",
"customerId": "cust789",
"amount": 150.00,
"transactionId": "txn123",
"reference": "order-4471",
"livemode": true,
"status": "succeeded"
}
}{
"event": "document.completed",
"timestamp": "2026-01-20T15:30:00Z",
"data": {
"signingRequestId": "req123",
"signingRequestName": "NDA - John Doe",
"templateId": "tpl456",
"status": "completed",
"customerId": "cust789",
"signedDocumentUrl": "https://s3.amazonaws.com/...",
"completedAt": "2026-01-20T15:30:00Z"
}
}X-Webhook-Signature header.