Event Catalogue

Eight event types are emitted in v1: seven describe a purchase and one describes a balance deposit. The full list is:

  • purchase.created
  • purchase.sent_to_steam
  • purchase.completed
  • purchase.failed
  • purchase.refunded
  • purchase.reverted_by_buyer
  • purchase.reverted_by_seller
  • balance.deposited

All payloads share the same envelope:

{
  "event": "purchase.completed",
  "event_id": "11111111-2222-4333-8444-555555555555",
  "occurred_at": "2026-05-05T12:40:00.000Z",
  "data": {
    "purchase_id": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa",
    "custom_id": "your-order-42",
    "completed_at": "2026-05-05T12:40:00.000Z"
  }
}
Envelope fieldTypeRequiredMeaning
eventstringYesEvent type; matches X-Event.
event_idUUID stringYesStored event ID; matches X-Event-Id, unchanged on retries and manual replay.
occurred_atISO 8601 UTC stringYesTime the payload was prepared, with millisecond precision (.fffZ). It is not a state version or guaranteed ordering key.
dataobjectYesEvent-specific fields described below.

Every purchase.* payload includes required purchase_id (UUID string) and custom_id (your business order string). For buy-by-name, several attempts can have different purchase IDs under the same custom_id; apply each event to its attempt. Additional required and optional fields are listed for each event. Optional fields are omitted when their value is unavailable. See Ordering.

Money fields (price, amount, balance_after, refunded_amount) are JSON numbers with up to two decimals — never strings. Nullable fields (including image_url, failure_message, txid, reason, target_steam_id) are omitted from the JSON when null, not serialized as null.

purchase.created

Describes a newly created purchase attempt for any purchase method. The actual price has already been debited from your balance; max_price is only your consent ceiling.

Data fieldTypeRequiredMeaning
itemobjectYesPurchase item summary.
item.market_hash_namestringYesExact Steam item name.
item.image_urlURL stringNoItem image when available.
pricenumberYesActual price debited, USD.
target_steam_idstringNoRecipient SteamID64.
{
  "event": "purchase.created",
  "event_id": "11111111-1111-1111-1111-111111111111",
  "occurred_at": "2026-05-05T12:34:56.789Z",
  "data": {
    "purchase_id": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa",
    "custom_id": "your-order-42",
    "item": {
      "market_hash_name": "AK-47 | Redline (Field-Tested)",
      "image_url": "https://steamcommunity-a.akamaihd.net/economy/image/..."
    },
    "price": 12.34,
    "target_steam_id": "76561198000000000"
  }
}

purchase.sent_to_steam

Fires when our worker has dispatched the trade offer on Steam. The buyer must accept it; this is not a terminal state.

Data fieldTypeRequiredMeaning
trade_offer_idstringNoSteam trade offer ID, when available.
trade_offer_urlURL stringNoLink to the Steam offer, when available.
{
  "event": "purchase.sent_to_steam",
  "event_id": "11111111-2222-4333-8444-000000000001",
  "occurred_at": "2026-05-05T12:40:00.000Z",
  "data": {
    "purchase_id": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa",
    "custom_id": "your-order-42",
    "trade_offer_id": "5123456789",
    "trade_offer_url": "https://steamcommunity.com/tradeoffer/5123456789/"
  }
}

purchase.completed

Delivery succeeded: the buyer accepted the trade and Steam reported the item delivered. A later protection rollback can still produce purchase.reverted_by_buyer, purchase.reverted_by_seller, or purchase.failed if the cause is unknown. A later manual refund can produce purchase.refunded. Keep handling events for a completed purchase.

Data fieldTypeRequiredMeaning
completed_atISO 8601 UTC stringNoRecorded delivery-completion time; omitted if unavailable.
{
  "event": "purchase.completed",
  "event_id": "11111111-2222-4333-8444-000000000002",
  "occurred_at": "2026-05-05T12:40:00.000Z",
  "data": {
    "purchase_id": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa",
    "custom_id": "your-order-42",
    "completed_at": "2026-05-05T12:40:00.000Z"
  }
}

