Webhooks API
Receive real-time notifications when events occur across MeshOS, Axis, and Smart Contracts. All three platforms use the same webhook infrastructure.
Creating a Subscription
POST /v1/webhooks/subscriptions
{
"name": "My CI/CD Integration",
"url": "https://your-app.com/webhooks/meshos",
"events": [
"review.completed",
"approval.granted"
],
"platform": "meshos",
"secret": "whsec_your_secret_string"
}Response:
{
"subscription_id": "sub_01HXYZ",
"name": "My CI/CD Integration",
"url": "https://your-app.com/webhooks/meshos",
"events": ["review.completed", "approval.granted"],
"status": "active",
"created_at": "2024-01-15T10:30:00Z"
}Webhook Payload Structure
All webhook deliveries use the same envelope format:
{
"id": "evt_01HXYZ",
"subscription_id": "sub_01HXYZ",
"event": "review.completed",
"timestamp": "2024-01-15T10:40:15Z",
"platform": "meshos",
"data": {
// Event-specific payload
}
}Signature Verification
Every delivery is signed with HMAC-SHA256 using your webhook secret:
Header: X-Signature: sha256={signature}
Verify in Node.js:
import crypto from 'crypto';
function verifyWebhookSignature(
rawBody: string,
signature: string,
secret: string
): boolean {
const expected = `sha256=${crypto
.createHmac('sha256', secret)
.update(rawBody, 'utf8')
.digest('hex')}`;
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expected)
);
}
app.post('/webhooks/meshos', (req, res) => {
const signature = req.headers['x-signature'] as string;
const rawBody = req.rawBody; // Use raw body, not parsed JSON
if (!verifyWebhookSignature(rawBody, signature, process.env.WEBHOOK_SECRET)) {
return res.status(401).json({ error: 'Invalid signature' });
}
// Process event
const event = JSON.parse(rawBody);
handleEvent(event);
res.status(200).json({ received: true });
});Always use timingSafeEqual. Regular string comparison is vulnerable to timing attacks.
Delivery and Retry
Delivery Policy
- First delivery attempt within 5 seconds of event
- Delivery considered successful: 2xx response within 10 seconds
- Delivery considered failed: non-2xx response, timeout, or connection error
Retry Schedule
| Attempt | Delay After Previous | |---------|---------------------| | 1st | Immediate | | 2nd | 1 minute | | 3rd | 5 minutes | | 4th | 30 minutes | | 5th | 2 hours |
After 5 failed attempts, event moves to dead-letter queue.
Dead-Letter Queue
GET /v1/webhooks/dead-letterReplay failed events:
POST /v1/webhooks/dead-letter/{eventId}/replayMeshOS Events
| Event | Trigger |
|-------|---------|
| application.uploaded | ZIP upload received |
| analysis.started | Code analysis began |
| analysis.completed | Static analysis finished |
| review.started | AI review started |
| review.completed | All agents finished |
| approval.granted | Application approved |
| approval.rejected | Application rejected |
| component.promoted | Component promoted to registry |
| component.deprecated | Component deprecated |
review.completed payload:
{
"review_id": "rev_01HXYZ",
"application_id": "app_01HXYZ",
"status": "completed",
"scores": {
"cloud_readiness": 74,
"library_readiness": 82,
"mesh_readiness": 61
},
"findings": {
"critical": 0,
"high": 3,
"medium": 12,
"low": 32
},
"patches_generated": 8,
"duration_seconds": 285
}Axis Events
| Event | Trigger |
|-------|---------|
| campaign.created | New campaign created |
| campaign.launched | Campaign execution started |
| campaign.paused | Campaign paused |
| campaign.completed | All sequences finished |
| lead.imported | Import job completed |
| lead.enriched | Lead enrichment finished |
| communication.sent | Message delivered |
| communication.opened | Email opened |
| communication.replied | Reply received |
| meeting.booked | Meeting scheduled |
| slo.violated | SLO threshold breached |
| budget.threshold_reached | Spend alert triggered |
meeting.booked payload:
{
"lead_id": "lead_01HXYZ",
"campaign_id": "camp_01HXYZ",
"meeting_time": "2024-01-22T14:00:00Z",
"duration_minutes": 30,
"sales_rep": "user_01HXYZ",
"attribution": {
"first_touch": "email_1",
"last_touch": "linkedin_dm_3",
"touchpoints": 5
}
}Smart Contracts Events
| Event | Trigger |
|-------|---------|
| contract.created | Contract registered |
| contract.deployed | Deployment confirmed |
| contract.deployment_failed | Deployment failed |
| execution.confirmed | Function execution confirmed |
| execution.failed | Transaction reverted |
| alert.triggered | Monitoring alert fired |
| backup.completed | Backup finished |
| backup.failed | Backup failed |
| scaling.event | Node scaled up or down |
execution.failed payload:
{
"execution_id": "exec_01HXYZ",
"contract_id": "contract_01HXYZ",
"network": "ethereum_mainnet",
"function": "transfer",
"transaction_hash": "[HASH]",
"error": {
"type": "revert",
"reason": "ERC20: transfer amount exceeds balance",
"gas_used": 28000
}
}Managing Subscriptions
List Subscriptions
GET /v1/webhooks/subscriptionsUpdate Subscription
PUT /v1/webhooks/subscriptions/{subId}
{
"events": ["review.completed", "approval.granted", "approval.rejected"],
"url": "https://your-app.com/webhooks/new-endpoint"
}Pause/Resume
POST /v1/webhooks/subscriptions/{subId}/pause
POST /v1/webhooks/subscriptions/{subId}/resumeDelete Subscription
DELETE /v1/webhooks/subscriptions/{subId}Delivery Logs
GET /v1/webhooks/subscriptions/{subId}/deliveries?limit=50{
"deliveries": [
{
"delivery_id": "del_01HXYZ",
"event": "review.completed",
"status": "success",
"response_code": 200,
"response_time_ms": 145,
"delivered_at": "2024-01-15T10:40:15Z",
"attempt": 1
},
{
"delivery_id": "del_01HXYA",
"event": "review.completed",
"status": "failed",
"response_code": 503,
"response_time_ms": 10000,
"last_attempt_at": "2024-01-15T12:40:15Z",
"attempt": 3,
"next_retry_at": "2024-01-15T14:40:15Z"
}
]
}Testing Webhooks
Send a test event to verify your endpoint:
POST /v1/webhooks/subscriptions/{subId}/test
{
"event": "review.completed"
}Sends a realistic sample payload with "test": true in the envelope. Your endpoint should return 200. Useful for verifying signature verification code.