Sail

Create External Connection

POST/users/{user_id}/connections

Creates a customer-managed connection for pushing your own transaction data (bring-your-own-data). Only type: external may be created directly. card, store, and benefit_account connections are established through a connect_account hosted session. Requires the connections key scope. External connections support only the expenses product.

Path Parameters

user_idstringrequired

The Sail user id.

Body application/json

type"external"required

Must be external — the only type creatable directly via this endpoint.

provider_labelstringrequired

Customer-declared label for the upstream source, shown wherever a provider name would appear (e.g. “Acme Budgeting · Plaid feed”).

products"expenses"[]

Products enabled on this connection. Currently only expenses is supported.

Response

Connection created.

idstring

The Sail connection id.

user_idstring

The Sail user this connection belongs to.

type"card" | "store" | "benefit_account" | "external"

The kind of data source this connection links to.

status"active" | "aggregating" | "user_input_required" | "error" | "disconnected"

The connection’s current sync/authentication status.

products"expenses" | "benefit_account" | "account_numbers" | "identity"[]

Capabilities enabled on this connection.

accountsobject[]

Embedded summaries of the accounts under this connection, so account ids are discoverable without a second call. Populated for benefit_account connections (a single administrator login can hold multiple benefit accounts, e.g. HSA + LPFSA); empty for connection types whose accounts are not exposed. Full detail — balances, numbers, contributions — lives at /users/{user_id}/accounts/{account_id}.

Show accounts properties
idstring

The Sail account id.

type"hsa" | "fsa" | "hra" | "checking" | "savings" | "credit_card"

The account type.

namestring

The account’s display name.

providerobject

The external party this connection links to.

Show provider properties
idstring

The provider’s identifier in Sail’s directory (merchant or administrator id).

namestring

The provider’s display name.

kind"administrator" | "financial_institution" | "merchant" | "external"

What kind of external party this provider is.

icon_urlstring

URL of the provider’s icon/logo.

errorobject

Present when the connection is in an error state. Null otherwise.

Show error properties
codestring

Machine-readable error code.

messagestring

Human-readable error message.

recoverableboolean

Whether the user can resolve this error via a reconnect hosted session.

last_sync_atstring<date-time>

When this connection last completed a sync. Null if it has never synced.

created_atstring<date-time>

When the connection was created.

updated_atstring<date-time>

When the connection was last updated.

invalid_request: type must be external.

Standard error envelope covering 400, 401, 403, 404, 429, and 500.

errorobject

The error detail.

Show error properties
codestring

Machine-readable code, e.g. not_found, token_scope_mismatch, product_not_enabled, insufficient_key_scope, insufficient_token_scope, user_token_required, user_token_expired, invalid_user_token, invalid_key_configuration, connection_not_reconnectable, rate_limited.

messagestring

Human-readable error message. May change, so match on error.code instead.

paramstring

The request field that caused the error, when applicable. Null otherwise.

Request
curl -X POST "https://live.savewithsail.com/api/v1/users/<user_id>/connections" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer <token>" \
  -d '{
  "type": "external",
  "provider_label": "Acme Budgeting · Plaid feed",
  "products": [
    "expenses"
  ]
}'
Response
{
  "id": "conn_9f2c",
  "user_id": "string",
  "type": "card",
  "status": "active",
  "products": [
    "expenses"
  ],
  "accounts": [
    {
      "id": "acct_77b1",
      "type": "hsa",
      "name": "HealthEquity HSA"
    }
  ],
  "provider": {
    "id": "string",
    "name": "HealthEquity",
    "kind": "administrator",
    "icon_url": "string"
  },
  "error": {
    "code": "INVALID_CREDENTIALS",
    "message": "string",
    "recoverable": true
  },
  "last_sync_at": "1970-01-01T00:00:00.000Z",
  "created_at": "1970-01-01T00:00:00.000Z",
  "updated_at": "1970-01-01T00:00:00.000Z"
}