Orchestrate
Webhooks

Webhooks

Receive real-time notifications when events occur in your Payload account


Webhooks enable real-time event notifications by sending HTTP POST requests to your server when specific events occur. Instead of polling the Payload API to check for changes, webhooks push event data to your application instantly, allowing you to build responsive, event-driven integrations that react immediately to activity in your account.

Common use cases

  • Automatically fulfill orders and send receipts when payments complete
  • Send email or SMS notifications for transaction events
  • Sync transaction data to accounting and external systems
  • Monitor business metrics and detect anomalies in real-time
  • Trigger marketing campaigns based on customer activity
  • Update databases and maintain audit trails of all activity

Quick Start Guide

Get started with webhooks in three steps: create a webhook, implement a handler, and test your integration.

Create a Webhook

Create a webhook by specifying a trigger event and your endpoint URL.

import payload
 
# Create a webhook to monitor events
webhook = payload.Webhook.create({
    'trigger': 'processed',
    'url': 'https://yourdomain.com/webhooks/handler'
})
 
print(f'Webhook created: {webhook.id}')
print(f'Listening for: {webhook.trigger}')
print(f'Posting to: {webhook.url}')

This creates a webhook that monitors for the processed trigger and posts to your URL when matching events occur.

Implement a Handler

Create an endpoint to receive webhook POST requests.

from flask import Flask, request, jsonify
 
app = Flask(__name__)
 
# Basic webhook handler
@app.route('/webhooks/handler', methods=['POST'])
def webhook_handler():
    webhook_data = request.json
 
    trigger = webhook_data['trigger']
    triggered_on = webhook_data['triggered_on']
 
    print(f'Webhook received: {trigger}')
    print(f'Event ID: {triggered_on["id"]}')
    print(f'Object type: {triggered_on["object"]}')
    print(f'Status: {triggered_on["value"]}')
 
    # Acknowledge receipt
    return jsonify({'status': 'received'}), 200
 
if __name__ == '__main__':
    app.run(port=3000)

Your webhook handler should:

  • Accept POST requests at your configured URL
  • Parse the JSON payload
  • Return a 200 status code within 30 seconds
  • Process events asynchronously for reliability

Test Your Webhook

Trigger an event to test your webhook:

  1. Trigger Event: Create a transaction or perform an action that matches your webhook trigger
  2. Verify Receipt: Confirm your handler receives the webhook POST request
  3. Check Logs: Review webhook logs to verify successful delivery
  4. Validate Data: Ensure the payload contains expected event details

Webhook Triggers

Webhooks support various triggers covering transaction events, payment activity, and payment method changes.

Transaction Lifecycle Events

TriggerWhen It FiresCommon Use Cases
paymentPayment transaction createdLog all payment attempts, fraud monitoring
processedTransaction completed successfullyFulfill orders, send receipts, update inventory
authorizedPayment authorized (not captured)Reserve inventory, verify orders before capture
declinePayment declined by processor/bankNotify customer, retry payment, update order status
adjustedTransaction amount adjustedUpdate accounting, notify customer of changes
refundRefund transaction createdUpdate inventory, notify customer, reverse charges
voidTransaction voidedCancel order, release inventory, notify customer
rejectTransaction rejectedHandle NSF, invalid accounts, retry payment

Payout and Funding Events

TriggerWhen It FiresCommon Use Cases
payoutPayout transaction createdTrack outgoing payments, notify recipients
depositDeposit transaction createdMonitor account funding, track cash flow
withdrawWithdraw transaction createdTrack outgoing funds, monitor cash outflows

Recurring Payment Events

TriggerWhen It FiresCommon Use Cases
automatic_paymentScheduled payment triggeredMonitor subscription billing, handle failures

Account and Link Status Events

TriggerWhen It FiresCommon Use Cases
intent:statusIntent status changes (coming soon)Track intent lifecycle, monitor setup
payment_link:statusPayment link status changesMonitor link usage, track completion
processing_statusAny processing account status changeSpecifies when account becomes active or goes on hold

Transaction Operation Events

TriggerWhen It FiresCommon Use Cases
transaction:operationTransaction operation triggeredMonitor batch operations, track processing status
transaction:operation:clearTransaction operation clearedTrack completion, handle post-operation tasks

For specific transaction webhook documentation, see:


Webhook Payload Structure

All webhooks send a consistent payload structure containing event details.

Standard Webhook Payload

{
  "object": "webhook_trigger",
  "event_id": "evt_XYZ789",
  "trigger": "processed",
  "triggered_on": {
    "id": "txn_ABC123",
    "object": "transaction",
    "value": "processed"
  }
}

Payload Fields

  • object: Always webhook_trigger for webhook events
  • event_id: Unique identifier for this webhook event that remains constant across retries. Use this for idempotency to prevent processing the same event multiple times.
  • trigger: The event type that triggered this webhook
  • triggered_on: Details about the object that caused the trigger
    • id: Unique identifier of the triggering object
    • object: Type of object (e.g., transaction, customer)
    • value: Current status or state of the object

