Webhooks

Webhooks

ApexMCP can notify your services in real time when tool calls are executed. Register webhook URLs in the Developer Portal → Webhooks section.

Registering a Webhook

  1. Go to Developer Portal → Webhooks → New Webhook
  2. Enter your endpoint URL (must be HTTPS)
  3. Select events to subscribe to
  4. Copy the signing secret — used to verify payloads
  5. Click Save

ApexMCP will immediately send a test ping to verify the endpoint is reachable.

Events

EventDescription
tool.calledFired after every successful tools/call execution
approval.requestedA tool call flagged for approval is now pending human review
approval.decidedA pending approval was resolved (executed, rejected, failed, or expired)

More events (connector health, quota warnings) are on the roadmap.

See Tool Approvals for the human-in-the-loop gate that emits the approval.* events.

Payload Format

All webhook payloads are POST requests with Content-Type: application/json.

tool.called example

{
  "event": "tool.called",
  "id": "evt_01j9x8k2m3n4p5q6r7s8t9u0v1",
  "timestamp": "2024-11-15T14:32:01.482Z",
  "org_slug": "acme-corp",
  "data": {
    "tool_name": "query_postgres_orders",
    "connector_id": "conn_01j9x8k2m3n4p5q6r7s8t9u0v1",
    "connector_type": "postgresql",
    "agent_id": "agent_01abc",
    "duration_ms": 143,
    "success": true,
    "error_code": null
  }
}

Fields:

FieldTypeDescription
eventstringEvent type
idstringUnique event ID (idempotency key)
timestampISO 8601When the event occurred (UTC)
org_slugstringOrganisation that triggered the event
data.tool_namestringName of the tool that was called
data.connector_idstringID of the connector
data.connector_typestringType of connector (e.g. postgresql)
data.agent_idstringAgent identity if registered, else null
data.duration_msintegerTool execution time in milliseconds
data.successbooleanWhether the call succeeded
data.error_codeinteger or nullJSON-RPC error code if failed

Signature Verification

Every request includes an X-Webhook-Signature header. The signature is an HMAC-SHA256 hex digest of the raw request body, using your signing secret as the key.

Verify in Node.js:

import crypto from 'crypto';
 
function verifyWebhook(rawBody, signature, secret) {
  const expected = crypto
    .createHmac('sha256', secret)
    .update(rawBody)
    .digest('hex');
  return crypto.timingSafeEqual(
    Buffer.from(signature),
    Buffer.from(expected)
  );
}
 
// Express example
app.post('/webhook', express.raw({ type: 'application/json' }), (req, res) => {
  const sig = req.headers['x-webhook-signature'];
  if (!verifyWebhook(req.body, sig, process.env.APEXMCP_WEBHOOK_SECRET)) {
    return res.status(401).send('Invalid signature');
  }
  const event = JSON.parse(req.body);
  // handle event...
  res.status(200).send('ok');
});

Verify in Python:

import hmac, hashlib
 
def verify_webhook(raw_body: bytes, signature: str, secret: str) -> bool:
    expected = hmac.new(
        secret.encode(), raw_body, hashlib.sha256
    ).hexdigest()
    return hmac.compare_digest(signature, expected)

Retry Policy

If your endpoint returns a non-2xx status or times out (> 10 seconds), ApexMCP retries with exponential backoff:

AttemptDelay
1st retry30 seconds
2nd retry5 minutes
3rd retry30 minutes
4th retry2 hours
5th retry8 hours

After 5 failed attempts the event is marked failed and no further retries are made. Failed events are visible in Developer Portal → Webhooks → [name] → Event Log for 72 hours.

Use the id field for idempotency — your endpoint may receive the same event more than once during retries.