Skip to main content
build your own credential collection ui instead of using the hosted page. stream login events, render the canonical fields and choices, then submit the user’s response with the current interaction id. use the programmatic flow when:
  • you need a custom credential collection ui
  • you’re building headless authentication
  • you already store credentials and want to handle only the inputs KERNEL cannot resolve automatically

How it works

1

Create a connection and start a session

Create a managed auth connection, then call .login().
2

Stream the session state

Follow the connection’s sse stream. When flow_step becomes AWAITING_INPUT, render fields and choices from the event.
3

Submit one interaction

Send the event’s interaction_id with either field_values or selected_choice_id. Keep listening because the next page may produce another interaction.

Interaction contract

Every paused interaction uses these properties together: Submit the current interaction_id with either field_values or selected_choice_id. Do not mix properties from different events. KERNEL rejects stale interaction ids so a delayed submission cannot act on a newer screen.

Get started

1. Create a connection

A managed auth connection attaches one authenticated domain to a profile. A profile can hold multiple connections.

2. Start a login session

A successful interactive login can save submitted credentials for automatic re-authentication.

3. Stream and submit

Listen for AWAITING_INPUT. Submit fields or a selected choice, then keep listening for the next interaction.
TypeScript
promptUser and promptUserToChoose represent your application’s ui. The submission examples below show the same requests in each sdk.
every programmatic login session also has a hosted_url. redirect the user there if you want the hosted ui to finish an unexpected state.
In the examples below, state is the current managed_auth_state event.

Fields

Each field includes: Submit values by field id, not by ref:

Replacing a rejected credential

A field with reason: 'rejected' means the site explicitly refused the previous value. Prompt for a new value and submit it against the new interaction:
An unattended reauth run does not ask for a corrected credential. It reports the exact terminal rejection—such as credentials_invalid or totp_code_rejected—rather than repeating a value the site already rejected.

Choices

All selectable auth routes use the same shape. choice.type identifies the category:
  • mfa_method
  • sso_provider
  • sign_in_method
  • auth_method
  • identifier_method
  • account
  • other
Render the visible label, optional description, and optional masked_destination. Submit the exact choice.id returned by the event:
Do not derive the submitted id from the label or mfa type. Two sms choices can have different masked destinations and different grounded targets.

Account and organization pickers

Account and organization rows are choices with type: 'account' or another non-mfa choice type. Show every returned row and submit the selected stable id:
Stored credential values are not returned for matching. Use only the masked or display context present on each choice.

External actions

When flow_step is AWAITING_EXTERNAL_ACTION, show external_action_message and keep listening. The flow resumes when the external action completes. Some external-action screens also expose fallback fields or choices. If interaction_id is present, submit a fallback through the same canonical contract:

Step reference

Status reference

The connection’s overall status is AUTHENTICATED or NEEDS_AUTH.

Connection configuration

Connection-level options such as a custom login url, allowed domains, proxy, session recording, and health-check interval apply to both hosted and programmatic flows. See connection configuration.

SSE stream behavior

auth.connections.follow() opens:
The stream delivers managed_auth_state events and closes when the flow succeeds, fails, expires, or is canceled. Prefer the stream over polling so your ui receives each interaction id in order.