NextSign API
Events

Webhooks

Receive real-time notifications when case and flow events occur in NextSign.

Webhooks

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.

LabelTrigger ValueDescription
Case createdcase_createdSent when a case is created
Recipient signedrecipient_signedSent when a recipient signs
Case signedcase_signedSent when the case is fully signed
Case deniedcase_deniedSent when a recipient denies the case
Workflow completedwebflow.completedSent when a webflow completes
Case reminder sentcase.reminder.sentSent when a reminder email is sent
Case expiredcase.expiry.expiredSent when a case expires
Case expiry 7 dayscase.expiry.7daysSent 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.

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:

KeyValue
X-Webhook-Secretyour-shared-secret
X-API-Version2026-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.


Payload Structure

Case Event Payload

Case-related triggers send a case object:

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:

Example Flow Payload
{
  "trigger": "webflow.completed",
  "flow": {
    "_id": "66a3c3bdb42398dff0d10123",
    "title": "Lead Intake",
    "updatedAt": "2024-01-15T14:30:00.000Z"
  }
}

Payload Fields

FieldTypeDescription
triggerstringMachine-readable trigger value such as case_signed
caseobjectPresent for case-related triggers
flowobjectPresent for flow-related triggers
case._idstringCase identifier, when case is present
flow._idstringFlow 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

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 / 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.

function getIdempotencyKey(payload) {
  const entity = payload.case || payload.flow;
  return [payload.trigger, entity?._id, entity?.updatedAt].filter(Boolean).join(':');
}

See Idempotency for request-side guidance as well.


Testing Webhooks

Local Development with ngrok

Expose your local server:

ngrok http 3000

Then configure the generated HTTPS URL in the NextSign dashboard.

Manual Test Request

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.