Webhook event reference
This page lists every webhook event a casino integration receives, with a sample payload and its fields. For delivery, signatures, retries and idempotency, read Webhooks first.
| Event | Fires when | Produced |
|---|---|---|
deposit.received | The payment is seen on-chain (0 confirmations). | Once per deposit |
deposit.confirmed | The payment has at least one confirmation. | Once per deposit |
kyt_complete | The KYT screening result is set or changes. | Once per kyt_result |
ofac_complete | The OFAC screen of the funding addresses completes or misses its deadline. | Once per ofac_result |
double_spend_alert | A zero-confirmation payment disappeared before confirming. | Once per deposit |
deposit.accepted | POST /casino/deposit/{id}/accept succeeded. | Once per deposit |
deposit.returned | POST /casino/deposit/{id}/return succeeded. | Once per deposit |
deposit.frozen | POST /casino/deposit/{id}/freeze succeeded. | Once per deposit |
transaction.<status> | The underlying transaction's status changes. | Once per status |
"Produced once" does not mean "received once": the same delivery can arrive more than once, and events can arrive out of order. See Delivery guarantees.
Reading an event
- Casino events carry an
eventfield with the event name. A body without aneventfield is atransaction.<status>event. - Check
testfirst. It is present, andtrue, only on test events. Never credit a test event. timestampis the time the event was built, in Unix seconds. Retries keep the original value.decision_atanddouble_spent_atare ISO 8601 timestamps in UTC.market_rateandcrypto_rateare decimal strings, so no precision is lost. Other amounts are JSON numbers.- The sample payloads below are real output of the API's payload builders for a synthetic $100 BTC deposit. They are shown pretty-printed; on the wire the body is compact.
Late payments
A restored late deposit carries late_payment: true on every webhook event built after
restoration, including transaction.<status> events. The boolean is present only when
true and absent otherwise. Events for the restored deposit can arrive after
transaction.timedout, when the player may already have been told it expired.
Check this field before accepting: a late deposit requires adjust_to_received: true and
is repriced at the current market rate. See Late payments.
The acceptable field
Casino deposit events carry acceptable; generic transaction.<status> events do not.
It reports decision, payment and screening readiness when the event was built, including
the instant ceiling valued at the deposit's recorded rate. It does not promise that an
accept request will succeed.
Decide whether to accept from acceptable, not from decision, kyt_result or the event name.
It is a snapshot, not a promise: a later change (a double-spend, a late KYT failure) can make accept fail, so keep handling 409 responses.
Decision events always carry acceptable: false, because the deposit is already decided.
See Accept, return, freeze.
acceptable does not check amount adjustments or late-payment repricing. A restored late
deposit requires adjust_to_received: true even when the amounts match, and acceptance can
still fail if it cannot be repriced.
A late accept uses the current market rate, which can put an instant deposit still
waiting for KYT over its ceiling. Accept then returns 409 code
instant_received_over_cap even if acceptable was true and the deposit's state has
not changed. Wait for KYT to pass.
deposit.received
Fires the first time the deposit's transaction reaches a status that means funds were received: Succeed, Underpaid or Overpaid, or any later status such as Confirmed.
If a deposit moves straight to a confirmed status, you receive deposit.received and then deposit.confirmed.
Produced once per deposit.
What to do: show the player that the payment was detected.
Check amount_matches: if it is false, the player sent a different amount than quoted, and accepting requires adjust_to_received (see Accept, return, freeze).
Do not credit the player on this event. Accept only when an event carries acceptable: true.
The OFAC screen's deadline starts when this event is produced. ofac_complete follows.
{
"acceptable": false,
"address": "bc1qsampleaddressdonotsendfunds000000000",
"affiliate_fee_usd": 1.75,
"amount_matches": true,
"bf_fee_usd": 0,
"bf_spread_usd": 2.608975,
"charges": [
{
"amount_crypto": 0.00002609,
"amount_usd": 2.608975,
"beneficiary": "BF",
"kind": "SPREAD",
"paid_by": "PLAYER",
"percent": 0.025
},
{
"amount_crypto": 0.00001795,
"amount_usd": 1.75,
"beneficiary": "AFFILIATE",
"kind": "FEE",
"paid_by": "PLAYER",
"percent": 0.0175
}
],
"crypto_amount": 0.00104359,
"currency": "BTC",
"delta_crypto": 0,
"delta_usd_at_market": 0,
"deposit_id": 0,
"entity_external_code": "your-casino-code",
"entity_id": 1234,
"event": "deposit.received",
"fee": 0,
"fee_usd": 0,
"gambler_ref": "sample-player-001",
"instant": false,
"instant_downgraded": false,
"machine_id": null,
"market_rate": "100000.0000000000",
"no_conf": false,
"player_amount": 100,
"received_crypto": 0.00104359,
"test": true,
"timestamp": 1790172000,
"transaction_id": 0,
"usd_amount": 101.75
}
Deposit fields
These fields appear on deposit.received and, where present, mean the same on every other event.
| Field | Type | Description |
|---|---|---|
event | string | The event name. |
deposit_id | number | The deposit's id. Use it with GET /casino/deposit/{id} and the decision endpoints. |
transaction_id | number | The id of the deposit's underlying transaction. It is the id of transaction.<status> events, as a string. |
late_payment | boolean | Present only when true on an event built after a late deposit is restored; absent otherwise. Also applies to transaction.<status> events. |
entity_id | number | The casino (entity) the deposit belongs to. |
entity_external_code | string or null | The casino's external code. |
gambler_ref | string | Your reference for the player, as sent when the deposit was created. |
currency | string | The cryptocurrency, for example BTC. |
address | string | The deposit address the player pays to. |
crypto_amount | number | The quoted crypto amount, or the final amount after an adjusted accept, at the deposit's quote precision. |
usd_amount | number or null | The total USD the player pays: player_amount plus the fees the player pays. Spreads are part of the rate, not of this amount. |
player_amount | number or null | P: the USD amount the casino credits the player. null when usd_amount is null. |
market_rate | string or null | The market USD price per coin used for the amounts shown, before spreads. A late accept updates it to the rate used at acceptance. |
charges | array | Every fee and spread booked against the deposit. See Charges. |
fee, fee_usd, bf_fee_usd, bf_spread_usd, affiliate_fee_usd | number | Deprecated. See Deprecated charge fields. |
no_conf | boolean | The deposit was created for acceptance before its first confirmation. |
instant | boolean | The deposit was created as an Instant deposit: it can be accepted before KYT completes, within the casino's limit. |
instant_downgraded | boolean | The received value exceeded the Instant ceiling, so the deposit now waits for KYT passed like a standard deposit. |
machine_id | string or null | The machine identifier sent when the deposit was created. |
acceptable | boolean | Decision, payment and screening readiness when the event was built. See The acceptable field for its limits. |
amount_matches | boolean or null | true when the received amount equals crypto_amount. null when no received amount is recorded. |
delta_crypto | number or null | Received minus quoted crypto. Positive means overpaid, negative means underpaid. |
delta_usd_at_market | number or null | delta_crypto × market_rate, rounded to cents. null when either is unknown. |
received_crypto | number or null | deposit.received only. The crypto amount received on-chain, as recorded when the event was built. |
timestamp | number | When the event was built, in Unix seconds. |
test | boolean | Present only on test events, and then always true. |
See Deposit lifecycle for zero-confirmation and Instant deposits.
Charges
Each entry in charges is one fee or spread.
| Field | Type | Description |
|---|---|---|
beneficiary | string | Who earns the charge: BF (ByteFederal), AFFILIATE (BurraPay) or ENTITY (the casino). |
kind | string | FEE or SPREAD. A spread is built into the exchange rate. |
paid_by | string | PLAYER: added on top of player_amount. ENTITY: deducted from the casino's settlement. |
percent | number | The rate as a fraction: 0.025 is 2.5%. |
amount_usd | number | The charge in USD. |
amount_crypto | number or null | The charge in crypto. |
In the sample, the player is credited $100 (player_amount).
The player pays a 1.75% BurraPay fee on top, so usd_amount is $101.75.
ByteFederal's 2.5% spread is in the rate the player was quoted, so it does not add to usd_amount.
Deprecated charge fields
These fields exist so older integrations keep working. They cannot describe every fee arrangement. Read charges instead.
| Field | Meaning |
|---|---|
fee | ByteFederal's player-paid fee, in crypto. 0 if none. |
fee_usd, bf_fee_usd | ByteFederal's player-paid fee, in USD. The same value under two names. 0 if none. |
bf_spread_usd | ByteFederal's spread, in USD. 0 if none. |
affiliate_fee_usd | BurraPay's player-paid fee, in USD. 0 if none. |
deposit.confirmed
Fires the first time the deposit's transaction reaches a status with at least one on-chain confirmation: Confirmed, ConfirmedKYTReview, ConfirmedApproved, Settled, Reimbursed or Refund.
Produced once per deposit.
What to do: for a standard deposit, confirmation is one of the conditions for accepting. KYT and OFAC must also pass.
If acceptable is true, you can accept.
KYT screening runs at confirmation, so kyt_complete usually follows.
The payload is the same as deposit.received, except that confirmed_crypto replaces received_crypto.
{
"acceptable": false,
"address": "bc1qsampleaddressdonotsendfunds000000000",
"affiliate_fee_usd": 1.75,
"amount_matches": true,
"bf_fee_usd": 0,
"bf_spread_usd": 2.608975,
"charges": [
{
"amount_crypto": 0.00002609,
"amount_usd": 2.608975,
"beneficiary": "BF",
"kind": "SPREAD",
"paid_by": "PLAYER",
"percent": 0.025
},
{
"amount_crypto": 0.00001795,
"amount_usd": 1.75,
"beneficiary": "AFFILIATE",
"kind": "FEE",
"paid_by": "PLAYER",
"percent": 0.0175
}
],
"confirmed_crypto": 0.00104359,
"crypto_amount": 0.00104359,
"currency": "BTC",
"delta_crypto": 0,
"delta_usd_at_market": 0,
"deposit_id": 0,
"entity_external_code": "your-casino-code",
"entity_id": 1234,
"event": "deposit.confirmed",
"fee": 0,
"fee_usd": 0,
"gambler_ref": "sample-player-001",
"instant": false,
"instant_downgraded": false,
"machine_id": null,
"market_rate": "100000.0000000000",
"no_conf": false,
"player_amount": 100,
"test": true,
"timestamp": 1790172000,
"transaction_id": 0,
"usd_amount": 101.75
}
| Field | Type | Description |
|---|---|---|
confirmed_crypto | number or null | The crypto amount received on-chain, as recorded when the event was built. |
Every other field is described under Deposit fields.
kyt_complete
Fires when the KYT (Know Your Transaction) screening result for the deposit's funds is set, and again each time it changes to a new value, for example from review to passed after a manual compliance review.
Produced once per kyt_result value per deposit. If the result returns to a value that was already sent for the deposit, it is not sent again.
What to do depends on kyt_result:
kyt_result | Meaning | What to do |
|---|---|---|
passed | The funds passed screening. | If acceptable is true, you can accept. |
failed | The funds matched a blocking rule. | If the deposit was still pending, ByteFederal froze it and decision is frozen. No deposit.frozen event is sent for this. If you already accepted the deposit (possible for Instant deposits), decision stays accepted; reverse the player's credit under your own policy. |
review | The funds need a manual compliance review. | Do not accept. decision stays pending, but acceptable is false and accept returns 409. Wait for another kyt_complete with the new result. |
review looks actionable but is notA review event carries decision: "pending", yet POST /casino/deposit/{id}/accept rejects the deposit with 409 until the result changes.
Always decide from acceptable.
{
"acceptable": true,
"confirmed_crypto": 0.00104359,
"crypto_amount": 0.00104359,
"currency": "BTC",
"decision": "pending",
"deposit_id": 0,
"entity_external_code": "your-casino-code",
"entity_id": 1234,
"event": "kyt_complete",
"gambler_ref": "sample-player-001",
"instant": false,
"instant_downgraded": false,
"kyt_result": "passed",
"risk_score": 0,
"test": true,
"timestamp": 1790172000,
"transaction_id": 0,
"triggered_rules": []
}
| Field | Type | Description |
|---|---|---|
kyt_result | string | passed, failed or review. |
risk_score | number | The risk score from the screening. Higher is riskier. |
triggered_rules | array | What the screening matched, as objects with ruleset (string or null), category and entity. Empty when nothing matched. Informational only; do not build logic on its contents. |
decision | string | The deposit's decision when the event was built: pending, accepted, returned or frozen. |
confirmed_crypto | number or null | The crypto amount received on-chain, as recorded when the event was built. |
kyt_complete does not carry address, usd_amount or charges.
Every other field is described under Deposit fields.
ofac_complete
Fires when the OFAC screen of the funding transaction's source addresses completes, or when it has not completed by its deadline.
The deadline starts when deposit.received is produced.
Produced once per ofac_result value per deposit.
Usually you receive a single clear. If the screen misses its deadline, you receive timed_out and later the real result.
What to do depends on ofac_result:
ofac_result | Meaning | What to do |
|---|---|---|
clear | No source address is sanctioned. | The OFAC condition for accept and return is met. If acceptable is true, you can accept. |
timed_out | The screen did not finish by its deadline. | Accept and return are allowed anyway. The screen keeps running, and a second ofac_complete with clear or hit follows. |
hit | A source address is sanctioned. | Accept and return are refused permanently (409, code ofac_sanctioned). If the deposit was pending, ByteFederal froze it and decision is frozen; no deposit.frozen event is sent. If you already accepted it (for example after timed_out), decision stays accepted; reverse the player's credit under your own policy. |
The matched addresses are never included in the event.
{
"acceptable": true,
"confirmed_crypto": 0.00104359,
"crypto_amount": 0.00104359,
"currency": "BTC",
"decision": "pending",
"deposit_id": 0,
"entity_external_code": "your-casino-code",
"entity_id": 1234,
"event": "ofac_complete",
"gambler_ref": "sample-player-001",
"instant": false,
"instant_downgraded": false,
"ofac_result": "clear",
"test": true,
"timestamp": 1790172000,
"transaction_id": 0
}
| Field | Type | Description |
|---|---|---|
ofac_result | string | clear, timed_out or hit. |
decision and confirmed_crypto are described under kyt_complete.
Every other field is described under Deposit fields.
double_spend_alert
Fires for a zero-confirmation deposit (no_conf: true) that had become eligible for acceptance before confirmation, when its funding transaction disappears from the network before confirming and the address no longer holds the expected amount.
Produced once per deposit.
What to do: ByteFederal does not change the decision.
If the deposit is still pending, do not accept it: acceptable is false and accept is refused from now on.
If you already accepted it, you took the zero-confirmation risk for this deposit; reverse the player's credit under your own policy.
{
"acceptable": false,
"address": "bc1qsampleaddressdonotsendfunds000000000",
"crypto_amount": 0.00104359,
"currency": "BTC",
"decision": "pending",
"deposit_id": 0,
"double_spend_txid": "0000000000000000000000000000000000000000000000000000000000000000",
"double_spent_at": "2026-09-23T14:00:00.000Z",
"entity_external_code": "your-casino-code",
"entity_id": 1234,
"event": "double_spend_alert",
"gambler_ref": "sample-player-001",
"machine_id": null,
"test": true,
"timestamp": 1790172000,
"transaction_id": 0,
"usd_amount": 101.75
}
| Field | Type | Description |
|---|---|---|
double_spend_txid | string | The id of the funding transaction that disappeared. |
double_spent_at | string or null | When the double-spend was recorded (ISO 8601, UTC). |
decision | string | The deposit's decision, unchanged by the double-spend. |
usd_amount | number | As under Deposit fields. |
Every other field is described under Deposit fields.
deposit.accepted
Fires right after POST /casino/deposit/{id}/accept succeeds.
Produced once per deposit.
What to do: credit the player player_amount.
The accept response returns the same amounts; this event matters most to a casino that receives events directly while BurraPay calls the API.
If the deposit was accepted with adjust_to_received, adjusted is true and the amounts and charges are recalculated from the amount actually received.
For a restored late deposit, acceptance uses the current market rate. See Late payments.
{
"acceptable": false,
"address": "bc1qsampleaddressdonotsendfunds000000000",
"adjusted": false,
"affiliate_fee_usd": 1.75,
"affiliate_net_usd": 100,
"bf_fee_usd": 0,
"bf_spread_usd": 2.608975,
"charges": [
{
"amount_crypto": 0.00002609,
"amount_usd": 2.608975,
"beneficiary": "BF",
"kind": "SPREAD",
"paid_by": "PLAYER",
"percent": 0.025
},
{
"amount_crypto": 0.00001795,
"amount_usd": 1.75,
"beneficiary": "AFFILIATE",
"kind": "FEE",
"paid_by": "PLAYER",
"percent": 0.0175
}
],
"confirmed_crypto": 0.00104359,
"crypto_amount": 0.00104359,
"crypto_rate": "97500.0000000000",
"currency": "BTC",
"decision": "accepted",
"decision_at": "2026-09-23T14:00:00.000Z",
"deposit_id": 0,
"entity_external_code": "your-casino-code",
"entity_id": 1234,
"entity_net_usd": 100,
"event": "deposit.accepted",
"fee": 0,
"fee_usd": 0,
"gambler_ref": "sample-player-001",
"instant": false,
"instant_downgraded": false,
"machine_id": null,
"no_conf": false,
"player_amount": 100,
"test": true,
"timestamp": 1790172000,
"transaction_id": 0,
"usd_amount": 101.75
}
Decision events share these fields:
| Field | Type | Description |
|---|---|---|
decision | string | accepted, returned or frozen, matching the event. |
decision_at | string or null | When the decision was made (ISO 8601, UTC). |
confirmed_crypto | number or null | The crypto amount received on-chain. |
acceptable | boolean | Always false on decision events. |
deposit.accepted adds:
| Field | Type | Description |
|---|---|---|
adjusted | boolean | true when the deposit was accepted at the received amount (adjust_to_received). |
crypto_rate | string or null | The exchange rate used: market_rate less the spreads. |
entity_net_usd | number or null | What the casino is settled for this deposit, after any fees the casino pays. |
affiliate_net_usd | number or null | The same value as entity_net_usd, under its older name. Use entity_net_usd. |
Decision events do not carry market_rate, amount_matches, delta_crypto or delta_usd_at_market.
Every other field is described under Deposit fields.
deposit.returned
Fires right after POST /casino/deposit/{id}/return succeeds.
Produced once per deposit.
What to do: do not credit the player.
The event means the return is queued, not sent. When the crypto is sent back on-chain, the transaction's status becomes Refund and you receive transaction.refund.
{
"acceptable": false,
"address": "bc1qsampleaddressdonotsendfunds000000000",
"affiliate_fee_usd": 1.75,
"bf_fee_usd": 0,
"bf_spread_usd": 2.608975,
"charges": [
{
"amount_crypto": 0.00002609,
"amount_usd": 2.608975,
"beneficiary": "BF",
"kind": "SPREAD",
"paid_by": "PLAYER",
"percent": 0.025
},
{
"amount_crypto": 0.00001795,
"amount_usd": 1.75,
"beneficiary": "AFFILIATE",
"kind": "FEE",
"paid_by": "PLAYER",
"percent": 0.0175
}
],
"confirmed_crypto": 0.00104359,
"crypto_amount": 0.00104359,
"currency": "BTC",
"decision": "returned",
"decision_at": "2026-09-23T14:00:00.000Z",
"deposit_id": 0,
"entity_external_code": "your-casino-code",
"entity_id": 1234,
"event": "deposit.returned",
"fee": 0,
"fee_usd": 0,
"gambler_ref": "sample-player-001",
"instant": false,
"instant_downgraded": false,
"machine_id": null,
"no_conf": false,
"player_amount": 100,
"reason": "Player cancelled the deposit",
"return_address": "bc1qsamplereturnaddressdonotsend0000000",
"return_id": 0,
"test": true,
"timestamp": 1790172000,
"transaction_id": 0,
"usd_amount": 101.75
}
| Field | Type | Description |
|---|---|---|
return_id | number | The id of the queued return. |
return_address | string | The address the crypto is returned to. |
reason | string or null | The reason sent with the return request. |
The shared decision fields are described under deposit.accepted; every other field under Deposit fields.
deposit.frozen
Fires right after POST /casino/deposit/{id}/freeze succeeds, and only then.
When ByteFederal freezes a deposit because KYT failed or OFAC found a sanctioned address, no deposit.frozen event is sent. That freeze shows as decision: "frozen" on kyt_complete or ofac_complete.
Produced once per deposit.
What to do: do not credit the player. The funds are held.
{
"acceptable": false,
"address": "bc1qsampleaddressdonotsendfunds000000000",
"affiliate_fee_usd": 1.75,
"bf_fee_usd": 0,
"bf_spread_usd": 2.608975,
"charges": [
{
"amount_crypto": 0.00002609,
"amount_usd": 2.608975,
"beneficiary": "BF",
"kind": "SPREAD",
"paid_by": "PLAYER",
"percent": 0.025
},
{
"amount_crypto": 0.00001795,
"amount_usd": 1.75,
"beneficiary": "AFFILIATE",
"kind": "FEE",
"paid_by": "PLAYER",
"percent": 0.0175
}
],
"confirmed_crypto": 0.00104359,
"crypto_amount": 0.00104359,
"currency": "BTC",
"decision": "frozen",
"decision_at": "2026-09-23T14:00:00.000Z",
"deposit_id": 0,
"entity_external_code": "your-casino-code",
"entity_id": 1234,
"event": "deposit.frozen",
"fee": 0,
"fee_usd": 0,
"gambler_ref": "sample-player-001",
"instant": false,
"instant_downgraded": false,
"machine_id": null,
"no_conf": false,
"player_amount": 100,
"test": true,
"timestamp": 1790172000,
"transaction_id": 0,
"usd_amount": 101.75
}
deposit.frozen has no fields of its own.
The shared decision fields are described under deposit.accepted; every other field under Deposit fields.
transaction.<status>
Fires when the deposit's underlying transaction moves to one of these statuses:
| Event | Status | Meaning |
|---|---|---|
transaction.pending | Pending | The deposit was created and is waiting for payment. |
transaction.succeed | Succeed | The payment was seen on-chain for the quoted amount, not yet confirmed. |
transaction.underpaid | Underpaid | The payment was seen on-chain for less than the quoted amount. |
transaction.overpaid | Overpaid | The payment was seen on-chain for more than the quoted amount. |
transaction.confirmed | Confirmed | The payment has at least one confirmation. |
transaction.timedout | Timedout | No payment was seen by expires_at, 30 minutes after creation. See Expiry and late payments. |
transaction.cancelled | Cancelled | The transaction was cancelled before payment. |
transaction.refund | Refund | The crypto for a returned deposit was sent back on-chain. |
transaction.settled | Settled | The transaction was settled. |
Other statuses, such as ConfirmedKYTReview, produce no transaction.<status> event.
See Deposit lifecycle for every status.
Produced once per status per transaction, and only for the status seen when a worker polls.
A status the transaction passes through between two polls produces no event.
Treat these events as informational and build on the deposit.* events, which are never skipped this way.
How to recognize one: the body has no event field.
It has id and status; the event name is transaction. followed by status in lowercase.
It has no deposit_id. Match id to the transaction_id returned when the deposit was created, or carried on any casino event.
{
"cryptoAmount": 0.00104359,
"entity_external_code": "your-casino-code",
"id": "0",
"receivedCrypto": 0.00104359,
"status": "Confirmed",
"test": true,
"timestamp": 1790172000
}
Before any payment is seen, receivedCrypto is null:
{
"cryptoAmount": 0.00104359,
"entity_external_code": "your-casino-code",
"id": "0",
"receivedCrypto": null,
"status": "Pending",
"test": true,
"timestamp": 1790172000
}
| Field | Type | Description |
|---|---|---|
id | string | The transaction id: the same number as transaction_id on casino events, as a string. |
late_payment | boolean | Present only when true on an event built after a late deposit is restored; absent otherwise. |
status | string | The new status, capitalized as in the table above. |
cryptoAmount | number | The quoted crypto amount, or the final amount after an adjusted accept, at the deposit's quote precision. |
receivedCrypto | number or null | The crypto amount received on-chain, as recorded when the event was built. null when nothing is recorded. |
entity_external_code | string or null | The casino's external code. snake_case, like on casino events. |
timestamp | number | When the event was built, in Unix seconds. |
test | boolean | Present only on test events, and then always true. |