# List Expenses

**GET** `/users/{user_id}/expenses`

Base URL: `https://live.savewithsail.com/api/v1`

Expenses adjudicated for Section 213(d) eligibility across the user's connections. Requires the `expenses` product.

## Authorization

- PartnerKey (http, bearer)

## Path parameters

- `user_id` (string, required)
  The Sail user id.

## Query parameters

- `status` (string)
  Filter to expenses in this status.
- `connection_id` (string)
  Restrict to expenses sourced from one connection.
- `source_type` (string)
  Filter to expenses from one source type.
- `since` (string)
  Expenses created or updated after this timestamp (ISO 8601).
- `limit` (integer)
  Max results per page.
- `offset` (integer)
  Pagination offset.

## Responses

### 200

Expenses.

- `data` (object[])
  - `id` (string)
    The Sail expense id.
  - `connection_id` (string)
    The connection this expense was sourced from.
  - `external_transaction_id` (string)
    Echo of the customer-supplied id for expenses ingested through an `external` connection; null for Sail-sourced expenses. Join key back to the customer's own transaction store.
  - `name` (string)
    The expense's display name.
  - `description` (string)
    Human-readable description of the expense.
  - `date` (string<date>)
    The transaction date.
  - `amount` (number<float>)
    USD.
  - `status` ("eligible" | "needs_review" | "ineligible" | "reimbursed" | "archived")
    The expense's current workflow status.
  - `source_type` ("card" | "store" | "receipt_upload" | "external")
    How this expense entered Sail; pairs with the `source_type` list filter.
  - `origin` (object)
    What the expense was derived from.
    - `type` ("transaction" | "product")
      `transaction` — derived from a bank/credit-card transaction (one expense per transaction, split via `parent_expense_id` after itemization). `product` — derived from an individual product line (store order data or an itemized receipt).
    - `detail` (string)
      The raw descriptor (transactions) or product title (products) the expense was derived from.
  - `merchant` (object)
    The merchant this expense was incurred at, as resolved by enrichment. `supported_merchant_id` references the /merchants directory only when the merchant is one Sail can connect to for store data; it is null for the long tail of merchants that are recognized but not scrape-supported.
    - `name` (string)
      The merchant's display name.
    - `logo_url` (string)
      URL of the merchant's logo. Null if unavailable.
    - `supported_merchant_id` (string)
      The `/merchants` directory id, when Sail can connect to this merchant for store data. Null otherwise.
  - `enrichment` (object)
    General transaction enrichment, independent of Section 213(d) adjudication.
    - `category` (string)
      General spend category.
    - `subcategory` (string)
      More granular spend subcategory, when available.
    - `mcc` (string)
      Merchant category code, when derived from a card transaction or supplied at ingest.
  - `parent_expense_id` (string)
    Set when this expense is a line item split from another expense (e.g. itemization of a mixed-basket transaction after receipt review); null for top-level expenses.
  - `section_213d_classification` (object)
    Section 213(d) eligibility adjudication. The review states pair with `origin.type`: `lmn_required` applies to product-origin expenses (a dual-purpose item that becomes eligible with a Letter of Medical Necessity); `itemization_required` applies to transaction-origin expenses (a mixed basket that needs an itemized receipt before line items can be adjudicated). Both surface as `needs_review` in the top-level workflow `status`.
    - `status` ("eligible" | "lmn_required" | "itemization_required" | "ineligible")
      The Section 213(d) review outcome.
    - `category` (string)
      Section 213(d) category.
    - `reasoning` (string)
      Human-readable explanation for the classification. Null if not available.
  - `created_at` (string<date-time>)
    When the expense was created.
  - `updated_at` (string<date-time>)
    When the expense was last updated.
- `pagination` (object)
  Pagination metadata for this page.
  - `total` (integer)
    Total number of matching records, across all pages.
  - `limit` (integer)
    The `limit` used for this page.
  - `offset` (integer)
    The `offset` used for this page.
  - `has_more` (boolean)
    Whether additional pages remain after this one.

Example:

```json
{
  "data": [
    {
      "id": "string",
      "connection_id": "string",
      "external_transaction_id": "string",
      "name": "string",
      "description": "string",
      "date": "2024-01-01",
      "amount": 0,
      "status": "eligible",
      "source_type": "card",
      "origin": {
        "type": "transaction",
        "detail": "string"
      },
      "merchant": {
        "name": "Costco",
        "logo_url": "string",
        "supported_merchant_id": "mer_costco"
      },
      "enrichment": {
        "category": "Pharmacies",
        "subcategory": "Drug Stores",
        "mcc": "string"
      },
      "parent_expense_id": "string",
      "section_213d_classification": {
        "status": "eligible",
        "category": "Health Monitoring Devices",
        "reasoning": "string"
      },
      "created_at": "1970-01-01T00:00:00.000Z",
      "updated_at": "1970-01-01T00:00:00.000Z"
    }
  ],
  "pagination": {
    "total": 0,
    "limit": 0,
    "offset": 0,
    "has_more": true
  }
}
```

### default

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

- `error` (object)
  The error detail.
  - `code` (string)
    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`.
  - `message` (string)
    Human-readable error message. May change, so match on `error.code` instead.
  - `param` (string)
    The request field that caused the error, when applicable. Null otherwise.

Example:

```json
{
  "error": {
    "code": "string",
    "message": "string",
    "param": "string"
  }
}
```