Skip to content

Webhooks

Webhooks allow your application to receive real-time notifications about events that occur in Pocketbook. Instead of polling for changes, webhooks push event data to your specified endpoint when events occur.

Overview

Pocketbook webhooks deliver event notifications via HTTP POST requests to your configured endpoint URLs. This enables you to:

  • React to events in real-time
  • Automate workflows based on Pocketbook events
  • Synchronize data with external systems
  • Trigger business logic in your application

Webhook Events

Pocketbook supports the following webhook event types:

User Events

  • user.created - New user account created
  • user.updated - User account information updated
  • user.deleted - User account deleted
  • user.verified - User email or identity verified

Transaction Events

  • transaction.created - New transaction recorded
  • transaction.updated - Transaction details updated
  • transaction.completed - Transaction successfully completed
  • transaction.failed - Transaction failed or was rejected

Payment Events

  • payment.succeeded - Payment successfully processed
  • payment.failed - Payment processing failed
  • payment.refunded - Payment was refunded

Subscription Events

  • subscription.created - New subscription started
  • subscription.updated - Subscription details changed
  • subscription.cancelled - Subscription cancelled
  • subscription.renewed - Subscription auto-renewed

Document Events

  • document.uploaded - New document uploaded
  • document.processed - Document processing completed
  • document.verified - Document verification completed

Managing Webhooks

Create Webhook Endpoint

http
POST /api/v1/webhooks

Request Body:

json
{
  "url": "https://your-app.com/webhooks/pocketbook",
  "events": ["transaction.created", "payment.succeeded"],
  "description": "Production webhook endpoint",
  "secret": "your_webhook_secret_key"
}

Parameters:

  • url (string, required): HTTPS endpoint to receive webhook events
  • events (array, required): List of event types to subscribe to
  • description (string, optional): Description for your reference
  • secret (string, optional): Secret key for signature validation

Example Request:

bash
curl -X POST "https://api.pocketbook.studio/api/v1/webhooks" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://your-app.com/webhooks/pocketbook",
    "events": ["transaction.created", "payment.succeeded"],
    "description": "Production webhook"
  }'

Example Response:

json
{
  "id": "webhook_abc123",
  "url": "https://your-app.com/webhooks/pocketbook",
  "events": ["transaction.created", "payment.succeeded"],
  "status": "active",
  "secret": "whsec_xyz789...",
  "created_at": "2024-01-15T10:30:00Z"
}

List Webhooks

http
GET /api/v1/webhooks

Example Request:

bash
curl -X GET "https://api.pocketbook.studio/api/v1/webhooks" \
  -H "Authorization: Bearer YOUR_API_KEY"

Get Webhook Details

http
GET /api/v1/webhooks/{webhook_id}

Update Webhook

http
PATCH /api/v1/webhooks/{webhook_id}

Request Body:

json
{
  "events": ["transaction.created", "transaction.completed", "payment.succeeded"],
  "status": "active"
}

Delete Webhook

http
DELETE /api/v1/webhooks/{webhook_id}

Webhook Payload Structure

All webhook events follow this standard structure:

json
{
  "id": "evt_abc123xyz",
  "type": "transaction.created",
  "created_at": "2024-01-15T10:30:00Z",
  "data": {
    "object": {
      "id": "txn_123456",
      "amount": 5000,
      "currency": "USD",
      "status": "completed",
      "user_id": "user_789",
      "created_at": "2024-01-15T10:29:55Z"
    }
  },
  "api_version": "2024-01"
}

Fields:

  • id: Unique event identifier
  • type: Event type (e.g., transaction.created)
  • created_at: ISO 8601 timestamp when event occurred
  • data.object: The resource object related to the event
  • api_version: API version used for the event

Example Event Payloads

Transaction Created

json
{
  "id": "evt_tx_created_123",
  "type": "transaction.created",
  "created_at": "2024-01-15T10:30:00Z",
  "data": {
    "object": {
      "id": "txn_123456",
      "type": "payment",
      "amount": 5000,
      "currency": "USD",
      "status": "pending",
      "user_id": "user_789",
      "description": "Monthly subscription payment",
      "metadata": {
        "plan": "premium",
        "billing_period": "2024-01"
      }
    }
  }
}

Payment Succeeded

json
{
  "id": "evt_pay_success_456",
  "type": "payment.succeeded",
  "created_at": "2024-01-15T10:30:05Z",
  "data": {
    "object": {
      "id": "pay_abc123",
      "transaction_id": "txn_123456",
      "amount": 5000,
      "currency": "USD",
      "payment_method": "card",
      "status": "succeeded",
      "receipt_url": "https://pocketbook.studio/receipts/pay_abc123"
    }
  }
}

User Verified

json
{
  "id": "evt_user_verified_789",
  "type": "user.verified",
  "created_at": "2024-01-15T11:00:00Z",
  "data": {
    "object": {
      "id": "user_789",
      "email": "user@example.com",
      "verification_type": "email",
      "verified_at": "2024-01-15T11:00:00Z"
    }
  }
}

Webhook Security

Signature Verification

Pocketbook signs all webhook requests with HMAC-SHA256. Verify signatures to ensure requests are authentic.

Signature Header:

