Skip to main content

Documentation Index

Fetch the complete documentation index at: https://guide.withvayu.com/llms.txt

Use this file to discover all available pages before exploring further.

Webhooks let Vayu push notifications to your server when billing events occur. You subscribe per event type with a callback URL, and Vayu sends a signed POST request whenever that event fires.

Subscribing

Endpoint: POST /webhook
{
  "callbackUrl": "https://your-server.com/webhooks/vayu",
  "eventType": "Overage"
}
curl -X POST "https://connect.withvayu.com/webhook" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "x-api-key: $VAYU_CLIENT_ID" \
  -d '{
    "callbackUrl": "https://your-server.com/webhooks/vayu",
    "eventType": "Overage"
  }'

Available event types

Event typeDescription
OverageCustomer exceeds their provisioned amount for a product
AnonymousCustomerEvent received for an unrecognized customer alias — auto-created as anonymous
UpcomingRenewalCustomer contract is approaching its renewal date
InvoiceApprovedAn invoice has been approved and is ready to send
UnchargedEventsEvents exist that have not been counted against any meter
TierCrossedCustomer usage has crossed into a new pricing tier
CommitmentCrossedCustomer has crossed their committed usage threshold
FinalTierExceededCustomer usage has exceeded the final pricing tier
InvoicePaymentStatusChangedAn invoice payment status has changed (e.g. paid, failed, overdue)

Webhook payloads

Overage

{
  "type": "Overage",
  "productId": "prod_123456789",
  "productName": "API Calls",
  "provisionedAmount": 1000,
  "consumedAmount": 1200,
  "usagePercentage": 120,
  "hasAccess": false,
  "remainingAmount": 0,
  "exceededAmount": 200
}

AnonymousCustomer

{
  "type": "AnonymousCustomer",
  "id": "cust_123456789",
  "externalId": "ext_987654321",
  "aliases": ["ext_987654321"],
  "name": "Anonymous Customer"
}

UpcomingRenewal

{
  "type": "UpcomingRenewal",
  "customerId": "cust_123456789",
  "customerName": "Acme Corp",
  "contractId": "contract_abc123",
  "renewalDate": "2026-02-01T00:00:00Z"
}

InvoiceApproved

{
  "type": "InvoiceApproved",
  "invoiceId": "inv_123456789",
  "customerId": "cust_123456789",
  "customerName": "Acme Corp",
  "amount": 1500.00,
  "currency": "USD",
  "dueDate": "2026-02-15T00:00:00Z"
}

InvoicePaymentStatusChanged

{
  "type": "InvoicePaymentStatusChanged",
  "invoiceId": "inv_123456789",
  "customerId": "cust_123456789",
  "previousStatus": "PENDING",
  "currentStatus": "PAID",
  "amount": 1500.00,
  "currency": "USD"
}

TierCrossed

{
  "type": "TierCrossed",
  "customerId": "cust_123456789",
  "productId": "prod_123456789",
  "productName": "API Calls",
  "previousTier": 1,
  "currentTier": 2,
  "currentUsage": 10001,
  "tierThreshold": 10000
}

CommitmentCrossed

{
  "type": "CommitmentCrossed",
  "customerId": "cust_123456789",
  "contractId": "contract_abc123",
  "committedAmount": 50000,
  "currentUsage": 50001
}

FinalTierExceeded

{
  "type": "FinalTierExceeded",
  "customerId": "cust_123456789",
  "productId": "prod_123456789",
  "productName": "API Calls",
  "finalTierThreshold": 100000,
  "currentUsage": 100500
}

UnchargedEvents

{
  "type": "UnchargedEvents",
  "customerId": "cust_123456789",
  "eventCount": 42,
  "earliestEventTimestamp": "2026-01-10T08:00:00Z"
}

Handling webhook events

A minimal server that receives Vayu webhooks, routes by event type, and reads the payload:
import express from 'express';
import { Vayu } from 'vayu-ts';

const vayu = new Vayu(process.env.VAYU_API_KEY);
const app = express();
app.use(express.json());

// Register the webhook (run once)
// await vayu.webhooks.subscribe({
//   callbackUrl: 'https://your-server.com/webhooks/vayu',
//   eventType: 'Overage',
// });

app.post('/webhooks/vayu', (req, res) => {
  const event = req.body;

  switch (event.type) {
    case 'Overage':
      console.log(
        `${event.productName}: ${event.consumedAmount}/${event.provisionedAmount} used ` +
        `(${event.exceededAmount} over limit)`
      );
      break;

    case 'InvoiceApproved':
      console.log(`Invoice ${event.invoiceId} for ${event.customerName}: $${event.amount} ${event.currency}`);
      break;

    case 'TierCrossed':
      console.log(`${event.productName}: customer moved from tier ${event.previousTier}${event.currentTier}`);
      break;

    case 'AnonymousCustomer':
      console.log(`New anonymous customer created: ${event.externalId}`);
      break;

    default:
      console.log(`Unhandled event: ${event.type}`);
  }

  res.sendStatus(200);
});

app.listen(3000, () => console.log('Listening on :3000'));
In production, always verify the webhook signature before processing the payload.

Webhook security

All Vayu webhook requests include headers for signature verification:
HeaderDescription
X-TimestampUnix timestamp (seconds) of when the request was sent
X-SignatureBase64-encoded RSA-SHA256 signature of timestamp.JSON(payload)
X-Signature-VersionSignature scheme version (currently v1)
The signed message is: ${timestamp}.${JSON.stringify(payload)} Only HTTPS callback URLs are supported.

Verifying signatures

The TypeScript SDK provides a built-in helper. Built-in verification for the Python and Go SDKs is coming soon — in the meantime, verify manually using Vayu’s public key.
import { Vayu } from 'vayu-ts';

const vayu = new Vayu(process.env.VAYU_API_KEY);

app.post('/webhooks', async (req, res) => {
  const payload = JSON.stringify(req.body);
  const timestamp = Number(req.headers['x-timestamp']);
  const signature = String(req.headers['x-signature']);

  const isValid = vayu.webhooks.verifyWebhookSignature({
    payload,
    timestamp,
    signature,
    tolerance: 300, // optional — reject if older than 5 minutes (default)
  });

  if (!isValid) {
    return res.sendStatus(401);
  }

  const event = req.body;
  console.log('Received webhook:', event.type);
  res.sendStatus(200);
});