Connect Events

Connect Events

The hosted Connect page streams a stable, namespaced connect/* event vocabulary to your onEvent handler through the Elements SDK. Use it to build a Plaid-parity conversion funnel — see exactly which pane a user reached, why they dropped, and when the link is ready.

See also: Elements SDK guide · Webhooks (the authoritative outcome record) · Live Connect playground

onEvent is a UX-analytics stream, not a source of truth. It runs in the user's browser and can be lost to a closed tab. Confirm the linked outcome with the link.completed / link_token.completed webhook.

The onEvent handler

Register an onEvent callback on any flow. It receives the event name and a snake_case metadata object. The name is typed as the ConnectEventName union (with autocomplete) widened to allow any string, so narrowing on the name is how you get the typed metadata shape.

ConnectEventHandler
TypeScript
import type {
  ConnectEventName,
  ConnectEventMetadataByName,
} from 'billerapi-js';

// The SDK-facing onEvent signature (from billerapi-js):
type ConnectEventHandler = (
  eventName: ConnectEventName | (string & Record<never, never>),
  metadata: Record<string, unknown>,
) => void;

const onEvent: ConnectEventHandler = (eventName, metadata) => {
  // Narrow on the name to get the typed metadata shape.
  if (eventName === 'connect/authenticated') {
    // metadata is a ConnectEventMetadataByName['connect/authenticated']
    const m = metadata as ConnectEventMetadataByName['connect/authenticated'];
    console.log('Login verified for', m.biller_id, 'at', m.timestamp);
    return;
  }

  if (eventName === 'connect/view') {
    const m = metadata as ConnectEventMetadataByName['connect/view'];
    analytics.track('connect_view', { step: m.view_name });
    return;
  }

  if (eventName === 'connect/error') {
    const m = metadata as ConnectEventMetadataByName['connect/error'];
    if (m.will_retry) return; // backgrounded transient failure — pending, not terminal
    analytics.track('connect_error', { code: m.error_code });
    return;
  }
};

elements.connect({ linkToken: 'lt_xxx', onEvent, onSuccess, onExit }).open();

Metadata envelope

Every connect/* event carries these base fields (all snake_case — the payload crosses the postMessage wire). Per-event tables below list only the fields added on top of this base.

ConnectEventMetadataBase

FieldTypeDescription
link_session_id*string | nullLink token id, used as the session key for v1. `null` before INIT.
timestamp*stringISO-8601 emission timestamp.
biller_idstringSelected biller id, once known.
biller_namestringSelected biller name, once known.
view_nameConnectViewNameThe pane the event was emitted from / about.
error_codeConnectErrorCodeMachine-readable error code (present on `connect/error`).
modeConnectFlowModeThe flow mode — `'connect'` (first-time) or `'update'` (repair). Present once the flow's mode is known.

Flow mode — ConnectFlowMode

The mode field is present on every event once the flow's mode is known. It is one of two values:

ValueMeaning
connectA first-time connect: the user picks a biller, consents, and links accounts for the first time.
updateA repair / re-authentication of an EXISTING Link (stale credentials, expired session, MFA again, lapsed consent). The biller is already known, consent is skipped, and the account step re-confirms the existing selection. connect/open with mode: 'update' marks an update flow starting.

Panes — ConnectViewName

The pane/step the user is on, in flow order. Fired as view_name on connect/view (and echoed on most other events), and as status on connect/exit.

view_namePane
loadingInitial/processing pane before the first interactive step.
biller_selectionThe biller picker.
consentThe data-sharing consent screen (connect flows only).
regionRegion / locale disambiguation, when a biller needs it.
credentialsThe credentials entry form.
mfaAn MFA / OTP challenge.
account_selectionSelecting which discovered accounts to link.
successTerminal success pane.
failureTerminal failure pane.

Error codes — ConnectErrorCode

Carried as error_code on connect/error. These are a fixed, machine-readable vocabulary derived from the underlying wire reason — never from localized UI copy — so you can key analytics and error handling off them safely.

error_codeMeaning
credentials_rejectedThe biller rejected the submitted credentials.
mfa_invalidThe submitted OTP / MFA code was wrong.
mfa_expiredThe OTP / MFA code (or challenge) timed out or expired.
mfa_locked_outToo many MFA attempts; the account is locked.
account_discovery_failedLogin succeeded but the account list couldn't be read.
connection_lostThe realtime stream dropped and couldn't reconnect.
link_expiredThe link token is no longer valid.
internal_errorA generic client/network or unclassified backend fault.

Event names — ConnectEventName

EventFires when
connect/openThe hosted page initialized after its INIT handshake. The first event of every flow. For an update flow, this carries `mode: "update"`.
connect/viewFires on every pane/step transition. The single most useful funnel signal — pipe `view_name` straight into your analytics to see where users drop.
connect/searchA settled (debounced) biller search query. Debounced ~400ms so you get the query the user paused on, not every keystroke.
connect/biller_selectedA biller was chosen from the picker. Carries the selected `biller_id` (and `biller_name` once resolved).
connect/credentials_submittedCredentials were submitted for login. No credential values are ever included — this is a funnel signal only.
connect/mfa_requiredAn MFA challenge is now required. Carries `mfa_type` when the biller reports the channel.
connect/otp_submittedAn OTP / MFA code was submitted. Like `credentials_submitted`, no code value is included.
connect/input_requiredThe flow transitioned from a waiting/processing state to one needing user input. Carries the `view_name` now awaiting input — use it to re-focus your UX or nudge the user back.
connect/authenticatedThe biller-side login was verified. Fires once per flow, as soon as the wire confirms authentication — usually well before accounts are discovered or the flow completes. A great signal to flip your UI to "connecting your accounts…".
connect/backgroundedThe user chose to continue the connect in the background: the backend auto-selects all discovered accounts and completes server-side. Followed by a clean `connect/exit` (no error) — a backgrounded close is NOT a bail. Watch for the `link_token.completed` webhook to know when the link is ready.
connect/errorAn error surfaced. `error_code` is always present and is drawn from the fixed ConnectErrorCode vocabulary (never localized copy). When `will_retry` is true, the backend is retrying a backgrounded transient failure — treat it as "pending", not terminal.
connect/exitThe user bailed (or a backgrounded flow closed). Carries `status` = the pane they exited from. Pair with `connect/backgrounded` to distinguish a genuine bail from a background handoff.
connect/handoffSuccessful handoff of the public token to the parent. This is the interactive success signal — the SDK `onSuccess` callback fires alongside it. Still confirm the outcome with the `link.completed` webhook before fulfilling anything server-side.

connect/open

The hosted page initialized after its INIT handshake. The first event of every flow. For an update flow, this carries `mode: "update"`.

No fields beyond the base envelope. Metadata type: ConnectEventMetadataBase.

connect/open metadata
metadata
handler.onEvent = (eventName, metadata) => {
  // eventName === 'connect/open'
  // metadata: { link_session_id, timestamp, mode? }
};

connect/view

Fires on every pane/step transition. The single most useful funnel signal — pipe `view_name` straight into your analytics to see where users drop.

ConnectViewEventMetadata — added fields

FieldTypeDescription
view_name*ConnectViewNameThe pane the user just landed on. Required on this event.
connect/view metadata
metadata
// { link_session_id: 'lt_…', view_name: 'credentials', timestamp: '…' }

connect/biller_selected

A biller was chosen from the picker. Carries the selected `biller_id` (and `biller_name` once resolved).

ConnectBillerSelectedEventMetadata — added fields

FieldTypeDescription
biller_id*stringThe biller the user selected. Required on this event.
connect/biller_selected metadata
metadata
// { link_session_id: 'lt_…', biller_id: 'test_electric_company', biller_name: 'Test Electric Company', timestamp: '…' }

connect/credentials_submitted

Credentials were submitted for login. No credential values are ever included — this is a funnel signal only.

No fields beyond the base envelope. Metadata type: ConnectEventMetadataBase.

connect/credentials_submitted metadata
metadata
// { link_session_id: 'lt_…', biller_id: 'test_electric_company', timestamp: '…' }

connect/mfa_required

An MFA challenge is now required. Carries `mfa_type` when the biller reports the channel.

ConnectMfaRequiredEventMetadata — added fields

FieldTypeDescription
mfa_typestringThe MFA channel (e.g. `sms`, `email`, `app`), when the biller reports one.
connect/mfa_required metadata
metadata
// { link_session_id: 'lt_…', view_name: 'mfa', mfa_type: 'sms', timestamp: '…' }

connect/otp_submitted

An OTP / MFA code was submitted. Like `credentials_submitted`, no code value is included.

No fields beyond the base envelope. Metadata type: ConnectEventMetadataBase.

connect/otp_submitted metadata
metadata
// { link_session_id: 'lt_…', view_name: 'mfa', timestamp: '…' }

connect/input_required

The flow transitioned from a waiting/processing state to one needing user input. Carries the `view_name` now awaiting input — use it to re-focus your UX or nudge the user back.

ConnectViewEventMetadata — added fields

FieldTypeDescription
view_name*ConnectViewNameThe pane now awaiting user input. Required on this event.
connect/input_required metadata
metadata
// { link_session_id: 'lt_…', view_name: 'mfa', timestamp: '…' }

connect/authenticated

The biller-side login was verified. Fires once per flow, as soon as the wire confirms authentication — usually well before accounts are discovered or the flow completes. A great signal to flip your UI to "connecting your accounts…".

No fields beyond the base envelope. Metadata type: ConnectEventMetadataBase.

connect/authenticated metadata
metadata
// { link_session_id: 'lt_…', biller_id: 'test_electric_company', timestamp: '…' }

connect/backgrounded

The user chose to continue the connect in the background: the backend auto-selects all discovered accounts and completes server-side. Followed by a clean `connect/exit` (no error) — a backgrounded close is NOT a bail. Watch for the `link_token.completed` webhook to know when the link is ready.

ConnectViewEventMetadata — added fields

FieldTypeDescription
view_name*ConnectViewNameThe pane the "continue in background" choice was made from. Required on this event.

A backgrounded flow completes server-side. The public token is never exposed on this path — do not wait for onSuccess; treat the link_token.completed webhook as the completion signal.

connect/backgrounded metadata
metadata
// { link_session_id: 'lt_…', view_name: 'account_selection', timestamp: '…' }

connect/error

An error surfaced. `error_code` is always present and is drawn from the fixed ConnectErrorCode vocabulary (never localized copy). When `will_retry` is true, the backend is retrying a backgrounded transient failure — treat it as "pending", not terminal.

ConnectErrorEventMetadata — added fields

FieldTypeDescription
error_code*ConnectErrorCodeMachine-readable error code. Required on this event.
will_retrybooleanPresent (true) when the failure is a backgrounded transient one the backend retries on its own. Treat as pending, not terminal.
connect/error metadata
metadata
// { link_session_id: 'lt_…', error_code: 'credentials_rejected', view_name: 'credentials', timestamp: '…' }

connect/exit

The user bailed (or a backgrounded flow closed). Carries `status` = the pane they exited from. Pair with `connect/backgrounded` to distinguish a genuine bail from a background handoff.

ConnectExitEventMetadata — added fields

FieldTypeDescription
status*ConnectViewNameThe pane the user exited from. Required on this event.
connect/exit metadata
metadata
// { link_session_id: 'lt_…', status: 'credentials', timestamp: '…' }

connect/handoff

Successful handoff of the public token to the parent. This is the interactive success signal — the SDK `onSuccess` callback fires alongside it. Still confirm the outcome with the `link.completed` webhook before fulfilling anything server-side.

No fields beyond the base envelope. Metadata type: ConnectEventMetadataBase.

connect/handoff metadata
metadata
// { link_session_id: 'lt_…', biller_id: 'test_electric_company', timestamp: '…' }
Was this page helpful?