Retrieving Full Object Details

The webhook payload includes minimal object information. To get complete details:

import payload
from flask import Flask, request, jsonify
 
app = Flask(__name__)
pl = payload.Session('secret_key_3bW9...', api_version='v2.0')
 
@app.route('/webhooks/handler', methods=['POST'])
def webhook_handler():
    webhook_data = request.json
    triggered_on = webhook_data['triggered_on']
 
    # Retrieve full object details
    transaction = pl.Transaction.get(triggered_on['id'])
 
    # Access complete transaction data
    print(f'Transaction amount: {transaction.amount}')
    print(f'Transaction description: {transaction.description}')
    print(f'Transaction status: {transaction.status}')
 
    # Process full transaction data here
    # ...
 
    return jsonify({'status': 'processed'}), 200
 
if __name__ == '__main__':
    app.run(port=3000)

Webhook Security

Secure your webhook endpoints to ensure requests are genuinely from Payload and haven't been tampered with.

Signature Verification

Verify webhook authenticity using HMAC-SHA256 signatures. Each webhook request includes an X-Payload-Signature header containing a signature of the payload. Implement Signature Verification to validate that webhook requests originate from Payload and haven't been tampered with, preventing unauthorized requests from triggering actions in your system.

OAuth Authentication

For endpoints requiring OAuth 2.0 authentication, configure OAuth parameters when creating webhooks. Payload automatically obtains and includes access tokens in webhook requests to protected endpoints. Learn how to set up OAuth Authentication for enterprise-grade security on your webhook endpoints.

Security Best Practices

Always use HTTPS for webhook URLs in production, verify signatures to prevent unauthorized access, and validate payloads before processing. Use cryptographically secure webhook secrets, rotate them periodically for security, and log verification failures for monitoring. Implement rate limiting to protect endpoints from abuse, and ensure handlers validate the webhook structure and content before taking action.


Webhook Reliability and Retries

Payload ensures reliable webhook delivery through automatic retries and comprehensive logging.

Automatic Retries

Webhooks are automatically retried when delivery fails:

  • Retry Triggers: HTTP errors (4xx, 5xx), timeouts, network failures
  • Max Retries: Configurable when creating webhooks (default: 0)
  • Success Condition: 2xx HTTP response stops retries

Configuring Retries

import payload
pl = payload.Session('secret_key_3bW9...', api_version='v2')
 
# Create webhook with custom retry configuration
webhook = pl.Webhook.create({
    'trigger': 'processed',
    'url': 'https://yourdomain.com/webhooks/handler',
    'retries': 5  # Retry up to 5 times
})
 
print('Webhook created with 5 retry attempts')

Idempotency

Since webhooks may be delivered multiple times, implement idempotent handlers:

from flask import Flask, request, jsonify
 
app = Flask(__name__)
 
processed_events = set()
 
@app.route('/webhooks/handler', methods=['POST'])
def webhook_handler():
    webhook_data = request.json
    event_id = webhook_data['event_id']
 
    # Check if already processed
    if event_id in processed_events:
        print('Duplicate webhook - skipping')
        return jsonify({'status': 'duplicate'}), 200
 
    # Process webhook
    processed_events.add(event_id)
 
    # Your webhook processing logic here
    trigger = webhook_data['trigger']
    triggered_on = webhook_data['triggered_on']
    print(f'Processing webhook: {trigger}')
    print(f'Event ID: {event_id}')
    print(f'Object: {triggered_on["object"]}')
 
    return jsonify({'status': 'processed'}), 200
 
if __name__ == '__main__':
    app.run(port=3000)

Use the event_id field from the webhook payload to detect and skip duplicate deliveries. The event_id remains constant across all retry attempts for the same event.


Monitoring and Debugging

Monitor webhook delivery and debug issues using webhook logs.

Viewing Webhook Logs

import payload
pl = payload.Session('secret_key_3bW9...', api_version='v2')
 
# Retrieve webhook with logs
webhook = pl.Webhook.select('*', 'logs').get('wh_webhook123')
 
print(f'Webhook ID: {webhook.id}')
print(f'Trigger: {webhook.trigger}')
print(f'\nWebhook Logs ({len(webhook.logs)} entries):')
 
for log in webhook.logs:
    print(f'\nDate: {log.created_at}')
    print(f'Trigger: {log.trigger}')
    print(f'HTTP Status: {log.http_status}')
    print(f'Triggered on: {log.triggered_on["id"]}')
    print(f'URL: {log.url}')

What Logs Show

Webhook logs include:

  • Delivery timestamps
  • HTTP response status codes
  • Triggered events
  • Objects that triggered webhooks
  • Retry attempts
  • Destination URLs

Common Status Codes

