Sign API Best Practices
Best practices for building reliable, efficient signing workflows with the Skribble Sign API.
API Version Selection
Use Sign API v2 for all production integrations — it is the current stable version.
Sign API v3 is available as a preview and is a great opportunity to test upcoming features early and share feedback with the Skribble team before general availability:
- Draft workflow — build signature requests before inviting signers
- Multi-document support — up to 50 documents per request
- Signer groups and flexible signer management
Sign API v3 is not yet stable. Breaking changes may occur before GA. Use it in non-production environments and share your feedback with us.
Signature Request Workflow
In v2, a signature request is created and goes directly to OPEN — signers are notified immediately. Include the document inline in the creation call, either as a URL reference or Base64-encoded content, or reference a previously uploaded document by ID.
OPEN → SIGNED/DECLINED/WITHDRAWN
Option 1 — Document via URL
{
"title": "Contract Agreement",
"message": "Please sign this contract.",
"document": {
"url": "https://your-app.com/documents/contract.pdf",
"filename": "contract.pdf"
},
"signatures": [
{ "signer_email_address": "alice@example.com" }
]
}
Option 2 — Document as Base64
{
"title": "Contract Agreement",
"message": "Please sign this contract.",
"document": {
"content": "<base64-encoded-pdf>",
"filename": "contract.pdf"
},
"signatures": [
{ "signer_email_address": "alice@example.com" }
]
}
Option 3 — Pre-uploaded document by ID
Upload the document first, then reference it by ID:
{
"content": "<base64-encoded-pdf>",
"filename": "contract.pdf"
}
Then reference the returned id in the signature request:
{
"title": "Contract Agreement",
"document_id": "doc-aaa-111",
"signatures": [
{ "signer_email_address": "alice@example.com" }
]
}
Sign API v3 (preview) introduces a document-first, explicit-initiation model with multi-document support and signer groups. See Migrating from v2 → Draft Workflow for the full flow.
Document Handling
Upload Best Practices
- Validate before upload - Ensure documents are valid PDFs
- Use meaningful filenames - They appear in the signing interface
- Optimize file size - Large files slow down the signing experience
- Base64 encode properly - Documents must be Base64-encoded
// Upload a document
const response = await api.post('/documents', {
title: 'Contract.pdf',
content_type: 'application/pdf',
content: base64EncodedPdf
});
Supported Attachment Types
- PDF documents
- Microsoft Office formats (Word, Excel, PowerPoint)
- Images (JPEG, PNG)
- Plain text files
Signer Management
Adding Signers
You can add signers in two ways:
1. By Email (Skribble account required):
{
"signer_email_address": "signer@example.com"
}
2. With Identity Data (no account required):
{
"signer_identity_data": {
"email_address": "signer@example.com",
"first_name": "John",
"last_name": "Doe",
"mobile_number": "+41791234567",
"language": "en"
}
}
Signer Modification Rules
- In v2, signers are part of the creation call — once the request is
OPEN, you cannot add or remove signers - Once any signer has signed, you cannot modify the request
- Use
DELETE /signature-requests/{id}to withdraw an open request and recreate it if changes are needed
Customizing the Signing URL
When a signature request is created, each signer receives a signing_url. This URL forwards the signer to a preview page of the PDF document where they can start the signature process as usual.
You can append query parameters to the signing_url to control the signer's experience after completing or declining the signature process.
| Parameter | Type | Description |
|---|---|---|
exitURL | string (URL-encoded) | Optional URL to redirect after the signature process finished or declined. Example: https%3A%2F%2Fgoogle.com |
errorURL | string (URL-encoded) | Optional URL to redirect in case of an error. Example: https%3A%2F%2Fbing.com |
hidedownload | boolean | Optional flag to hide and disable the download document option. Example: true |
redirectTimeout | integer | Optional timeout in seconds before the redirect is executed. Example: 20 |
lang | string | Optional language code to display the signing page in a specific language. Supported values: en (default), de, fr. Example: lang=fr |
overlay | boolean | Optional flag to show or hide the Skribble overlay on the signing page. |
const signingUrlWithParams = `${signer.signing_url}?exitURL=${encodeURIComponent('https://your-app.com/done')}&lang=de&hidedownload=true`;
Missing signer fields like first name and last name can be adjusted by the signer directly through the Skribble signing interface — they are not required to be set upfront via the API.
Signature Qualities
Choose the appropriate signature quality based on your requirements:
| Quality | Description | Use Case |
|---|---|---|
SES | Simple Electronic Signature | Low-risk documents, internal approvals |
AES | Advanced Electronic Signature | Standard contracts, agreements |
QES | Qualified Electronic Signature | High-value contracts, regulatory requirements |
DEMO | Test signature | Development and testing |
Legislation Compliance
| Legislation | Region | Standard |
|---|---|---|
ZERTES | Switzerland | Swiss Federal Act on Electronic Signatures |
EIDAS | European Union | EU Regulation on electronic identification |
Callbacks and Webhooks
Setting Up Callbacks
Configure callbacks to receive real-time updates:
{
"callback_success_url": "https://your-app.com/webhooks/skribble/success",
"callback_error_url": "https://your-app.com/webhooks/skribble/error",
"callback_update_url": "https://your-app.com/webhooks/skribble/update"
}
Callback Types
| Type | Triggered When |
|---|---|
SUCCESS | All signers have signed |
UPDATE | A signer has signed (partial completion) |
ERROR | An error occurred |
START_SIGN | A signer started signing |
INITIATE | Request was initiated |
IDENT_ERROR | Identification error |
Callback Best Practices
- Acknowledge quickly - Return 200 within 5 seconds
- Process asynchronously - Queue the work, respond immediately
- Handle idempotency - The same callback may be delivered multiple times
- Verify authenticity - Validate callback requests come from Skribble
Reminders
Sending Manual Reminders
POST /signature-requests/{id}/remind
Reminder Limitations
- Rate limit: One reminder per signer per hour
- Only works for signers who haven't completed signing
- Consider using automatic reminders for better UX
Sealing Documents
For documents that need an organizational seal rather than personal signatures:
const sealedDoc = await api.post('/seal', {
content: base64EncodedPdf,
account_name: 'your-seal-account'
});
SendTo Feature
For simple one-off document signings without the full signature request flow. No API key or authentication is required — this endpoint is publicly accessible.
Because no credentials are needed, SendTo is ideal for letting end-users or third parties trigger a signing directly from your application without exposing any API keys.
// Create a send-to request
const sendTo = await api.post('/sendto', {
content: base64EncodedPdf,
signer_email_address: 'signer@example.com',
title: 'Document to Sign'
});
// Track status
const status = await api.get(`/sendto/${sendTo.id}/track`);
// Download when complete
const signedDoc = await api.get(`/sendto/${sendTo.id}/download`);
SendTo Limitations
- Documents expire after 90 days
- Unclaimed requests expire after 1 day
- Simpler than full signature requests but less flexible
Performance Tips
- Batch document uploads - Upload documents in parallel before creating requests
- Cache authentication tokens - Reuse tokens until they expire
- Use pagination - When listing requests, use
page_numberandpage_size - Poll efficiently - Use callbacks instead of polling when possible
Common Pitfalls
Avoid These Mistakes
- Missing required fields - Ensure all signers and document details are complete in the creation call, since v2 requests go directly to
OPEN - Ignoring status - Always check
status_overallbefore performing actions - Not handling callbacks - Rely on callbacks rather than polling for status updates
- Hardcoding URLs - Use environment-specific base URLs
Status Checking
Always verify the current status before operations:
const request = await api.get(`/signature-requests/${id}`);
if (request.status_overall === 'OPEN') {
// Waiting for signatures — can send reminders
} else if (request.status_overall === 'SIGNED') {
// All signers signed — download the signed document
} else if (request.status_overall === 'DECLINED') {
// A signer declined — handle accordingly
} else if (request.status_overall === 'WITHDRAWN') {
// Request was withdrawn
}