Skip to main content

Documentation Index

Fetch the complete documentation index at: https://conto.finance/docs/llms.txt

Use this file to discover all available pages before exploring further.

Webhooks

Conto sends HTTP POST requests to your configured URL when payments progress, an A2A payment request is created, a service spend record is written, or an agent is frozen or unfrozen.

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
payment_request.createdAn agent-to-agent payment request is created
service_payment.recordedAn x402 or MPP transaction is recorded
service_payment.failedAn x402 or MPP transaction fails
agent.frozenAn agent is frozen
agent.unfrozenAn agent is restored to active status

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 express from 'express';
import { createHmac, timingSafeEqual } from 'crypto';

function verifyWebhook(rawBody: string, signature: string | undefined, secret: string): boolean {
  if (!signature) return false;

  const expected = createHmac('sha256', secret).update(rawBody).digest('hex');
  const signatureBuffer = Buffer.from(signature, 'hex');
  const expectedBuffer = Buffer.from(expected, 'hex');

  if (signatureBuffer.length !== expectedBuffer.length) {
    return false;
  }

  return timingSafeEqual(signatureBuffer, expectedBuffer);
}

// In your handler
app.post('/webhooks/conto', express.raw({ type: 'application/json' }), (req, res) => {
  const signature = req.header('x-conto-signature');
  const rawBody = req.body.toString('utf8');

  if (!verifyWebhook(rawBody, signature, process.env.WEBHOOK_SECRET!)) {
    return res.status(401).send('Invalid signature');
  }

  const { event, data } = JSON.parse(rawBody);

  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');
});
The same rule applies in other frameworks: verify the exact raw request body before you parse it.

Retry Behavior

Failed deliveries are retried up to 3 times with exponential backoff:
AttemptTiming
1Immediate
2~1 second later
3~3 seconds after the initial delivery
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.