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
| Field | Type | Description |
|---|
url | string | HTTPS endpoint to receive events |
events | array | List of event types to subscribe to |
description | string | Human-readable description |
status | string | active or disabled |
Event Types
Transaction Events
| Event | Description |
|---|
transaction.created | New transaction recorded |
transaction.settled | Transaction settled |
transaction.declined | Transaction was declined |
Intent Events
| Event | Description |
|---|
intent.created | New intent declared |
intent.matched | Intent matched to transaction |
intent.mismatched | Transaction didn’t match intent |
intent.expired | Intent expired |
intent.pending_approval | Intent awaiting approval |
Card Events
| Event | Description |
|---|
card.created | New card issued |
card.frozen | Card was frozen |
card.closed | Card 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:
| Attempt | Delay |
|---|
| 1 | Immediate |
| 2 | 5 minutes |
| 3 | 30 minutes |
| 4 | 2 hours |
| 5 | 24 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