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-letter

Replay failed events:

POST /v1/webhooks/dead-letter/{eventId}/replay

MeshOS 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/subscriptions

Update 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}/resume

Delete 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.