Sail

Transaction-Level and Item-Level Expenses

Every expense Sail classifies starts as either a transaction or an item. This page explains the difference, how enrichment connects them, and why Sail keeps them separate instead of merging everything into one combined view.

What an expense is

An expense is the core unit Sail works with, a transaction or a purchased item that Sail classifies for Section 213(d) eligibility. Every expense comes from one of four sources (source_type):

  • A card or bank connection
  • A store connection
  • A pushed external transaction
  • An uploaded receipt

Which source produced an expense determines whether you get transaction-level or item-level detail. See the full field list in List Expenses.

Transaction-level data

A card or bank connection reports one expense per transaction, the same level of detail your bank statement shows:

  • A merchant
  • A date
  • An amount

On the Expense object, this shows up as origin.type: transaction, with origin.detail carrying the raw bank descriptor (for example, WALGREENS #4821 RX).

Transaction-level data tells you that a purchase happened and where, but not what was actually bought. A $49.00 charge at Walgreens could be a prescription, a birthday card, or both in the same basket. That ambiguity is part of what drives some expenses into itemization_required during classification. See Expense Classification for all four classification outcomes.

Item-level data

A store connection, where a user logs into a merchant directly through Sail’s Connect Account Widget (Amazon, Walgreens, Target, CVS, Walmart, and Costco, among others), reports individual products instead of one transaction total. The same is true for a receipt upload once it’s processed. On the Expense object, this shows up as origin.type: product, with origin.detail carrying the product title.

Item-level data resolves the ambiguity transaction-level data can’t, Sail classifies each item on its own, so a prescription and a birthday card from the same store visit no longer have to share one eligibility outcome.

When a card or bank transaction’s merchant is one Sail can also connect to directly, the expense’s merchant.supported_merchant_id field is set (for example, mer_costco). A non-null supported_merchant_id is the signal to prompt the user to connect that store, so future purchases at the same merchant get item-level detail instead of a flat transaction total. The same signal applies to transactions you push yourself through an external connection. Connecting a matching store login enriches your pushed data with item-level detail too.

Whether that item-level data comes from a store connection or from an itemized receipt, the resulting item-level expenses carry parent_expense_id, pointing back to the original transaction-level expense they enriched. See Expense Classification for when itemization is required.

Why Sail keeps them separate

Sail doesn’t merge a transaction and its item-level detail into one combined record. Each stays its own expense, connected only through supported_merchant_id or parent_expense_id. This is deliberate, a flat, item-level view lets you classify at whatever level of detail you actually have, without waiting for every source to be connected before anything becomes usable.

Data sources at a glance

Because each source stays separate rather than merging into one record, here’s exactly what each one produces on its own.

Source (source_type)ProducesConnected through
cardTransaction-levelA card or bank connection
storeItem-levelA store or merchant connection
externalTransaction-level, unless enrichedA pushed transaction, through your own external connection
receipt_uploadItem-levelAn uploaded receipt

Next steps