Skip to content

Setting Up Webhooks

Webhooks allow you to receive real-time notifications when events occur in your Subnoto workspace. This guide will help you set up and configure webhooks to integrate with your systems.

Webhooks are HTTP POST requests sent to your specified URL when specific events occur in Subnoto. They allow you to:

  • Automatically update your systems when envelopes are signed
  • Trigger workflows when documents are completed
  • Keep external systems synchronized with Subnoto

Subnoto supports the following webhook events:

Triggered when a recipient signs an envelope. This event is fired each time any recipient completes their signature.

Triggered when an envelope is fully completed and all required signatures have been collected. This event is fired once per envelope completion.

  1. Log into your Subnoto workspace at app.subnoto.com
  2. Navigate to Settings → Webhooks
  3. Click “Create webhook”
  4. Fill in the webhook configuration (see Configuration Options below)
  5. Click “Add webhook”

The HTTPS endpoint where webhook payloads will be sent. This must be a publicly accessible URL.

Example: https://api.example.com/webhooks/subnoto

Choose how the webhook payload should be formatted:

  • application/json: Payload sent as JSON in the request body
  • application/x-www-form-urlencoded: Payload sent as form-encoded data

A secret key used to generate HMAC signatures for webhook verification. If provided, the webhook request will include a signature header that you can use to verify the request authenticity.

Security Best Practice: Always use a secret to verify webhook authenticity and prevent unauthorized requests.

By default, Subnoto verifies SSL certificates when delivering webhook payloads. Disabling SSL verification is not recommended as it may expose your webhook endpoint to security risks.

You can choose to receive:

  • All events: Receive notifications for both ENVELOPE_SIGNED and ENVELOPE_COMPLETED
  • Individual events: Select specific events you want to be notified about

You can filter webhooks to specific workspaces:

  • All workspaces: Receive events from all workspaces in your team
  • Specific workspaces: Select the workspaces that should trigger this webhook

When enabled, the webhook will receive event notifications. When disabled, the webhook is inactive and will not be triggered.

For non-Discord/Slack endpoints with application/json content type:

{
"eventUuid": "0f29a0a2-3a6f-44a4-b2a2-5d7b3f1b1f9b",
"eventType": "ENVELOPE_SIGNED",
"eventTime": 1704067200000,
"webhookUuid": "webhook-uuid",
"teamUuid": "team-uuid",
"data": {
"workspaceUuid": "workspace-uuid",
"envelopeUuid": "envelope-uuid",
"recipientEmail": "[email protected]",
"recipientName": "John Doe"
}
}

ENVELOPE_COMPLETED event example:

{
"eventUuid": "f5a6c2d3-2e1b-4c7a-92ab-5e8f2d1c0b9a",
"eventType": "ENVELOPE_COMPLETED",
"eventTime": 1704067200000,
"webhookUuid": "webhook-uuid",
"teamUuid": "team-uuid",
"data": {
"workspaceUuid": "workspace-uuid",
"envelopeUuid": "envelope-uuid",
"documentUuid": "document-uuid",
"finalRevisionVersion": 1
}
}

For application/x-www-form-urlencoded content type, the payload is sent as a form field:

Terminal window
payload={"eventUuid":"...","eventType":"ENVELOPE_SIGNED","eventTime":1704067200000,"webhookUuid":"...","teamUuid":"...","data":{"workspaceUuid":"...","envelopeUuid":"...","recipientEmail":"...","recipientName":"..."}}

Subnoto automatically detects Discord and Slack webhook URLs and formats the payload according to their respective API requirements. For these platforms, the payload format is optimized for their notification systems. The structure above applies to default JSON/form payloads only.

All webhook requests include the following headers:

The unique identifier of the webhook that sent the request.

Terminal window
X-Webhook-Id: webhook-uuid

X-Webhook-Signature (When Secret is Configured)

Section titled “X-Webhook-Signature (When Secret is Configured)”

The HMAC signature for webhook verification. Only included when a secret is configured.

Terminal window
X-Webhook-Signature: t=1704067200000,v1=<signature>

The signature format is:

  • t: Unix timestamp in milliseconds
  • v1: HMAC SHA256 signature

To verify that a webhook request is authentic and comes from Subnoto, you should verify the HMAC signature:

import crypto from "crypto";
function verifyWebhookSignature(signature, timestamp, body, secret) {
// Parse signature header: t=<timestamp>,v1=<signature>
const match = signature.match(/t=(\d+),v1=(.+)/);
if (!match) return false;
const [, sigTimestamp, sigValue] = match;
// Verify timestamp is recent (optional: prevent replay attacks)
const now = Date.now();
const requestTime = parseInt(sigTimestamp);
if (Math.abs(now - requestTime) > 300000) {
// 5 minutes tolerance
return false;
}
// Recreate signature
const expectedSignature = crypto.createHmac("sha256", secret).update(`t:${sigTimestamp}:${body}`).digest("hex");
return crypto.timingSafeEqual(Buffer.from(sigValue), Buffer.from(expectedSignature));
}
import hmac
import hashlib
import time
def verify_webhook_signature(signature_header, body, secret):
# Parse signature header: t=<timestamp>,v1=<signature>
try:
parts = signature_header.split(',')
timestamp = parts[0].split('=')[1]
sig_value = parts[1].split('=')[1]
except (IndexError, ValueError):
return False
# Verify timestamp is recent (optional: prevent replay attacks)
now = int(time.time() * 1000)
request_time = int(timestamp)
if abs(now - request_time) > 300000: # 5 minutes tolerance
return False
# Recreate signature
message = f't:{timestamp}:{body}'
expected_signature = hmac.new(
secret.encode('utf-8'),
message.encode('utf-8'),
hashlib.sha256
).hexdigest()
return hmac.compare_digest(sig_value, expected_signature)

You can test your webhook configuration directly from the web interface:

  1. Navigate to Settings → Webhooks
  2. Find the webhook you want to test
  3. Click the actions menu (three dots)
  4. Select “Test webhook”

This will send a test payload to your webhook URL with sample data, allowing you to verify that your endpoint is correctly receiving and processing webhook requests.

  • Verify the webhook is active in the Subnoto settings
  • Check that your endpoint URL is publicly accessible
  • Ensure your endpoint accepts POST requests
  • Verify SSL certificate validity if SSL verification is enabled
  • Check your server logs for incoming requests
  • Verify you’re using the correct secret key
  • Ensure you’re including the full request body in signature calculation
  • Check that the timestamp in the signature header is recent
  • For form-encoded payloads, verify you’re using the form data, not JSON
  • Verify the event type is included in your webhook configuration
  • Check that the workspace filter matches the event’s workspace
  • Ensure the webhook is active
  • Verify the envelope status matches the expected event trigger