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.
Overview
Section titled “Overview”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
Available Events
Section titled “Available Events”Subnoto supports the following webhook events:
ENVELOPE_SIGNED
Section titled “ENVELOPE_SIGNED”Triggered when a recipient signs an envelope. This event is fired each time any recipient completes their signature.
ENVELOPE_COMPLETED
Section titled “ENVELOPE_COMPLETED”Triggered when an envelope is fully completed and all required signatures have been collected. This event is fired once per envelope completion.
Creating a Webhook
Section titled “Creating a Webhook”- Log into your Subnoto workspace at app.subnoto.com
- Navigate to Settings → Webhooks
- Click “Create webhook”
- Fill in the webhook configuration (see Configuration Options below)
- Click “Add webhook”
Configuration Options
Section titled “Configuration Options”Payload URL
Section titled “Payload URL”The HTTPS endpoint where webhook payloads will be sent. This must be a publicly accessible URL.
Example: https://api.example.com/webhooks/subnoto
Content Type
Section titled “Content Type”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
Secret (Optional)
Section titled “Secret (Optional)”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.
SSL Verification
Section titled “SSL Verification”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.
Events
Section titled “Events”You can choose to receive:
- All events: Receive notifications for both
ENVELOPE_SIGNEDandENVELOPE_COMPLETED - Individual events: Select specific events you want to be notified about
Workspaces
Section titled “Workspaces”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
Active Status
Section titled “Active Status”When enabled, the webhook will receive event notifications. When disabled, the webhook is inactive and will not be triggered.
Webhook Payload Format
Section titled “Webhook Payload Format”Standard JSON Payload
Section titled “Standard JSON Payload”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", "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 }}Form-Encoded Payload
Section titled “Form-Encoded Payload”For application/x-www-form-urlencoded content type, the payload is sent as a form field:
payload={"eventUuid":"...","eventType":"ENVELOPE_SIGNED","eventTime":1704067200000,"webhookUuid":"...","teamUuid":"...","data":{"workspaceUuid":"...","envelopeUuid":"...","recipientEmail":"...","recipientName":"..."}}Discord and Slack Integration
Section titled “Discord and Slack Integration”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.
Webhook Headers
Section titled “Webhook Headers”All webhook requests include the following headers:
X-Webhook-Id
Section titled “X-Webhook-Id”The unique identifier of the webhook that sent the request.
X-Webhook-Id: webhook-uuidX-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.
X-Webhook-Signature: t=1704067200000,v1=<signature>The signature format is:
t: Unix timestamp in millisecondsv1: HMAC SHA256 signature
Verifying Webhook Signatures
Section titled “Verifying Webhook Signatures”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));}Python Example
Section titled “Python Example”import hmacimport hashlibimport 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)Testing Webhooks
Section titled “Testing Webhooks”You can test your webhook configuration directly from the web interface:
- Navigate to Settings → Webhooks
- Find the webhook you want to test
- Click the actions menu (three dots)
- 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.
Troubleshooting
Section titled “Troubleshooting”Webhook Not Received
Section titled “Webhook Not Received”- 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
Invalid Signature
Section titled “Invalid Signature”- 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
Event Not Triggering
Section titled “Event Not Triggering”- 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