purchase.failed

Failure before delivery, a non-recoverable delivery error, or a later rollback with an unknown cause. failure_code is a stable machine-readable identifier; failure_message is human-readable and may change.

If the failure resulted in an automatic refund, refunded_amount is the amount returned to your merchant balance (USD, JSON number, ≤ 2 decimals). Otherwise the field is omitted. Do not infer a refund from a failure status alone.

Data fieldTypeRequiredMeaning
failure_codestringYesFailure identifier; UNKNOWN when no more specific code is available.
failure_messagestringNoHuman-readable explanation.
refunded_amountnumberNoAmount returned to your balance, USD.
{
  "event": "purchase.failed",
  "event_id": "11111111-2222-4333-8444-000000000003",
  "occurred_at": "2026-05-05T12:40:00.000Z",
  "data": {
    "purchase_id": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa",
    "custom_id": "your-order-42",
    "failure_code": "ITEM_UNAVAILABLE",
    "failure_message": "Item is no longer available from the provider",
    "refunded_amount": 12.34
  }
}

purchase.refunded

Describes a separately issued refund, for example a manual refund from support. It can follow a failed, completed, or still-progressing purchase. Do not require a previous purchase.failed event before processing the credit.

A failure that auto-refunds at the same time emits a single purchase.failed with refunded_amount set; you will not also receive purchase.refunded in that case.

Data fieldTypeRequiredMeaning
refunded_amountnumberYesAmount credited, USD.
balance_afternumberYesBalance immediately after that credit, USD; not necessarily your current balance.
reasonstringNoRefund explanation.
{
  "event": "purchase.refunded",
  "event_id": "11111111-2222-4333-8444-000000000004",
  "occurred_at": "2026-05-05T12:40:00.000Z",
  "data": {
    "purchase_id": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa",
    "custom_id": "your-order-42",
    "refunded_amount": 12.34,
    "balance_after": 987.66,
    "reason": "Manual refund (support request)"
  }
}

purchase.reverted_by_buyer

Fires when a purchase was already completed (skin delivered) and the buyer then rolled the accepted trade back during Steam’s Trade Protection hold (up to several days after delivery). Per policy this is treated as buyer fault: no refund is issued — refunded_amount is 0. The purchase moves to a terminal failed state.

The data fields are the same as purchase.failed, except refunded_amount is required and always 0. failure_code is present; failure_message is optional.

{
  "event": "purchase.reverted_by_buyer",
  "event_id": "11111111-2222-4333-8444-000000000005",
  "occurred_at": "2026-05-05T12:40:00.000Z",
  "data": {
    "purchase_id": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa",
    "custom_id": "your-order-42",
    "failure_code": "REVERTED_BY_BUYER",
    "failure_message": "Buyer reverted accepted trade (Steam Trade Protection rollback). No refund.",
    "refunded_amount": 0
  }
}

purchase.reverted_by_seller

Fires when a purchase was already completed and the seller (provider side) rolled the accepted trade back during the protection hold. You are refunded in full — refunded_amount is the purchase price, credited back to your merchant balance.

The data fields are the same as purchase.failed, except refunded_amount is required and equals the refunded purchase price. failure_code is present; failure_message is optional.

{
  "event": "purchase.reverted_by_seller",
  "event_id": "11111111-2222-4333-8444-000000000006",
  "occurred_at": "2026-05-05T12:40:00.000Z",
  "data": {
    "purchase_id": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa",
    "custom_id": "your-order-42",
    "failure_code": "REVERTED_BY_SELLER",
    "failure_message": "Seller reverted accepted trade (Steam Trade Protection rollback). Full refund.",
    "refunded_amount": 12.34
  }
}

Reverts can arrive after purchase.completed. A completed purchase is not final until Steam’s Trade Protection hold passes. Handle these two events idempotently on top of an already-completed order. When we cannot determine who caused the rollback, you receive a plain purchase.failed with refunded_amount omitted, and the case is reviewed manually — a purchase.refunded follows if support issues a refund.

