Event Catalogue
Eight event types are emitted in v1: seven describe a purchase and one describes a balance deposit. The full list is:
purchase.createdpurchase.sent_to_steampurchase.completedpurchase.failedpurchase.refundedpurchase.reverted_by_buyerpurchase.reverted_by_sellerbalance.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 field | Type | Required | Meaning |
|---|---|---|---|
event | string | Yes | Event type; matches X-Event. |
event_id | UUID string | Yes | Stored event ID; matches X-Event-Id, unchanged on retries and manual replay. |
occurred_at | ISO 8601 UTC string | Yes | Time the payload was prepared, with millisecond precision (.fffZ). It is not a state version or guaranteed ordering key. |
data | object | Yes | Event-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 (includingimage_url,failure_message,txid,reason,target_steam_id) are omitted from the JSON when null, not serialized asnull.
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 field | Type | Required | Meaning |
|---|---|---|---|
item | object | Yes | Purchase item summary. |
item.market_hash_name | string | Yes | Exact Steam item name. |
item.image_url | URL string | No | Item image when available. |
price | number | Yes | Actual price debited, USD. |
target_steam_id | string | No | Recipient 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 field | Type | Required | Meaning |
|---|---|---|---|
trade_offer_id | string | No | Steam trade offer ID, when available. |
trade_offer_url | URL string | No | Link 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 field | Type | Required | Meaning |
|---|---|---|---|
completed_at | ISO 8601 UTC string | No | Recorded 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 field | Type | Required | Meaning |
|---|---|---|---|
failure_code | string | Yes | Failure identifier; UNKNOWN when no more specific code is available. |
failure_message | string | No | Human-readable explanation. |
refunded_amount | number | No | Amount 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 field | Type | Required | Meaning |
|---|---|---|---|
refunded_amount | number | Yes | Amount credited, USD. |
balance_after | number | Yes | Balance immediately after that credit, USD; not necessarily your current balance. |
reason | string | No | Refund 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 plainpurchase.failedwithrefunded_amountomitted, and the case is reviewed manually — apurchase.refundedfollows 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 field | Type | Required | Meaning |
|---|---|---|---|
amount | number | Yes | Amount credited, USD. |
currency | string | Yes | USD. |
balance_after | number | Yes | Balance after this credit, USD; not necessarily current. |
network | string | Yes | Deposit network, tron or bsc. |
txid | string | No | Provider transaction hash or internal transfer identifier. |
provider | string | Yes | heleket or whitepay. |
provider_uuid | string | Yes | Stable 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.completedbefore a retriedpurchase.sent_to_steam. occurred_atis 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.createdorpurchase.sent_to_steamoverwrite 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_afteras 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_idand raw body. OnlyX-TimestampandX-Signaturechange.
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.