Introduction
Overview of the SignSecure REST API.
The SignSecure API lets you programmatically create envelopes, add recipients, send for signing, track progress, and manage templates and credits.
Base URL
https://api.signpad.signsecure.in/api/v1All endpoints are relative to this base URL. For local development, the server runs at http://localhost:3000/api/v1.
Authentication
Every request (except /health) requires an API key sent as a Bearer token in the Authorization header:
Authorization: Bearer signsecure_xxxxxxxx...API keys are created from Settings > API Keys in the dashboard. The key is shown once on creation — store it securely.
Organization Context
API keys inherit the workspace context in which they were created:
- Personal workspace — the key operates on your personal account and uses your personal credits.
- Organization workspace — the key is bound to that organization. Envelopes created with this key belong to the organization, credits are deducted from the organization wallet, and envelope listings are scoped to the organization. Only organization owners and admins can create API keys for an organization.
The workspace context is permanent for each key. To switch contexts, create a new API key in the desired workspace.
Permissions
Keys can be scoped with granular permissions per resource:
| Resource | Actions |
|---|---|
envelopes | read, write, delete |
signing | read, write |
templates | read, write, delete |
credits | read |
webhooks | read, write, delete |
Keys can also have an optional expiration date (1–365 days).
Rate Limits
Each API key is limited to 1,000 requests per hour. When the limit is exceeded, requests return 403.
Error Format
All errors return a consistent JSON envelope. Use code for programmatic handling and message for display. The details object carries structured context that helps you fix the problem — its shape depends on the error code.
{
"code": "VALIDATION_ERROR",
"message": "fileName: Required",
"details": {
"validation": {
"fieldErrors": { "fileName": ["Required"] },
"formErrors": [],
"issues": [
{ "path": "fileName", "message": "Required", "code": "invalid_type" }
]
}
},
"requestId": "req_abc123",
"timestamp": "2026-03-11T10:30:00.000Z"
}| Field | Type | Description |
|---|---|---|
code | string | Machine-readable constant — switch on this in your code |
message | string | Human-readable explanation — safe to show to users |
details | object? | Structured context (shape varies by code, omitted when not applicable) |
requestId | string | Unique request ID — include this when contacting support |
timestamp | string | ISO 8601 timestamp of when the error occurred |
Error Codes
Authentication & Authorization
| Code | HTTP | When it happens | How to fix |
|---|---|---|---|
UNAUTHORIZED | 401 | Missing, invalid, or revoked API key | Check the key is correct and not deleted in Settings > API Keys |
FORBIDDEN | 403 | Key is disabled, expired, rate-limited, or lacks the required permission | Re-enable the key, create a new one, or add the missing permission scope |
UNSUPPORTED_AUTH_HEADER | 400 | Sent the key via x-api-key header | Use Authorization: Bearer <key> instead |
INVALID_AUTHORIZATION_HEADER | 400 | Authorization header is malformed (e.g., missing Bearer prefix) | Format as Authorization: Bearer signsecure_xxxxx... |
Validation & Input
| Code | HTTP | When it happens | details shape | How to fix |
|---|---|---|---|---|
VALIDATION_ERROR | 400 | Request body or query params failed schema validation | { validation: { fieldErrors, formErrors, issues[] } } | Check details.validation.issues for the exact field paths and messages |
BAD_REQUEST | 400 | Invalid parameters that aren't covered by schema validation | { invalidRecipientIds[] } (when applicable) | Read the message and fix the indicated parameter |
INVALID_FILE_TYPE | 400 | fileType is not application/pdf | — | Only PDFs are supported |
FILE_TOO_LARGE | 400 | fileSize exceeds 10 MB | { maxSize, providedSize } | Compress or split the PDF to fit under 10,485,760 bytes |
DUPLICATE_RECIPIENT | 400 | A recipient email already exists on the envelope, or the same email appears twice in your request | { duplicateEmails[] } | Remove the duplicate email(s) or set replaceExisting: true |
Envelope & Signing
| Code | HTTP | When it happens | details shape | How to fix |
|---|---|---|---|---|
NOT_FOUND | 404 | Envelope, template, recipient, or form field does not exist (or you don't own it) | — | Verify the ID is correct and belongs to your account |
INVALID_STATUS | 400 | Operation requires a specific envelope status (usually draft) but the envelope is in a different state | — | Check message for the required status; you may need to create a new envelope |
NO_RECIPIENTS | 400 | Tried to send an envelope that has no signers or approvers | — | Add at least one recipient with role signer or approver before sending |
NOT_AVAILABLE | 400 | Requested the download URL but the PDF file hasn't been uploaded yet | — | Upload the PDF via the presigned uploadUrl first |
INVALID_SIGNATURE_POSITIONS | 400 | Signature fields reference page numbers that exceed the PDF's actual page count | { pdfPageCount, invalidPositions[], message } | Reposition fields to valid pages (check details.pdfPageCount) |
INVALID_SIGNATURE_PLACEMENT | 400 | Used text-based placement (type: "text") with a non-electronic signature method | { invalidRecipients[], recommendation } | Switch to signatureMethod: "electronic" or use type: "coordinates" placement |
TEXT_NOT_FOUND_IN_PDF | 400 | The searchText for text-based signature placement was not found anywhere in the PDF | { missingTexts[], totalPages } | Update the PDF to include the text, or change to coordinate-based placement |
PDF_VALIDATION_FAILED | 400 | The PDF file is corrupted, unreadable, or couldn't be downloaded from S3 | { details } | Re-upload the PDF file |
Database & Server
| Code | HTTP | When it happens | details shape | How to fix |
|---|---|---|---|---|
DUPLICATE_ENTRY | 409 | A unique database constraint was violated (e.g., duplicate email) | { constraint, detail } | Use a different value for the conflicting field |
CONSTRAINT_VIOLATION | 400 | A database integrity constraint was violated | { constraint, detail } | Check details.constraint for the specific rule that failed |
INTERNAL_SERVER_ERROR | 500 | Unexpected server error | — | Retry the request; if it persists, contact support with the requestId |
Validation Error Details
VALIDATION_ERROR is the most common error. The details.validation object gives you everything you need to show per-field error messages in your UI:
{
"code": "VALIDATION_ERROR",
"message": "fileName: Required",
"details": {
"validation": {
"fieldErrors": {
"fileName": ["Required"],
"fileSize": ["Expected number, received string"]
},
"formErrors": [],
"issues": [
{ "path": "fileName", "message": "Required", "code": "invalid_type" },
{ "path": "fileSize", "message": "Expected number, received string", "code": "invalid_type" }
]
}
},
"requestId": "req_abc123",
"timestamp": "2026-03-11T10:30:00.000Z"
}fieldErrors— keyed by field path (e.g.,"recipients.0.email"), each value is an array of error messages for that fieldformErrors— array of errors not tied to a specific field (e.g., cross-field validation)issues— flat array with each Zod validation issue including thecode(e.g.,invalid_type,too_small,invalid_enum_value)
Available Resources
| Resource | Description |
|---|---|
| Envelopes | Create, read, update, delete envelopes and manage file uploads |
| Recipients | Add and remove envelope recipients (signers, approvers, CC) |
| Form Fields | Place interactive fields on envelope pages |
| Signing Workflow | Send envelopes for signing, track progress, send reminders |
| Templates | Create reusable envelope templates |
| Credits | Check balance, view transactions, get usage stats |
| Webhooks | Receive real-time event notifications via HTTP |