Skip to main content
Callbacks are passed to invest() (or to <CestoProvider>, <CestoInvestButton>, useCestoInvest, or bindInvestButtons — the same set everywhere).

How often each fires

Each callback fires at most once per session — with two exceptions:
  • onPositionClosed fires once per closure. A user can close several positions without leaving the flow, and each one is reported.
  • onHandoff fires once per handoff. A user can cancel one sign-in window and start another.

The ones that mean something happened

Only onError means the invest failed. Only onPositionClosed means a position was closed. onClose is about the surface being dismissed and nothing else.
onClose is ambiguous by design. It fires after a success too, because the user eventually dismisses the dialog or closes the window. At the moment it fires, the user may have invested, abandoned the flow, or still have an execution completing server-side. Use it to reset your own pending UI — never to record a failure or an abandonment. onHandoff is likewise not an ending: the dialog stays on your page and still reports onSuccess or onError when the window is done. See Google login in dialog mode.

Payloads

Treat status and code as opaque strings you log and branch on defensively, not as a closed enum — the app can add values. Same for reason.

onVerified and partner attribution

onVerified is the signal that your publishable key matched the page’s origin. Partner branding and attribution only activate after it fires. The flow still works without it — just unbranded and unattributed — which makes a silent onVerified the single most useful thing to check when an integration “works but looks wrong”. See Troubleshooting.

Callback precedence in React

When the same callback is supplied at more than one level, all of them run, outermost first:
That ordering is deliberate: put analytics on the provider so nothing is missed, and put UI state at the call site where the component that owns it lives.

A worked example

In React, useCestoInvest() already tracks the lifecycle for you — status, isOpen, and error cover most of what you’d otherwise write by hand. See React.

Configuration errors

If the loader can’t be constructed at all — no key, a rejected baseUrl, or a non-browser environment — the React bindings fire onError with code: 'config_error' rather than throwing, and return an inert session. In plain JavaScript, new Cesto(...) throws at construction instead, which is where you want to find that mistake.

Modes

Why COOP can silence every callback in popup mode.

Troubleshooting

When events don’t arrive.