balance.deposited

Fires when a top-up to your merchant balance has been confirmed. provider identifies heleket or whitepay; provider_uuid is that provider’s stable deposit identifier. Deduplicate balance credits by (provider, provider_uuid). The purchase.* events use (purchase_id, event); balance events do not have a purchase.

network remains tron or bsc for either provider (USDT-TRC20 / USDT-BEP20). txid is the provider transaction hash or identifier when available; the field is omitted otherwise. An internal WhiteBIT transfer reported by Whitepay can carry an internal identifier instead of a blockchain hash. network identifies the confirmed deposit wallet and does not establish on-chain execution for an internal transfer. Two different event IDs can refer to one deposit. A new event ID does not authorize a second credit.

Whitepay support is not enabled yet. Existing Heleket addresses and deposit history remain valid.

Data fieldTypeRequiredMeaning
amountnumberYesAmount credited, USD.
currencystringYesUSD.
balance_afternumberYesBalance after this credit, USD; not necessarily current.
networkstringYesDeposit network, tron or bsc.
txidstringNoProvider transaction hash or internal transfer identifier.
providerstringYesheleket or whitepay.
provider_uuidstringYesStable deposit identifier within the named provider.
{
  "event": "balance.deposited",
  "event_id": "11111111-2222-4333-8444-000000000007",
  "occurred_at": "2026-05-05T12:40:00.000Z",
  "data": {
    "amount": 100.0,
    "currency": "USD",
    "balance_after": 1100.0,
    "network": "tron",
    "txid": "0xabc...",
    "provider": "heleket",
    "provider_uuid": "11111111-2222-3333-4444-555555555555"
  }
}

Ordering guarantees

This diagram describes business transitions, not guaranteed delivery order:

purchase.created → purchase.sent_to_steam → purchase.completed
        ↘ purchase.failed [→ purchase.refunded]
purchase.completed → purchase.reverted_by_buyer
                   → purchase.reverted_by_seller
                   → purchase.failed [unknown rollback cause]
any eligible purchase → purchase.refunded [manual refund]
  • Events can arrive out of order within a purchase and across purchases. You might receive purchase.completed before a retried purchase.sent_to_steam.
  • occurred_at is a wall-clock timestamp of payload preparation. It is not guaranteed to be monotonic, a sequence number, or the time a state was committed. Do not use it alone to select the latest state or deduplicate events.
  • Store event facts and apply idempotent business effects. Do not let a late purchase.created or purchase.sent_to_steam overwrite a completed or reverted state. A protection rollback is a new outcome that must still be processed.
  • If events disagree or a transition appears missing, read the current purchase. Treat balance_after as a historical observation; read balance for reconciliation.
  • Automatic retries apply to stored events within the finite retry policy. Delivery is not proof that every intermediate business transition generated a stored event.
  • Retried and manually replayed deliveries keep the same event_id and raw body. Only X-Timestamp and X-Signature change.

Versioning

Forward compatibility rules for v1:

  • We may add new fields to existing payloads without notice. Treat unknown fields as optional.
  • We may add new event types. Receivers MUST tolerate unknown, correctly signed event types (record or ignore them and return 2xx) — failing on an unknown event type would create false alarms in our retry pipeline.
  • Schema changes are tracked in the Changelog.

Failure code compatibility and offer IDs

New failure and rollback events use the common purchase codes, the same as GET/list. purchase.failed, purchase.reverted_by_buyer, and purchase.reverted_by_seller include trade_offer_id and trade_offer_url when known. Their absence means the ID is unknown, not proof that no offer was sent.

Stored events are immutable: retries of historical events retain their old raw codes, messages and shape. No old events are rewritten or automatically sent again by this change. TRADE_CANCELLED does not identify a counteroffer; TRADE_COUNTERED requires an explicit provider signal. REVERT_UNKNOWN requires manual review and does not trigger an automatic refund.