> ## Documentation Index
> Fetch the complete documentation index at: https://docs.agentcard.sh/llms.txt
> Use this file to discover all available pages before exploring further.

# Webhooks

> How Agentcard events reach your server: connect an endpoint, verify the signature, and handle retries.

Everything that happens to your connected users reaches your server as a webhook: a card stored in the Vault, a checkout approved, an order placed, an identity check finished. SDK callbacks are UI signals. Webhooks are the record.

## Connect an endpoint

Register a URL and the events it should receive. Do it once per mode: a sandbox token registers a sandbox endpoint, a production token a production one.

```bash theme={null}
curl -X POST https://api.agentcard.sh/api/v2/webhook_endpoints \
  -H "Authorization: Bearer $ORG_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"url": "https://your.app/agentcard/webhooks", "enabled_events": ["vault.*", "checkout_authorization.*", "order.*"]}'
```

The response carries the endpoint's `secret` once. Store it. `enabled_events` takes exact names, prefix wildcards like `vault.*`, or `["*"]` for everything. An endpoint receives only what it lists.

Manage endpoints, read or rotate the secret, and inspect recent deliveries with the [Webhook endpoints API](/api-reference/webhook-endpoints/overview). The same is available in the dashboard under Developers → Webhooks.

## The envelope

Every event has the same shape:

```json theme={null}
{
  "id": "evt_9f8e7d6c5b4a",
  "type": "vault.card_stored",
  "created": 1757264400,
  "livemode": false,
  "data": { }
}
```

`type` is stable and part of the API contract, so branch on it directly. `livemode: false` is sandbox. Delivery is at least once, so deduplicate on `id`.

## Verify the signature

Each delivery carries an `AgentCard-Signature` header:

```
AgentCard-Signature: t=1757264400,v1=5257a869e7…
```

Take `t`, join it to the raw request body with a dot, compute HMAC-SHA256 with your endpoint's secret, and compare it to `v1` in constant time. Reject timestamps older than a few minutes to block replays. Verify against the raw bytes, never re-serialized JSON.

<CodeGroup>
  ```javascript Node theme={null}
  const [t, v1] = header.split(",").map((p) => p.split("=")[1]);
  const expected = crypto
    .createHmac("sha256", process.env.AGENTCARD_WEBHOOK_SECRET)
    .update(`${t}.${rawBody}`)
    .digest("hex");
  const valid = crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(v1));
  ```

  ```python Python theme={null}
  import hmac, hashlib, os

  t, v1 = (part.split("=", 1)[1] for part in header.split(","))
  expected = hmac.new(
      os.environ["AGENTCARD_WEBHOOK_SECRET"].encode(),
      f"{t}.{raw_body}".encode(),
      hashlib.sha256,
  ).hexdigest()
  valid = hmac.compare_digest(expected, v1)
  ```
</CodeGroup>

A legacy `X-AgentCard-Signature: sha256=…` header signs the body alone. New integrations should use `AgentCard-Signature`.

## Delivery and retries

Return a `2xx` quickly and do slow work afterwards. A slow handler looks like a failure. Failed deliveries retry up to five times: immediately, then after 1 minute, 5 minutes, 30 minutes, and 1 hour. Recent deliveries with payloads and response codes are on [List recent deliveries](/api-reference/webhook-endpoints/deliveries) and in the dashboard.

## Develop locally

No tunnel needed. The CLI registers a listener endpoint and forwards every event to your machine, signed exactly like production:

```bash theme={null}
agent-cards companies webhooks listen --forward-to localhost:4242/webhooks
agent-cards companies webhooks test
```

## Sandbox

Sandbox events deliver exactly like production, with `livemode: false`. Endpoints are scoped to one mode: if your sandbox integration completes actions but nothing arrives, check that the endpoint was created with a sandbox token.

## Events by object

<CardGroup cols={2}>
  <Card title="Connections" href="/webhooks/connections/overview">`connection.created`, `wallet_link.opened`, `approval.requested`</Card>
  <Card title="Vault" href="/webhooks/vault/overview">`vault.session_linked`, `vault.card_stored`</Card>
  <Card title="Checkout authorizations" href="/webhooks/checkout-authorizations/overview">approved, submitted, declined, expired, amount\_mismatch</Card>
  <Card title="Orders" href="/webhooks/orders/overview">`order.placed`, `order.confirmed`, `order.failed`</Card>
  <Card title="Cards and transactions" href="/webhooks/cards/overview">`card.*`, `transaction.*`, `balance.low`</Card>
  <Card title="Identity verification" href="/webhooks/identity-verification/overview">`identity.verification.updated`</Card>
  <Card title="Wallet" href="/webhooks/wallet/overview">`user_wallet.funding_detected`, `user_wallet.funded`</Card>
  <Card title="Rewards and merchants" href="/webhooks/rewards/overview">`reward.*`, `merchant.connected`</Card>
  <Card title="Company wallet" href="/webhooks/company-wallet/overview">`cardholder.*`, `card_flow.*`, `transfer.*`, `recovery.*`, `wallet.*`</Card>
</CardGroup>
