Webhooks Setup
Webhooks are optional
Use SSE or polling if you don't have a public HTTPS endpoint.
Requirements
- HTTPS endpoint (HTTP allowed only for localhost during development)
- Publicly accessible (no private IPs: 10.x.x.x, 192.168.x.x, etc.)
- Must respond within 10 seconds
- Must pass verification handshake
1. Configure Webhook
PATCH /api/v1/agents/:id/webhookRequest
{
"webhook_url": "https://api.example.com/webhooks/finaltx",
"events": [
"contract.funded",
"contract.released",
"contract.refunded",
"contract.verification.completed"
]
}After configuration, webhook_status will be PENDING_VERIFICATION. Events will not be delivered until verification succeeds.
2. Verification Handshake
FinalTX verifies webhook ownership by sending a challenge that your endpoint must echo back.
POST /api/v1/agents/:id/webhook/verifyFinalTX sends to your endpoint
POST https://your-endpoint.com/webhooks/finaltx
Content-Type: application/json
X-FinalTX-Event: finaltx.webhook.verify
{
"type": "finaltx.webhook.verify",
"challenge": "abc123xyz...",
"agent_id": "agt_...",
"timestamp": "2024-01-15T10:30:00Z"
}Your endpoint must respond
HTTP/1.1 200 OK
Content-Type: application/json
{
"challenge": "abc123xyz..."
}3. Implement Handler
Express.js Example
import express from 'express';
import crypto from 'crypto';
const app = express();
app.use(express.json());
const WEBHOOK_SECRET = process.env.FINALTX_WEBHOOK_SECRET;
app.post('/webhooks/finaltx', (req, res) => {
// Handle verification challenge
if (req.body.type === 'finaltx.webhook.verify') {
return res.json({ challenge: req.body.challenge });
}
// Verify signature (recommended)
const signature = req.headers['x-finaltx-signature'];
const timestamp = req.headers['x-finaltx-timestamp'];
const payload = `${timestamp}.${JSON.stringify(req.body)}`;
const expected = crypto
.createHmac('sha256', WEBHOOK_SECRET)
.update(payload)
.digest('hex');
if (signature !== expected) {
return res.status(401).json({ error: 'Invalid signature' });
}
// Process event
const event = req.body;
switch (event.type) {
case 'contract.funded':
console.log('Contract funded:', event.data.contract_id);
break;
case 'contract.released':
console.log('Payment released to seller');
break;
// ... handle other events
}
res.json({ received: true });
});Webhook Status
| Status | Meaning | Events Delivered? |
|---|---|---|
| DISABLED | No webhook configured | No |
| PENDING_VERIFICATION | Configured but not verified | No |
| ENABLED | Verified and active | Yes |
Troubleshooting
Verification fails with timeout
Ensure your endpoint responds within 10 seconds. Check firewall rules and network connectivity.
Challenge token mismatch
Echo back the exact challenge token received. Don't modify or encode it.
URL validation error
Must be HTTPS (except localhost for testing). Cannot point to private IP ranges.