# Webhook Confirmations

Elements callbacks keep the browser moving. Signed webhooks let your backend reconcile links after tabs close, mobile WebViews background, or networks drop. Card storage returns a method identifier but no webhook. The Pay event contract is reserved, but execution is not available.

## Callbacks are UX signals

> **Warning — Never fulfill from a browser callback alone**
>
> `onSuccess`, redirect URLs, and hosted-page completion screens run in user-controlled clients. Use them to update UI and send identifiers to your server, but verify signatures and dedupe webhook event IDs before changing durable server state.

## Confirmation paths by flow

| Flow | Browser signal | Durable confirmation | Webhook types |
| --- | --- | --- | --- |
| Interactive Connect | `onSuccess(public_token)` | Your server exchanges the public token, then reconcile link.completed. connection.ready and bill.created confirm the first retrieval. | `link.completed`, `connection.ready`, `bill.created` |
| Hosted/background Connect | Hosted page completion or redirect | Use link_token.completed as the durable server-side confirmation that BillerAPI completed the background exchange and created the link. | `link_token.completed`, `connection.ready`, `bill.created` |
| Add Card | `onSuccess(payment_method_id)` | No money moves and no payment webhook is emitted for card storage. Persist the payment_method_id server-side; it cannot currently be used to execute a bill payment. | No payment webhook |
| Pay (unavailable) | `501 PAYMENT_EXECUTION_NOT_AVAILABLE` | No payment attempt is created and no pay.* webhook is emitted in sandbox or production. | No payment webhook |

## Connect reconciliation

Interactive Connect returns a `public_token` to the browser so your backend can exchange it. Background Connect does the exchange inside BillerAPI and emits `link_token.completed`. In both paths, wait for signed link and bill webhooks before assuming bill data is ready.

> **Note — Ready is separate from linked**
>
> `link.completed` means the link exists. `connection.ready` and `bill.created` tell you the first bill retrieval has produced usable data.

## Add Card reconciliation

Add Card returns a tokenized `payment_method_id` plus display-safe metadata. That is a stored method, not a paid bill. Because there is no payment attempt, BillerAPI does not emit a `pay.*` webhook for Add Card by itself.

> **Warning — Do not infer money movement**
>
> Store the `payment_method_id` for later use, but do not infer money movement or build fulfillment around unavailable Pay events.

## Reserved Pay outcome contract

- `pay.scheduled` — Reserved contract: if Pay launches, this will mean an attempt entered processing.
- `pay.succeeded` — Reserved contract: if Pay launches, this will be the signed terminal paid event.
- `pay.failed` — Reserved contract: if Pay launches, this will report a terminal failed attempt.
- `pay.escalated` — Reserved contract: if Pay launches, this will identify an attempt needing human review.

> **Note — Payment execution is unavailable**
>
> Sandbox and production initiation return `501 PAYMENT_EXECUTION_NOT_AVAILABLE`. No payment attempt or `pay.*` event is produced. These event names document a reserved future contract, not a testable execution path.

## Related

- [Elements SDK](/docs/guides/elements-sdk) — Available and reserved hosted flows
- [Webhook Reference](/docs/api/webhooks) — Event payloads and follow-up API calls
- [Store Payment Methods](/docs/guides/accept-payments) — Add Card, the reserved Pay contract, and the PCI boundary
- [Webhook Delivery](/docs/concepts/webhook-delivery) — Retries, signatures, and ordering
