API Documentation
Signeum's REST API lets you integrate legally binding document signing directly into your product. Create documents, manage signers, and track status — all through the API.
Base URL
All requests and responses use JSON. Always include the header
Content-Type: application/json.
Widget integration?
If you're looking to embed the signing experience in your application, see the Widget Guide.
Concepts & Data Model
Before you start integrating, it helps to understand how Signeum is structured. Here are the core concepts and how they relate to each other.
Organization → Project → Document
Signeum's data model has three levels. An organization is your account and owns all resources. Within the organization, projects group documents and carry settings for branding, signing methods, and notifications. Documents are always created within a project and inherit the project's settings.
Default Projects
Every new organization automatically gets two system projects:
-
Standard Signing — supports methods
drawn,typed, andnone(simple e-signature) -
eID Signing — configured for
bankid_se(Swedish BankID)
You can create your own projects with custom settings via the API.
Signing Methods
A project uses either standard methods (drawn,
typed, none) or eID (bankid_se).
You cannot mix standard and eID methods in the same project. For eID projects, signers
must have a personalNumber (Swedish personal number).
Project Settings
Each project has settings that affect all documents within it:
- Branding — logo, colors, and company name in the signing view
- Email notifications — can be enabled or disabled per project
- Chat — allow participants to communicate in the signing view
- Audit log — embed the audit trail in the signed PDF
- Post-signing — show a message or redirect to a URL after signing
- Auto-reminder — send a reminder after N days
Document Status
A document goes through the following statuses during its lifecycle:
| Status | Description |
|---|---|
pending |
The document is created and waiting for the first signer to act |
in_progress |
At least one signer has signed, but not all |
processing |
All signers have signed and the document is being processed before completion |
completed |
All signers have signed — the document is complete |
expired |
The signing deadline has passed without all signatures |
cancelled |
The document has been cancelled by the sender |
declined |
A signer has declined to sign |
Multi-tenant Pattern
If you're building a SaaS product and want to offer signing to your customers, you can create one project per customer (tenant). Each project can have its own logo, colors, and company name — so the signing view matches the customer's brand.
White-label Email
Want to send your own emails instead of Signeum's default notifications? Disable email
notifications at the project level and listen to webhook events (e.g.
document.completed, document.signed). Then send your own
emails with your sender address and design.
Signing Order
Participants can be assigned a signingOrder. Signers with a lower order
number must sign before the next in line gets access to the document. Participants with
the same order number can sign in parallel.
Authentication
All API calls require a valid API key. Keys are created in the organization settings by an owner. Each key is scoped to one organization.
Project-Scoped API Keys
API keys support two access modes:
- All projects (default) — the key can access every project in the organization. This is the standard behavior.
-
Scoped — the key is restricted to one or more specific projects.
Requests to projects outside the allowed list will receive a
403 Forbiddenerror.
Scoped keys are useful for integrations, CI pipelines, or third-party services that should only access a subset of your projects. You choose the mode when creating the key.
API keys are prefixed with ds_ and sent via the Authorization header:
Authorization: Bearer ds_your_api_key_here
Rate Limiting
The API allows 100 requests per minute per API key. If the limit is exceeded, status
code 429 Too Many Requests is returned.
The response includes a Retry-After header with the number of seconds until
the window resets.
Error Handling
All errors are returned with a consistent JSON structure:
{
"error": {
"code": "VALIDATION_ERROR",
"message": "Validation failed",
"trackingId": "abc-123-def",
"details": {
"fields": {
"name": "Document name is required"
}
}
}
}
| Status Code | Code | Description |
|---|---|---|
| 400 | VALIDATION_ERROR | Invalid request or validation error |
| 401 | UNAUTHORIZED | Missing or invalid API key |
| 403 | FORBIDDEN | Access denied to the resource |
| 404 | NOT_FOUND | Resource not found |
| 402 | PAYMENT_REQUIRED | Payment method required |
| 429 | RATE_LIMITED | Rate limit exceeded |
| 500 | INTERNAL_ERROR | Internal server error |
Webhooks
Configure webhook endpoints at the project level to receive real-time notifications about document events. Signeum sends a POST request with a JSON payload to your URL for documents created both via the API and the UI.
{
"eventType": "document.signed",
"timestamp": "2026-03-16T10:30:00Z",
"documentId": "d4f5a6b7-...",
"organizationId": "a1b2c3d4-...",
"projectId": "e5f6a7b8-...",
"participant": {
"name": "Anna Lindström",
"email": "[email]",
"role": "signer"
}
}
| Event | Description |
|---|---|
| document.viewed | A signer has opened the document |
| document.signed | A signer has signed the document |
| document.reviewed | A reviewer has reviewed the document |
| document.processing | All signers have signed — the document is being processed (PDF compilation) |
| document.completed | All signers have signed — the document is complete |
| document.failed | Document processing failed (e.g. PDF compilation error) |
| document.cancelled | The document has been cancelled |
| document.expired | The signing deadline has passed |
| document.expiring | The signing deadline is approaching (default: within 3 days) |
| participant.declined | A signer has declined to sign |
| document.analyzed | AI analysis has completed (tags, taxonomy, embeddings generated) |
| document.tags_updated | Tags or taxonomy have been added, edited, or removed on a document |
| chat.message | A new chat message on the document |
| email.bounced | An email notification bounced |
| email.failed | An email notification failed to send |
Webhook Signatures
Every webhook request is signed with an HMAC-SHA256 signature so you can verify it came from Signeum and hasn't been tampered with. Each organization has a unique webhook secret, available in your organization settings.
Signature Headers
Signeum includes two headers on every webhook request:
| Header | Description |
|---|---|
X-Signeum-Signature |
HMAC-SHA256 signature in the format sha256=<hex-digest> |
X-Signeum-Timestamp |
Unix timestamp (seconds) when the signature was computed |
How to Verify
The signature is computed over <timestamp>.<body> — the
timestamp value, a dot, and the raw JSON request body. To verify:
- Read the
X-Signeum-Timestampheader - Read the raw request body (before any JSON parsing)
- Concatenate them as
<timestamp>.<body> - Compute HMAC-SHA256 using your organization's webhook secret as the key
-
Compare the hex digest with the value after
sha256=in the signature header
import crypto from 'crypto';
function verifyWebhook(secret, body, signatureHeader, timestampHeader) {
const message = `${timestampHeader}.${body}`;
const expected = crypto
.createHmac('sha256', secret)
.update(message)
.digest('hex');
const received = signatureHeader.replace('sha256=', '');
return crypto.timingSafeEqual(
Buffer.from(expected),
Buffer.from(received)
);
}
import hmac, hashlib
def verify_webhook(secret, body, signature_header, timestamp_header):
message = f"{timestamp_header}.{body}"
expected = hmac.new(
secret.encode(), message.encode(), hashlib.sha256
).hexdigest()
received = signature_header.replace("sha256=", "")
return hmac.compare_digest(expected, received)
timingSafeEqual or
hmac.compare_digest) to prevent timing attacks.
Replay Protection
The timestamp is included in the signed message to prevent replay attacks. You should reject webhooks where the timestamp is more than 5 minutes old:
const MAX_AGE_SECONDS = 300; // 5 minutes
const now = Math.floor(Date.now() / 1000);
const timestamp = parseInt(req.headers['x-signeum-timestamp']);
if (Math.abs(now - timestamp) > MAX_AGE_SECONDS) {
return res.status(403).json({ error: 'Webhook timestamp too old' });
}
Managing Your Secret
Your webhook secret is auto-generated when the organization is created. You can view and copy it from the organization settings page, or rotate it via the API:
curl https://api.signeum.se/api/v1/organizations/{orgId}/webhook-secret \
-H "Authorization: Bearer ds_your_api_key"
# Response: { "webhookSecret": "a1b2c3d4..." }
curl -X POST https://api.signeum.se/api/v1/organizations/{orgId}/webhook-secret/rotate \
-H "Authorization: Bearer ds_your_api_key"
# Response: { "webhookSecret": "e5f6a7b8..." }
Retry Behavior
If your endpoint returns a non-2xx response or is unreachable, Signeum retries the delivery with increasing delays over a span of several hours.
| Attempt | Delay |
|---|---|
| 1 | Immediate |
| 2 | 30 seconds |
| 3 | 2 minutes |
| 4 | 5 minutes |
| 5 | 15 minutes |
| 6 | 30 minutes |
| 7 | 45 minutes |
| 8 | 1 hour |
| 9 | 1.5 hours |
After all attempts are exhausted, the delivery is marked as failed.
Response Handling
- 2xx — Delivery successful, no retry
- 4xx (except 429) — Permanent failure, no retry. Fix the issue on your end
-
429 — Rate limited. Signeum respects the
Retry-Afterheader - 5xx / timeout — Transient failure, retried per the schedule above
Tip
Return a 200 quickly and process the webhook asynchronously. If your
endpoint takes too long (over 10 seconds), the request will time out and be retried.
Projects
POST /organizations/:orgId/projects
Create a new project in the organization. Projects are used to group documents and can have separate settings for branding, signing methods, and notifications.
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| name | string | Yes | Project name (1–255 characters) |
curl -X POST https://api.signeum.se/api/v1/organizations/{orgId}/projects \
-H "Authorization: Bearer ds_your_api_key" \
-H "Content-Type: application/json" \
-d '{ "name": "Customer Contracts" }'
{
"project": {
"id": "e5f6a7b8-c9d0-4e1f-a2b3-c4d5e6f7a8b9",
"organizationId": "a1b2c3d4-...",
"name": "Customer Contracts",
"isDefault": false,
"createdAt": "2026-03-16T10:00:00Z",
"updatedAt": "2026-03-16T10:00:00Z",
"settings": {
"emailNotificationsEnabled": true,
"includeAuditInDocument": true,
"chatEnabled": true,
"allowedSigningMethods": ["drawn", "typed", "none", "bankid_se"]
}
}
}
GET /organizations/:orgId/projects
List all projects in the organization with pagination.
Query parameters
| Parameter | Type | Description |
|---|---|---|
| page | number | Page number (default: 1) |
| limit | number | Items per page (default: 20, max: 100) |
| search | string | Search by project name |
| sort | string | Sort order, e.g. createdAt:desc |
curl https://api.signeum.se/api/v1/organizations/{orgId}/projects \
-H "Authorization: Bearer ds_your_api_key"
{
"projects": [
{
"id": "e5f6a7b8-...",
"organizationId": "a1b2c3d4-...",
"name": "Customer Contracts",
"isDefault": false,
"createdAt": "2026-03-16T10:00:00Z"
}
],
"pagination": {
"page": 1,
"limit": 20,
"total": 1,
"totalPages": 1
}
}
GET /organizations/:orgId/projects/:projectId
Get details for a specific project including settings.
curl https://api.signeum.se/api/v1/organizations/{orgId}/projects/{projectId} \
-H "Authorization: Bearer ds_your_api_key"
{
"project": {
"id": "e5f6a7b8-c9d0-4e1f-a2b3-c4d5e6f7a8b9",
"organizationId": "a1b2c3d4-...",
"name": "Customer Contracts",
"isDefault": false,
"createdAt": "2026-03-16T10:00:00Z",
"updatedAt": "2026-03-16T10:00:00Z",
"creator": {
"type": "user",
"name": "Richard Barkestam",
"email": "[email]",
"userId": "u1a2b3c4-..."
},
"settings": {
"emailNotificationsEnabled": true,
"includeAuditInDocument": true,
"chatEnabled": true,
"allowedSigningMethods": ["drawn", "typed", "none"]
}
}
}
Documents
POST /organizations/:orgId/projects/:projectId/documents
Create a new document with participants and send it for signing. The document can be uploaded as a base64-encoded PDF, fetched from a URL, created from a template, or referenced via a temp session (for large files uploaded separately).
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| name | string | Yes | Document name (1–255 characters) |
| document | object | Yes* |
PDF upload. Either { type: "base64", content: "..." } or
{ type: "url", url: "..." }
|
| templateId | string | Yes* |
UUID of a template to create the document from. Alternative to
document
|
| sessionId | string | Yes* |
UUID of a temp session containing an uploaded PDF. Alternative to
document. Use this for large files uploaded via the temp session flow
|
| participants | array | Yes | List of participants (1–50). See participant object below |
| signingDeadline | string | No | ISO 8601 date. Max 30 days ahead |
| allowedSigningMethods | array | No |
Allowed signing methods: drawn, typed,
none. Ignored for eID projects (BankID is configured at the project
level)
|
| autoReminderDays | number | No | Send automatic reminder after N days (1–30) |
| roleMapping | object | No | Mapping of template roles to participants (for template-based creation) |
* Either document, templateId, or sessionId must
be provided.
Participant object
| Parameter | Type | Required | Description |
|---|---|---|---|
| name | string | Yes | Participant's name |
| string | Yes | Email address | |
| role | string | Yes | signer or reviewer |
| signingOrder | number | No | Signing order (positive integer) |
| language | string | No | sv or en (default: en) |
| otpEnabled | boolean | No | Require OTP verification via email (default: false) |
| personalNumber | string | No | Swedish personal number (12 digits, YYYYMMDDXXXX). Required for signers in eID projects (BankID) |
curl -X POST https://api.signeum.se/api/v1/organizations/{orgId}/projects/{projectId}/documents \
-H "Authorization: Bearer ds_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"name": "Service Agreement 2026",
"document": {
"type": "base64",
"content": "JVBERi0xLjQK..."
},
"participants": [
{
"name": "Anna Lindström",
"email": "[email]",
"role": "signer",
"signingOrder": 1,
"language": "sv"
},
{
"name": "Erik Johansson",
"email": "[email]",
"role": "signer",
"signingOrder": 2,
"language": "sv"
}
],
"signingDeadline": "2026-04-15T23:59:59Z"
}'
{
"document": {
"id": "d4f5a6b7-c8d9-4e0f-a1b2-c3d4e5f6a7b8",
"projectId": "e5f6a7b8-...",
"organizationId": "a1b2c3d4-...",
"name": "Service Agreement 2026",
"status": "pending",
"createdAt": "2026-03-16T10:00:00Z",
"participants": [
{
"id": "p1a2b3c4-...",
"name": "Anna Lindström",
"email": "[email]",
"role": "signer",
"signingOrder": 1,
"status": "pending"
}
],
"signingUrls": [
{
"participantId": "p1a2b3c4-...",
"name": "Anna Lindström",
"email": "[email]",
"role": "signer",
"signingUrl": "https://app.signeum.se/sign/abc123..."
}
]
}
}
GET /organizations/:orgId/projects/:projectId/documents
List documents in a project with pagination and filtering.
Query parameters
| Parameter | Type | Description |
|---|---|---|
| page | number | Page number (default: 1) |
| limit | number | Items per page (default: 20, max: 100) |
| status | string |
Filter by status: pending, in_progress,
completed, expired, cancelled
|
| sort | string | Sort order, e.g. createdAt:desc |
curl https://api.signeum.se/api/v1/organizations/{orgId}/projects/{projectId}/documents?status=pending&limit=10 \
-H "Authorization: Bearer ds_your_api_key"
GET /organizations/:orgId/projects/:projectId/documents/:documentId
Get details for a specific document including participants and their status.
curl https://api.signeum.se/api/v1/organizations/{orgId}/projects/{projectId}/documents/{documentId} \
-H "Authorization: Bearer ds_your_api_key"
{
"document": {
"id": "d4f5a6b7-...",
"projectId": "e5f6a7b8-...",
"organizationId": "a1b2c3d4-...",
"name": "Service Agreement 2026",
"status": "in_progress",
"createdAt": "2026-03-16T10:00:00Z",
"signingDeadline": "2026-04-15T23:59:59Z",
"creator": {
"type": "user",
"name": "Richard Barkestam",
"email": "[email]",
"userId": "u1a2b3c4-..."
},
"participants": [
{
"id": "p1a2b3c4-...",
"name": "Anna Lindström",
"email": "[email]",
"role": "signer",
"signingOrder": 1,
"status": "signed",
"signedAt": "2026-03-16T14:30:00Z",
"signatureMode": "drawn"
},
{
"id": "p2b3c4d5-...",
"name": "Erik Johansson",
"email": "[email]",
"role": "signer",
"signingOrder": 2,
"status": "pending"
}
]
}
}
PUT /organizations/:orgId/projects/:projectId/documents/:documentId/cancel
Cancel a document that hasn't been completed yet. All pending signers will be notified.
curl -X PUT https://api.signeum.se/api/v1/organizations/{orgId}/projects/{projectId}/documents/{documentId}/cancel \
-H "Authorization: Bearer ds_your_api_key" \
-H "Content-Type: application/json" \
-d '{ "reason": "Agreement needs to be updated" }'
GET /organizations/:orgId/projects/:projectId/documents/:documentId/download
Get a signed URL to download the signed document (PDF). Available when the document has
status completed.
curl https://api.signeum.se/api/v1/organizations/{orgId}/projects/{projectId}/documents/{documentId}/download \
-H "Authorization: Bearer ds_your_api_key"
{
"downloadUrl": "https://storage.signeum.se/signed/...",
"expiresAt": "2026-03-16T11:00:00Z"
}
POST /organizations/:orgId/projects/:projectId/documents/:documentId/send-reminder
Send a reminder to all pending signers on a document. Optionally include a custom message that will be shown in the reminder email.
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| customMessage | string | No | Optional message to include in the reminder email |
curl -X POST https://api.signeum.se/api/v1/organizations/{orgId}/projects/{projectId}/documents/{documentId}/send-reminder \
-H "Authorization: Bearer ds_your_api_key" \
-H "Content-Type: application/json" \
-d '{ "customMessage": "Please sign the agreement before Friday." }'
{
"sent": 1,
"recipients": [
{
"id": "p2b3c4d5-...",
"name": "Erik Johansson",
"email": "[email]"
}
]
}
POST /organizations/:orgId/projects/:projectId/documents/:documentId/analyze
Trigger asynchronous AI analysis on an existing document. Generates tags, taxonomy classification, and text embeddings. The analysis runs in the background — poll the status endpoint to check progress.
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| force | boolean | No | Re-analyze even if the document already has results (default: false) |
curl -X POST https://api.signeum.se/api/v1/organizations/{orgId}/projects/{projectId}/documents/{documentId}/analyze \
-H "Authorization: Bearer ds_your_api_key" \
-H "Content-Type: application/json" \
-d '{ "force": false }'
{
"jobId": "abc-123",
"status": "processing"
}
| Status Code | Description |
|---|---|
| 202 | Analysis job enqueued |
| 409 |
Analysis already in progress, or already completed (use
force: true to override)
|
| 404 | Document not found |
Webhook
When analysis completes, a document.analyzed webhook event is emitted.
GET /organizations/:orgId/projects/:projectId/documents/:documentId/analyze/status
Check the analysis status for a document.
curl https://api.signeum.se/api/v1/organizations/{orgId}/projects/{projectId}/documents/{documentId}/analyze/status \
-H "Authorization: Bearer ds_your_api_key"
{
"status": "completed",
"analyzedAt": "2026-03-16T10:05:00Z"
}
| Status value | Description |
|---|---|
not_analyzed |
Analysis has not been triggered |
processing |
Analysis is running |
completed |
Analysis finished — tags and embeddings are available |
failed |
Analysis failed (includes error field) |
GET /organizations/:orgId/projects/:projectId/documents/:docId/risk
Get AI-generated risk flags and sign summary for a document. Requires the document to have been analyzed first via the Analyze document endpoint.
Authentication
Requires a valid API key or session token via the Authorization header. The
requesting user must have at least the user role in the organization.
curl https://api.signeum.se/api/v1/organizations/{orgId}/projects/{projectId}/documents/{docId}/risk \
-H "Authorization: Bearer ds_your_api_key"
{
"riskFlags": [
{
"type": "warning",
"label": "Unusual termination clause",
"description": "The termination clause allows immediate termination without notice."
}
],
"signSummary": "Standard service agreement with two signers and a 30-day deadline."
}
GET /organizations/:orgId/projects/:projectId/documents/:docId/similar
Find documents in the organization that are semantically similar to the specified document. Uses AI-generated embeddings and cosine similarity to rank results. The source document must have been analyzed first.
Authentication
Requires a valid API key or session token via the Authorization header. The
requesting user must have at least the user role in the organization.
Query parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
| limit | integer | 10 | Maximum number of results to return (1–50) |
| threshold | float | 0.5 | Minimum similarity score to include a result (0.0–1.0) |
curl https://api.signeum.se/api/v1/organizations/{orgId}/projects/{projectId}/documents/{docId}/similar?limit=5&threshold=0.7 \
-H "Authorization: Bearer ds_your_api_key"
{
"similarDocuments": [
{
"id": "d4f5a6b7-c8d9-4e0f-a1b2-c3d4e5f6a7b8",
"name": "Service Agreement 2025",
"similarityScore": 0.87,
"status": "completed",
"createdAt": "2025-11-20T14:30:00Z"
},
{
"id": "a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d",
"name": "Consulting Contract Q4",
"similarityScore": 0.74,
"status": "completed",
"createdAt": "2025-12-01T09:15:00Z"
}
]
}
No embedding?
If the source document has not been analyzed (no embedding), the endpoint returns an empty array. Use the Analyze document endpoint first to generate embeddings.
Temp Sessions
When to use
Temp sessions let you upload large PDFs (up to 50 MB) separately from the document creation request. Upload the file first, then create the document by referencing the session ID. This avoids base64-encoding large files in the create request body.
For analysis (tags, taxonomy, embeddings), use the Analyze document endpoint after creating the document.
POST /organizations/:orgId/projects/:projectId/temp-sessions
Create a temporary upload session. The session expires after 30 minutes of inactivity.
curl -X POST https://api.signeum.se/api/v1/organizations/{orgId}/projects/{projectId}/temp-sessions \
-H "Authorization: Bearer ds_your_api_key"
{
"sessionId": "83cf255a-32bc-4186-86e3-fee9c394e88e"
}
POST /organizations/:orgId/projects/:projectId/temp-sessions/:sessionId/upload
Upload a file to the session. Accepts PDF files directly, or convertible formats (DOCX, XLSX, PPTX, etc.) which are converted to PDF server-side. Max 50 MB.
curl -X POST https://api.signeum.se/api/v1/organizations/{orgId}/projects/{projectId}/temp-sessions/{sessionId}/upload \
-H "Authorization: Bearer ds_your_api_key" \
-F "file=@contract.pdf"
{
"storageKey": "temp/83cf255a-.../document.pdf",
"fileName": "contract.pdf",
"fileSize": 2500000
}
Then create the document
After uploading, create the document with
{ "sessionId": "83cf255a-..." } instead of the
document field. The PDF is moved to permanent storage automatically.
Templates
GET /organizations/:orgId/templates
List all templates in the organization.
curl https://api.signeum.se/api/v1/organizations/{orgId}/templates \
-H "Authorization: Bearer ds_your_api_key"
{
"templates": [
{
"id": "t1a2b3c4-...",
"name": "Employment Agreement",
"description": "Standard template for employment agreements",
"signerCount": 2,
"signerRoles": [
{ "index": 0, "label": "Employer", "role": "signer", "signingOrder": 1 },
{ "index": 1, "label": "Employee", "role": "signer", "signingOrder": 2 }
],
"createdAt": "2026-01-15T08:00:00Z"
}
]
}
GET /organizations/:orgId/templates/:templateId
Get details for a specific template including field definitions and signer roles.
curl https://api.signeum.se/api/v1/organizations/{orgId}/templates/{templateId} \
-H "Authorization: Bearer ds_your_api_key"
{
"template": {
"id": "t1a2b3c4-...",
"name": "Employment Agreement",
"description": "Standard template for employment agreements",
"signerCount": 2,
"signerRoles": [
{ "index": 0, "label": "Employer", "role": "signer", "signingOrder": 1 },
{ "index": 1, "label": "Employee", "role": "signer", "signingOrder": 2 }
],
"creator": {
"type": "user",
"name": "Richard Barkestam",
"email": "[email]",
"userId": "u1a2b3c4-..."
},
"fields": [
{
"id": "f1a2b3c4-...",
"signerRoleIndex": 0,
"fieldType": "signature",
"pageNumber": 1,
"xPosition": 0.1,
"yPosition": 0.8,
"width": 0.3,
"height": 0.05,
"isRequired": true
}
],
"createdAt": "2026-01-15T08:00:00Z"
}
}
POST /organizations/:orgId/projects/:projectId/documents
Create a document from a template. Provide templateId instead of
document and map the template's roles to actual participants with
roleMapping.
curl -X POST https://api.signeum.se/api/v1/organizations/{orgId}/projects/{projectId}/documents \
-H "Authorization: Bearer ds_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"name": "Employment Agreement — Anna Lindström",
"templateId": "t1a2b3c4-...",
"participants": [
{
"name": "Acme Corp",
"email": "[email]",
"role": "signer",
"language": "en"
},
{
"name": "Anna Lindström",
"email": "[email]",
"role": "signer",
"language": "sv"
}
],
"roleMapping": { "0": 0, "1": 1 }
}'
roleMapping maps the template's role index (key) to the participant's index
in the participants array (value). In the example above, template role 0
maps to participant 0 and template role 1 maps to participant 1.
Contacts
POST /organizations/:orgId/contacts
Create a new contact in the organization's address book. Contacts can be global (organization-wide) or scoped to a specific project.
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| name | string | Yes | Contact name (1–255 characters) |
| string | Yes | Email address | |
| projectId | string | No | UUID of a project to scope the contact to. Omit for a global contact |
| phone | string | No | Phone number (max 50 characters) |
| title | string | No | Job title (max 255 characters) |
| company | string | No | Company name (max 255 characters) |
| personalNumber | string | No | Swedish personal number (12 digits, YYYYMMDDXXXX) |
| defaultRole | string | No | signer or reviewer (default: signer) |
| defaultLanguage | string | No | sv or en (default: sv) |
| defaultOtpEnabled | boolean | No | Require OTP verification by default (default: false) |
curl -X POST https://api.signeum.se/api/v1/organizations/{orgId}/contacts \
-H "Authorization: Bearer ds_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"name": "Anna Lindström",
"email": "[email]",
"company": "Acme Corp",
"title": "CEO",
"defaultRole": "signer",
"defaultLanguage": "sv"
}'
{
"contact": {
"id": "c1a2b3c4-d5e6-4f7a-8b9c-0d1e2f3a4b5c",
"organizationId": "a1b2c3d4-...",
"name": "Anna Lindström",
"email": "[email]",
"company": "Acme Corp",
"title": "CEO",
"defaultRole": "signer",
"defaultLanguage": "sv",
"defaultOtpEnabled": false,
"createdAt": "2026-03-16T10:00:00Z",
"updatedAt": "2026-03-16T10:00:00Z"
}
}
GET /organizations/:orgId/contacts
List contacts in the organization with pagination, search, and scope filtering.
Query parameters
| Parameter | Type | Description |
|---|---|---|
| page | number | Page number (default: 1) |
| limit | number | Items per page (default: 50, max: 100) |
| q | string | Search by name or email |
| scope | string |
Filter by scope: global, project:{projectId}, or
all (default)
|
| sort | string | Sort order, e.g. name:asc, createdAt:desc |
curl https://api.signeum.se/api/v1/organizations/{orgId}/contacts?q=anna&scope=global \
-H "Authorization: Bearer ds_your_api_key"
{
"contacts": [
{
"id": "c1a2b3c4-...",
"organizationId": "a1b2c3d4-...",
"name": "Anna Lindström",
"email": "[email]",
"company": "Acme Corp",
"defaultRole": "signer",
"defaultLanguage": "sv",
"defaultOtpEnabled": false,
"createdAt": "2026-03-16T10:00:00Z"
}
],
"pagination": {
"page": 1,
"limit": 50,
"total": 1,
"totalPages": 1
}
}
GET /organizations/:orgId/contacts/:contactId
Get details for a specific contact.
curl https://api.signeum.se/api/v1/organizations/{orgId}/contacts/{contactId} \
-H "Authorization: Bearer ds_your_api_key"
{
"contact": {
"id": "c1a2b3c4-d5e6-4f7a-8b9c-0d1e2f3a4b5c",
"organizationId": "a1b2c3d4-...",
"name": "Anna Lindström",
"email": "[email]",
"phone": "+46701234567",
"company": "Acme Corp",
"title": "CEO",
"defaultRole": "signer",
"defaultLanguage": "sv",
"defaultOtpEnabled": false,
"createdAt": "2026-03-16T10:00:00Z",
"updatedAt": "2026-03-16T10:00:00Z"
},
"creator": {
"type": "api_key",
"name": "Production API Key",
"apiKeyId": "k1a2b3c4-..."
}
}
PUT /organizations/:orgId/contacts/:contactId
Update an existing contact. Only the fields you include will be updated.
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| name | string | No | Contact name (1–255 characters) |
| string | No | Email address | |
| phone | string | null | No | Phone number. Set to null to clear |
| title | string | null | No | Job title. Set to null to clear |
| company | string | null | No | Company name. Set to null to clear |
| personalNumber | string | null | No | Swedish personal number. Set to null to clear |
| defaultRole | string | No | signer or reviewer |
| defaultLanguage | string | No | sv or en |
| defaultOtpEnabled | boolean | No | Require OTP verification by default |
curl -X PUT https://api.signeum.se/api/v1/organizations/{orgId}/contacts/{contactId} \
-H "Authorization: Bearer ds_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"phone": "+46701234567",
"title": "CTO"
}'
{
"contact": {
"id": "c1a2b3c4-d5e6-4f7a-8b9c-0d1e2f3a4b5c",
"organizationId": "a1b2c3d4-...",
"name": "Anna Lindström",
"email": "[email]",
"phone": "+46701234567",
"company": "Acme Corp",
"title": "CTO",
"defaultRole": "signer",
"defaultLanguage": "sv",
"defaultOtpEnabled": false,
"createdAt": "2026-03-16T10:00:00Z",
"updatedAt": "2026-03-16T12:00:00Z"
}
}
DELETE /organizations/:orgId/contacts/:contactId
Delete a contact from the address book.
curl -X DELETE https://api.signeum.se/api/v1/organizations/{orgId}/contacts/{contactId} \
-H "Authorization: Bearer ds_your_api_key"
No content
Webhook Endpoints
POST /organizations/:orgId/projects/:projectId/webhooks
Create a new webhook endpoint for a project. Webhook endpoints receive POST requests for document lifecycle events.
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| url | string | Yes | HTTPS URL to receive webhook events (max 2048 chars) |
| eventTypes | array | Yes |
Event types to subscribe to, e.g.
["document.signed", "document.completed"]
|
| description | string | No | Human-readable description (max 255 chars) |
| includeFields | array | No |
Extra fields to include in the payload:
tags, taxonomy, risk_flags
|
| enabled | boolean | No | Whether the endpoint is active (default: true) |
curl -X POST https://api.signeum.se/api/v1/organizations/{orgId}/projects/{projectId}/webhooks \
-H "Authorization: Bearer ds_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"url": "https://your-app.com/webhooks/signeum",
"eventTypes": ["document.signed", "document.completed"],
"description": "Production webhook"
}'
{
"endpoint": {
"id": "w1a2b3c4-...",
"projectId": "e5f6a7b8-...",
"organizationId": "a1b2c3d4-...",
"url": "https://your-app.com/webhooks/signeum",
"description": "Production webhook",
"eventTypes": ["document.signed", "document.completed"],
"includeFields": null,
"enabled": true,
"createdAt": "2026-03-16T10:00:00Z",
"updatedAt": "2026-03-16T10:00:00Z"
}
}
GET /organizations/:orgId/projects/:projectId/webhooks
List all webhook endpoints configured for a project.
curl https://api.signeum.se/api/v1/organizations/{orgId}/projects/{projectId}/webhooks \
-H "Authorization: Bearer ds_your_api_key"
{
"endpoints": [
{
"id": "w1a2b3c4-...",
"projectId": "e5f6a7b8-...",
"url": "https://your-app.com/webhooks/signeum",
"description": "Production webhook",
"eventTypes": ["document.signed", "document.completed"],
"enabled": true,
"createdAt": "2026-03-16T10:00:00Z",
"updatedAt": "2026-03-16T10:00:00Z"
}
]
}
GET /organizations/:orgId/projects/:projectId/webhooks/:webhookId
Get details for a specific webhook endpoint.
curl https://api.signeum.se/api/v1/organizations/{orgId}/projects/{projectId}/webhooks/{webhookId} \
-H "Authorization: Bearer ds_your_api_key"
{
"endpoint": {
"id": "w1a2b3c4-...",
"projectId": "e5f6a7b8-...",
"organizationId": "a1b2c3d4-...",
"url": "https://your-app.com/webhooks/signeum",
"description": "Production webhook",
"eventTypes": ["document.signed", "document.completed"],
"includeFields": null,
"enabled": true,
"createdAt": "2026-03-16T10:00:00Z",
"updatedAt": "2026-03-16T10:00:00Z"
}
}
PUT /organizations/:orgId/projects/:projectId/webhooks/:webhookId
Update an existing webhook endpoint. All fields are optional.
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| url | string | No | New HTTPS URL |
| eventTypes | array | No | Updated event types |
| description | string | null | No | Updated description (set to null to clear) |
| includeFields | array | null | No | Updated extra fields (set to null to clear) |
| enabled | boolean | No | Enable or disable the endpoint |
curl -X PUT https://api.signeum.se/api/v1/organizations/{orgId}/projects/{projectId}/webhooks/{webhookId} \
-H "Authorization: Bearer ds_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"eventTypes": ["document.signed", "document.completed", "document.cancelled"],
"enabled": true
}'
{
"endpoint": {
"id": "w1a2b3c4-...",
"projectId": "e5f6a7b8-...",
"organizationId": "a1b2c3d4-...",
"url": "https://your-app.com/webhooks/signeum",
"description": "Production webhook",
"eventTypes": ["document.signed", "document.completed", "document.cancelled"],
"includeFields": null,
"enabled": true,
"createdAt": "2026-03-16T10:00:00Z",
"updatedAt": "2026-03-16T12:00:00Z"
}
}
DELETE /organizations/:orgId/projects/:projectId/webhooks/:webhookId
Delete a webhook endpoint.
curl -X DELETE https://api.signeum.se/api/v1/organizations/{orgId}/projects/{projectId}/webhooks/{webhookId} \
-H "Authorization: Bearer ds_your_api_key"
No content
POST /organizations/:orgId/projects/:projectId/webhooks/:webhookId/test
Send a webhook.test event to the endpoint. The test event goes through the
full delivery pipeline including HMAC signing, so you can verify your receiver
end-to-end.
curl -X POST https://api.signeum.se/api/v1/organizations/{orgId}/projects/{projectId}/webhooks/{webhookId}/test \
-H "Authorization: Bearer ds_your_api_key"
{
"success": true,
"statusCode": 200,
"error": null
}
GET /organizations/:orgId/projects/:projectId/webhooks/:webhookId/logs
Return paginated delivery logs for a webhook endpoint, most recent first.
Query parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
| page | integer | 1 | Page number |
| limit | integer | 20 | Items per page (max 100) |
curl https://api.signeum.se/api/v1/organizations/{orgId}/projects/{projectId}/webhooks/{webhookId}/logs?page=1&limit=10 \
-H "Authorization: Bearer ds_your_api_key"
{
"logs": [
{
"id": "log-uuid-...",
"webhookEndpointId": "w1a2b3c4-...",
"eventType": "document.signed",
"statusCode": 200,
"success": true,
"createdAt": "2026-03-16T10:30:00Z"
}
],
"total": 42,
"page": 1,
"limit": 10
}