Webhooks API

Webhooks API Reference

Receive real-time notifications about events in your Waffy integration. Configure secure webhook endpoints to stay updated on payments, contracts, and user activities.

Overview

Webhooks allow your application to receive real-time notifications when events occur in your Waffy integration. Instead of polling our API, webhooks push event data to your endpoints immediately when something happens.

Real-time

Instant notifications when events occur

Secure

HMAC signature verification and HTTPS required

Reliable

Automatic retries with exponential backoff

POSTCreate Webhook

Register a new webhook endpoint to receive event notifications

Endpoint

POST /webhooks

Request Body

{
  "url": "https://your-app.com/webhooks/waffy",
  "events": [
    "payment.completed",
    "payment.failed", 
    "contract.signed",
    "contract.milestone.completed",
    "user.verified"
  ],
  "description": "Main webhook for production environment",
  "enabled": true,
  "secret": "your-webhook-secret-key",
  "metadata": {
    "environment": "production",
    "version": "v1"
  }
}

Response (201 Created)

{
  "success": true,
  "data": {
    "webhook_id": "wh_1a2b3c4d5e6f7g8h",
    "url": "https://your-app.com/webhooks/waffy",
    "events": [
      "payment.completed",
      "payment.failed",
      "contract.signed", 
      "contract.milestone.completed",
      "user.verified"
    ],
    "status": "active",
    "secret": "whsec_1a2b3c4d5e6f7g8h...",
    "created_at": "2024-01-15T10:30:00Z",
    "last_delivery": null,
    "successful_deliveries": 0,
    "failed_deliveries": 0
  },
  "meta": {
    "request_id": "req_1a2b3c4d5e6f",
    "timestamp": "2024-01-15T10:30:00Z"
  }
}

Available Events

Complete list of events you can subscribe to

Payment Events

payment.createdNew payment initiated
payment.processingPayment is being processed
payment.completedPayment successfully completed
payment.failedPayment failed or was declined

Contract Events

contract.createdNew contract created
contract.signedContract signed by all parties
contract.milestone.completedContract milestone marked complete
contract.completedAll contract milestones completed
contract.cancelledContract was cancelled

User Events

user.createdNew user account created
user.verifiedUser completed KYC verification
user.updatedUser information updated

Webhook Payload Structure

Example of how webhook events are delivered to your endpoint

Payment Completed Event

{
  "id": "evt_1a2b3c4d5e6f7g8h",
  "type": "payment.completed",
  "created": "2024-01-15T10:32:15Z",
  "data": {
    "object": {
      "payment_id": "pay_1a2b3c4d5e6f7g8h",
      "amount": 1000.00,
      "currency": "USD",
      "status": "completed",
      "payer": {
        "user_id": "usr_1a2b3c4d5e6f",
        "email": "payer@example.com"
      },
      "recipient": {
        "user_id": "usr_7g8h9i0j1k2l",
        "email": "recipient@example.com"
      },
      "transaction_id": "txn_9i8h7g6f5e4d",
      "completed_at": "2024-01-15T10:32:15Z",
      "metadata": {
        "order_id": "ord_abc123"
      }
    }
  },
  "request": {
    "id": "req_original_1a2b3c4d5e6f",
    "idempotency_key": "idem_xyz789"
  },
  "pending_webhooks": 1,
  "api_version": "2024-01-01"
}

Contract Milestone Completed Event

{
  "id": "evt_2b3c4d5e6f7g8h9i",
  "type": "contract.milestone.completed",
  "created": "2024-01-15T14:22:30Z",
  "data": {
    "object": {
      "contract_id": "ctr_1a2b3c4d5e6f",
      "milestone_id": "mls_1a2b3c4d",
      "title": "Design Approval",
      "amount": 1500.00,
      "currency": "USD",
      "completed_by": "usr_dev456",
      "completed_at": "2024-01-15T14:22:30Z",
      "deliverables": [
        {
          "name": "Design Mockups",
          "url": "https://files.example.com/mockups.pdf"
        }
      ],
      "payment_status": "released"
    }
  },
  "pending_webhooks": 2,
  "api_version": "2024-01-01"
}

Webhook Security

Verify webhook authenticity with HMAC signatures

Signature Verification

Waffy signs webhook payloads with your webhook secret using HMAC-SHA256. The signature is included in the Waffy-Signature header.

// Node.js example
const crypto = require('crypto');

function verifyWebhookSignature(payload, signature, secret) {
  const expectedSignature = crypto
    .createHmac('sha256', secret)
    .update(payload, 'utf8')
    .digest('hex');
    
  const expectedHeader = `sha256=${expectedSignature}`;
  
  return crypto.timingSafeEqual(
    Buffer.from(signature, 'utf8'),
    Buffer.from(expectedHeader, 'utf8')
  );
}

// Usage in Express.js
app.post('/webhooks/waffy', express.raw({type: 'application/json'}), (req, res) => {
  const payload = req.body;
  const signature = req.headers['waffy-signature'];
  const secret = process.env.WAFFY_WEBHOOK_SECRET;
  
  if (!verifyWebhookSignature(payload, signature, secret)) {
    return res.status(401).send('Invalid signature');
  }
  
  const event = JSON.parse(payload);
  
  // Process the webhook event
  switch (event.type) {
    case 'payment.completed':
      handlePaymentCompleted(event.data.object);
      break;
    case 'contract.milestone.completed':
      handleMilestoneCompleted(event.data.object);
      break;
    // Handle other event types...
  }
  
  res.status(200).send('OK');
});

GETList Webhooks

Retrieve all webhook endpoints for your account

Endpoint

GET /webhooks

Response (200 OK)

{
  "success": true,
  "data": [
    {
      "webhook_id": "wh_1a2b3c4d5e6f7g8h",
      "url": "https://your-app.com/webhooks/waffy",
      "events": [
        "payment.completed",
        "payment.failed",
        "contract.signed"
      ],
      "status": "active",
      "created_at": "2024-01-15T10:30:00Z",
      "last_delivery": "2024-01-15T14:22:30Z",
      "successful_deliveries": 42,
      "failed_deliveries": 2
    }
  ],
  "pagination": {
    "total": 1,
    "count": 1,
    "per_page": 20,
    "current_page": 1,
    "total_pages": 1
  }
}

Delivery & Retry Policy

Retry Policy

• Webhooks are retried up to 3 times on failure

• Exponential backoff: 1s, 10s, 100s delays

• HTTP 200-299 responses are considered successful

• Timeout after 30 seconds

Best Practices

• Respond with HTTP 200 status code quickly

• Implement idempotency using the event ID

• Always verify the webhook signature

• Process webhooks asynchronously when possible

Common Errors

400 Bad Request - Invalid URL

{
  "success": false,
  "error": {
    "code": "INVALID_WEBHOOK_URL",
    "message": "Webhook URL must be HTTPS and publicly accessible",
    "details": {
      "url": "http://localhost:3000/webhooks",
      "requirements": ["HTTPS required", "Must be publicly accessible"]
    }
  }
}

422 Unprocessable Entity - Invalid Events

{
  "success": false,
  "error": {
    "code": "INVALID_EVENTS",
    "message": "One or more event types are not supported",
    "details": {
      "invalid_events": ["payment.refunded", "user.deleted"],
      "supported_events": ["payment.completed", "payment.failed", "..."]
    }
  }
}

Webhook Testing Tool

Test your webhook endpoint with sample events

Webhook Guide

Complete guide to implementing webhooks