Get Item-Level Expenses
Connect a store account and pull back your first item-level expenses in three calls.
Read the individual products behind a user's purchases from your own backend. Follow this quickstart to connect their store account and pull back every item, priced and classified on its own.
Requirements
Before you start, make sure you have:
- A Sail user created for your end user
- An API key with the
expensesscope - A webhook endpoint configured to receive events
Step 1: Open the Connect Account Widget
Your end user needs to log into their store account before Sail can retrieve their orders. Create a session with Create Hosted Session, setting flow to connect_account and connection_type to store.
curl -X POST https://live.savewithsail.com/api/v1/hosted-sessions \
-H "Authorization: Bearer sk_live_..." \
-H "Content-Type: application/json" \
-d '{
"user_id": "usr_abc123",
"flow": "connect_account",
"options": {
"connection_type": "store"
}
}'{
"url": "https://<HOSTED_SESSION_URL>",
"expires_at": "2026-08-10T15:31:07Z",
"flow": "connect_account"
}Open the url in an iframe or redirect your end user to it. The session is single-use and stops working at expires_at. Your end user's store credentials are entered inside Sail's iframe only, so they never reach your backend.
To skip the store selection grid and open straight into one store's login screen, pass merchant_id in options. The List Merchants endpoint returns the stores Sail supports.
Step 2: Wait for the sync to finish
Sail syncs the store's order history after your end user finishes logging in. Results arrive by webhook, so there's nothing to poll.
| Event | What it means |
|---|---|
connection.created | Your end user finished the widget and the connection exists. |
connection.status_changed | The connection moved to a new status, for example syncing to active. |
connection.synced | A sync finished. Check expenses_updated to see whether new expenses landed. |
Fetch expenses once you receive connection.synced with expenses_updated: true.
Check the
Sail-Signatureheader before you process any event. Delivery is at-least-once, so deduplicate on the eventid. See Receiving New Data for both.
The first sync pulls up to 15 months of order history, not only new orders, so expect more data on this call than on later ones.
Step 3: Read item-level expenses
This is the step where you see what was actually inside each purchase. Sail returns one expense per product, each with its own name, amount, merchant, and eligibility outcome, instead of one record per transaction total.
Call List Expenses with source_type=store to get only the products that came from store connections.
curl "https://live.savewithsail.com/api/v1/users/usr_abc123/expenses?source_type=store" \
-H "Authorization: Bearer sk_live_..."{
"data": [
{
"id": "exp_9f2c",
"connection_id": "conn_8e3a",
"name": "Digital Thermometer",
"date": "2026-08-05",
"amount": 27.02,
"source_type": "store",
"origin": {
"type": "product",
"detail": "Digital Thermometer - Forehead"
},
"merchant": {
"name": "Walgreens",
"supported_merchant_id": "mer_walgreens"
},
"enrichment": {
"category": "Pharmacies",
"subcategory": ["Drug Stores"]
},
"parent_expense_id": null,
"section_213d_classification": {
"status": "eligible",
"category": "Health Monitoring Devices"
}
}
],
"pagination": { "limit": 25, "offset": 0, "total": 3 }
}Amounts are decimal numbers in USD, so 27.02 means $27.02.
You now have item-level data. Each expense in the response carries origin.type: product, which is how you tell an individual product apart from a basket total.
Next steps
- To understand what each classification outcome means, see Classifying Item-Level Expenses.
- To see the full integration, including how item-level data enriches transactions you already have, see Enrich Transactions with Item-Level Data.
- To learn when a
supported_merchant_idis worth acting on, see Transaction-Level and Item-Level Expenses.
Updated 14 days ago