# NextSign API Documentation Full Context > Expanded machine-readable reference for the NextSign API docs. This derivative file is published for tooling that prefers a single long context file. Primary docs index: https://docs.nextsign.dk/docs Short overview file: https://docs.nextsign.dk/llms.txt OpenAPI contract: https://docs.nextsign.dk/openapi.json Important notes: - This file is generated from the published documentation pages in the repository. - It preserves examples and code blocks, but removes frontmatter and import boilerplate. - The canonical human-facing pages remain the source of truth. ## Contents - [Introduction](https://docs.nextsign.dk/docs): Complete API documentation for automating forms, cases, document signing, and file retrieval using NextSign. - [Authorization](https://docs.nextsign.dk/docs/authorization): Find your API key and companyId, and authenticate requests to the NextSign API. - [Test Environment](https://docs.nextsign.dk/docs/testenv): Build and validate your NextSign integration safely without affecting production data. - [API Versioning](https://docs.nextsign.dk/docs/versioning): Understand how NextSign versions endpoints and how to safely work with both v2 and v3 routes. - [Rate Limits](https://docs.nextsign.dk/docs/rate-limits): Understand the current NextSign API request limit and how to build retry-safe clients around it. - [Idempotency](https://docs.nextsign.dk/docs/idempotency): Understand the difference between request idempotency, resource IDs, and webhook deduplication in NextSign. - [Forms Get](https://docs.nextsign.dk/docs/forms-get): Get a form by ID using GET /api/v2/{company}/forms/{form_id}/get. - [Forms Tags](https://docs.nextsign.dk/docs/forms-tags): Get form tags using GET /api/v2/{company}/forms/{form_id}/tags/get. - [Forms Submit](https://docs.nextsign.dk/docs/forms-submit): Submit a form using POST /api/v2/{company}/forms/{form_id}/submit. - [Case List](https://docs.nextsign.dk/docs/cases): List cases with filters using GET /api/v2/{company}/cases/get. - [Case Get Multiple](https://docs.nextsign.dk/docs/cases-get-multiple): Get multiple cases by ID using GET /api/v2/{company}/cases/{case_ids}/get. - [Case Get](https://docs.nextsign.dk/docs/case): Get a single case by ID using GET /api/v2/{company}/case/{case_id}/get. - [Case Create](https://docs.nextsign.dk/docs/case-create): Create a case using POST /api/v2/{company}/case/create. - [Case Delete](https://docs.nextsign.dk/docs/case-delete): Delete a case using GET /api/v2/{company}/case/{case_id}/delete. - [Document Upload](https://docs.nextsign.dk/docs/documents-upload): Upload a file using POST /v3/company/{company}/file/upload. - [Document Retrieve](https://docs.nextsign.dk/docs/documents): Retrieve a pre-signed document URL using POST /v3/company/{company}/file/view-presigned-url. - [Document Convert](https://docs.nextsign.dk/docs/documents-convert): Convert documents using POST /api/v2/document/convert. - [Webhooks](https://docs.nextsign.dk/docs/webhooks): Receive real-time notifications when case and flow events occur in NextSign. - [Webhook Retry Logic](https://docs.nextsign.dk/docs/webhook-retry-logic): Understand how NextSign retries failed webhook deliveries and how to build duplicate-safe handlers. ## Getting Started Authentication, environments, and the fastest way to make your first request. ### Introduction Source: https://docs.nextsign.dk/docs Summary: Complete API documentation for automating forms, cases, document signing, and file retrieval using NextSign. Welcome to the **NextSign API** documentation. NextSign is a comprehensive API platform that allows you to **automate document creation, digital signing, case management, and secure document retrieval**. It is designed for developers who want to integrate legally binding electronic signatures and document workflows directly into their applications. With NextSign, you can generate contracts from templates, populate documents dynamically, send them for signing, track progress, and retrieve signed files — all through a secure and scalable API. --- ## What You Can Build Using the NextSign API, you can: - Automate contract creation from reusable templates - Populate documents dynamically using tags - Send documents for electronic signing - Track and manage signing cases - Retrieve signed documents securely - Convert DOCX templates to PDF with tag replacement The API is suitable for CRMs, ERP systems, internal tools, and customer-facing platforms. --- ## Core API Modules ### Forms Forms allow you to create reusable document templates that can be submitted multiple times with different data. They are ideal for contracts, agreements, and standardized documents. With the Forms API, you can: - Retrieve form details and configuration - Fetch available tags for dynamic data population - Submit forms to create signing cases - Control reminders, expiration, access roles, and deletion policies --- ### Cases Cases represent individual signing processes created from forms or direct submissions. Using the Cases API, you can: - Retrieve multiple cases using filters - Fetch specific cases by ID - Monitor signing status and metadata - Build dashboards and reporting tools --- ### Documents The Documents API provides secure access to files, uploads, and document conversion features. You can: - Upload larger files before case creation - Generate pre-signed URLs for signed documents - Retrieve completed contracts securely - Convert DOCX files to PDF - Replace tags dynamically before sending or previewing documents --- ## Platform Guides The platform guides explain how to build integrations that stay reliable in production: - Use [Authorization](https://docs.nextsign.dk/docs/authorization) to set up API keys and `companyId` - Use [API Versioning](https://docs.nextsign.dk/docs/versioning) to understand when to call `v2` and when to call `v3` - Use [Rate Limits](https://docs.nextsign.dk/docs/rate-limits) to design around the current `100 requests per minute` limit - Use [Idempotency](https://docs.nextsign.dk/docs/idempotency) to understand request deduplication limits and webhook-safe handling - Use [Webhooks](https://docs.nextsign.dk/docs/webhooks) and [Webhook Retry Logic](https://docs.nextsign.dk/docs/webhook-retry-logic) to handle event delivery safely --- ## Authentication All API requests require authentication using a **Bearer Token**. Use your NextSign API key as the bearer token in the `Authorization` header for every request. ```http Authorization: Bearer YOUR_API_KEY ``` ### Creating an API Key NextSign uses Bearer Token authentication, and your API key is the bearer credential you send with each request. #### Steps to Create an API Key 1. **Log in to your NextSign Dashboard** 2. Navigate to **Dashboard → Config → API Keys** 3. Click **Create API Key** 4. Enter a name for the key (for example: `Production API Key` or `Backend Service`) 5. Click **Generate** Once generated, your API key will be displayed only once. Your `companyId` is also available on the **API Keys** page, shown at the top of the screen. **Important:** For security reasons, the API key cannot be viewed again after you leave this screen. Make sure to copy and store it securely on your computer or in a password manager before closing the page. If the key is lost, you will need to generate a new API key from the dashboard. #### Using the API Key Include the API key in the `Authorization` header of your API requests: ```http Authorization: Bearer YOUR_API_KEY ``` **Example Request:** ```bash title="cURL" curl --location 'https://www.nextsign.dk/api/v2/{company}/cases/get' \ -H 'Authorization: Bearer YOUR_API_KEY' ``` ```javascript title="JavaScript" const response = await fetch( 'https://www.nextsign.dk/api/v2/{company}/cases/get', { method: 'GET', headers: { 'Authorization': 'Bearer YOUR_API_KEY' } } ); ``` ```python title="Python" import requests url = "https://www.nextsign.dk/api/v2/{company}/cases/get" headers = { "Authorization": "Bearer YOUR_API_KEY" } response = requests.get(url, headers=headers) ``` ### Security Best Practices Following security best practices is essential when working with API keys: - **Do not expose your API key in client-side code** - API keys should only be used in server-side applications - **Never commit API keys to version control** - Use environment variables or `.env` files (added to `.gitignore`) - **Rotate keys periodically** and revoke unused ones from the dashboard - **Store keys securely** using environment variables or a secrets manager (e.g., AWS Secrets Manager, Azure Key Vault, HashiCorp Vault) - **Use separate API keys** for development, staging, and production environments - **Monitor API key usage** regularly to detect any unauthorized access If you suspect your API key has been compromised, immediately revoke it from the NextSign dashboard and generate a new one. --- ## Getting Started Ready to integrate NextSign into your application? Here's how to get started: 1. **Generate your API key** from the NextSign dashboard 2. **Choose an API module** - Start with Forms, Cases, or Documents based on your use case 3. **Make your first request** - Test authentication and basic endpoints 4. **Build your integration** - Refer to the detailed endpoint documentation for each module Explore the documentation sections for detailed information on each API module and endpoint. --- ## Endpoint Reference ### Setup - [Authorization](https://docs.nextsign.dk/docs/authorization) - API key, bearer token, and companyId setup - [API Versioning](https://docs.nextsign.dk/docs/versioning) - Understand how `v2` and `v3` endpoints are versioned - [Rate Limits](https://docs.nextsign.dk/docs/rate-limits) - Plan around the current `100 requests per minute` limit - [Idempotency](https://docs.nextsign.dk/docs/idempotency) - Build duplicate-safe request and webhook handling - [Test Environment](https://docs.nextsign.dk/docs/testenv) - Work safely against the test environment ### Forms - [Forms Get](https://docs.nextsign.dk/docs/forms-get) - Retrieve a form by ID - [Forms Tags](https://docs.nextsign.dk/docs/forms-tags) - Retrieve available tags for a form - [Forms Submit](https://docs.nextsign.dk/docs/forms-submit) - Submit a form and create a case ### Cases - [Case List](https://docs.nextsign.dk/docs/cases) - List cases with filters - [Case Get Multiple](https://docs.nextsign.dk/docs/cases-get-multiple) - Fetch multiple cases by ID - [Case Get](https://docs.nextsign.dk/docs/case) - Fetch a single case by ID - [Case Create](https://docs.nextsign.dk/docs/case-create) - Create a new case - [Case Delete](https://docs.nextsign.dk/docs/case-delete) - Delete a case by ID ### Documents - [Document Upload](https://docs.nextsign.dk/docs/documents-upload) - Upload larger files before case creation - [Document Retrieve](https://docs.nextsign.dk/docs/documents) - Generate a pre-signed URL for a stored document - [Document Convert](https://docs.nextsign.dk/docs/documents-convert) - Convert DOCX to PDF with tag replacement ### Platform - [Webhooks](https://docs.nextsign.dk/docs/webhooks) - Receive event notifications - [Webhook Retry Logic](https://docs.nextsign.dk/docs/webhook-retry-logic) - Understand retry timing and duplicate-safe consumers ### Authorization Source: https://docs.nextsign.dk/docs/authorization Summary: Find your API key and companyId, and authenticate requests to the NextSign API. NextSign API requests use your **API key** as a bearer token and your **companyId** as part of the request URL. ## 1. Create an API Key Create API keys from the NextSign dashboard: 1. Open **Dashboard → Config → API Keys** 2. Click **Create API Key** 3. Enter a name such as `Backend Service` or `Test Integration` 4. Click **Generate** 5. Copy the key and store it securely Your API key is shown only once. If you lose it, create a new one and revoke the old key. ## 2. Use the API Key as Bearer Token Send the API key in the `Authorization` header: ```http Authorization: Bearer YOUR_API_KEY ``` Example: ```bash title="cURL" curl --location 'https://www.nextsign.dk/api/v2/{company}/cases/get' \ -H 'Authorization: Bearer YOUR_API_KEY' ``` ## 3. Find Your companyId Your `companyId` is the company identifier used in API paths such as: ```text https://www.nextsign.dk/api/v2/{company}/cases/get ``` The easiest places to find it: - At the top of **Dashboard → Config → API Keys** - In many dashboard URLs, where it appears as the `/user/{companyId}/...` path segment - In existing API requests or integration config where the company identifier is already stored Recommended approach: 1. Open **Dashboard → Config → API Keys** 2. Copy the `companyId` shown at the top of the page 3. Use the URL only as a fallback if needed Example dashboard-style URL: ```text https://www.nextsign.dk/user/6576e19662ec13c4faa7de7b/settings ``` In this example, `6576e19662ec13c4faa7de7b` is the `companyId`. ## 4. Recommended Environment Variables ```bash title=".env" NEXTSIGN_API_KEY=your_api_key_here NEXTSIGN_COMPANY_ID=your_company_id_here NEXTSIGN_API_URL=https://www.nextsign.dk/api ``` ## 5. First Authenticated Request ```bash title="cURL" curl --location "https://www.nextsign.dk/api/v2/${NEXTSIGN_COMPANY_ID}/cases/get" \ -H "Authorization: Bearer ${NEXTSIGN_API_KEY}" ``` ## Typical Error Responses Missing bearer token: ```json title="Unauthorized" { "errors": [ { "code": 401, "message": "Unauthorized", "description": "Bearer token not found" } ] } ``` Invalid company id: ```json title="Bad Request" { "code": 400, "message": "Bad Request", "description": "Invalid company id" } ``` Some older v2 endpoints return validation or authorization problems inside a `200 OK` response with an `errors` array. In integrations, validate both the HTTP status code and the response body. ### Test Environment Source: https://docs.nextsign.dk/docs/testenv Summary: Build and validate your NextSign integration safely without affecting production data. NextSign provides a **Test Environment** that is a full replica of the production environment. It allows you to safely build, test, and validate your integration without affecting live data or real signing workflows. The test environment mirrors production behavior, APIs, and workflows, making it the perfect sandbox for development and quality assurance. --- ## Use Cases The test environment is ideal for: - **API integration testing** - Test all API endpoints and responses - **Webhook testing** - Validate webhook payloads and event handling - **Document generation and signing flows** - Test form submissions and case creation - **End-to-end workflow validation** - Verify complete signing workflows - **Development and QA** - Safe environment for developers and QA teams - **Staging deployments** - Pre-production testing before going live --- ## Test Environment URL Access the test environment at: ```text https://test.nextsign.dk ``` Test environment users are separate from production. You must create a new user on [https://test.nextsign.dk](https://test.nextsign.dk) to access the test environment, and all data and configurations are kept completely separate. --- ## Key Characteristics The test environment provides: | Feature | Description | | ------- | ----------- | | **Same API structure** | Identical endpoints and request/response formats as production | | **Same authentication** | Uses Bearer Token authentication, configured the same way | | **Same webhook flow** | Full webhook configuration and delivery system | | **Isolated data** | No impact on production data or workflows | | **Safe testing** | Perfect for development, QA, and staging purposes | | **Full feature parity** | All production features available for testing | Data created in the test environment is completely isolated from production and may be periodically cleared. Do not rely on test data for long-term storage. --- ## API Keys in Test Environment API keys are environment-specific and **cannot be shared between test and production**. ### Key Separation You must generate separate API keys for: - ✅ **Test environment** - For development and testing - ✅ **Production environment** - For live applications API keys created in the test environment are NOT valid in production, and vice versa. Attempting to use a production API key in the test environment will result in authentication errors. ### Creating Test API Keys Generate test API keys from the test environment dashboard: 1. Create a new user on [https://test.nextsign.dk](https://test.nextsign.dk) and log in with that test account 2. Navigate to **Dashboard → Config → API Keys** 3. Click **Create API Key** 4. Enter a descriptive name (e.g., `Development API Key`) 5. Click **Generate** 6. Copy and securely store the API key **Example test API key usage:** ```bash title="cURL - Test Environment" curl --location 'https://www.nextsign.dk/api/v2/{company}/cases/get' \ -H 'Authorization: Bearer TEST_API_KEY_HERE' ``` --- ## Webhooks in Test Environment Webhooks configured in the test environment are also isolated from production. ### Test Webhook Behavior Webhooks in the test environment: - ✅ Trigger only for test cases and test workflows - ✅ Must point to a publicly accessible test or staging endpoint - ✅ Are ideal for validating webhook payloads and event handling - ✅ Use the same event types and payload structure as production ### Recommended Webhook Setup For testing webhooks, you can: 1. **Use a staging server** - Point to your staging/development backend 2. **Use webhook testing tools** - Services like [webhook.site](https://webhook.site) or [RequestBin](https://requestbin.com) 3. **Use ngrok for local development** - Tunnel your local server for testing **Example test webhook configuration:** ```text URL: https://staging.yourapp.com/webhooks Headers: X-Signature: test-secret-key Triggers: ☑ Case created ☑ Case signed ☑ Case denied ``` Test webhooks will only receive events from test cases. This ensures complete isolation between test and production environments. --- ## Testing Best Practices ### 1. Use Environment Variables Keep your API keys and configuration separate using environment variables: ```javascript title=".env.development" NEXTSIGN_API_URL=https://www.nextsign.dk/api NEXTSIGN_API_KEY=your_test_api_key_here NEXTSIGN_COMPANY_ID=your_test_company_id ``` ```javascript title=".env.production" NEXTSIGN_API_URL=https://www.nextsign.dk/api NEXTSIGN_API_KEY=your_production_api_key_here NEXTSIGN_COMPANY_ID=your_production_company_id ``` ### 2. Test All Event Types Ensure you test all webhook event types in the test environment: - Case created - Recipient signed - Case signed - Case denied - Workflow completed ### 3. Validate Complete Workflows Test the entire signing workflow from start to finish: 1. Create a form or submit a case 2. Send for signing 3. Complete the signing process 4. Verify webhook delivery 5. Retrieve signed documents ### 4. Load Testing Use the test environment for load testing to understand API rate limits and performance characteristics. ### 5. Error Handling Test error scenarios: - Invalid API keys - Missing required fields - Malformed requests - Webhook failures and retries --- ## Differences from Production While the test environment mirrors production, be aware of these differences: | Aspect | Test Environment | Production Environment | | ------ | ---------------- | ---------------------- | | **Data persistence** | May be periodically cleared | Permanent storage | | **Email delivery** | May go to test email addresses | Real recipient emails | | **API keys** | Separate test keys required | Separate production keys required | | **Webhooks** | Point to staging/test endpoints | Point to production endpoints | | **Domain** | test.nextsign.dk | nextsign.dk | | **Performance** | May have lower rate limits | Production rate limits | Email notifications in the test environment may be sent to test addresses or suppressed entirely to prevent accidental communication with real recipients. --- ## Going Live Once your integration has been fully tested in the test environment, follow these steps to deploy to production: ### Pre-Launch Checklist - [ ] All API integrations tested and working in test environment - [ ] Webhook endpoints tested and validated - [ ] Complete signing workflows tested end-to-end - [ ] Error handling and edge cases covered - [ ] Security best practices implemented - [ ] Documentation and runbooks prepared ### Migration Steps 1. **Switch to the production dashboard** - Log in to [https://nextsign.dk](https://nextsign.dk) 2. **Generate production API keys** - Navigate to **Dashboard → Config → API Keys** - Create new API keys for production use - Store them securely in your production secrets manager 3. **Update your application configuration** - Replace test API keys with production keys - Update environment variables - Change API base URLs if different - Update company identifiers 4. **Recreate webhooks in production** - Configure the same webhook triggers as test - Point to production endpoints (not staging) - Use production secret keys for signature validation - Test webhook delivery with a real case 5. **Verify production setup** - Test a complete workflow in production - Verify webhook delivery - Check document retrieval - Monitor for errors ### Example Environment Switch ```javascript title="config.js" const config = { test: { apiUrl: 'https://www.nextsign.dk/api', apiKey: process.env.NEXTSIGN_TEST_API_KEY, companyId: 'test-company-id' }, production: { apiUrl: 'https://www.nextsign.dk/api', apiKey: process.env.NEXTSIGN_PROD_API_KEY, companyId: 'prod-company-id' } }; const env = process.env.NODE_ENV || 'test'; module.exports = config[env]; ``` Never use test API keys in production or production API keys in test. Always maintain strict separation between environments. --- ## Monitoring and Debugging ### Test Environment Logs Monitor your test integration using: - **Dashboard logs** - View case creation and signing activity - **Webhook logs** - Check webhook delivery status and payloads - **API response codes** - Validate request/response patterns ### Common Issues #### Authentication Errors - Verify you're using test API keys in the test environment - Check that API keys are correctly configured in headers #### Webhook Not Triggering - Ensure webhooks are enabled (Active toggle is ON) - Verify the webhook URL is publicly accessible - Check webhook logs in the dashboard for delivery errors #### Rate Limiting - Test environment may have lower rate limits than production - Design your client around the documented `100 requests per minute` limit - Implement retry logic with exponential backoff --- ## Support If you encounter issues while testing: 1. Check the **dashboard logs** for error messages 2. Review the **webhook delivery logs** for webhook issues 3. Verify your **API key configuration** 4. Consult the **API documentation** for endpoint requirements 5. Contact NextSign support with specific error details When reporting issues, always specify whether the problem occurs in the test environment, production environment, or both. ## Platform Versioning, rate limits, and idempotency guidance for reliable integrations. ### API Versioning Source: https://docs.nextsign.dk/docs/versioning Summary: Understand how NextSign versions endpoints and how to safely work with both v2 and v3 routes. NextSign versions its API in the request path. Integrate against the exact version documented for each endpoint instead of assuming all resources exist in the same version. ## Current Versioning Pattern The current documentation uses URL-based versioning: ```text https://www.nextsign.dk/api/v2/... https://api.nextsign.dk/v3/... ``` Examples from the current API surface: - Cases and forms are primarily documented under `v2` - Newer file endpoints such as upload and view-presigned-url are documented under `v3` Do not try to upgrade an endpoint by changing only the version segment in the URL. Treat each documented path as its own contract. ## What This Means for Integrations - Pin the full endpoint URL in your client code - Keep tests for each version you depend on - Expect request and response behavior to differ between older and newer endpoints - Validate both HTTP status codes and response bodies on older `v2` endpoints ## Recommended Strategy When building or updating an integration: 1. Use the exact endpoint path from the docs 2. Keep `v2` and `v3` routes separate in code 3. Test version changes in the test environment before switching production traffic 4. Review request examples and error handling for the specific endpoint you call ## Common Mistakes to Avoid - Assuming every file endpoint is under `v2` - Assuming every newer endpoint also has a `v2` equivalent - Using a single global version variable for all NextSign requests - Skipping regression tests when moving from one versioned route to another ## Commonly Used Examples - [Case Create](https://docs.nextsign.dk/docs/case-create) uses `POST /api/v2/{company}/case/create` - [Forms Submit](https://docs.nextsign.dk/docs/forms-submit) uses `POST /api/v2/{company}/forms/{form_id}/submit` - [Document Upload](https://docs.nextsign.dk/docs/documents-upload) uses `POST /v3/company/{company}/file/upload` - [Document Retrieve](https://docs.nextsign.dk/docs/documents) uses `POST /v3/company/{company}/file/view-presigned-url` ## Related Guides - [Authorization](https://docs.nextsign.dk/docs/authorization) - [Rate Limits](https://docs.nextsign.dk/docs/rate-limits) - [Test Environment](https://docs.nextsign.dk/docs/testenv) ### Rate Limits Source: https://docs.nextsign.dk/docs/rate-limits Summary: Understand the current NextSign API request limit and how to build retry-safe clients around it. The current documented rate limit for the NextSign API is **100 requests per minute**. Design your integration so it stays below the limit during normal traffic and during retries after transient failures. ## Practical Guidance - Queue outbound requests from your backend instead of firing large bursts at once - Limit concurrency for write operations such as case creation and form submission - Cache repeated reads where it makes sense - Avoid automatic retry storms after timeouts or network errors ## How to Handle Throttling If your client is throttled or you receive temporary failures: 1. Stop sending immediate repeat requests 2. Retry with exponential backoff and jitter 3. Keep write operations idempotent on your side 4. Resume gradually instead of sending a full burst again Suggested retry pattern for your own client: - Retry 1: after 1 second - Retry 2: after 2 seconds - Retry 3: after 4 seconds - Retry 4: after 8 seconds - Retry 5: after 16 seconds If your HTTP client receives a `Retry-After` header, follow that value before applying your own schedule. ## Planning for Production Treat `100 requests per minute` as a hard planning ceiling for your integration. If your use case includes imports, bulk signing flows, or scheduled jobs, add buffering and queueing in your application layer. ## Test Environment Validate your throughput strategy in the test environment before going live: - confirm your retry behavior - confirm your queue drains at an acceptable pace - confirm your UI or backend degrades gracefully when requests are delayed ## Related Guides - [Idempotency](https://docs.nextsign.dk/docs/idempotency) - [Webhook Retry Logic](https://docs.nextsign.dk/docs/webhook-retry-logic) - [Test Environment](https://docs.nextsign.dk/docs/testenv) ### Idempotency Source: https://docs.nextsign.dk/docs/idempotency Summary: Understand the difference between request idempotency, resource IDs, and webhook deduplication in NextSign. Idempotency means a repeated operation should not create unintended duplicates or side effects. This matters when your client retries requests or when webhook deliveries are repeated. ## Request Idempotency NextSign does **not** currently provide built-in request idempotency for create endpoints such as [Case Create](https://docs.nextsign.dk/docs/case-create) or [Forms Submit](https://docs.nextsign.dk/docs/forms-submit). That means sending the same `POST` request twice can create two separate resources if both requests are accepted. Do not treat `_id` or `referenceId` as a built-in idempotency key for requests. They do not prevent duplicate creates by themselves. ## What `_id` Means `_id` is the unique identifier of a resource after it exists. Use `_id` for: - fetching an existing case, flow, or form - storing relations between your records and NextSign resources - correlating webhook payloads with resources already stored in your system `_id` is resource identity, not request idempotency. You only know it after the resource has been created. ## What `referenceId` Means Case creation and form submission also support `referenceId`. Use it as a business correlation field between your system and NextSign. Typical uses: - attach your internal order or contract number to a case - trace retries and support cases more easily - reconcile user actions and webhook events in your own system `referenceId` is useful for your own matching and reconciliation, but it is not a uniqueness guarantee enforced by the API. ## Recommended Pattern for Create Requests For create flows where the client might retry: 1. Generate your own operation ID before sending the request 2. Store the request intent in your own database 3. Send the request once 4. If the result is unknown, reconcile using your own stored context before retrying 5. Store the returned `_id` once the request succeeds ## Webhook Deduplication Webhook deliveries can be repeated when delivery fails and is retried. Your webhook consumer should therefore be idempotent even though the create endpoints themselves are not request-idempotent. Because the same case can emit multiple webhook events over time, a practical event-level deduplication key is: ```javascript function getIdempotencyKey(payload) { const entity = payload.case || payload.flow; return [payload.trigger, entity?._id, entity?.updatedAt].filter(Boolean).join(':'); } ``` Store that key in your database or queue processor and ignore deliveries you have already handled successfully. ## Good Defaults - make write operations originate from your backend, not directly from the browser - log your own operation ID together with `referenceId` and the returned `_id` - do not retry unknown `POST` outcomes blindly - make webhook handlers safe to run more than once ## Related Guides - [Webhook Retry Logic](https://docs.nextsign.dk/docs/webhook-retry-logic) - [Webhooks](https://docs.nextsign.dk/docs/webhooks) - [Rate Limits](https://docs.nextsign.dk/docs/rate-limits) ## Forms Retrieve templates, inspect tags, and submit forms to create signing cases. ### Forms Get Source: https://docs.nextsign.dk/docs/forms-get Summary: Get a form by ID using GET /api/v2/{company}/forms/{form_id}/get. Use this endpoint to retrieve a single form and inspect its settings, recipients, tags, and attached documents. ## Authorization All requests to this endpoint require authentication using a **Bearer Token**. Use your NextSign API key as the bearer token for this endpoint. --- ## Endpoint ```text GET https://www.nextsign.dk/api/v2/{company}/forms/{form_id}/get ``` ## Path Parameters | Parameter | Type | Description | | --------- | ---- | ----------- | | `company` | `string` | Your company identifier | | `form_id` | `string` | Unique identifier of the form | ## Example Request ```bash title="cURL" curl --location 'https://www.nextsign.dk/api/v2/{company}/forms/{form_id}/get' \ -H 'Authorization: Bearer YOUR_API_KEY' ``` ## Example Response ```json title="200 OK" { "_id": "65f493fb52bb6d8baa5d16af", "companyId": "6576e19662ec13c4faa7de7b", "folder": "Default", "active": true, "title": "Example Form", "settings": { "deletion": { "autoDelete": true, "days": 30 }, "availability": { "unlimited": false, "days": 10, "isExpired": false }, "reminders": { "send": true, "amount": 2, "daysBetween": 3 }, "allowAPI": true }, "tags": [], "recipients": [], "documents": [] } ``` ## Response Fields | Field | Type | Description | | ----- | ---- | ----------- | | `_id` | `string` | Unique form identifier | | `companyId` | `string` | Company identifier | | `title` | `string` | Form title | | `folder` | `string` | Folder name where the form is stored | | `active` | `boolean` | Whether the form is active | | `settings` | `object` | Form configuration settings | | `tags` | `array` | Form tag definitions | | `recipients` | `array` | Recipient configuration | | `documents` | `array` | Documents attached to the form | ## Common Error Responses Form not found: ```json { "errors": [ { "code": 400, "message": "Bad Request", "description": "Form not found" } ] } ``` Server error: ```json { "message": "Internal server error", "errors": [] } ``` ## Related Endpoints - [Forms Tags](https://docs.nextsign.dk/docs/forms-tags) - [Forms Submit](https://docs.nextsign.dk/docs/forms-submit) ### Forms Tags Source: https://docs.nextsign.dk/docs/forms-tags Summary: Get form tags using GET /api/v2/{company}/forms/{form_id}/tags/get. Use this endpoint to retrieve the tags a form expects so you can construct a valid submit payload. ## Authorization All requests to this endpoint require authentication using a **Bearer Token**. Use your NextSign API key as the bearer token for this endpoint. --- ## Endpoint ```text GET https://www.nextsign.dk/api/v2/{company}/forms/{form_id}/tags/get ``` ## Path Parameters | Parameter | Type | Description | | --------- | ---- | ----------- | | `company` | `string` | Your company identifier | | `form_id` | `string` | Unique identifier of the form | ## Example Request ```bash title="cURL" curl --location 'https://www.nextsign.dk/api/v2/{company}/forms/{form_id}/tags/get' \ -H 'Authorization: Bearer YOUR_API_KEY' ``` ## Example Response ```json title="200 OK" [ { "name": "customer_name", "tag": "customer_name", "value": "", "type": "text", "_id": "65d34e9df381256d6be8cb78" }, { "name": "customer_zip", "tag": "customer_zip", "value": "", "type": "text", "_id": "65d34e9df381256d6be8cb76" } ] ``` ## Response Fields | Field | Type | Description | | ----- | ---- | ----------- | | `_id` | `string` | Unique tag identifier | | `name` | `string` | Human-readable tag name | | `tag` | `string` | Tag identifier used in the template | | `value` | `string` | Default tag value | | `type` | `string` | Tag type, for example `text`, `number`, or `date` | ## Common Error Responses Company or form not found: ```json { "errors": [ { "code": 400, "message": "Bad Request", "description": "Company not found" } ] } ``` Missing bearer token: ```json { "errors": [ { "code": 401, "message": "Unauthorized", "description": "Bearer token not found" } ] } ``` ## Related Endpoints - [Forms Get](https://docs.nextsign.dk/docs/forms-get) - [Forms Submit](https://docs.nextsign.dk/docs/forms-submit) ### Forms Submit Source: https://docs.nextsign.dk/docs/forms-submit Summary: Submit a form using POST /api/v2/{company}/forms/{form_id}/submit. Use this endpoint to submit a form with tag values and recipients. A successful submission creates a case from the form configuration. ## Authorization All requests to this endpoint require authentication using a **Bearer Token**. Use your NextSign API key as the bearer token for this endpoint. --- ## Endpoint ```text POST https://www.nextsign.dk/api/v2/{company}/forms/{form_id}/submit ``` ## Path Parameters | Parameter | Type | Description | | --------- | ---- | ----------- | | `company` | `string` | Your company identifier | | `form_id` | `string` | Unique identifier of the form to submit | ## Request Body | Field | Type | Required | Description | | ----- | ---- | -------- | ----------- | | `title` | `string` | No | Override the default form title | | `customMessage` | `boolean` | No | Whether to use a custom message | | `message` | `string` | No | Custom message shown to recipients | | `user_name` | `string` | No | Sender name | | `user_email` | `string` | No | Sender email | | `state` | `string` | No | Case state such as `open` | | `signingSchemas` | `array` | No | Allowed signature methods | | `settings` | `object` | No | Case settings override | | `folder` | `string` | No | Destination folder | | `tags` | `array` | **Yes** | Tag values to inject into the form | | `recipients` | `array` | **Yes** | Recipient list for the generated case | The structure of `settings`, `signingSchemas`, and recipient objects follows the same pattern as [Case Create](https://docs.nextsign.dk/docs/case-create), including optional `recipient.signingSchema` overrides. ### Recipient Fields | Field | Type | Description | | ----- | ---- | ----------- | | `name` | `string` | Recipient name | | `email` | `string` | Recipient email | | `signing` | `boolean` | Whether the recipient must sign | | `group` | `number` | Preferred signing order/group. Lower numbers are invited first | | `order` | `number` | Legacy alias for `group` on forms submit | | `needsCpr` | `boolean` | Whether CPR validation is required | | `cpr` | `string` | Recipient CPR number. Required when `needsCpr` is `true` | | `signingSchema` | `string` | Optional recipient-specific signing method override | | `redirectUrl` | `string` | Redirect URL after signing. Must be sent inside the recipient object, and is returned back on `contract.recipients[].redirectUrl` in the response | | `phone` | `string` | Phone number for SMS delivery | | `position` | `string` | Signature title/position | | `type` | `string` | Delivery type. Supported values are `email`, `phone`, and `eboks` | | `template` | `string` | Optional recipient-specific template id | | `message` | `string` | Custom recipient message | | `eboks` | `object` | e-Boks delivery payload such as `kvhx`, `address`, and `name` | If a submitted recipient uses `needsCpr: true`, you must also provide `recipient.cpr`. See [Case Create](https://docs.nextsign.dk/docs/case-create) for the full recipient schema and CPR handling notes. ## Example Request ```bash title="cURL" curl --location 'https://www.nextsign.dk/api/v2/{company}/forms/{form_id}/submit' \ -H 'Authorization: Bearer YOUR_API_KEY' \ -H 'Content-Type: application/json' \ --data-raw '{ "title": "Ny Kontrakt", "state": "open", "tags": [ { "tag": "kunde_fornavn", "value": "Andreas", "type": "text" }, { "tag": "generel_price", "value": 4500, "type": "number" } ], "recipients": [ { "name": "Andreas Lauridsen", "email": "al@nextengine.dk", "signing": true, "group": 0, "redirectUrl": "https://example.com/complete", "signingSchema": "draw" } ] }' ``` ## Example Response ```json title="200 OK" { "status": "OK", "message": "Contract created and sent", "contract": { "_id": "66263484e601d81006d1a132", "title": "Ny Kontrakt", "folder": "Default", "type": "Simple sign", "recipients": [ { "name": "Andreas Lauridsen", "email": "al@nextengine.dk", "redirectUrl": "https://example.com/complete", "url": "https://www.nextsign.dk/sign/66263484e601d81006d1a132/2/WGOfO-7xvoh-5LPvK-O9cVt-Q1GnY" } ], "createdAt": "2024-04-22T09:57:24.498Z", "updatedAt": "2024-04-22T09:57:24.498Z" } } ``` ## Response Fields | Field | Type | Description | | ----- | ---- | ----------- | | `status` | `string` | Request status, typically `OK` | | `message` | `string` | Human-readable result message | | `contract` | `object` | Created case object | For open cases, the response includes `contract.recipients[].url`, which is the direct signing URL for each recipient. If you submitted `recipient.redirectUrl`, the same value is returned on `contract.recipients[].redirectUrl` for that recipient. ## Common Error Responses Validation error: ```json { "message": "Bad request", "errors": [ "No ID provided" ] } ``` Form not found: ```json { "errors": [ { "code": 400, "message": "Bad Request", "description": "Form not found" } ] } ``` ## Related Endpoints - [Forms Get](https://docs.nextsign.dk/docs/forms-get) - [Forms Tags](https://docs.nextsign.dk/docs/forms-tags) - [Case Create](https://docs.nextsign.dk/docs/case-create) ## Cases Create, list, inspect, and manage signing cases across their lifecycle. ### Case List Source: https://docs.nextsign.dk/docs/cases Summary: List cases with filters using GET /api/v2/{company}/cases/get. Use this endpoint to list cases for a company. You can filter by status, folder, and paginate the result set. ## Authorization All requests to this endpoint require authentication using a **Bearer Token**. Use your NextSign API key as the bearer token for this endpoint. --- ## Endpoint ```text GET https://www.nextsign.dk/api/v2/{company}/cases/get ``` ## Path Parameters | Parameter | Type | Description | | --------- | ---- | ----------- | | `company` | `string` | Your company identifier | ## Query Parameters | Parameter | Type | Description | | --------- | ---- | ----------- | | `status` | `string` | Filter by signing status: `signed`, `denied`, or `pending` | | `limit` | `number` | Maximum number of cases to return. Default is `100` | | `index` | `number` | Zero-based page index used together with `limit` | | `folder` | `string` | Filter by folder name | ## Example Request ```bash title="cURL" curl --location 'https://www.nextsign.dk/api/v2/{company}/cases/get?status=signed&limit=10&index=0&folder=Default' \ -H 'Authorization: Bearer YOUR_API_KEY' ``` ```javascript title="JavaScript" const params = new URLSearchParams({ status: 'signed', limit: '10', index: '0', folder: 'Default' }); const response = await fetch( `https://www.nextsign.dk/api/v2/{company}/cases/get?${params}`, { method: 'GET', headers: { 'Authorization': 'Bearer YOUR_API_KEY' } } ); const data = await response.json(); ``` ```python title="Python" import requests url = "https://www.nextsign.dk/api/v2/{company}/cases/get" headers = { "Authorization": "Bearer YOUR_API_KEY" } params = { "status": "signed", "limit": 10, "index": 0, "folder": "Default" } response = requests.get(url, headers=headers, params=params) data = response.json() ``` ## Example Response ```json title="200 OK" { "status": "company_found", "cases": [ { "_id": "657b2ffb0965b111023fb0d1", "title": "Contract Agreement", "folder": "Default", "createdAt": "2023-12-14T16:40:27.431Z", "updatedAt": "2024-02-19T10:52:25.836Z" } ] } ``` ## Response Fields | Field | Type | Description | | ----- | ---- | ----------- | | `status` | `string` | Status of the lookup, typically `company_found` | | `cases` | `array` | Array of matching case objects | The API returns `404` when no cases match the filter. ## Common Error Responses Invalid company id: ```json { "code": 400, "message": "Bad Request", "description": "Invalid company id" } ``` No matching cases: ```json { "message": "Case not found" } ``` ## Related Endpoints - [Case Get Multiple](https://docs.nextsign.dk/docs/cases-get-multiple) - [Case Get](https://docs.nextsign.dk/docs/case) - [Case Create](https://docs.nextsign.dk/docs/case-create) ### Case Get Multiple Source: https://docs.nextsign.dk/docs/cases-get-multiple Summary: Get multiple cases by ID using GET /api/v2/{company}/cases/{case_ids}/get. Use this endpoint to fetch multiple specific cases in a single request by passing a comma-separated list of case IDs. ## Authorization All requests to this endpoint require authentication using a **Bearer Token**. Use your NextSign API key as the bearer token for this endpoint. --- ## Endpoint ```text GET https://www.nextsign.dk/api/v2/{company}/cases/{case_ids}/get ``` ## Path Parameters | Parameter | Type | Description | | --------- | ---- | ----------- | | `company` | `string` | Your company identifier | | `case_ids` | `string` | Comma-separated list of case IDs | ## Example Request ```bash title="cURL" curl --location 'https://www.nextsign.dk/api/v2/{company}/cases/6618ddb22f3dfc40f20af288,661cf690d0a8e1ab7c2c951c/get' \ -H 'Authorization: Bearer YOUR_API_KEY' ``` ## Example Response ```json title="200 OK" { "status": "cases_found", "cases": [ { "_id": "6618ddb22f3dfc40f20af288", "title": "Contract 1", "createdAt": "2023-12-14T16:40:27.431Z", "updatedAt": "2024-02-19T10:52:25.836Z" }, { "_id": "661cf690d0a8e1ab7c2c951c", "title": "Contract 2", "createdAt": "2024-01-10T08:20:15.123Z", "updatedAt": "2024-03-05T14:30:45.678Z" } ] } ``` ## Response Fields | Field | Type | Description | | ----- | ---- | ----------- | | `status` | `string` | Status of the lookup, typically `cases_found` | | `cases` | `array` | Array of matching case objects | The API returns `404` when none of the requested IDs are found for the company. ## Common Error Responses Missing bearer token: ```json { "errors": [ { "code": 401, "message": "Unauthorized", "description": "Bearer token not found" } ] } ``` No matching cases: ```json { "message": "Cases not found" } ``` ## Related Endpoints - [Case List](https://docs.nextsign.dk/docs/cases) - [Case Get](https://docs.nextsign.dk/docs/case) ### Case Get Source: https://docs.nextsign.dk/docs/case Summary: Get a single case by ID using GET /api/v2/{company}/case/{case_id}/get. Use this endpoint to fetch a single case by its unique identifier. ## Authorization All requests to this endpoint require authentication using a **Bearer Token**. Use your NextSign API key as the bearer token for this endpoint. --- ## Endpoint ```text GET https://www.nextsign.dk/api/v2/{company}/case/{case_id}/get ``` ## Path Parameters | Parameter | Type | Description | | --------- | ---- | ----------- | | `company` | `string` | Your company identifier | | `case_id` | `string` | Unique identifier of the case | ## Example Request ```bash title="cURL" curl --location 'https://www.nextsign.dk/api/v2/{company}/case/657b2ffb0965b111023fb0d1/get' \ -H 'Authorization: Bearer YOUR_API_KEY' ``` ```javascript title="JavaScript" const response = await fetch( 'https://www.nextsign.dk/api/v2/{company}/case/657b2ffb0965b111023fb0d1/get', { method: 'GET', headers: { 'Authorization': 'Bearer YOUR_API_KEY' } } ); const data = await response.json(); ``` ```python title="Python" import requests url = "https://www.nextsign.dk/api/v2/{company}/case/657b2ffb0965b111023fb0d1/get" headers = { "Authorization": "Bearer YOUR_API_KEY" } response = requests.get(url, headers=headers) data = response.json() ``` ## Example Response ```json title="200 OK" { "status": "case_found", "case": { "settings": { "deletion": { "autoDelete": true, "days": 30 }, "availability": { "days": 10, "isExpired": false, "unlimited": false }, "reminders": { "amount": 2, "daysBetween": 3, "send": true }, "allowAPI": false, "allowedRoles": [] }, "_id": "657b2ffb0965b111023fb0d1", "user_name": "Daniel Prior", "user_email": "123@live.dk", "title": "Example Agreement", "type": "Simple sign", "createdAt": "2023-12-14T16:40:27.431Z", "updatedAt": "2024-02-19T10:52:25.836Z" } } ``` ## Response Fields | Field | Type | Description | | ----- | ---- | ----------- | | `status` | `string` | Status of the request, typically `case_found` | | `case` | `object` | Case object containing the current case data | | `case._id` | `string` | Unique case identifier | | `case.title` | `string` | Case title | | `case.type` | `string` | Case type | | `case.createdAt` | `string` | ISO 8601 creation timestamp | | `case.updatedAt` | `string` | ISO 8601 update timestamp | | `case.settings` | `object` | Case settings, including reminders and availability | ## Common Error Responses Invalid company id: ```json { "errors": [ { "code": 400, "message": "Bad Request", "description": "Invalid company id" } ] } ``` Case not found: ```json { "message": "Case not found" } ``` ## Related Endpoints - [Case Create](https://docs.nextsign.dk/docs/case-create) - [Case Delete](https://docs.nextsign.dk/docs/case-delete) - [Case List](https://docs.nextsign.dk/docs/cases) ### Case Create Source: https://docs.nextsign.dk/docs/case-create Summary: Create a case using POST /api/v2/{company}/case/create. Use this endpoint to create a case with recipients, settings, and documents for signing. ## Authorization All requests to this endpoint require authentication using a **Bearer Token**. Use your NextSign API key as the bearer token for this endpoint. --- ## Endpoint ```text POST https://www.nextsign.dk/api/v2/{company}/case/create ``` ## Path Parameters | Parameter | Type | Description | | --------- | ---- | ----------- | | `company` | `string` | Your company identifier | ## Request Body | Field | Type | Required | Description | | ----- | ---- | -------- | ----------- | | `title` | `string` | **Yes** | Case title | | `referenceId` | `string` | No | Internal reference ID | | `folder` | `string` | No | Folder name | | `autoSend` | `boolean` | No | Send immediately after creation | | `customMessage` | `boolean` | No | Enable a custom recipient message | | `message` | `string` | No | Custom recipient message | | `user_email` | `string` | No | Sender email address | | `emailtemplate` | `string` | No | Email template ID | | `settings` | `object` | **Yes** | Case settings | | `signingSchemas` | `array` | No | Allowed signature methods | | `recipients` | `array` | **Yes** | Recipient list | | `documents` | `array` | **Yes** | Documents to sign | ### Signing Schemas - `urn:grn:authn:dk:mitid:substantial` - `urn:grn:authn:dk:mitid:low` - `urn:grn:authn:dk:mitid:business` - `urn:grn:authn:se:bankid` - `draw-cpr` - `draw` - `sms-email` ### Recipient Fields | Field | Type | Description | | ----- | ---- | ----------- | | `name` | `string` | Recipient name | | `email` | `string` | Recipient email | | `signing` | `boolean` | Whether the recipient must sign | | `group` | `number` | Preferred signing order/group. Lower numbers are invited first | | `order` | `number` | Legacy alias for `group` on create endpoints | | `needsCpr` | `boolean` | Whether CPR validation is required | | `cpr` | `string` | Recipient CPR number. Required when `needsCpr` is `true` | | `signingSchema` | `string` | Optional recipient-specific signing method override | | `redirectUrl` | `string` | Redirect URL after signing. Must be sent inside the recipient object | | `phone` | `string` | Phone number for SMS delivery | | `position` | `string` | Signature title/position | | `type` | `string` | Delivery type. Supported values are `email`, `phone`, and `eboks` | | `template` | `string` | Optional recipient-specific template id | | `message` | `string` | Custom recipient message | | `eboks` | `object` | e-Boks delivery payload such as `kvhx`, `address`, and `name` | `recipient.signingSchema` accepts the same values listed under `Signing Schemas`. If `recipient.signingSchema` is omitted, the recipient falls back to the case-level `signingSchemas`. If it is set, that recipient is locked to the selected method and cannot choose other signing options during signing. If `needsCpr` is `true` and `recipient.signingSchema` is set, CPR verification still uses `urn:grn:authn:dk:mitid:substantial` before the signer continues with the forced signing method. If `needsCpr` is `true`, you must also send `recipient.cpr` in the payload. If `recipient.cpr` is missing, the signer cannot complete CPR verification. If you need the CPR number later in your own workflow, store it on your side as well. Do not rely on NextSign as a source for recovering the original plain-text CPR value after processing. Example recipient with CPR validation: ```json { "name": "Andreas", "email": "al@nextsign.dk", "cpr": "190497xxxx", "needsCpr": true, "signing": true } ``` ### Document Fields | Field | Type | Description | | ----- | ---- | ----------- | | `name` | `string` | File name including extension | | `file` | `string` | Hosted file URL, uploaded file URL, or base64 data | | `fileIsBlob` | `boolean` | Set to `true` for base64 payloads. Omit it when using an uploaded file URL | | `signObligated` | `boolean` | Whether signing is required | | `documentMustBeRead` | `boolean` | Whether the document must be read before signing | | `signatories` | `array` | Selected signatories | If the combined size of the documents in a case is more than **3.5 MB**, upload the files first with [Document Upload](https://docs.nextsign.dk/docs/documents-upload) and use the returned URL in `documents[].file`. ### Large Document Upload Flow 1. Upload the file with [Document Upload](https://docs.nextsign.dk/docs/documents-upload) 2. Take the `url` from the upload response 3. Pass that URL into `documents[].file` in the case creation payload Example: ```json { "documents": [ { "name": "Lejekontrakt.pdf", "file": "https://nextsign-dev.hel1.your-objectstorage.com/.../Lejekontrakt.pdf" } ] } ``` ## Example Request ```bash title="cURL" curl --location 'https://www.nextsign.dk/api/v2/{company}/case/create' \ -H 'Authorization: Bearer YOUR_API_KEY' \ -H 'Content-Type: application/json' \ --data-raw '{ "title": "Lejeaftale", "referenceId": "Jrn. 2342-23", "folder": "Default", "autoSend": true, "customMessage": true, "message": "Kære {recipient_name}, som aftalt er hermed dokumenter til underskrift", "user_email": "info@nextengine.dk", "settings": { "reminders": { "send": true, "amount": 2, "daysBetween": 3 }, "lang": "da", "deletion": { "autoDelete": false, "days": 30 }, "availability": { "unlimited": true, "days": 10 }, "template": 2 }, "signingSchemas": [ "urn:grn:authn:dk:mitid:substantial", "urn:grn:authn:dk:mitid:business", "draw" ], "recipients": [ { "name": "Andreas Lauridsen", "email": "al@nextengine.dk", "signing": true, "group": 0, "redirectUrl": "https://example.com/complete", "signingSchema": "urn:grn:authn:dk:mitid:business" } ], "documents": [ { "name": "Lejekontrakt.pdf", "file": "YOUR_BASE64_STRING_OR_URL", "fileIsBlob": true } ] }' ``` ## Example Response ```json title="200 OK" { "data": { "user": "65ab12cd34ef56ab78cd90ef", "nextSignKey": "7QmLp2Xs8HdNa4VrTc9YwKbJf", "realtime": false, "user_email": "api@example.com", "user_company": "65ab12cd34ef56ab78cd90a1", "title": "Lejeaftale", "referenceId": "REF-48392", "bin": false, "type": "Simple sign", "folder": "Default", "folderId": "65ab12cd34ef56ab78cd90a2", "signingSchemas": [ "urn:grn:authn:dk:mitid:substantial", "urn:grn:authn:dk:mitid:low", "urn:grn:authn:dk:mitid:business", "urn:grn:authn:se:bankid", "draw-cpr", "draw" ], "settings": { "deletion": { "autoDelete": false, "days": 30 }, "availability": { "unlimited": false, "days": 10, "expiration": "2026-05-11T08:54:36.371Z", "isExpired": false }, "reminders": { "send": true, "amount": 2, "daysBetween": 3 }, "template": 2, "lang": "da", "allowedRoles": [], "allowAPI": false, "allowRecipientsViewOthers": true }, "recipients": [ { "group": 0, "name": "Mia Sørensen", "needsCpr": false, "redirectUrl": "https://example.com/complete", "position": "Director", "cpr": "U2FsdGVkX18nR4mQ2Lp6YvZ8nT1aKs5JwP3eHx9LmNo=", "email": "mia.soerensen@example.com", "phone": "28123456", "sort": 0, "template": "", "type": "email", "signing": true, "signed": "pending", "signingToken": "Qp7Lm2Va9XrTc4Hs8KdNy5WbE", "signingSchema": "", "smsEmailVerification": { "attempts": 0 }, "signer": { "signer_type": "recipient", "identity": { "confirmed": false, "cprIsMatch": false } }, "message": { "enable": true, "content": "Insert Custom message per Recipient here." }, "uid": "pL8xRt2Q", "_id": "65ab12cd34ef56ab78cd90b1", "emailSent": [], "log": [], "emailEvents": [], "events": [], "url": "https://www.nextsign.dk/sign/65ab12cd34ef56ab78cd90b0/2/Qp7Lm2Va9XrTc4Hs8KdNy5WbE" } ], "integrations": { "microsoft": { "status": false }, "uniconta": { "status": false } }, "errorEmailSent": false, "reminders": { "allSigned": false, "createdAt": "2026-05-01T08:54:36.578Z" }, "documents": [ { "name": "Lejekontrakt.pdf", "file": "https://nextsign.hel1.your-objectstorage.com/65ab12cd34ef56ab78cd90a1/case/1777625676406-Lejekontrakt.pdf?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Content-Sha256=UNSIGNED-PAYLOAD&X-Amz-Credential=R4ND0MCR3D3NT14LKEY1%2F20260501%2Fhel1%2Fs3%2Faws4_request&X-Amz-Date=20260501T085437Z&X-Amz-Expires=3600&X-Amz-Signature=9f1c2d3a4b5e6f708192a3b4c5d6e7f8091a2b3c4d5e6f708192a3b4c5d6e7f8&X-Amz-SignedHeaders=host&x-amz-checksum-mode=ENABLED&x-id=GetObject", "document_id": "https://nextsign.hel1.your-objectstorage.com/65ab12cd34ef56ab78cd90a1/case/1777625676406-Lejekontrakt.pdf", "signObligated": true, "documentMustBeRead": true, "signatories": [], "_id": "65ab12cd34ef56ab78cd90b2" } ], "roles": [ "admin", "økonomi", "salg", "marketing", "standard", "it", "support", "hr" ], "message": "Kære {recipient_name}, som aftalt er hermed dokumenter til underskrift", "customMessage": true, "autoSend": true, "reminderAutoSend": true, "attachFiles": true, "source": { "type": "api" }, "state": "open", "_id": "65ab12cd34ef56ab78cd90b0", "tags": [], "errors": [], "otherDocuments": [], "signedDocuments": [], "logs": [], "createdAt": "2026-05-01T08:54:36.581Z", "updatedAt": "2026-05-01T08:54:36.581Z", "__v": 0, "id": "65ab12cd34ef56ab78cd90b0" }, "notices": [ { "message": "No user id provided, adding the email.", "using": "65ab12cd34ef56ab78cd90ef" }, { "message": "No user name provided" }, { "message": "No roles provided - default roles used", "using": [ "admin", "økonomi", "salg", "marketing", "standard", "it", "support", "hr" ] }, { "message": "No email template provided - using default" } ] } ``` ## Response Fields | Field | Type | Description | | ----- | ---- | ----------- | | `data` | `object` | Created case object | | `notices` | `array` | Non-fatal notices returned during processing | ## Common Error Responses Validation error: ```json { "errors": [ { "message": "Missing recipients" } ], "notices": [] } ``` Server error: ```json { "errors": [ { "message": "Server error", "error": "Internal error details" } ] } ``` ## Related Endpoints - [Case Get](https://docs.nextsign.dk/docs/case) - [Case Delete](https://docs.nextsign.dk/docs/case-delete) - [Document Upload](https://docs.nextsign.dk/docs/documents-upload) - [Forms Submit](https://docs.nextsign.dk/docs/forms-submit) ### Case Delete Source: https://docs.nextsign.dk/docs/case-delete Summary: Delete a case using GET /api/v2/{company}/case/{case_id}/delete. Use this endpoint to delete a case by its unique identifier. ## Authorization All requests to this endpoint require authentication using a **Bearer Token**. Use your NextSign API key as the bearer token for this endpoint. --- ## Endpoint ```text GET https://www.nextsign.dk/api/v2/{company}/case/{case_id}/delete ``` ## Path Parameters | Parameter | Type | Description | | --------- | ---- | ----------- | | `company` | `string` | Your company identifier | | `case_id` | `string` | Unique identifier of the case to delete | ## Example Request ```bash title="cURL" curl --location 'https://www.nextsign.dk/api/v2/{company}/case/{case_id}/delete' \ -H 'Authorization: Bearer YOUR_API_KEY' ``` ## Example Response ```json title="200 OK" { "status": "removed", "return": { "acknowledged": true, "deletedCount": 1 } } ``` ## Response Fields | Field | Type | Description | | ----- | ---- | ----------- | | `status` | `string` | Status of the deletion operation | | `return` | `object` | Deletion result from the database | | `return.acknowledged` | `boolean` | Whether the delete operation was acknowledged | | `return.deletedCount` | `number` | Number of deleted cases | ## Common Error Responses Missing case id: ```json { "errors": [ { "code": 400, "message": "Bad Request", "description": "Missing case id" } ] } ``` Case not found: ```json { "message": "Case not found" } ``` ## Related Endpoints - [Case Get](https://docs.nextsign.dk/docs/case) - [Case Create](https://docs.nextsign.dk/docs/case-create) ## Documents Upload larger files, retrieve signed files, and convert source documents before they are sent. ### Document Upload Source: https://docs.nextsign.dk/docs/documents-upload Summary: Upload a file using POST /v3/company/{company}/file/upload. Use this endpoint to upload a file before case creation. If the combined size of the documents you want to send in a case is more than **3.5 MB**, upload the files first and pass the returned URL into `documents[].file` in [Case Create](https://docs.nextsign.dk/docs/case-create). ## Authorization All requests to this endpoint require authentication using a **Bearer Token**. Use your NextSign API key as the bearer token for this endpoint. --- ## Endpoint ```text POST https://api.nextsign.dk/v3/company/{company}/file/upload ``` ## Path Parameters | Parameter | Type | Description | | --------- | ---- | ----------- | | `company` | `string` | Your company identifier | ## Request Formats This endpoint supports both: - `multipart/form-data` - Base64 payloads in the request body ## Accepted File Types - `application/pdf` - `application/vnd.openxmlformats-officedocument.wordprocessingml.document` ## Multipart Form Data Fields | Field | Type | Required | Description | | ----- | ---- | -------- | ----------- | | `file` | `file` | **Yes** | File to upload | | `name` | `string` | No | File name to store with the upload | | `type` | `string` | No | Valid MIME type such as `application/pdf` | ## Base64 Body Fields | Field | Type | Required | Description | | ----- | ---- | -------- | ----------- | | `file` | `string` | **Yes** | Base64 string or full data URL | | `name` | `string` | **Yes** | File name to store with the upload | | `type` | `string` | Conditionally | MIME type. Optional when `file` is a full data URL like `data:application/pdf;base64,...` | For base64 uploads, send either a full data URL such as `data:application/pdf;base64,...` or a raw base64 string plus an explicit `type`. ## Example Request ```bash title="cURL" curl --location 'https://api.nextsign.dk/v3/company/{company}/file/upload' \ -H 'Authorization: Bearer YOUR_API_KEY' \ -F 'file=@"/path/to/Hi.pdf"' \ -F 'name=Hi.pdf' \ -F 'type=application/pdf' ``` ## Example Request With Base64 ```json title="JSON Body" { "file": "data:application/pdf;base64,JVBERi0xLjQKJcfs...", "name": "Hi.pdf" } ``` ```bash title="cURL - Base64" curl --location 'https://api.nextsign.dk/v3/company/{company}/file/upload' \ -H 'Authorization: Bearer YOUR_API_KEY' \ -H 'Content-Type: application/json' \ --data '{ "file": "data:application/pdf;base64,JVBERi0xLjQKJcfs...", "name": "Hi.pdf" }' ``` ## Example Response ```json title="200 OK" { "url": "https://nextsign-dev.hel1.your-objectstorage.com/686557955db9ec08e6a94c39/1774622990607-Hi.pdf", "type": "application/pdf" } ``` ## Response Fields | Field | Type | Description | | ----- | ---- | ----------- | | `url` | `string` | Uploaded file URL to use in later API requests | | `type` | `string` | MIME type stored for the uploaded file | ## Use In Case Create Pass the returned `url` into the `documents[].file` field: ```json { "documents": [ { "name": "Hi.pdf", "file": "https://nextsign-dev.hel1.your-objectstorage.com/686557955db9ec08e6a94c39/1774622990607-Hi.pdf" } ] } ``` ## Related Endpoints - [Case Create](https://docs.nextsign.dk/docs/case-create) - [Document Retrieve](https://docs.nextsign.dk/docs/documents) ## Common Error Responses Missing file: ```json { "error": "missing-file" } ``` Missing file name: ```json { "error": "missing-file-name" } ``` Missing file type: ```json { "error": "missing-file-type" } ``` Invalid file type: ```json { "error": "invalid-file-type" } ``` ### Document Retrieve Source: https://docs.nextsign.dk/docs/documents Summary: Retrieve a pre-signed document URL using POST /v3/company/{company}/file/view-presigned-url. Use this endpoint to generate a pre-signed URL for a case document stored in NextSign object storage. ## Authorization All requests to this endpoint require authentication using a **Bearer Token**. Use your NextSign API key as the bearer token for this endpoint. --- ## Endpoint ```text POST https://api.nextsign.dk/v3/company/{company}/file/view-presigned-url ``` ## Path Parameters | Parameter | Type | Description | | --------- | ---- | ----------- | | `company` | `string` | Your company identifier | ## Request Body | Field | Type | Required | Description | | ----- | ---- | -------- | ----------- | | `url` | `string` | **Yes** | Stored document URL that should be converted into a pre-signed URL | ## Example Request ```bash title="cURL" curl --location 'https://api.nextsign.dk/v3/company/{company}/file/view-presigned-url' \ -H 'Authorization: Bearer YOUR_API_KEY' \ -H 'Content-Type: application/json' \ --data '{ "url": "https://nextsign.hel1.your-objectstorage.com/655e252f708ff057141e577e/case/example.pdf" }' ``` ## Example Response ```json title="200 OK" { "key": "655e252f708ff057141e577e/case/example.pdf", "signedUrl": "https://nextsign.hel1.your-objectstorage.com/655e252f708ff057141e577e/case/example.pdf?...", "type": "application/pdf", "cached": true } ``` ## Response Fields | Field | Type | Description | | ----- | ---- | ----------- | | `key` | `string` | Object storage key for the file | | `signedUrl` | `string` | Time-limited pre-signed URL | | `type` | `string` | MIME type of the file, when available | | `cached` | `boolean` | Whether the response came from the URL cache | The generated URL is cached for up to 1 hour. ## Common Error Responses Missing file URL: ```json { "error": "missing-url" } ``` ## Related Endpoints - [Document Upload](https://docs.nextsign.dk/docs/documents-upload) - [Document Convert](https://docs.nextsign.dk/docs/documents-convert) ### Document Convert Source: https://docs.nextsign.dk/docs/documents-convert Summary: Convert documents using POST /api/v2/document/convert. Use this endpoint to convert DOCX documents to PDF and replace tags before sending or previewing a document. ## Authorization All requests to this endpoint require authentication using a **Bearer Token**. Use your NextSign API key as the bearer token for this endpoint. --- ## Endpoint ```text POST https://www.nextsign.dk/api/v2/document/convert ``` ## Request Body | Field | Type | Required | Description | | ----- | ---- | -------- | ----------- | | `tags` | `array` | No | Tag replacements used when converting DOCX files | | `documents` | `array` | **Yes** | Array of documents to convert | ### Tag Fields | Field | Type | Description | | ----- | ---- | ----------- | | `tag` | `string` | Template tag name | | `value` | `string \| number` | Replacement value | | `type` | `string` | Tag type such as `text`, `number`, or `date` | | `format` | `string` | Optional date format | ### Document Fields | Field | Type | Description | | ----- | ---- | ----------- | | `file` | `string` | URL to the source document | | `name` | `string` | Document name | ## Example Request ```bash title="cURL" curl --location 'https://www.nextsign.dk/api/v2/document/convert' \ -H 'Authorization: Bearer YOUR_API_KEY' \ -H 'Content-Type: application/json' \ --data '{ "tags": [ { "tag": "customer_name", "value": "Andreas", "type": "text" }, { "tag": "contract_number", "value": 200, "type": "number" } ], "documents": [ { "file": "https://example.com/path/to/contract.docx", "name": "Contract Agreement.docx" } ] }' ``` ## Example Response ```json title="200 OK" { "status": { "code": 200, "message": "OK" }, "documents": [ { "file": "https://example.com/path/to/converted-contract.pdf", "name": "Contract Agreement.pdf" } ] } ``` ## Response Fields | Field | Type | Description | | ----- | ---- | ----------- | | `status.code` | `number` | HTTP-style status code in the payload | | `status.message` | `string` | Status message | | `documents` | `array` | Converted documents | | `documents[].file` | `string` | URL to the converted file | | `documents[].name` | `string` | Converted file name | PDF files are passed through, while DOCX files are converted to PDF. ## Common Error Responses Missing documents array: ```json { "message": "Bad request", "errors": [ { "status": "error", "message": "minimum of 1 documents is required" } ] } ``` ## Events Subscribe to lifecycle notifications and understand webhook delivery retries. ### Webhooks Source: https://docs.nextsign.dk/docs/webhooks Summary: Receive real-time notifications when case and flow events occur in NextSign. NextSign webhooks let NextSign push case and flow events to your system in near real time. Use them when you want your backend to react to signing activity without polling the API. ## Common Use Cases - Update internal systems when a case is created, signed, or denied - Start document archival or downstream processing after signing - Keep CRM or ERP records in sync with NextSign activity - React to reminder and expiry events in your own workflow engine - Handle completed webflows without polling for status changes --- ## Where to Configure Webhooks Manage webhooks from the **Webhooks** page in the NextSign dashboard. From there you can create, edit, enable or disable webhooks, configure custom headers, and inspect recent delivery runs. --- ## Creating a Webhook ### Name Use a descriptive internal name so the webhook is easy to identify in the dashboard. **Example:** `Signed case webhook` --- ### Triggers (Required) Select one or more trigger values. Each selected trigger can independently deliver a webhook request to the same URL. | Label | Trigger Value | Description | | ----- | ------------- | ----------- | | **Case created** | `case_created` | Sent when a case is created | | **Recipient signed** | `recipient_signed` | Sent when a recipient signs | | **Case signed** | `case_signed` | Sent when the case is fully signed | | **Case denied** | `case_denied` | Sent when a recipient denies the case | | **Workflow completed** | `webflow.completed` | Sent when a webflow completes | | **Case reminder sent** | `case.reminder.sent` | Sent when a reminder email is sent | | **Case expired** | `case.expiry.expired` | Sent when a case expires | | **Case expiry 7 days** | `case.expiry.7days` | Sent when a case is 7 days from expiry | You can attach multiple trigger values to a single webhook configuration. --- ### URL (Required) This is the public endpoint on your server that will receive webhook deliveries. ```text https://example.com/webhooks/nextsign ``` Localhost URLs will not receive webhook traffic unless you expose them with a tunneling service such as ngrok. --- ### Method NextSign supports `POST`, `GET`, `PUT`, and `DELETE` delivery methods. - Default: `POST` - Recommended: `POST` If you do not configure a `Content-Type` header yourself, NextSign sends `application/json`. --- ### Active Use the active toggle to enable or pause delivery without deleting the webhook configuration. - **On**: events are delivered - **Off**: events are ignored for this webhook --- ### Headers (Optional) Custom headers are sent exactly as configured in the dashboard. **Example:** | Key | Value | | --- | ----- | | `X-Webhook-Secret` | `your-shared-secret` | | `X-API-Version` | `2026-03` | Common uses: - Pass a shared secret your server can validate - Add routing metadata for your integration - Version your internal webhook consumer NextSign does not generate an HMAC signature automatically for webhook requests. If you want to verify origin, configure a shared secret header such as `X-Webhook-Secret` and compare the exact header value in your handler. --- ## Delivery Behavior When a selected trigger fires, NextSign sends an HTTP request to the configured URL. The request body contains the machine-readable trigger name plus either a `case` object or a `flow` object, depending on what caused the webhook. ### Expected Response Your endpoint should: - Return a `2xx` HTTP status code to acknowledge successful delivery - Respond quickly and offload slow work to background jobs or queues - Treat retries as possible duplicate deliveries ### Retry Logic If delivery fails or your endpoint returns a non-`2xx` response, NextSign retries using exponential backoff. - First retry: about 2 minutes after the failed attempt - Then approximately: 4, 8, 16, 32 minutes, and so on - Retries continue until delivery succeeds or the retry limit is reached - The current retry limit is up to 14 total delivery attempts Webhook deliveries can be repeated when retries happen. Make your webhook handler idempotent. For a focused guide, see [Webhook Retry Logic](https://docs.nextsign.dk/docs/webhook-retry-logic). --- ## Payload Structure ### Case Event Payload Case-related triggers send a `case` object: ```json title="Example Case Payload" { "trigger": "case_signed", "case": { "_id": "66263484e601d81006d1a132", "title": "Contract Agreement", "updatedAt": "2024-01-15T14:30:00.000Z", "recipients": [ { "name": "Andreas Lauridsen", "email": "al@nextengine.dk" } ], "signedDocuments": [ { "name": "Contract.pdf", "file": "https://nextsign.hel1.your-objectstorage.com/..." } ] } } ``` ### Flow Event Payload Flow-related triggers send a `flow` object: ```json title="Example Flow Payload" { "trigger": "webflow.completed", "flow": { "_id": "66a3c3bdb42398dff0d10123", "title": "Lead Intake", "updatedAt": "2024-01-15T14:30:00.000Z" } } ``` ### Payload Fields | Field | Type | Description | | ----- | ---- | ----------- | | `trigger` | `string` | Machine-readable trigger value such as `case_signed` | | `case` | `object` | Present for case-related triggers | | `flow` | `object` | Present for flow-related triggers | | `case._id` | `string` | Case identifier, when `case` is present | | `flow._id` | `string` | Flow identifier, when `flow` is present | The `case` or `flow` object contains the current resource data at delivery time. Exact fields can vary depending on the trigger and the resource state. --- ## Implementing a Webhook Endpoint ### Node.js / Express ```javascript title="Node.js / Express" const express = require('express'); const app = express(); app.use(express.json()); app.post('/webhooks/nextsign', (req, res) => { if (req.headers['x-webhook-secret'] !== process.env.NEXTSIGN_WEBHOOK_SECRET) { return res.status(401).send('Unauthorized'); } const { trigger, case: webhookCase, flow } = req.body; const entity = webhookCase || flow; switch (trigger) { case 'case_signed': console.log(`Case signed: ${entity?._id}`); break; case 'case_denied': console.log(`Case denied: ${entity?._id}`); break; case 'webflow.completed': console.log(`Flow completed: ${entity?._id}`); break; default: console.log(`Unhandled trigger: ${trigger}`); } res.status(200).send('OK'); }); app.listen(3000); ``` ### Python / Flask ```python title="Python / Flask" from flask import Flask, request, jsonify import os app = Flask(__name__) @app.route('/webhooks/nextsign', methods=['POST']) def webhook(): if request.headers.get('X-Webhook-Secret') != os.getenv('NEXTSIGN_WEBHOOK_SECRET'): return jsonify({'error': 'Unauthorized'}), 401 payload = request.json or {} trigger = payload.get('trigger') entity = payload.get('case') or payload.get('flow') if trigger == 'case_signed': print(f"Case signed: {entity.get('_id')}") elif trigger == 'case_denied': print(f"Case denied: {entity.get('_id')}") elif trigger == 'webflow.completed': print(f"Flow completed: {entity.get('_id')}") else: print(f"Unhandled trigger: {trigger}") return jsonify({'status': 'ok'}), 200 if __name__ == '__main__': app.run(port=3000) ``` --- ## Best Practices ### Use HTTPS Serve your webhook endpoint over HTTPS so request data stays encrypted in transit. ### Validate a Shared Secret Header If you configure a header such as `X-Webhook-Secret`, compare it directly against a secret stored in your environment. ### Process Asynchronously Return a success response quickly, then hand off heavier work such as document downloads, CRM sync, or notifications to background processing. ### Implement Idempotency A practical idempotency key can be built from the trigger plus the resource ID and update timestamp. ```javascript function getIdempotencyKey(payload) { const entity = payload.case || payload.flow; return [payload.trigger, entity?._id, entity?.updatedAt].filter(Boolean).join(':'); } ``` See [Idempotency](https://docs.nextsign.dk/docs/idempotency) for request-side guidance as well. --- ## Testing Webhooks ### Local Development with ngrok Expose your local server: ```bash ngrok http 3000 ``` Then configure the generated HTTPS URL in the NextSign dashboard. ### Manual Test Request ```bash curl -X POST https://your-domain.com/webhooks/nextsign \ -H "Content-Type: application/json" \ -H "X-Webhook-Secret: your-shared-secret" \ -d '{ "trigger": "case_signed", "case": { "_id": "test-case-123", "title": "Test Agreement", "updatedAt": "2024-01-15T14:30:00.000Z" } }' ``` --- ## Troubleshooting ### No Webhooks Arrive - Verify the webhook is active in the dashboard - Confirm the URL is public and reachable - Confirm the correct trigger values are selected - Review webhook runs in the dashboard to see failed attempts ### 401 or 403 Responses - Verify the shared secret header name matches exactly - Verify the expected header value matches the configured value - Check for proxy or load balancer rules stripping custom headers ### Duplicate Deliveries - Expect retries after failures or timeouts - Use idempotency keys when storing or processing events ### Timeouts - Return `200 OK` quickly - Move slow processing to workers or queues - Avoid downloading large files inline before acknowledging the webhook Use the webhook runs view in the dashboard to inspect recent deliveries, statuses, and retry behavior. ### Webhook Retry Logic Source: https://docs.nextsign.dk/docs/webhook-retry-logic Summary: Understand how NextSign retries failed webhook deliveries and how to build duplicate-safe handlers. If a webhook delivery fails, NextSign retries automatically using exponential backoff. ## What Counts as a Failed Delivery A delivery is treated as failed when: - the destination cannot be reached - the request times out - your endpoint returns a non-`2xx` HTTP response ## Delivery Schedule NextSign attempts delivery immediately, then retries with increasing delay when the delivery fails. Typical schedule: - Attempt 1: immediate delivery - Attempt 2: about 2 minutes later - Attempt 3: about 4 minutes later - Attempt 4: about 8 minutes later - Attempt 5: about 16 minutes later - Further attempts continue with exponential backoff The current limit is **up to 14 total delivery attempts** for a webhook event. Webhook delivery is at-least-once, not exactly-once. Your handler must tolerate duplicate deliveries. ## Recommended Handler Design - return `2xx` as soon as the payload is accepted - move slow work into a queue or background worker - deduplicate events before performing side effects - log failures with enough detail to replay safely ## Example Deduplication Key ```javascript function getIdempotencyKey(payload) { const entity = payload.case || payload.flow; return [payload.trigger, entity?._id, entity?.updatedAt].filter(Boolean).join(':'); } ``` ## Operational Advice - do not download large files before acknowledging the webhook - do not send emails or sync external systems inline if the work can be queued - monitor repeated failures so you can fix broken endpoints before the retry window is exhausted ## Related Guides - [Webhooks](https://docs.nextsign.dk/docs/webhooks) - [Idempotency](https://docs.nextsign.dk/docs/idempotency)