Transaction lifecycle

Calculation is stateless; committing creates a durable transaction you can read back, void, refund, adjust, or credit. Every state change is recorded as an append-only history event.

Committed adjustments

POST /api/v1/transactions/{code}/adjust performs one atomic revision: it creates a full reversal under refund_code and a replacement sale under the replacement's own code. Matching lines inherit the original marketplace responsibility and reporting identifiers unless explicitly overridden. The replacement exposes revision_of_code and revision_number; the refund keeps its separate parent_code. If replacement validation fails, the original remains committed.

Tax-only credits

POST /api/v1/transactions/{code}/tax-only-credit accepts selected line numbers and tax amounts. The resulting credit has zero taxable base, retains the original line's jurisdiction and remitter, and cannot exceed remaining committed tax. This reverses tax in filing totals without reversing gross sales.

Calculate vs commit

  • Calculate (POST /api/v1/tax/calculate and /tax/quotes) computes tax and persists nothing — always safe to retry.
  • Commit (POST /api/v1/transactions) records the transaction and its calculated tax under a unique code in the key's environment, ready for downstream reporting.

Void

POST /api/v1/transactions/{code}/void cancels a committed transaction before it feeds compliance workflows. Voiding is idempotent; voided transactions are excluded from filing, nexus, collections, and reporting. A transaction with linked refunds cannot be voided.

Refunds

POST /api/v1/transactions/{code}/refund creates a refund linked to the original via parent_code. Omit lines for a full refund of the remaining refundable amount, or include lines [{ number, amount }] for a partial refund. A refund can never exceed the remaining refundable amount (otherwise 422 refund_exceeds_refundable), and tax is calculated as of the original transaction date unless you pass tax_date. The original transaction is never mutated beyond its status moving to partially_refunded or refunded.

Duplicate semantics

A transaction code is a natural duplicate guard within your organization, location, and environment. Reusing a code returns 409 duplicate_transaction and never modifies the original — fetch it with GET /api/v1/transactions/{code}. Refund codes get the same deterministic 409, so retried refund requests are safe.

Audit replay

POST /api/v1/transactions/{code}/replay reconstructs the calculation from persisted addresses, lines, store/reporting codes, and marketplace responsibility without writing anything. It returns the persisted record beside the fresh calculation and an explicit tax_delta, so later rate or content changes are visible without rewriting historical evidence.

Audit history

Each transaction keeps an append-only status history (created, voided, refund created/received, adjustment committed, and tax-only credit linked). History rows are never mutated or deleted, so the full lifecycle is auditable.

Try it in sandbox

A useful first committed test — two lines including shipping as an explicit line:

{
  "transaction_date": "2026-06-01",
  "code": "TEST-0001",
  "type": "sale",
  "customer_code": "CUST-1001",
  "ship_to": { "city": "New York", "state": "NY", "zip_code": "10001" },
  "lines": [
    { "number": 1, "amount": 120.00, "quantity": 2, "description": "Widget" },
    { "number": 2, "amount": 35.50, "line_type": "shipping", "description": "Shipping" }
  ]
}

Read it back with GET /api/v1/transactions/TEST-0001, then confirm the duplicate guard by sending the same payload again (expect 409). See the full endpoint reference.