StatusMeaningNext Steps
200SuccessWebhook processed successfully
401UnauthorizedCheck signature verification or OAuth
404Not FoundVerify webhook URL is correct
500Server ErrorCheck application logs, fix bugs
TimeoutNo ResponseOptimize handler, return 200 faster

For comprehensive debugging, see:


Managing Webhooks

List, update, and delete webhooks using the Payload API.

Listing Webhooks

import payload
 
pl = payload.Session('secret_key_3bW9...', api_version='v2')
 
# Get all webhooks
webhooks = pl.Webhook.all()
 
for webhook in webhooks:
    print(f"{webhook.id}: {webhook.trigger} -> {webhook.url}")

Updating Webhooks

import payload
 
pl = payload.Session('secret_key_3bW9...', api_version='v2')
 
# Get webhook
webhook = pl.Webhook.get('wh_webhook123')
 
# Update webhook URL or configuration
webhook.update(
    url='https://yourdomain.com/new-webhook-url',
    retries=5,
)
 
print('Webhook updated successfully')

Deleting Webhooks

import payload
 
pl = payload.Session('secret_key_3bW9...', api_version='v2')
 
# Remove a webhook
wh = pl.Webhook.get('wh_webhook123')
wh.delete()
 
print('Webhook deleted successfully')

Webhook changes take effect immediately for new events. In-flight deliveries may still use old configurations.


Testing Webhooks

Test webhook implementations before production deployment.

Local Development Testing

Using ngrok:

# Expose local server to receive webhooks
ngrok http 3000
 
# Use the HTTPS URL for webhook creation
# https://abc123.ngrok.io/webhooks/handler

Using webhook.site:

  1. Visit https://webhook.site (opens in a new tab)
  2. Copy the unique URL
  3. Create webhook with that URL
  4. Trigger events and inspect payloads

Testing Checklist

Test these scenarios before production:

  • Webhook handler receives POST requests correctly
  • Handler returns 200 status within 30 seconds
  • Signature verification works (if implemented)
  • OAuth authentication works (if implemented)
  • Invalid signatures are rejected properly
  • Handler is idempotent (handles duplicates)
  • Error handling returns appropriate status codes
  • Handler scales under load
  • Logs capture important information
  • Monitoring alerts trigger correctly

Test Mode

Use Payload test mode to test webhooks safely:

  • Test transactions trigger test webhooks
  • No impact on production data
  • Same webhook behavior as production
  • Verify implementation before going live

Best Practices

Follow these best practices to build reliable, secure webhook integrations.

  • Return 200 Immediately: Acknowledge webhooks quickly, process asynchronously
  • Implement Idempotency: Handle duplicate deliveries safely
  • Always Use HTTPS: Webhook URLs must be HTTPS in production
  • Verify Signatures: Required for production webhook security
  • Validate Payloads: Check webhook structure before processing

Schema Reference

Webhook configuration fields for creating and managing webhooks.

Webhook Configuration

trigger
enum[string]
No description
Values: bank_account_reject, reject, refund, void, chargeback, chargeback_reversal, automatic_payment, payment, processed, authorized, adjusted, decline, processing_status, deposit, payment_activation:status, payment_link:status, reversal, credit, transaction:operation, transaction:operation:clear, payout, withdraw
url
string
Triggered URL
Max length: 512
retries
integer (int64)
Number of times to retry a webhook on failure
sender_secret
string
Secret key for HMAC-SHA256 signature verification. When set, webhook requests will include an X-Payload-Signature header. This field is write-only and will not be returned in responses.
Max length: 64
oauth_params
object
OAuth Parameters
auth_url
string
The url of the OAuth 2.0 token request
client_id
string
The client identifier of the OAuth 2.0 client
client_secret
string
The client secret of the OAuth 2.0 client
grant_type
string
Indicates that a token exchange is being performed
resource
string
Indicates the location of the target service or resource
scope
string
Optional parameter to limit access to a user's account

Next Steps

Secure webhooks, monitor delivery, and build event-driven integrations


Secure Your Webhooks

Implement Signature Verification with HMAC-SHA256 to verify webhook authenticity, configure OAuth Authentication for protected endpoints requiring enterprise-grade security, and review the Webhook API Reference for complete security options.

Monitor and Debug Webhooks

Troubleshoot delivery issues with Debugging Webhooks to identify and resolve failed webhook deliveries, monitor webhook history to track event processing, and implement retry logic to handle temporary failures gracefully.

Monitor Specific Events

Subscribe to Transaction Webhooks to monitor payment and payout events in real-time, track invoice lifecycle events for automated billing workflows, and receive notifications for account status changes and entity verification updates.

Build Event-Driven Integrations

Use webhooks to automate order fulfillment when payments complete, sync transaction data to external systems and accounting platforms, trigger customer notifications via email or SMS, and build responsive systems that react instantly to events without polling.


Related articles