Set up inventory and gains

Send a user's trading activity to Taxbit, let inventory update, and read back realized gains for a date range.

By the end of this page you have one account's tax year, from raw trades to reportable gains:

  • Transactions accepted on the account.
  • Inventory that reflects those transactions.
  • Short-term, long-term, and total gains for 2026.

This walkthrough spans three pages. This one runs the whole path; Retrieve inventory and Retrieve gains cover the last two steps in depth.

Before you begin

  • A bearer token. See Authenticate your tenant.
  • A default filer with a disposition_method, which accounts under it use unless they set their own. Set up your filer shows how to set it when you create the filer.
  • An account owner and an account. See Create account owners and accounts.
  • Transactions sent in order, as soon as your platform processes them. Inventory stays near real time only when each account's transactions arrive in sequence.

Send transactions

Post each trade to POST /v1/transactions/external-id, one transaction per request. Put your own identifier in id: resending the same id updates the transaction instead of adding a second one. sent and received are arrays, each entry an asset_amount plus rates.

This account buys 2.5 ETH for $8,125 in February:

curl -X POST $BASE_URL/v1/transactions/external-id \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "id": "trade_88213",
    "account_id": "acct_5f2e7d10",
    "type": "trade",
    "datetime": "2026-02-14T16:20:11Z",
    "received": [{
      "asset_amount": { "amount": "2.5", "asset": { "code": "ETH", "type": "Crypto" } },
      "rates": [{ "amount": "3250.00", "asset": { "code": "USD", "type": "Fiat" } }]
    }],
    "sent": [{
      "asset_amount": { "amount": "8125.00", "asset": { "code": "USD", "type": "Fiat" } },
      "rates": [{ "amount": "1.00", "asset": { "code": "USD", "type": "Fiat" } }]
    }]
  }'
{
  "status": "success",
  "message": "Transaction post successful."
}

Then sells 1 ETH for $3,000 in July:

curl -X POST $BASE_URL/v1/transactions/external-id \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "id": "trade_88214",
    "account_id": "acct_5f2e7d10",
    "type": "trade",
    "datetime": "2026-07-15T13:05:40Z",
    "sent": [{
      "asset_amount": { "amount": "1", "asset": { "code": "ETH", "type": "Crypto" } },
      "rates": [{ "amount": "3000.00", "asset": { "code": "USD", "type": "Fiat" } }]
    }],
    "received": [{
      "asset_amount": { "amount": "3000.00", "asset": { "code": "USD", "type": "Fiat" } },
      "rates": [{ "amount": "1.00", "asset": { "code": "USD", "type": "Fiat" } }]
    }]
  }'

You should see 201 with "status": "success" for each transaction you send.

For bulk loads and backfills, use the file uploader in the Taxbit Dashboard, which takes CSV and JSONL.

Wait for inventory to update

There is nothing to trigger. Inventory and gains recalculate automatically, in the background, every time a transaction is processed.

  • The newest transaction for an account is usually reflected in under a second when transactions arrive in order.
  • A transaction that lands earlier in an account's history is a historical change and has no SLA.
  • To know when inventory is current, subscribe to the INVENTORY_UPDATE webhook.

If you've subscribed to webhooks, you receive an INVENTORY_UPDATE event. SLAs & async behavior has the details.

Retrieve inventory

GET /v1/inventory returns one asset per call and needs asset_code or asset_id along with account_id. Each open lot carries its acquisition date, term, quantity, and cost, and the response also carries a summary for the asset. For all assets at once, use GET /v1/inventory/summaries. More in Retrieve inventory.

curl -G $BASE_URL/v1/inventory \
  -H "Authorization: Bearer $TOKEN" \
  --data-urlencode "account_id=acct_5f2e7d10" \
  --data-urlencode "asset_code=ETH"
{
  "data": {
    "asset": { "code": "ETH", "name": "Ethereum", "type": "Crypto", "uuid": "b7a005b5-f4d5-44ea-ae80-d4f9e8313558" },
    "summary": {
      "total_quantity": "1.5",
      "total_quantity_with_cost_basis": "1.5",
      "total_cost": "4875.00",
      "average_unit_cost": "3250.00"
    },
    "lots": [
      {
        "id": "b72696f9-0f10-5827-94c3-289b6366a599",
        "acquisition_datetime": "2026-02-14T16:20:11Z",
        "term": "short-term",
        "quantity": "1.5",
        "fiat_asset_code": "USD",
        "cost": "4875.00",
        "unit_cost": "3250.00",
        "client_acquisition_transaction_id": "trade_88213",
        "client_modified_by_transaction_id": "trade_88214"
      }
    ],
    "lots_ordered_by": "HIFO"
  }
}

You should see one entry per open ETH lot, each with acquisition_datetime, quantity, and cost. Here the July sale has taken 1 ETH from the February lot, leaving 1.5.

Retrieve gains

For short-term, long-term, and total figures, call GET /v1/gains/breakdown with start_date and end_date as full datetimes, such as 2026-01-01T00:00:00Z. Date-only values return 400. Pass both to scope the results to a tax year. For one row per disposition, with a gain_type of short-term or long-term, call GET /v1/gains with the same datetimes. More in Retrieve gains.

curl -G $BASE_URL/v1/gains/breakdown \
  -H "Authorization: Bearer $TOKEN" \
  --data-urlencode "account_id=acct_5f2e7d10" \
  --data-urlencode "start_date=2026-01-01T00:00:00Z" \
  --data-urlencode "end_date=2026-12-31T23:59:59Z"

You should see short_term, long_term, and total for 2026, each an asset and a quantity. Here the July sale is a $250 short-term loss: 1 ETH sold for $3,000 against a cost of $3,250.

📘

Gains move when the inventory changes

Backfilling older transactions or changing the disposition method triggers a recalculation. Do not cache gains across a backfill or a method change.

Where to go next