{"openapi":"3.0.3","info":{"title":"API Sign API","description":"Programmatically create, send, and manage contracts for e-signatures. Contracts are created as drafts and charged when sent ($0.25 per send at the default price; test-mode sends are free). Errors return `{ \"error\": string }`; request validation errors add a `details` array. API key requests are not rate limited.","version":"1.0.0","contact":{"name":"API Sign Support","url":"https://apisign.io"}},"servers":[{"url":"https://apisign.io/api","description":"Production"}],"security":[{"ApiKeyAuth":[]}],"paths":{"/contract/create":{"post":{"summary":"Create a contract","description":"Create a draft contract from a template (`template_id` + `variables`) or from your own markdown (`content` + `fields`). Nothing is emailed or charged until `/contract/send`. The content must contain at least one signer-completed signature field; with more than one signer, every signer needs `signerFields` that include a signature field of their own.","operationId":"createContract","tags":["Contracts"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateContractRequest"}}}},"responses":{"200":{"description":"Draft contract created","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"contract":{"$ref":"#/components/schemas/Contract"},"signers":{"type":"array","items":{"$ref":"#/components/schemas/Signer"}}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"500":{"$ref":"#/components/responses/ServerError"}}}},"/contract/get":{"get":{"summary":"Get a contract","description":"Retrieve a contract with its signers, ordered by `signing_order`. Each signer's `email_delivery` reports whether their latest signing request email was delivered, or is `null` if none was sent (or it was sent before delivery tracking existed).","operationId":"getContract","tags":["Contracts"],"parameters":[{"name":"id","in":"query","required":true,"schema":{"type":"string"},"description":"Contract ID"}],"responses":{"200":{"description":"Contract details","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"contract":{"allOf":[{"$ref":"#/components/schemas/Contract"},{"type":"object","properties":{"signers":{"type":"array","items":{"allOf":[{"$ref":"#/components/schemas/Signer"},{"type":"object","properties":{"email_delivery":{"allOf":[{"$ref":"#/components/schemas/EmailDelivery"}],"nullable":true,"description":"Delivery of this signer's most recent signing request"}}}]}}}}]}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"500":{"$ref":"#/components/responses/ServerError"}}}},"/contract/find":{"get":{"summary":"List contracts","description":"List every contract in the organization, newest first, including test contracts. There are no filter or pagination parameters.","operationId":"listContracts","tags":["Contracts"],"responses":{"200":{"description":"List of contracts","content":{"application/json":{"schema":{"type":"object","properties":{"contracts":{"type":"array","items":{"$ref":"#/components/schemas/ContractSummary"}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"500":{"$ref":"#/components/responses/ServerError"}}}},"/contract/update":{"put":{"summary":"Update a draft contract","description":"Update a contract whose status is `draft`. Only the properties sent are changed. `signers`, when sent, replaces the whole signer list. The merged result must pass the same signature-field checks as create. The expiration date cannot be changed.","operationId":"updateContract","tags":["Contracts"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateContractRequest"}}}},"responses":{"200":{"description":"Contract updated","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"contract":{"$ref":"#/components/schemas/Contract"},"signers":{"type":"array","items":{"$ref":"#/components/schemas/Signer"},"description":"The updated signer list; empty unless `signers` was sent"},"message":{"type":"string"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"500":{"$ref":"#/components/responses/ServerError"}}}},"/contract/send":{"post":{"summary":"Send or resend a contract","description":"Send a `draft` contract, or resend a `sent` one. Every signer is emailed a signing link at the same time. Each send of a live contract is charged the organization's per-contract price (default $0.25) from the account balance; test-mode contracts are free and their emails are redirected to the sender. A resend emails every signer again, resets every signer to `pending` (including those who already signed), and is charged again. The expiration date is not checked.","operationId":"sendContract","tags":["Contracts"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["contract_id"],"properties":{"contract_id":{"type":"string","description":"Contract ID to send (cuid)"}}}}}},"responses":{"200":{"description":"Contract sent","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"contract":{"$ref":"#/components/schemas/Contract"},"signers_notified":{"type":"integer"},"cost_charged":{"type":"number","description":"Amount charged in dollars; 0 for test contracts"},"message":{"type":"string","enum":["Contract sent successfully","Contract resent successfully"]}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"description":"Insufficient balance. Nothing was sent. Example: `{ \"error\": \"Insufficient balance. You need at least $0.25 to send a contract.\" }`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"$ref":"#/components/responses/NotFound"},"500":{"$ref":"#/components/responses/ServerError"}}}},"/contract/cancel":{"post":{"summary":"Cancel a contract","description":"Cancel a `draft` or `sent` contract. Pending signers are set to `declined`. The send charge is not refunded.","operationId":"cancelContract","tags":["Contracts"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["contract_id"],"properties":{"contract_id":{"type":"string","description":"Contract ID to cancel (cuid)"}}}}}},"responses":{"200":{"description":"Contract cancelled","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"contract":{"$ref":"#/components/schemas/Contract"},"message":{"type":"string"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"500":{"$ref":"#/components/responses/ServerError"}}}},"/contract/delete":{"post":{"summary":"Delete a draft contract","description":"Permanently delete a `draft` contract with its signers, activity log and audit log. Other statuses return 400.","operationId":"deleteContract","tags":["Contracts"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["contract_id"],"properties":{"contract_id":{"type":"string","description":"Contract ID to delete (cuid)"}}}}}},"responses":{"200":{"description":"Contract deleted","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"500":{"$ref":"#/components/responses/ServerError"}}}},"/contract/logs":{"get":{"summary":"Get a contract's activity log","description":"Activity log entries for a contract, newest first. `action` is one of created, updated, sent, resent, signed, declined, completed, cancelled.","operationId":"getContractLogs","tags":["Contracts"],"parameters":[{"name":"contract_id","in":"query","required":true,"schema":{"type":"string"},"description":"Contract ID"}],"responses":{"200":{"description":"Log entries","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"logs":{"type":"array","items":{"$ref":"#/components/schemas/ContractLog"}}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"500":{"$ref":"#/components/responses/ServerError"}}}},"/contract/audit-logs":{"get":{"summary":"Get a contract's audit trail","description":"The 50 most recent audit events for a contract, newest first.","operationId":"getContractAuditLogs","tags":["Contracts"],"parameters":[{"name":"contract_id","in":"query","required":true,"schema":{"type":"string"},"description":"Contract ID"}],"responses":{"200":{"description":"Audit events","content":{"application/json":{"schema":{"type":"object","properties":{"auditLogs":{"type":"array","items":{"$ref":"#/components/schemas/ContractAuditLog"}}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"500":{"$ref":"#/components/responses/ServerError"}}}},"/contract/webhook-deliveries":{"get":{"summary":"List webhook deliveries for a contract","description":"Webhook delivery attempts for events on one contract, newest first.","operationId":"getContractWebhookDeliveries","tags":["Contracts"],"parameters":[{"name":"contractId","in":"query","required":true,"schema":{"type":"string"},"description":"Contract ID (camelCase parameter name)"},{"name":"page","in":"query","required":false,"schema":{"type":"integer","default":1,"minimum":1},"description":"Page number"},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":50,"minimum":1},"description":"Results per page"}],"responses":{"200":{"description":"Deliveries","content":{"application/json":{"schema":{"type":"object","properties":{"contract":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"}}},"deliveries":{"type":"array","items":{"allOf":[{"$ref":"#/components/schemas/WebhookDelivery"},{"type":"object","properties":{"webhook":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"url":{"type":"string"}}}}}]}},"pagination":{"type":"object","properties":{"page":{"type":"integer"},"limit":{"type":"integer"},"totalCount":{"type":"integer"},"totalPages":{"type":"integer"},"hasNext":{"type":"boolean"},"hasPrev":{"type":"boolean"}}}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"500":{"$ref":"#/components/responses/ServerError"}}}},"/contract/emails":{"get":{"summary":"Get a contract's emails","description":"Every email sent about a contract, newest first, with its delivery status: signing requests, completed-contract copies, and decline notices.","operationId":"getContractEmails","tags":["Contracts"],"parameters":[{"name":"contract_id","in":"query","required":true,"schema":{"type":"string"},"description":"Contract ID"}],"responses":{"200":{"description":"Emails","content":{"application/json":{"schema":{"type":"object","properties":{"emails":{"type":"array","items":{"$ref":"#/components/schemas/ContractEmail"}}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"500":{"$ref":"#/components/responses/ServerError"}}}},"/contract/convert":{"post":{"summary":"Convert a .docx file to markdown","description":"Convert a Word document to markdown for use as `content` in `/contract/create`. Embedded images are uploaded and replaced with URLs; nothing else is saved.","operationId":"convertDocument","tags":["Contracts"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FileUploadRequest"}}}},"responses":{"200":{"description":"Converted document","content":{"application/json":{"schema":{"type":"object","properties":{"content":{"type":"string","description":"Markdown content"},"filename":{"type":"string","description":"File name without its extension"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"500":{"$ref":"#/components/responses/ServerError"}}}},"/contract/verify":{"get":{"summary":"Verify a signed contract","description":"Public; no API key needed. Checks a signed contract's digital seal. `valid` is true when the contract is signed, sealed, and the seal's certificate serial matches the platform's current certificate. The stored PDF is not re-hashed, so `documentIntact` is always true for a sealed contract. Unsigned or unsealed contracts return 200 with `valid: false` and an `error` string. The response includes signer names and emails.","operationId":"verifyContract","tags":["Contracts"],"security":[],"parameters":[{"name":"id","in":"query","required":true,"schema":{"type":"string"},"description":"Contract ID"}],"responses":{"200":{"description":"Verification result","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VerificationResult"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"404":{"$ref":"#/components/responses/NotFound"},"500":{"description":"Verification failed","content":{"application/json":{"schema":{"type":"object","properties":{"valid":{"type":"boolean"},"error":{"type":"string"},"details":{"type":"string"}}}}}}}}},"/template/create":{"post":{"summary":"Create a template","description":"Create a template. Only `name` and `content` are read; set field definitions with `/template/update`.","operationId":"createTemplate","tags":["Templates"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateTemplateRequest"}}}},"responses":{"200":{"description":"Template created","content":{"application/json":{"schema":{"type":"object","properties":{"template":{"$ref":"#/components/schemas/Template"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"500":{"$ref":"#/components/responses/ServerError"}}}},"/template/get":{"get":{"summary":"Get a template","description":"Retrieve a template by ID, including archived templates. Returns 200 with `template: null` when no template with that ID exists in the organization.","operationId":"getTemplate","tags":["Templates"],"parameters":[{"name":"id","in":"query","required":true,"schema":{"type":"string"},"description":"Template ID"}],"responses":{"200":{"description":"Template details","content":{"application/json":{"schema":{"type":"object","properties":{"template":{"nullable":true,"allOf":[{"type":"object","properties":{"id":{"type":"string"},"created_at":{"type":"string","format":"date-time"},"name":{"type":"string"},"content":{"type":"string","nullable":true},"fields":{"$ref":"#/components/schemas/StoredFields"}}}]}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"500":{"$ref":"#/components/responses/ServerError"}}}},"/template/find":{"get":{"summary":"List templates","description":"List non-archived templates, newest first. There are no filter or pagination parameters.","operationId":"listTemplates","tags":["Templates"],"responses":{"200":{"description":"List of templates","content":{"application/json":{"schema":{"type":"object","properties":{"templates":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"}}}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"500":{"$ref":"#/components/responses/ServerError"}}}},"/template/update":{"post":{"summary":"Update a template","description":"Update a non-archived template. `name` and `content` are required on every call. Unless `content` is empty, at least one variable must resolve to a signer-completed signature field.","operationId":"updateTemplate","tags":["Templates"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateTemplateRequest"}}}},"responses":{"200":{"description":"Template updated","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"template":{"$ref":"#/components/schemas/Template"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"500":{"$ref":"#/components/responses/ServerError"}}}},"/template/archive":{"post":{"summary":"Archive a template","description":"Archive a template (soft delete). Archived templates are excluded from `/template/find` and cannot be updated or used for new contracts.","operationId":"archiveTemplate","tags":["Templates"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["id"],"properties":{"id":{"type":"string","description":"Template ID to archive"}}}}}},"responses":{"200":{"description":"Template archived","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"500":{"$ref":"#/components/responses/ServerError"}}}},"/template/upload":{"post":{"summary":"Create a template from a .docx file","description":"Create a template from a Word document. The template is named after the file and has no field definitions.","operationId":"uploadTemplate","tags":["Templates"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FileUploadRequest"}}}},"responses":{"200":{"description":"Template created","content":{"application/json":{"schema":{"type":"object","properties":{"template":{"$ref":"#/components/schemas/Template"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"500":{"$ref":"#/components/responses/ServerError"}}}},"/webhook/create":{"post":{"summary":"Create a webhook","description":"Create a webhook endpoint. The signing secret is returned only in this response.","operationId":"createWebhook","tags":["Webhooks"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateWebhookRequest"}}}},"responses":{"200":{"description":"Webhook created","content":{"application/json":{"schema":{"type":"object","properties":{"webhook":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"url":{"type":"string","format":"uri"},"events":{"type":"array","items":{"$ref":"#/components/schemas/WebhookEventType"}},"status":{"$ref":"#/components/schemas/WebhookStatus"},"secret":{"type":"string","description":"Signing secret. Shown only once."},"createdAt":{"type":"string","format":"date-time"}}},"message":{"type":"string"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"500":{"$ref":"#/components/responses/ServerError"}}}},"/webhook/list":{"get":{"summary":"List webhooks","description":"List the organization's webhooks, newest first. Secrets are not included.","operationId":"listWebhooks","tags":["Webhooks"],"responses":{"200":{"description":"List of webhooks","content":{"application/json":{"schema":{"type":"object","properties":{"webhooks":{"type":"array","items":{"$ref":"#/components/schemas/Webhook"}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"500":{"$ref":"#/components/responses/ServerError"}}}},"/webhook/get":{"get":{"summary":"Get a webhook","description":"Get a webhook with its 20 most recent deliveries. The secret is replaced by `hasSecret`.","operationId":"getWebhook","tags":["Webhooks"],"parameters":[{"name":"id","in":"query","required":true,"schema":{"type":"string"},"description":"Webhook ID"}],"responses":{"200":{"description":"Webhook details","content":{"application/json":{"schema":{"type":"object","properties":{"webhook":{"allOf":[{"$ref":"#/components/schemas/Webhook"},{"type":"object","properties":{"orgId":{"type":"string"},"headers":{"type":"object","additionalProperties":{"type":"string"}},"hasSecret":{"type":"boolean"},"deliveries":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"eventType":{"$ref":"#/components/schemas/WebhookEventType"},"status":{"$ref":"#/components/schemas/WebhookDeliveryStatus"},"attempts":{"type":"integer"},"responseCode":{"type":"integer","nullable":true},"errorMessage":{"type":"string","nullable":true},"createdAt":{"type":"string","format":"date-time"},"completedAt":{"type":"string","format":"date-time","nullable":true},"durationMs":{"type":"integer","nullable":true}}}}}}]}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"500":{"$ref":"#/components/responses/ServerError"}}}},"/webhook/update":{"post":{"summary":"Update a webhook","description":"Update a webhook. Only the properties sent are changed. Setting `status` to `active` resets the failure count.","operationId":"updateWebhook","tags":["Webhooks"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateWebhookRequest"}}}},"responses":{"200":{"description":"Webhook updated","content":{"application/json":{"schema":{"type":"object","properties":{"webhook":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"url":{"type":"string"},"events":{"type":"array","items":{"$ref":"#/components/schemas/WebhookEventType"}},"status":{"$ref":"#/components/schemas/WebhookStatus"},"headers":{"type":"object","additionalProperties":{"type":"string"}},"updatedAt":{"type":"string","format":"date-time"}}},"message":{"type":"string"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"500":{"$ref":"#/components/responses/ServerError"}}}},"/webhook/delete":{"post":{"summary":"Delete a webhook","description":"Delete a webhook and its delivery history.","operationId":"deleteWebhook","tags":["Webhooks"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["id"],"properties":{"id":{"type":"string","description":"Webhook ID"}}}}}},"responses":{"200":{"description":"Webhook deleted","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"500":{"$ref":"#/components/responses/ServerError"}}}},"/webhook/rotate-secret":{"post":{"summary":"Rotate a webhook secret","description":"Generate a new signing secret. The old secret stops working immediately; the new one is shown only in this response.","operationId":"rotateWebhookSecret","tags":["Webhooks"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["id"],"properties":{"id":{"type":"string","description":"Webhook ID"}}}}}},"responses":{"200":{"description":"Secret rotated","content":{"application/json":{"schema":{"type":"object","properties":{"secret":{"type":"string"},"message":{"type":"string"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"500":{"$ref":"#/components/responses/ServerError"}}}},"/webhook/test":{"post":{"summary":"Send a test event","description":"Send a sample `contract_signed` payload (with `test: true`) to the webhook URL and return the result.","operationId":"testWebhook","tags":["Webhooks"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["id"],"properties":{"id":{"type":"string","description":"Webhook ID"}}}}}},"responses":{"200":{"description":"Test result","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"durationMs":{"type":"integer"},"responseCode":{"type":"integer","nullable":true},"responseBody":{"type":"string","description":"First 1000 characters of the response","nullable":true},"errorMessage":{"type":"string","nullable":true},"payload":{"type":"object"},"headers":{"type":"object","additionalProperties":{"type":"string"}}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"500":{"$ref":"#/components/responses/ServerError"}}}},"/webhook/logs":{"get":{"summary":"List a webhook's deliveries","description":"Delivery history for one webhook, newest first.","operationId":"getWebhookLogs","tags":["Webhooks"],"parameters":[{"name":"webhookId","in":"query","required":true,"schema":{"type":"string"},"description":"Webhook ID (camelCase parameter name)"},{"name":"page","in":"query","required":false,"schema":{"type":"integer","default":1,"minimum":1},"description":"Page number"},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":50,"minimum":1},"description":"Results per page"}],"responses":{"200":{"description":"Deliveries","content":{"application/json":{"schema":{"type":"object","properties":{"webhook":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"url":{"type":"string"}}},"deliveries":{"type":"array","items":{"$ref":"#/components/schemas/WebhookDelivery"}},"pagination":{"type":"object","properties":{"page":{"type":"integer"},"limit":{"type":"integer"},"totalCount":{"type":"integer"},"totalPages":{"type":"integer"},"hasNext":{"type":"boolean"},"hasPrev":{"type":"boolean"}}}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"500":{"$ref":"#/components/responses/ServerError"}}}},"/organization/get":{"get":{"summary":"Get organization","description":"Get the organization the API key belongs to, with its members.","operationId":"getOrganization","tags":["Organization"],"responses":{"200":{"description":"Organization details","content":{"application/json":{"schema":{"type":"object","properties":{"organization":{"$ref":"#/components/schemas/Organization"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"500":{"$ref":"#/components/responses/ServerError"}}}},"/organization/audit-logs":{"get":{"summary":"Get organization audit trail","description":"Audit events across the organization, newest first.","operationId":"getOrganizationAuditLogs","tags":["Organization"],"parameters":[{"name":"page","in":"query","required":false,"schema":{"type":"integer","default":1,"minimum":1},"description":"Page number"},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":50,"minimum":1},"description":"Results per page"}],"responses":{"200":{"description":"Audit events","content":{"application/json":{"schema":{"type":"object","properties":{"auditLogs":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"eventType":{"$ref":"#/components/schemas/WebhookEventType"},"createdAt":{"type":"string","format":"date-time"},"contract":{"nullable":true,"allOf":[{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"}}}]},"signer":{"nullable":true,"allOf":[{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"email":{"type":"string"}}}]},"user":{"nullable":true,"allOf":[{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"email":{"type":"string"},"image":{"type":"string","nullable":true}}}]},"ipAddress":{"type":"string","nullable":true},"country":{"type":"string","nullable":true},"city":{"type":"string","nullable":true},"deviceType":{"type":"string","nullable":true},"browser":{"type":"string","nullable":true},"os":{"type":"string","nullable":true},"metadata":{"type":"object","nullable":true}}}},"pagination":{"type":"object","properties":{"page":{"type":"integer"},"limit":{"type":"integer"},"totalCount":{"type":"integer"},"totalPages":{"type":"integer"},"hasMore":{"type":"boolean"}}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"500":{"$ref":"#/components/responses/ServerError"}}}},"/apikey/create":{"post":{"summary":"Create an API key","description":"Create an API key. The raw key is returned only in this response.","operationId":"createApiKey","tags":["API Keys"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["name"],"properties":{"name":{"type":"string","minLength":1,"maxLength":100},"permission":{"$ref":"#/components/schemas/ApiKeyPermission"},"expiresInDays":{"type":"number","description":"Days until the key expires. Omit for no expiry."}}}}}},"responses":{"200":{"description":"Key created","content":{"application/json":{"schema":{"type":"object","properties":{"apiKey":{"allOf":[{"$ref":"#/components/schemas/ApiKey"},{"type":"object","properties":{"key":{"type":"string","description":"The raw API key (starts with `ask_`). Shown only once."}}}]},"message":{"type":"string"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"500":{"$ref":"#/components/responses/ServerError"}}}},"/apikey/list":{"get":{"summary":"List API keys","description":"List the organization's API keys, newest first. Raw keys are never returned.","operationId":"listApiKeys","tags":["API Keys"],"responses":{"200":{"description":"Keys","content":{"application/json":{"schema":{"type":"object","properties":{"apiKeys":{"type":"array","items":{"allOf":[{"$ref":"#/components/schemas/ApiKey"},{"type":"object","properties":{"lastRequest":{"type":"string","format":"date-time","nullable":true},"requestCount":{"type":"integer"}}}]}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"500":{"$ref":"#/components/responses/ServerError"}}}},"/apikey/delete":{"post":{"summary":"Delete an API key","description":"Permanently delete an API key.","operationId":"deleteApiKey","tags":["API Keys"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["keyId"],"properties":{"keyId":{"type":"string","description":"API key ID"}}}}}},"responses":{"200":{"description":"Key deleted","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"500":{"$ref":"#/components/responses/ServerError"}}}},"/health/check":{"get":{"summary":"Liveness check","operationId":"healthCheck","tags":["Health"],"security":[],"responses":{"200":{"description":"Service is up","content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string","enum":["ok"]}}}}}}}}},"/health/db":{"get":{"summary":"Database check","description":"Always returns 200; read `status`.","operationId":"healthDb","tags":["Health"],"security":[],"responses":{"200":{"description":"Database status","content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string","enum":["ok","not ok"]},"database":{"type":"string","enum":["ok","not ok"]},"databaseError":{"type":"string","description":"Present only when the database query failed"},"timestamp":{"type":"string","format":"date-time"},"version":{"type":"string"}}}}}}}}},"/openapi":{"get":{"summary":"OpenAPI spec","description":"This document.","operationId":"getOpenApiSpec","tags":["Health"],"security":[],"responses":{"200":{"description":"OpenAPI 3 document","content":{"application/json":{"schema":{"type":"object"}}}}}}}},"components":{"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"x-api-key","description":"API key from the dashboard (Account → MCP & API Keys). Keys start with `ask_`. A key's `read`/`read_write` permission applies only to the MCP server; REST endpoints do not check it."}},"responses":{"BadRequest":{"description":"Invalid request, or the action is not allowed in the resource's current state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unauthorized":{"description":"Missing, invalid, expired or deleted API key. Body: `{ \"error\": \"Unauthorized\" }`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"NotFound":{"description":"Resource not found in this organization","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"ServerError":{"description":"Internal server error (also returned for a request body that is not valid JSON)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"schemas":{"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"string","description":"Error message"},"details":{"type":"array","description":"Present on request validation errors","items":{"type":"object","properties":{"field":{"type":"string","description":"Dot path of the invalid field"},"message":{"type":"string"}}}}}},"ContractStatus":{"type":"string","enum":["draft","queued","sent","signed","cancelled","expired"],"description":"`queued` is not set by any endpoint. `expired` is not currently set automatically: a contract past `expires_at` stays `sent`."},"SignerStatus":{"type":"string","enum":["pending","signed","declined"]},"FieldDefinition":{"type":"object","required":["name","type","completedBy"],"properties":{"name":{"type":"string","description":"Variable name without braces"},"type":{"type":"string","description":"signature, initials, signer_name, timestamp, text, email, phone or date"},"completedBy":{"type":"string","enum":["signer","creator"]},"label":{"type":"string","description":"Prompt shown for a text field"}}},"StoredFields":{"type":"object","nullable":true,"description":"Field data as stored on a contract or template","properties":{"fields":{"type":"array","items":{"$ref":"#/components/schemas/FieldDefinition"}},"signers":{"type":"array","description":"Per-signer field assignments, matched to signers by signing_order","items":{"type":"object","properties":{"signing_order":{"type":"integer"},"signerFields":{"type":"array","items":{"type":"string"}}}}}}},"Signer":{"type":"object","properties":{"id":{"type":"string"},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"},"org_id":{"type":"string"},"contract_id":{"type":"string"},"email":{"type":"string","format":"email"},"name":{"type":"string"},"mobile":{"type":"string","nullable":true},"company":{"type":"string"},"signing_order":{"type":"integer","minimum":1,"description":"Display order. Unique per contract. Does not enforce a signing sequence: all signers are emailed at once and may sign in any order."},"status":{"$ref":"#/components/schemas/SignerStatus"},"test_mode":{"type":"boolean"},"signature_data":{"type":"string","description":"Base64 signature image","nullable":true},"signed_at":{"type":"string","format":"date-time","nullable":true},"field_values":{"type":"object","nullable":true,"description":"Values this signer submitted"}}},"Contract":{"type":"object","properties":{"id":{"type":"string"},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"},"status":{"$ref":"#/components/schemas/ContractStatus"},"org_id":{"type":"string"},"sent_at":{"type":"string","format":"date-time","nullable":true},"expires_at":{"type":"string","format":"date-time","nullable":true,"description":"Creation time plus expires_in_days. Signing links stop working after this."},"cancelled_at":{"type":"string","format":"date-time","nullable":true},"name":{"type":"string"},"content":{"type":"object","properties":{"markdown":{"type":"string"}}},"fields":{"$ref":"#/components/schemas/StoredFields"},"template_id":{"type":"string","nullable":true},"final_pdf":{"type":"string","description":"URL of the signed PDF; empty string until every signer has signed"},"metadata":{"type":"object"},"test_mode":{"type":"boolean"},"seal_document_hash":{"type":"string","nullable":true},"seal_created_at":{"type":"string","format":"date-time","nullable":true},"seal_certificate_serial":{"type":"string","nullable":true}}},"ContractSummary":{"type":"object","properties":{"id":{"type":"string"},"created_at":{"type":"string","format":"date-time"},"name":{"type":"string"},"status":{"$ref":"#/components/schemas/ContractStatus"},"test_mode":{"type":"boolean"}}},"CreateSignerInput":{"type":"object","required":["email","name","signing_order"],"properties":{"email":{"type":"string","format":"email"},"name":{"type":"string","minLength":1,"maxLength":255},"signing_order":{"type":"integer","minimum":1,"description":"Unique per contract. Display order only; does not enforce a signing sequence."},"company":{"type":"string","maxLength":255},"mobile":{"type":"string","description":"Stored only; nothing is sent to it"},"signerFields":{"type":"array","items":{"type":"string"},"description":"Variable names this signer fills in. Required on every signer when there is more than one signer; each needs a signature field no other signer has."}}},"CreateContractRequest":{"type":"object","required":["name","signers"],"properties":{"name":{"type":"string","minLength":1,"maxLength":255},"template_id":{"type":"string","description":"Active template ID (cuid). Omit for a blank contract."},"content":{"type":"string","description":"Markdown. Required for blank contracts; rejected with template_id."},"variables":{"type":"object","additionalProperties":{"type":"string"},"description":"Values substituted into the template. Required for every creator-completed template field. Rejected for blank contracts."},"fields":{"type":"array","items":{"$ref":"#/components/schemas/FieldDefinition"},"description":"Required for blank contracts; rejected with template_id."},"signers":{"type":"array","minItems":1,"items":{"$ref":"#/components/schemas/CreateSignerInput"}},"expires_in_days":{"type":"integer","minimum":1,"maximum":365,"default":30},"test_mode":{"type":"boolean","default":false,"description":"Fixed at creation"},"metadata":{"type":"object"}}},"UpdateContractRequest":{"type":"object","required":["contract_id"],"properties":{"contract_id":{"type":"string","description":"Contract ID (cuid)"},"name":{"type":"string","minLength":1,"maxLength":255},"template_id":{"type":"string","description":"Changes the linked template; content is not re-rendered"},"content":{"type":"object","properties":{"markdown":{"type":"string"}}},"fields":{"$ref":"#/components/schemas/StoredFields"},"metadata":{"type":"object"},"test_mode":{"type":"boolean","description":"Must equal the current value"},"signers":{"type":"array","description":"Replaces the signer list. Entries with an existing `id` are updated, others are added, and existing signers left out are deleted.","items":{"type":"object","required":["email","name","mobile","signing_order"],"properties":{"id":{"type":"string","description":"Existing signer ID"},"email":{"type":"string","format":"email"},"name":{"type":"string","minLength":1,"maxLength":255},"mobile":{"type":"string","minLength":1,"maxLength":50},"company":{"type":"string","maxLength":255},"signing_order":{"type":"integer","minimum":1}}}}}},"ContractLog":{"type":"object","properties":{"id":{"type":"string"},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"},"org_id":{"type":"string"},"contract_id":{"type":"string"},"action":{"type":"string","enum":["created","updated","sent","resent","signed","declined","completed","cancelled"]},"user_id":{"type":"string","description":"User ID, `api-key:<id>`, or empty"},"signer_id":{"type":"string","description":"Signer ID, or empty"},"test_mode":{"type":"boolean"}}},"ContractAuditLog":{"type":"object","properties":{"id":{"type":"string"},"eventType":{"$ref":"#/components/schemas/WebhookEventType"},"createdAt":{"type":"string","format":"date-time"},"ipAddress":{"type":"string","nullable":true},"browser":{"type":"string","nullable":true},"browserVersion":{"type":"string","nullable":true},"os":{"type":"string","nullable":true},"osVersion":{"type":"string","nullable":true},"deviceType":{"type":"string","nullable":true},"country":{"type":"string","nullable":true},"city":{"type":"string","nullable":true},"metadata":{"type":"object","nullable":true},"signer":{"nullable":true,"allOf":[{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"email":{"type":"string"}}}]},"user":{"nullable":true,"allOf":[{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"email":{"type":"string"},"image":{"type":"string","nullable":true}}}]}}},"EmailDeliveryStatus":{"type":"string","enum":["sent","deferred","delivered","bounced","rejected","complained","failed"],"description":"`sent`: handed to the email provider, no outcome reported yet. `deferred`: temporary failure, still retrying. `delivered`: the recipient's mail server accepted it (it may still land in spam). `bounced`: the recipient's server refused it, at send time or later. `rejected`: never attempted, for example because the address bounced or complained before. `complained`: the recipient marked it as spam. `failed`: the send itself failed."},"EmailDelivery":{"type":"object","properties":{"status":{"$ref":"#/components/schemas/EmailDeliveryStatus"},"recipient":{"type":"string","format":"email","description":"The address the email went to. In test mode, this is your organization's address, not the signer's."},"sent_at":{"type":"string","format":"date-time"},"status_updated_at":{"type":"string","format":"date-time","description":"When the current status was reported"},"detail":{"type":"string","nullable":true,"description":"Why it bounced, was deferred, rejected or failed, e.g. `550 5.1.1 mailbox does not exist`"}}},"ContractEmail":{"allOf":[{"type":"object","properties":{"id":{"type":"string"},"kind":{"type":"string","enum":["signing_request","contract_completed","contract_declined"],"description":"`signing_request`: the link a signer signs with. `contract_completed`: the signed PDF, to a signer (with `signer_id`) or to your organization. `contract_declined`: the notice to your organization that a signer declined."},"signer_id":{"type":"string","nullable":true},"test_mode":{"type":"boolean"}}},{"$ref":"#/components/schemas/EmailDelivery"},{"type":"object","properties":{"events":{"type":"array","description":"Delivery events reported for this email, oldest first","items":{"type":"object","properties":{"type":{"type":"string","description":"e.g. `email.delivered`, `email.bounced`"},"occurred_at":{"type":"string","format":"date-time"}}}}}}]},"VerificationResult":{"type":"object","properties":{"valid":{"type":"boolean"},"error":{"type":"string","description":"Present when valid is false"},"contract":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"status":{"$ref":"#/components/schemas/ContractStatus"},"sealedAt":{"type":"string","format":"date-time","nullable":true},"testMode":{"type":"boolean"},"signers":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"email":{"type":"string"},"signedAt":{"type":"string","format":"date-time","nullable":true}}}}}},"seal":{"nullable":true,"allOf":[{"type":"object","properties":{"documentHash":{"type":"string"},"certificateSerial":{"type":"string"},"algorithm":{"type":"string"},"issuer":{"type":"string"}}}]},"verification":{"type":"object","properties":{"hasDigitalSeal":{"type":"boolean"},"signatureValid":{"type":"boolean"},"documentIntact":{"type":"boolean"},"certificateValid":{"type":"boolean"},"verifiedAt":{"type":"string","format":"date-time"},"note":{"type":"string"}}},"certificate":{"nullable":true,"allOf":[{"type":"object","properties":{"issuer":{"type":"string"},"validFrom":{"type":"string","format":"date-time"},"validTo":{"type":"string","format":"date-time"},"algorithm":{"type":"string"}}}]}}},"Template":{"type":"object","properties":{"id":{"type":"string"},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"},"org_id":{"type":"string"},"name":{"type":"string"},"content":{"type":"string","description":"Markdown with {{variables}}","nullable":true},"fields":{"$ref":"#/components/schemas/StoredFields"},"archived":{"type":"boolean"},"test_mode":{"type":"boolean"}}},"CreateTemplateRequest":{"type":"object","required":["name"],"properties":{"name":{"type":"string","minLength":3},"content":{"type":"string","description":"Markdown with {{variables}}"}}},"UpdateTemplateRequest":{"type":"object","required":["id","name","content"],"properties":{"id":{"type":"string"},"name":{"type":"string","minLength":1},"content":{"type":"string","description":"Full markdown content"},"fields":{"type":"array","description":"Replaces stored field definitions; omit to keep them","items":{"type":"object","required":["name","type"],"properties":{"name":{"type":"string"},"type":{"type":"string"},"completedBy":{"type":"string","description":"Defaults to signer","enum":["signer","creator"]},"label":{"type":"string"}}}}}},"FileUploadRequest":{"type":"object","required":["filename","fileData"],"properties":{"filename":{"type":"string","description":"File name ending in .docx"},"fileData":{"type":"string","description":"File bytes, base64-encoded"}}},"WebhookEventType":{"type":"string","enum":["contract_created","contract_sent","contract_resent","contract_viewed","contract_signed","contract_completed","contract_declined","contract_expired","contract_cancelled"]},"WebhookStatus":{"type":"string","enum":["active","inactive","failed"]},"WebhookDeliveryStatus":{"type":"string","enum":["pending","success","failed","retrying"]},"Webhook":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"url":{"type":"string","format":"uri"},"status":{"$ref":"#/components/schemas/WebhookStatus"},"events":{"type":"array","items":{"$ref":"#/components/schemas/WebhookEventType"}},"failureCount":{"type":"integer"},"lastSuccess":{"type":"string","format":"date-time","nullable":true},"lastFailure":{"type":"string","format":"date-time","nullable":true},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}},"WebhookDelivery":{"type":"object","properties":{"id":{"type":"string"},"eventType":{"$ref":"#/components/schemas/WebhookEventType"},"status":{"$ref":"#/components/schemas/WebhookDeliveryStatus"},"attempts":{"type":"integer"},"maxAttempts":{"type":"integer"},"responseCode":{"type":"integer","nullable":true},"responseBody":{"type":"string","nullable":true},"errorMessage":{"type":"string","nullable":true},"createdAt":{"type":"string","format":"date-time"},"lastAttempt":{"type":"string","format":"date-time","nullable":true},"nextAttempt":{"type":"string","format":"date-time","nullable":true},"completedAt":{"type":"string","format":"date-time","nullable":true},"durationMs":{"type":"integer","nullable":true},"payload":{"type":"object"}}},"CreateWebhookRequest":{"type":"object","required":["name","url","events"],"properties":{"name":{"type":"string","minLength":1,"maxLength":100},"url":{"type":"string","format":"uri"},"events":{"type":"array","minItems":1,"items":{"$ref":"#/components/schemas/WebhookEventType"}},"headers":{"type":"object","additionalProperties":{"type":"string"},"description":"Extra headers sent with each delivery"}}},"UpdateWebhookRequest":{"type":"object","required":["id"],"properties":{"id":{"type":"string"},"name":{"type":"string","minLength":1,"maxLength":100},"url":{"type":"string","format":"uri"},"events":{"type":"array","minItems":1,"items":{"$ref":"#/components/schemas/WebhookEventType"}},"headers":{"type":"object","additionalProperties":{"type":"string"}},"status":{"type":"string","enum":["active","inactive"]}}},"Organization":{"type":"object","description":"The organization record, including billing and branding settings","properties":{"id":{"type":"string"},"name":{"type":"string"},"slug":{"type":"string","nullable":true},"logo":{"type":"string","nullable":true},"createdAt":{"type":"string","format":"date-time"},"billing_email":{"type":"string","nullable":true},"contract_sent_price":{"type":"integer","description":"Price per contract send, in cents"},"auto_recharge_enabled":{"type":"boolean"},"auto_recharge_threshold":{"type":"integer","nullable":true,"description":"Cents"},"auto_recharge_amount":{"type":"integer","nullable":true,"description":"Cents"},"brand_logo":{"type":"string","nullable":true},"brand_color_accent":{"type":"string","nullable":true},"members":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"role":{"type":"string"},"user":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"email":{"type":"string"},"image":{"type":"string","nullable":true}}}}}}},"additionalProperties":true},"ApiKeyPermission":{"type":"string","enum":["read","read_write"],"default":"read_write","description":"Applies to the MCP server only; REST endpoints do not check it"},"ApiKey":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"start":{"type":"string","description":"First 8 characters of the key"},"permission":{"$ref":"#/components/schemas/ApiKeyPermission"},"enabled":{"type":"boolean"},"expiresAt":{"type":"string","format":"date-time","nullable":true},"createdAt":{"type":"string","format":"date-time"}}}}},"tags":[{"name":"Contracts","description":"Create, manage, and track contracts"},{"name":"Templates","description":"Manage reusable contract templates"},{"name":"Webhooks","description":"Configure webhook endpoints for events"},{"name":"Organization","description":"Organization details and audit trail"},{"name":"API Keys","description":"Manage API keys"},{"name":"Health","description":"Unauthenticated service checks"}]}