Sail

Expense Classification

Sail ingests transaction-level and item-level expenses and classifies each one, with fine-grained detail, against a rules engine. Every expense ends up in one of four outcomes, eligible, ineligible, needs itemization, or needs a letter of medical necessity. This page explains what each outcome means, what triggers classification, and how you find out once it finishes.

What expense classification is

Classification, called “adjudication” in Sail’s API and webhooks, is how Sail decides whether an expense is a permitted expense under a specific set of rules.

Today, Sail classifies against Section 213(d) of the US tax code and IRS Publication 502, the same rules HSA, FSA, and HRA administrators use to decide what counts as a qualified medical expense. The result lives on the Expense object’s section_213d_classification field, the field your integration checks for the outcome.

However, other rules can be set up for your account on request, beyond the Section 213(d) default rule set. Classification runs automatically on every expense, whether it comes from a synced card, bank, or store connection, or from a batch of transactions you push yourself through an external connection.

The four outcomes

section_213d_classification.status surfaces one of four outcomes:

  • eligible: the expense qualifies as a Section 213(d) medical expense.
  • ineligible: the expense doesn’t qualify.
  • itemization_required: Sail can’t classify the expense yet, it needs itemization. See below.
  • lmn_required: Sail can’t classify the expense yet, it needs a Letter of Medical Necessity. See below.
Reimbursement#

Once an expense is eligible, it can be reimbursed from the user’s HSA/FSA. See Reimbursement for the preconditions, the status lifecycle, and how a reimbursement can fail.

Needs itemization

itemization_required applies to transaction-origin expenses, meaning origin.type: transaction. It means the purchase came from a mixed-basket merchant (a store like Walgreens or Target that sells both eligible and ineligible items in the same transaction), so Sail can’t tell what was actually bought from the transaction alone. See Transaction-Level and Item-Level Expenses for the difference between transaction- and item-level data.

Needs letter

lmn_required applies to product-origin expenses, meaning origin.type: product. It means the item is dual-purpose (something with both a medical and a non-medical use, like a general-purpose humidifier), and it becomes eligible only with a Letter of Medical Necessity from the user’s provider.

What triggers classification

Classification runs automatically as soon as expense data lands in Sail, regardless of source. That includes expenses synced from a card, bank, or store connection, and expenses you push yourself through Push Transactions on an external connection. You never call a separate “classify this” endpoint, classification happens as part of processing the expense itself.

How you find out the result

Classification results arrive by webhook, not by polling. What you get depends on how the expense entered Sail.

  • From a synced connection: expense.created for a new, already-classified expense, or expense.updated with section_213d_classification listed in changes if an existing expense’s classification changes.
  • From a pushed batch: transactions.ingested fires at the job level once the whole batch finishes, listing expense_ids. Sail also fires expense.created per new expense within that batch, the same as a synced connection.

Once notified, the result is available from Get Expense or List Expenses. See Receiving New Data for how to verify and deduplicate any webhook delivery, classification included.

Resolving needs itemization or needs letter

For itemization_required, attach an itemized receipt through Upload Receipt or an upload_receipt flow of the Connect Account Widget. Either path triggers asynchronous reclassification. The outcome arrives as a receipt.processed webhook, listing the resulting child expenses, or receipt.failed if the receipt is unreadable.

Next steps