Expense Classification
Learn how Sail classifies transaction-level and item-level expenses, and how to use the results.
Before a user can be paid back from their HSA/FSA, Sail has to decide that what they bought actually qualifies. That decision is expense classification, and eligible is the outcome a reimbursement needs.
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. It can be eligible or ineligible, or it can need itemization or a Letter of Medical Necessity (LMN). 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 the process of checking an expense against a defined set of eligibility rules to determine whether it qualifies as a medical expense that a benefit account can pay for.
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 classification 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 because it needs itemization. See When an expense needs itemization.lmn_required: Sail can't classify the expense yet because it needs a Letter of Medical Necessity. See When an expense needs a Letter of Medical Necessity.
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.
When an expense needs itemization
itemization_required applies to transaction-origin expenses, meaning origin.type: transaction. It means the purchase came from a mixed-basket store (one 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.
When an expense needs a Letter of Medical Necessity
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. Instead of calling a separate "classify this" endpoint, classification happens as part of processing the expense itself.
How classification results reach you
Classification results arrive by webhook, not by polling. See Webhook Events for the full payload shape of each one.
Two webhooks tell you that results are ready:
- For any expense:
expenses.213d_verifiedfires once every expense on the user has finished classification. Sail sends it only if expense notifications are enabled on your partner account. - For a pushed batch:
transactions.ingestedalso fires at the job level once the whole batch finishes, listingexpense_ids.
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 itemization_required
itemization_requiredThere are two ways to supply the missing detail:
- Attach an itemized receipt through Upload Receipt or the Upload Receipt Widget, the
upload_receiptflow of a hosted session. Either path triggers asynchronous reclassification. The outcome arrives as areceipt.processedwebhook, listing the resulting child expenses, orreceipt.failedif the receipt is unreadable. - Connect the store itself. When
merchant.supported_merchant_idis non-null, Sail has a connector for that store, and prompting the user to log into it supplies the item-level detail for the expenses already waiting on it, as well as for their future purchases there. See Classifying Item-Level Expenses for how item-level data changes which outcomes you see.
Next steps
- To pay a user back once an expense is
eligible, see Create and Track a Reimbursement. - To push your own transaction data and retrieve eligibility results, see Classify Your Own Transactions.
- To see how expense data enters Sail in the first place, see Transaction-Level and Item-Level Expenses.
- To see the same classification behavior from the item-level side, where product data avoids the itemization outcome, see Classifying Item-Level Expenses.
Updated 13 days ago