Skip to main content

Webhooks

Webhooks provide real-time notifications when events occur in your Proxy account. Configure endpoints to receive HTTP POST requests for events you care about.

Setting Up Webhooks

Create a Webhook Endpoint

curl -X POST https://api.useproxy.ai/v1/webhooks \
  -H "Authorization: Bearer your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://your-app.com/webhooks/proxy",
    "events": ["transaction.created", "intent.matched"],
    "description": "Production webhook"
  }'

Webhook Fields

FieldTypeDescription
urlstringHTTPS endpoint to receive events
eventsarrayList of event types to subscribe to
descriptionstringHuman-readable description
statusstringactive or disabled

Event Types

Transaction Events

EventDescription
transaction.createdNew transaction recorded
transaction.settledTransaction settled
transaction.declinedTransaction was declined

Intent Events

EventDescription
intent.createdNew intent declared
intent.matchedIntent matched to transaction
intent.mismatchedTransaction didn’t match intent
intent.expiredIntent expired
intent.pending_approvalIntent awaiting approval

Card Events

EventDescription
card.createdNew card issued
card.frozenCard was frozen
card.closedCard was closed

Webhook Payload

{
  "id": "evt_xxx",
  "type": "transaction.created",
  "createdAt": "2024-01-15T10:30:00Z",
  "data": {
    "transactionId": "txn_xxx",
    "cardId": "card_xxx",
    "amount": 5000,
    "merchant": "Amazon"
  }
}

Signature Verification

All webhooks include a Proxy-Signature header for verification. The header format is:
Proxy-Signature: t=1706745600,v1=5257a869e7ecebeda32affa62cdca3fa51cad7e77a0e56ff536d0ce8e108d8bd
Where:
  • t is the Unix timestamp (seconds) when the webhook was sent
  • v1 is the HMAC-SHA256 signature
The signature is computed over {timestamp}.{json_payload} using your webhook secret.

Node.js Example

const crypto = require('crypto');

function verifyWebhook(req, secret) {
  const signature = req.headers['proxy-signature'];
  const payload = JSON.stringify(req.body);

  // Parse the signature header
  const parts = signature.split(',');
  const timestamp = parts[0].split('=')[1];
  const receivedSig = parts[1].split('=')[1];

  // Compute expected signature
  const signedPayload = `${timestamp}.${payload}`;
  const expectedSig = crypto
    .createHmac('sha256', secret)
    .update(signedPayload)
    .digest('hex');

  // Compare signatures (use timing-safe comparison)
  return crypto.timingSafeEqual(
    Buffer.from(receivedSig),
    Buffer.from(expectedSig)
  );
}

// Express.js handler example
app.post('/webhooks/proxy', express.json(), (req, res) => {
  const secret = 'whsec_your_webhook_secret';

  if (!verifyWebhook(req, secret)) {
    return res.status(401).send('Invalid signature');
  }

  // Process the webhook
  const event = req.body;
  console.log('Received event:', event.type);

  res.status(200).send('OK');
});

Convex Example

import { httpAction } from "./_generated/server";

export const webhookHandler = httpAction(async (ctx, request) => {
  const signature = request.headers.get("proxy-signature");
  const body = await request.text();
  const secret = process.env.PROXY_WEBHOOK_SECRET!;

  // Parse signature
  const parts = signature?.split(',') || [];
  const timestamp = parts[0]?.split('=')[1];
  const receivedSig = parts[1]?.split('=')[1];

  // Verify signature
  const signedPayload = `${timestamp}.${body}`;
  const encoder = new TextEncoder();
  const key = await crypto.subtle.importKey(
    "raw",
    encoder.encode(secret),
    { name: "HMAC", hash: "SHA-256" },
    false,
    ["sign"]
  );
  const sig = await crypto.subtle.sign("HMAC", key, encoder.encode(signedPayload));
  const expectedSig = Array.from(new Uint8Array(sig))
    .map(b => b.toString(16).padStart(2, '0'))
    .join('');

  if (receivedSig !== expectedSig) {
    return new Response("Invalid signature", { status: 401 });
  }

  // Process webhook
  const event = JSON.parse(body);
  console.log("Received:", event.type);

  return new Response("OK", { status: 200 });
});
Always verify webhook signatures before processing events. This ensures the request came from Proxy and hasn’t been tampered with.

Retry Policy

Failed webhook deliveries are retried with exponential backoff:
AttemptDelay
1Immediate
25 minutes
330 minutes
42 hours
524 hours
After 5 failed attempts, the webhook is marked as failed and requires manual retry.

Managing Webhooks

List Webhooks

curl https://api.useproxy.ai/v1/webhooks \
  -H "Authorization: Bearer your_api_key"

Update a Webhook

curl -X PATCH https://api.useproxy.ai/v1/webhooks/wh_xxx \
  -H "Authorization: Bearer your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "events": ["transaction.created", "transaction.declined"]
  }'

Delete a Webhook

curl -X DELETE https://api.useproxy.ai/v1/webhooks/wh_xxx \
  -H "Authorization: Bearer your_api_key"

Next Steps