Skip to main content

Webhooks

Conto sends HTTP POST requests to your configured URL when events occur — payments requested, approved, denied, executed, confirmed, or failed.

Setup

  1. Go to Settings > Webhooks in the dashboard
  2. Enter your HTTPS endpoint URL
  3. Copy the generated signing secret — you’ll need this to verify payloads
Webhook URLs must use HTTPS. HTTP, localhost, and private IP ranges are blocked for security (SSRF protection).

Event Types

EventFires when
payment.requestedA payment request is created
payment.approvedA payment passes policy evaluation
payment.deniedA payment is blocked by policy
payment.executedA payment transaction is submitted onchain
payment.confirmedA payment is confirmed onchain
payment.failedA payment transaction fails
service_payment.recordedAn x402 or MPP transaction is recorded
service_payment.failedAn x402 or MPP transaction fails
agent.createdA new agent is registered
agent.updatedAn agent’s status or config changes
agent.pausedAn agent is paused
agent.resumedAn agent is resumed from paused state
wallet.fundedA wallet receives funds
wallet.low_balanceA wallet balance drops below threshold
alert.createdA new alert is generated
policy.violatedA policy violation is detected

Payload Format

Every webhook POST includes these headers and a JSON body:

Headers

HeaderDescription
X-Conto-EventEvent type (e.g., payment.confirmed)
X-Conto-TimestampISO 8601 timestamp of the event
X-Conto-SignatureHMAC-SHA256 signature of the body
X-Conto-Delivery-AttemptAttempt number (1, 2, or 3)

Body

{
  "event": "payment.confirmed",
  "timestamp": "2026-04-07T12:00:00.000Z",
  "data": {
    "paymentId": "req_abc123",
    "amount": 50,
    "transactionHash": "0xabc...",
    "transactionId": "tx_xyz",
    "organizationId": "org_123",
    "agentId": "agent_456"
  }
}

Verifying Signatures

Every payload is signed with your webhook secret using HMAC-SHA256. Verify signatures to confirm the request came from Conto.
import { createHmac, timingSafeEqual } from 'crypto';

function verifyWebhook(body: string, signature: string, secret: string): boolean {
  const expected = createHmac('sha256', secret).update(body).digest('hex');
  return timingSafeEqual(Buffer.from(signature), Buffer.from(expected));
}

// In your handler
app.post('/webhooks/conto', (req, res) => {
  const signature = req.headers['x-conto-signature'];
  const isValid = verifyWebhook(JSON.stringify(req.body), signature, process.env.WEBHOOK_SECRET);

  if (!isValid) {
    return res.status(401).send('Invalid signature');
  }

  const { event, data } = req.body;

  switch (event) {
    case 'payment.confirmed':
      console.log('Payment confirmed:', data.transactionHash);
      break;
    case 'payment.denied':
      console.log('Payment denied:', data.reasons);
      break;
  }

  res.status(200).send('OK');
});

Retry Behavior

Failed deliveries are retried up to 3 times with exponential backoff:
AttemptDelay
1Immediate
2~1 second
3~4 seconds
A delivery is considered failed if your endpoint returns a non-2xx status code or doesn’t respond within 10 seconds.

Agent Callback URLs

In addition to organization-level webhooks, individual agents can have a callbackUrl set during creation. When set, webhooks are delivered to both the organization URL and the agent’s callback URL.

Delivery Targets

TargetConfigured viaReceives events for
Organization webhookSettings > WebhooksAll events in the org
Agent callback URLAgent configEvents for that agent only
Both targets receive the same payload format and signature headers.