Sail

The Connect Account Widget

The Connect Account Widget is a single-use, time-limited URL for a Sail-rendered flow:

  • Connecting an account
  • Reconnecting one
  • Uploading a receipt

Sail’s API calls this a hosted session. This page explains why it exists and what each of the three flows does.

How it works and why it exists

It works by calling the Create Hosted Session endpoint server-side with a user_id and a flow, and Sail returns a single-use, time-limited url and an expires_at timestamp. You embed that URL, and once the user completes the flow inside it, or its time runs out, the session ends.

It exists so your backend never has to see, store, or transmit a user’s actual login: a user’s bank, merchant, or HSA/FSA administrator credentials are entered only inside it, and stored only by Sail. Your backend still authenticates every other API call with a partner key, but a user’s own credentials never pass through your systems.

The widget’s three flows

The flow you request determines what the user sees.

  • connect_account: the only way to establish a Sail-managed connection (card, store, or benefit_account). Requires connection_type in options.
  • reconnect: re-authenticates an existing Sail-managed connection once its credentials go stale. Requires connection_id in options. Calling this against an external connection, which has no credentials to refresh, returns 409 connection_not_reconnectable.
  • upload_receipt: lets a user submit an itemized receipt, optionally against a specific itemization_required expense by passing expense_id.

Both connect_account and reconnect accept a modal option to render the flow as an overlay instead of a full-page redirect.

Scope and expiry

Not every integration uses the Connect Account Widget. If you’re pushing your own transaction data through an external connection, there’s no end-user login step at all, the connection is created directly through the API with no widget involved.

When a session is used, it doesn’t necessarily complete: a session that expires before the user finishes fires a hosted_session.expired webhook, naming the session_id and flow. Treat this as a prompt to give the user a way to start over, not as an error to surface directly.

Next steps