Skip to main content

Webhook Overview

Webhooks allow NotaryCam to send real-time notifications to your application when events occur during a transaction's lifecycle.

Webhook Flow

Webhook Events

NotaryCam sends webhooks for the following events:

Event TypeDescription
transaction-status-updateTransaction status changed (in-progress, complete, cancelled)
documents-processedDocuments uploaded via API finished processing
tagging-startedDocument tagging began in the session
recording-availableSession recording finished processing
auto-assigned-notaryA notary was auto-assigned to the transaction
tip

See Event Types for detailed payload examples for each event.

Quick Setup

1. Create Webhook Endpoint

Create an HTTPS endpoint at your domain:

// Express.js example
const express = require('express');
const app = express();

app.post('/webhooks/notarycam', express.json(), async (req, res) => {
try {
const event = req.body;

// Validate the Authorization header
const authHeader = req.headers['authorization'];
if (authHeader !== process.env.NOTARYCAM_WEBHOOK_AUTH_TOKEN) {
return res.status(401).send('Unauthorized');
}

// Process the event
await handleWebhookEvent(event);

// Respond quickly (< 5 seconds)
res.status(200).send('OK');

} catch (error) {
console.error('Webhook error:', error);
res.status(500).send('Error processing webhook');
}
});

app.listen(3000);

2. Register with NotaryCam

Provide your webhook URL to NotaryCam:

  • URL Format: https://yourdomain.com/webhooks/notarycam
  • Method: POST
  • Content-Type: application/json
  • SSL: Required (HTTPS only)

Contact your NotaryCam account manager to register your webhook endpoint. You will provide:

  • Your webhook URL
  • An Authorization header value — a secret token that NotaryCam will include in the Authorization header of every webhook request, allowing you to verify the request is from NotaryCam

3. Validate the Authorization Header

When NotaryCam sends a webhook, it includes the Authorization header you configured. Always validate this header to ensure the request is legitimate:

function validateWebhook(req) {
const authHeader = req.headers['authorization'];
const expectedToken = process.env.NOTARYCAM_WEBHOOK_AUTH_TOKEN;

if (!authHeader || authHeader !== expectedToken) {
return false;
}
return true;
}
tip

Store your Authorization token securely as an environment variable. Never hard-code it in your application.

4. Handle Events

Route events to appropriate handlers:

async function handleWebhookEvent(event) {
switch (event.event) {
case 'transaction-status-update':
await handleStatusUpdate(event);
break;

case 'documents-processed':
await handleDocumentsProcessed(event);
break;

case 'tagging-started':
await handleTaggingStarted(event);
break;

case 'recording-available':
await handleRecordingAvailable(event);
break;

case 'auto-assigned-notary':
await handleAutoAssignedNotary(event);
break;

default:
console.log('Unhandled event:', event.event);
}
}

5. Respond Quickly

Respond to webhooks:

app.post('/webhooks/notarycam', async (req, res) => {
// Respond immediately
res.status(200).send('OK');

// Process asynchronously
setImmediate(async () => {
try {
await handleWebhookEvent(req.body);
} catch (error) {
console.error('Background processing error:', error);
}
});
});

Webhook Payload Structure

All webhooks share a common structure:

{
"event": "transaction-status-update",
"departmentId": "dept_abc123",
"data": {
"transactionId": "txn_abc123def456",
"status": "in-progress"
}
}

Common Fields

FieldTypeDescription
eventstringEvent type identifier
departmentIdstringDepartment ID for the transaction
dataobjectEvent-specific data (always includes transactionId)

Example Events

Transaction Status Update

{
"event": "transaction-status-update",
"departmentId": "dept_abc123",
"data": {
"transactionId": "txn_abc123def456",
"status": "complete"
}
}

Documents Processed

{
"event": "documents-processed",
"departmentId": "dept_abc123",
"data": {
"success": true,
"transactionId": "txn_abc123def456"
}
}

Recording Available

{
"event": "recording-available",
"departmentId": "dept_abc123",
"data": {
"transactionId": "txn_abc123def456",
"recordingId": "rec_xyz789"
}
}

Event Deduplication

Prevent duplicate processing:

async function handleWebhookWithDedup(event) {
const eventId = `${event.event}_${event.data.transactionId}_${Date.now()}`;

// Check if already processed
const exists = await redis.get(`webhook:${eventId}`);
if (exists) {
console.log('Duplicate event, skipping:', eventId);
return { duplicate: true };
}

// Mark as processing
await redis.setex(`webhook:${eventId}`, 3600, 'processing');

try {
await handleWebhookEvent(event);
await redis.setex(`webhook:${eventId}`, 86400, 'processed');
} catch (error) {
await redis.del(`webhook:${eventId}`);
throw error;
}
}

Testing Events

Simulate Webhooks Locally

// Send test event to your webhook endpoint
const testEvent = {
event: 'transaction-status-update',
departmentId: 'test_dept_123',
data: {
transactionId: 'test_transaction_123',
status: 'in-progress'
}
};

await axios.post('http://localhost:3000/webhooks/notarycam', testEvent);

Next Steps

  • Event Types - Detailed documentation for each event type