X-Pocketbook-Signature: t=1234567890,v1=abc123def456...

Verification Steps:

  1. Extract timestamp and signature from header
  2. Construct signed payload: {timestamp}.{request_body}
  3. Compute HMAC-SHA256 hash using your webhook secret
  4. Compare computed hash with signature from header

Example (Node.js):

javascript
const crypto = require('crypto');

function verifyWebhookSignature(payload, signature, secret) {
  const parts = signature.split(',');
  const timestamp = parts.find(p => p.startsWith('t=')).split('=')[1];
  const receivedSignature = parts.find(p => p.startsWith('v1=')).split('=')[1];

  // Construct signed payload
  const signedPayload = `${timestamp}.${payload}`;

  // Compute HMAC
  const expectedSignature = crypto
    .createHmac('sha256', secret)
    .update(signedPayload)
    .digest('hex');

  // Compare signatures
  return crypto.timingSafeEqual(
    Buffer.from(expectedSignature),
    Buffer.from(receivedSignature)
  );
}

Example (Python):

python
import hmac
import hashlib

def verify_webhook_signature(payload, signature, secret):
    parts = dict(part.split('=') for part in signature.split(','))
    timestamp = parts['t']
    received_signature = parts['v1']

    # Construct signed payload
    signed_payload = f"{timestamp}.{payload}"

    # Compute HMAC
    expected_signature = hmac.new(
        secret.encode(),
        signed_payload.encode(),
        hashlib.sha256
    ).hexdigest()

    # Compare signatures
    return hmac.compare_digest(expected_signature, received_signature)

Timestamp Validation

Always verify the timestamp to prevent replay attacks:

javascript
const MAX_TIMESTAMP_AGE = 5 * 60; // 5 minutes

function isTimestampValid(timestamp) {
  const now = Math.floor(Date.now() / 1000);
  return Math.abs(now - timestamp) < MAX_TIMESTAMP_AGE;
}

HTTPS Requirement

All webhook endpoints must use HTTPS. HTTP endpoints will be rejected.

Implementing a Webhook Endpoint

Best Practices

  1. Respond Quickly: Return 200 OK as fast as possible
  2. Process Asynchronously: Queue events for background processing
  3. Verify Signatures: Always validate webhook signatures
  4. Handle Duplicates: Events may be delivered multiple times
  5. Log Everything: Keep detailed logs for debugging

Example Implementation (Express.js)

javascript
const express = require('express');
const app = express();

app.post('/webhooks/pocketbook',
  express.raw({ type: 'application/json' }),
  async (req, res) => {
    const signature = req.headers['x-pocketbook-signature'];
    const payload = req.body;

    // Verify signature
    if (!verifyWebhookSignature(payload, signature, process.env.WEBHOOK_SECRET)) {
      return res.status(401).send('Invalid signature');
    }

    // Respond immediately
    res.status(200).send('OK');

    // Process event asynchronously
    const event = JSON.parse(payload);
    processWebhookEvent(event).catch(console.error);
  }
);

async function processWebhookEvent(event) {
  switch (event.type) {
    case 'transaction.created':
      await handleTransactionCreated(event.data.object);
      break;
    case 'payment.succeeded':
      await handlePaymentSucceeded(event.data.object);
      break;
    // Handle other event types...
  }
}

Webhook Delivery

Retry Policy

If your endpoint returns a non-2xx response, Pocketbook will retry the webhook:

  • Initial retry: After 1 minute
  • Second retry: After 5 minutes
  • Third retry: After 15 minutes
  • Fourth retry: After 1 hour
  • Final retry: After 6 hours

After 5 failed attempts, the webhook will be marked as failed.

Timeout

Webhook requests timeout after 30 seconds. Ensure your endpoint responds within this time.

Monitoring

Monitor webhook deliveries in your Pocketbook dashboard:

  • View delivery status for each event
  • See retry attempts and failures
  • Access webhook logs and error messages

Testing Webhooks

Test Mode

Create test webhooks to receive events from test mode transactions:

bash
curl -X POST "https://api.pocketbook.studio/api/v1/webhooks" \
  -H "Authorization: Bearer YOUR_TEST_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://your-dev-app.com/webhooks",
    "events": ["transaction.created"]
  }'

Local Testing

Use tools like ngrok to test webhooks locally:

bash
# Start ngrok tunnel
ngrok http 3000

# Use the ngrok URL as your webhook endpoint
https://abc123.ngrok.io/webhooks/pocketbook

Manual Trigger

Trigger test webhooks from the Pocketbook dashboard to verify your endpoint implementation.

Webhook Events Reference

For detailed information about each webhook event and its payload structure, see the Events Reference documentation.

Troubleshooting

Common Issues

  1. Signature Verification Fails

    • Ensure you're using the raw request body
    • Check that your webhook secret is correct
    • Verify timestamp is within acceptable range
  2. Webhooks Not Received

    • Verify endpoint is HTTPS
    • Check firewall settings
    • Ensure endpoint returns 200 OK quickly
  3. Duplicate Events

    • Implement idempotency using event IDs
    • Check retry logic isn't triggering unnecessarily

For more help, see Error Handling or contact support.

Released under the MIT License.