Skip to content

Webhooks

Webhooks allow your application to receive real-time HTTP notifications when events occur in WPsigner. Instead of polling the API for changes, you can subscribe to events and receive instant updates.

When an event occurs (like a document being signed), WPsigner sends an HTTP POST request to your configured webhook URL with details about the event.

Document Signed → WPsigner → HTTP POST → Your Server
  1. Go to WPsigner → More → Webhooks
  2. Click Add Webhook
  3. Enter your webhook URL (must be HTTPS in production)
  4. Select which events to listen for
  5. Click Save Webhook
SettingDescription
URLThe endpoint that will receive webhook events
SecretA secret key for verifying webhook signatures
EventsWhich events trigger this webhook
StatusActive or paused

EventDescription
document.createdA new document was created
document.sentDocument was sent for signing
document.viewedA signer viewed the document
document.signedA signer completed their signature
document.completedAll signers have signed
document.declinedA signer declined to sign
document.expiredDocument has expired
document.deletedDocument was deleted
EventDescription
signer.addedA new signer was added
signer.remindedA reminder was sent to signer
signer.viewedSigner opened the document
signer.signedSigner completed their signature
signer.declinedSigner declined to sign

All webhooks include a standard payload structure:

{
"event": "document.signed",
"timestamp": "2024-01-20T15:30:45Z",
"webhook_id": "wh_abc123",
"data": {
"document_id": "44",
"document_title": "Employment Contract",
"signer_id": "38",
"signer_name": "John Doe",
"signer_email": "john@example.com",
"status": "signed"
},
"metadata": {
"site_url": "https://your-site.com",
"plugin_version": "2.8.0"
}
}
FieldTypeDescription
eventstringThe event type
timestampstringISO 8601 timestamp
webhook_idstringUnique webhook delivery ID
dataobjectEvent-specific data
metadataobjectAdditional context

{
"event": "document.created",
"timestamp": "2024-01-15T10:00:00Z",
"data": {
"document_id": "44",
"document_title": "Employment Contract",
"created_by": "admin",
"status": "draft"
}
}
{
"event": "document.sent",
"timestamp": "2024-01-15T10:15:00Z",
"data": {
"document_id": "44",
"document_title": "Employment Contract",
"signers_count": 2,
"signers": [
{
"id": "38",
"name": "John Doe",
"email": "john@example.com"
},
{
"id": "39",
"name": "Jane Smith",
"email": "jane@example.com"
}
]
}
}
{
"event": "document.signed",
"timestamp": "2024-01-20T15:30:45Z",
"data": {
"document_id": "44",
"document_title": "Employment Contract",
"signer_id": "38",
"signer_name": "John Doe",
"signer_email": "john@example.com",
"signing_order": 1,
"remaining_signers": 1,
"ip_address": "203.0.113.50"
}
}
{
"event": "document.completed",
"timestamp": "2024-01-21T09:15:00Z",
"data": {
"document_id": "44",
"document_title": "Employment Contract",
"completed_at": "2024-01-21T09:15:00Z",
"total_signers": 2,
"signed_document_url": "https://your-site.com/wp-json/wpsigner/v1/documents/44/file"
}
}
{
"event": "document.declined",
"timestamp": "2024-01-18T14:20:00Z",
"data": {
"document_id": "44",
"document_title": "Employment Contract",
"signer_id": "38",
"signer_name": "John Doe",
"signer_email": "john@example.com",
"decline_reason": "I need more time to review the terms."
}
}

All webhooks include a signature header to verify authenticity:

X-WPS-Signature: sha256=abc123...
function verify_webhook_signature($payload, $signature, $secret) {
$expected = 'sha256=' . hash_hmac('sha256', $payload, $secret);
return hash_equals($expected, $signature);
}
// In your webhook handler
$payload = file_get_contents('php://input');
$signature = $_SERVER['HTTP_X_WPS_SIGNATURE'] ?? '';
$secret = 'your_webhook_secret';
if (!verify_webhook_signature($payload, $signature, $secret)) {
http_response_code(401);
exit('Invalid signature');
}
$event = json_decode($payload, true);
// Process the event...
const crypto = require('crypto');
function verifyWebhookSignature(payload, signature, secret) {
const expected = 'sha256=' +
crypto.createHmac('sha256', secret)
.update(payload)
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(expected),
Buffer.from(signature)
);
}
// Express middleware
app.post('/webhooks/wpsigner', (req, res) => {
const payload = JSON.stringify(req.body);
const signature = req.headers['x-wps-signature'];
const secret = process.env.WEBHOOK_SECRET;
if (!verifyWebhookSignature(payload, signature, secret)) {
return res.status(401).send('Invalid signature');
}
const event = req.body;
// Process the event...
res.status(200).send('OK');
});

Return a 2xx status code to acknowledge receipt:

HTTP/1.1 200 OK

If your endpoint returns a non-2xx status or times out:

  1. WPsigner will retry the webhook
  2. Retries occur at: 1 min, 5 min, 30 min, 2 hours
  3. After 4 failed attempts, the webhook is marked as failed

Webhooks have a 30-second timeout. For long-running processes:

app.post('/webhooks/wpsigner', async (req, res) => {
// Acknowledge immediately
res.status(200).send('OK');
// Process async
processWebhookAsync(req.body);
});

Never trust webhook payloads without verification:

if (!verifySignature(payload, signature, secret)) {
return res.status(401).send('Unauthorized');
}

Don’t block the response:

// ❌ Bad - blocks response
await processLongRunningTask(event);
res.send('OK');
// ✅ Good - async processing
res.send('OK');
processLongRunningTask(event);

Webhooks may occasionally be delivered more than once:

const processed = new Set();
function handleWebhook(event) {
if (processed.has(event.webhook_id)) {
return; // Already processed
}
processed.add(event.webhook_id);
// Process event...
}

Keep detailed logs for debugging:

console.log('Webhook received:', {
event: event.event,
webhook_id: event.webhook_id,
timestamp: new Date().toISOString()
});

Use a tunneling service like ngrok for local testing:

Terminal window
ngrok http 3000
# Use the generated URL: https://abc123.ngrok.io/webhooks

You can trigger test webhooks from the WordPress admin:

  1. Go to WPsigner → Webhooks
  2. Click on your webhook
  3. Click Send Test Event
  4. Select an event type
  5. Click Send

View webhook delivery history in WordPress admin:

  1. Go to WPsigner → More → Webhooks
  2. Click on a webhook to view details
  3. See Recent Deliveries section

Each log entry shows:

  • Event type
  • Response code
  • Response time
  • Timestamp
  • Retry count (if failed)