Docs menu
Guides

Bonus and withdrawal checks

Screening the sign-up is not the end of the story: abuse shows up when a bonus is claimed or money is paid out. Call POST /api/v1/events/bonus before you pay a bonus and POST /api/v1/events/withdrawal before you pay a withdrawal, and act on the answer, which has the same shape as /verify.

Last updated

How it works

  1. Screen the sign-up as before, and send your own accountId for the player with it. InlineShield binds the account to that sign-up.
  2. When the player claims a bonus or asks for a withdrawal, your server calls the event endpoint with the accountId, the player's current ip and, for a withdrawal, the payout account as a fingerprint.
  3. InlineShield compares the event with the sign-up, with the account's linked group, with your other accounts that used the same payout account, and with the IP. It answers with a decision, an action, a recommendedAction, a riskScore and the reasons.
  4. Pay, hold or refuse. A held event waits in Review › Held for up to 72 hours, and the outcome reaches your server by webhook or by polling.

The checks add to what the sign-up screening knows. They only hold or advise, with one exception: an event is BLOCKED when the IP and bot checks of your sign-up rules would block it (see Decision and advice).

Bind a player account to its sign-up

The checks work best when InlineShield knows which sign-up an account came from. There are two ways to tell it:

  • Send accountId with the sign-up call: POST /api/v1/verify or POST /api/v1/siteverify. The account is bound to the screening that call recorded.
  • Send signupRequestId (the requestId, AUD-…, of the sign-up) with a bonus or withdrawal event, for an account that is not bound yet.
  • The first binding stays. A later call with the same accountId and another sign-up changes nothing, and so does a signupRequestId on an event for an account that is already bound.
  • An accountId is 1 to 128 printable characters, and not only white space. Anything else gets 400 INVALID_ACCOUNT_ID and nothing is screened.
  • Accounts are yours alone: the same accountId in another InlineShield account is another player. Test keys and live keys have separate accounts, so a test event never meets a live sign-up.

An event for an account with no sign-up bound is still answered. Its reasons say "Sign-up not linked to this account", and nothing that needs the sign-up (its country, its verdict, its linked group) is checked. There is no penalty for that.

Check a bonus claim

Call it before you credit a bonus. Only accountId is required; the more you send, the more it can check.

FieldTypeRequiredDescription
accountIdstringyesYour own id for the player account (1 to 128 printable characters). It is stored as you send it, shown masked in the console (the first 2 characters and ***) and sent back in the webhooks. If the account has a sign-up bound to it (by accountId on /verify or /siteverify, or by signupRequestId below), the check compares the event with that sign-up. An unbound account is still answered, with the reason "Sign-up not linked to this account" and no penalty. Anything else gets 400 INVALID_ACCOUNT_ID.at least 1 character; at most 128 characters
ipstringnoThe player's IP address (IPv4 or IPv6) for this event. Without it the IP checks do not run: the request's own address is your server's, so it is never used. Anything that is not an IP address gets 400 INVALID_IP.
userAgentstringnoThe player's browser User-Agent. Only this field is read; a scripted HTTP client here counts as an automation signal.at most 1024 characters
bonusTypestringnoWhat the player claims, for example welcome or reload. When given, the check for "another account of the same person already claimed this bonus" compares bonuses of this type only; without it any earlier bonus claim counts.at most 40 characters
amountnumbernoThe bonus amount, 0 or more. Stored and shown in the console; it does not change the score.0 to 1000000000000000
currencystringnoThe ISO 4217 code of amount: 3 letters, stored in upper case.at most 3 characters; matches ^[A-Za-z]{3}$
tokenstringnoThe player's sign-up /check token (the one the browser script got at sign-up), for the browser's time zone and automation signals. Usable only within its 10-minute life and only if it was not already redeemed with /siteverify. Never run /check on bonus or cashier pages: that records a new sign-up screening, counts against the quota and creates false links. Without a token the event is screened from server-side data only. The event spends the token: a later /siteverify with the same token answers TOKEN_ALREADY_USED. A token of the other mode (test or live) is left unused, and a token that is unknown, used or expired is ignored.at most 512 characters
signupRequestIdstringnoThe requestId (AUD-…) that the sign-up's /verify, /check or /siteverify returned. It binds the account to that sign-up, but only while the account has none yet: the first binding stays. A sign-up of another account, mode or organisation never binds.at most 128 characters
cURL
curl -X POST https://api.inlineshield.io/api/v1/events/bonus \
  -H "X-Api-Key: $INLINESHIELD_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "accountId": "player-1042",
  "ip": "203.0.113.24",
  "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36",
  "bonusType": "welcome",
  "amount": 50,
  "currency": "EUR"
}'

Every field, answer and error: POST /api/v1/events/bonus.

Check a withdrawal

Call it before you pay a withdrawal. accountId, amount and payout are required.

FieldTypeRequiredDescription
accountIdstringyesYour own id for the player account (1 to 128 printable characters). It is stored as you send it, shown masked in the console (the first 2 characters and ***) and sent back in the webhooks. If the account has a sign-up bound to it (by accountId on /verify or /siteverify, or by signupRequestId below), the check compares the event with that sign-up. An unbound account is still answered, with the reason "Sign-up not linked to this account" and no penalty. Anything else gets 400 INVALID_ACCOUNT_ID.at least 1 character; at most 128 characters
ipstringnoThe player's IP address (IPv4 or IPv6) for this event. Without it the IP checks do not run: the request's own address is your server's, so it is never used. Anything that is not an IP address gets 400 INVALID_IP.
userAgentstringnoThe player's browser User-Agent. Only this field is read; a scripted HTTP client here counts as an automation signal.at most 1024 characters
amountnumberyesThe withdrawal amount, 0 or more. Stored and shown in the console; it does not change the score.0 to 1000000000000000
currencystringnoThe ISO 4217 code of amount: 3 letters, stored in upper case.at most 3 characters; matches ^[A-Za-z]{3}$
payoutobjectyesThe account the money goes to, as a fingerprint, never as the card number, IBAN or wallet itself. A value that looks like a raw card number or IBAN is refused with 400 RAW_PAYMENT_DATA, whatever type says. InlineShield stores only a keyed hash of the fingerprint.
payout.typestringyesWhat kind of account it is. The same fingerprint with another type is another payout account.Values: card, iban, wallet, other
payout.fingerprintstringyesYour payment provider's fingerprint of the account (for example Stripe's fp_…), or a value you compute yourself with a secret key. 16 to 128 characters of A-Z, a-z, 0-9, _, -, : and .. It must be the same string every time the same account is used.at least 16 characters; at most 128 characters; matches ^[A-Za-z0-9_\-:.]+$
tokenstringnoThe player's sign-up /check token (the one the browser script got at sign-up), for the browser's time zone and automation signals. Usable only within its 10-minute life and only if it was not already redeemed with /siteverify. Never run /check on bonus or cashier pages: that records a new sign-up screening, counts against the quota and creates false links. Without a token the event is screened from server-side data only. The event spends the token: a later /siteverify with the same token answers TOKEN_ALREADY_USED. A token of the other mode (test or live) is left unused, and a token that is unknown, used or expired is ignored.at most 512 characters
signupRequestIdstringnoThe requestId (AUD-…) that the sign-up's /verify, /check or /siteverify returned. It binds the account to that sign-up, but only while the account has none yet: the first binding stays. A sign-up of another account, mode or organisation never binds.at most 128 characters
cURL
curl -X POST https://api.inlineshield.io/api/v1/events/withdrawal \
  -H "X-Api-Key: $INLINESHIELD_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "accountId": "player-2087",
  "ip": "203.0.113.24",
  "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36",
  "amount": 120,
  "currency": "EUR",
  "payout": {
    "type": "wallet",
    "fingerprint": "3f9a1c7e5b2d48a0b6c1e9d4f7a28b35"
  }
}'

Every field, answer and error: POST /api/v1/events/withdrawal.

Limits on the text fields, as in the API reference: userAgent 1,024 characters, bonusType 40, currency 3, token 512, signupRequestId 128, accountId 128. A longer value gets 400 BAD_REQUEST (INVALID_ACCOUNT_ID for accountId) before anything is screened, stored or counted.

The payout fingerprint and the no-raw-data rule

InlineShield never receives the card number, IBAN or wallet address of a payout account, and refuses it when it is sent. You send a fingerprint of the account instead: your payment provider's fingerprint of the card or bank account (for example Stripe's fp_…), or a value you compute yourself from the account with a secret key that stays with you. It must be the same string every time the same account is used, so that two accounts that pay out to one card are recognised.

  • payout.type is one of card, iban, wallet or other. The same fingerprint under another type is another payout account.
  • payout.fingerprint is 16 to 128 characters of A-Z, a-z, 0-9, _, -, : and .. Anything else gets 400 BAD_REQUEST with field: "payout".

What is refused

A value that looks like raw payment data gets 400 RAW_PAYMENT_DATA, whatever type says. This check comes before the other payout checks, so a card number with spaces is RAW_PAYMENT_DATA, not BAD_REQUEST.

Looks likeRefused when
A card number13 to 19 digits that pass the Luhn check, alone or with the separators space, -, ., _ and : between them. A 16-digit value that fails the Luhn check is not a card number and is accepted as a fingerprint.
An IBANTwo letters, two digits and 11 to 30 letters or digits, after white space is removed and letters are upper-cased (so lower-case IBANs and IBANs with spaces are refused), and its check digits are valid (ISO 7064 mod 97). A value of the same shape with wrong check digits is accepted as a fingerprint.

The refusal message never repeats the value you sent.

What is stored

  • Only a keyed hash: HMAC-SHA256 of your InlineShield account, the payout type and the fingerprint, with a key that only InlineShield's servers hold. The fingerprint itself is never stored or logged.
  • The hash includes your account, so the same fingerprint in two InlineShield accounts gives two different hashes: payout accounts are never compared across customers.
  • The hash is removed from the stored event after 180 days, with the IP address and User-Agent (the default retention period). Payout accounts are compared over the last 180 days.

Computing it with the SDK

With SDK 1.1.0 or later, payoutFingerprint(type, value, secret) computes the fingerprint on your server: a lower-case hex HMAC-SHA256 of type:normalized value, keyed by a secret key of at least 16 bytes that only you hold. A card is reduced to its digits (no expiry), an IBAN loses its white space and is upper-cased, a wallet is trimmed (and lower-cased for a 0x address), and any other type is trimmed. An unknown type or an empty value throws an error (nothing is sent). The card number, IBAN or wallet address never leaves your server, and the result is 64 characters, which InlineShield accepts. Call it on your server only, never in the browser, and keep the secret the same forever: changing it makes every payout account look new.

import { payoutFingerprint } from '@inlineshield/node';

const fingerprint = payoutFingerprint('card', cardNumber, process.env.PAYOUT_FINGERPRINT_SECRET);
The payout fingerprint says nothing about the player's device. It identifies the payout account only.

The answer

Both endpoints answer 200 with the same fields:

FieldTypeRequiredDescription
requestIdstringyesThe event's id, starting with EVT-. Use it for support; the webhooks carry it too.matches ^EVT-[0-9A-F]{16}$
decisionstringyesALLOWED, QUARANTINED (a rule of Settings › Rules held it) or BLOCKED (your sign-up rules would block this IP or client).Values: ALLOWED, QUARANTINED, BLOCKED
actionstringyesWhat to do with the event: ALLOWED → allow (pay), QUARANTINED → hold (do not pay until the review is decided), BLOCKED → block.Values: allow, hold, block
recommendedActionstringyesAdvice next to action. Bonus claims: deny_bonus (do not pay this bonus) when another account of the same person already claimed one and the rule "Recommend deny_bonus" is on, or when the score is 50 or more; else review when the claim is held, else allow. Withdrawals: review when held, deny (do not pay) at a score of 70 or more, else allow. Both: block when the decision is BLOCKED.Values: allow, review, deny_bonus, deny, block
reviewIdstringnoLive holds only (action: "hold"): the review to poll with GET /api/v1/reviews/{reviewId} or wait for in the bonus.approved / bonus.rejected (withdrawal.*) webhook. A held test event has none.matches ^rev_
riskScorenumberyes0 (lowest risk) to 100.0 to 100
reasonsarray of stringyesPlain-language reasons for the result.
triggersarray of stringyesMachine-readable signal codes: EVENT_LINKED_TO_FRAUD, EVENT_COUNTRY_CHANGED, EVENT_DATACENTER_OR_VPN_IP, EVENT_TOR_IP, EVENT_TIMEZONE_MISMATCH, EVENT_BOT_SIGNALS, SIGNUP_WAS_FLAGGED, BONUS_ALREADY_CLAIMED_IN_GROUP, PAYOUT_SHARED, PAYOUT_SHARED_WITH_FRAUD, BONUS_THEN_CASHOUT and WITHDRAWAL_VELOCITY; TEST_MAGIC_ADDRESS on a magic test account id. New codes may be added.
linkedLinked or nullyesThe linked-accounts summary of the account's bound sign-up (see Linked), including the links that a withdrawal with a shared payout account just added (same_payout), or null: no bound sign-up, no links, test mode, or the lookup did not finish within its budget.
linked.groupIdstring or nullyesThe linked-accounts group of this screening, as the console names it. null when there are only possible links (they never form a group). Groups can merge later, and then the merged group keeps one of the ids, so a later screening can report another id for the same people. In rare cases (the links could not be stored) the console has no group with this id.matches ^lg_[A-Za-z0-9_-]{16}$
linked.sizeintegeryesThe number of screenings in the group, this one included. With only possible links: 1 plus the number of possibly linked screenings.at least 1
linked.strengthstringyesstrong when at least one link is strong, otherwise possible.Values: strong, possible
linked.linkedToFraudbooleanyesTrue when at least one strong link goes to a screening that was BLOCKED or that you reported as fraud (feedback, a rejected review or a mark in the console). It is the condition of the Linked-to-fraud rule (Settings › Rules, off by default), and is reported whether the rule is on or off.
  • action: allow (pay), hold (do not pay until the review is decided) or block.
  • The requestId of an event starts with EVT-. Store it with the payment.
  • A live hold carries a reviewId; a held test event does not.

Signals and points

Each signal that fires adds its points and a line to reasons, and its code to triggers. The riskScore is the sum, at most 100.

TriggerApplies toWhenPoints
EVENT_LINKED_TO_FRAUDBothThe sign-up bound to the account is strongly linked to a blocked or reported sign-up (see Linked accounts). This is the linkedToFraud value, whether or not the Linked-to-fraud rule is on.40
EVENT_COUNTRY_CHANGEDBothThe country of the event IP differs from the country of the sign-up IP. Both must be known.15
EVENT_DATACENTER_OR_VPN_IPBothThe event IP is a datacenter (hosting) or VPN address.20
EVENT_TOR_IPBothThe event IP is a Tor exit node. It replaces the line above.30
EVENT_TIMEZONE_MISMATCHBothThe browser time zone from the token is not used in the country of the event IP. Needs a token and an IP.15
EVENT_BOT_SIGNALSBothThe browser behind the token shows headless or automation signals. Needs a token.40
SIGNUP_WAS_FLAGGEDBothThe bound sign-up was held (QUARANTINED), BLOCKED, or allowed without a bonus (allow_no_bonus).15
BONUS_ALREADY_CLAIMED_IN_GROUPBonusAnother account bound to a sign-up of the same linked group already claimed a bonus, of the same bonusType when you send one.40
PAYOUT_SHAREDWithdrawalThe payout account was used by at least one other account of yours in the last 180 days.30, or 35 when 3 or more accounts in total use it (this one included)
PAYOUT_SHARED_WITH_FRAUDWithdrawalOne of those other accounts is bound to a sign-up that was blocked or that you reported as fraud.50
BONUS_THEN_CASHOUTWithdrawalA bonus was claimed on this account less than bonusCashoutHours ago (default 24).15
WITHDRAWAL_VELOCITYWithdrawal3 or more withdrawals from this account in 24 hours, this one included.15
  • A signal whose facts are not known never fires: no IP, no token, no bound sign-up, or a lookup that did not finish in time (see Time budget).
  • Unbound accounts count toward PAYOUT_SHARED: an account that never had a sign-up bound still counts as another account using the payout account. PAYOUT_SHARED_WITH_FRAUD and the same_payout links can only come from accounts bound to a sign-up.
  • Only the ip in the body is used. The address of the request is your server's, so without ip the IP signals (country, datacenter, VPN, Tor, time zone) are unknown.

Decision and advice

decisionactionWhen
ALLOWEDallowNo hold rule fired.
QUARANTINEDholdA hold rule of Settings › Rules fired. A live hold creates a review.
BLOCKEDblockThe IP and bot checks of your sign-up rules would block this event: a Tor exit node, a datacenter IP, or a headless or scripted client, as far as your sign-up rules block them. The bonus and withdrawal rules themselves never block.

BLOCKED comes first: an event that your sign-up rules would block is BLOCKED even if a hold rule also fired.

recommendedActionBonus claimWithdrawal
allowNot held, score below the deny level.Not held, score below the deny level.
reviewHeld.Held.
deny_bonusAnother account of the same person already claimed this bonus and the rule below is on, or the score is 50 or more. Do not pay this bonus.Never.
denyNever.Not held, and the score is 70 or more. Do not pay without a manual check.
blockBLOCKED.BLOCKED.

A held bonus claim with a score of 50 or more is deny_bonus, not review. Read recommendedAction as well as action: an ALLOWED event can still say deny_bonus or deny.

Rules

In the console, Settings › Rules › Bonus and withdrawal checks has five switches and one number. They hold or advise; nothing is denied automatically.

SettingApplies toDefaultEffect
holdPayoutSharedWithFraud: Hold a withdrawal whose payout account is shared with a blocked or reported accountWithdrawalOnHolds the withdrawal when PAYOUT_SHARED_WITH_FRAUD fires.
holdPayoutSharedMulti: Hold a withdrawal whose payout account is shared with 2 or more other accountsWithdrawalOnHolds the withdrawal when the payout account was used by 2 or more other accounts.
adviseDenyRepeatBonus: Recommend deny_bonus when the same person already claimed a bonus (linked group)BonusOnSets recommendedAction to deny_bonus when BONUS_ALREADY_CLAIMED_IN_GROUP fires. Advice only: it never holds the claim.
holdBonusCashout: Hold a withdrawal within bonusCashoutHours of a bonus claimWithdrawalOffHolds the withdrawal when BONUS_THEN_CASHOUT fires.
holdEventCountryChange: Hold an event when the country changed since sign-upBothOffHolds the event when EVENT_COUNTRY_CHANGED fires.
bonusCashoutHoursWithdrawal24The window of BONUS_THEN_CASHOUT, in hours: a whole number from 1 to 720.

A stored change applies to the next event. An account without stored rules uses the defaults above.

Time budget

The lookups of one event (your rules, the bound sign-up and its links, the token's browser signals, the account's history, the other accounts of the payout account, the bonus claims of the linked group) start together and share one budget of 150 ms. A lookup that fails or has not finished by then is treated as unknown: it never fires a signal, and the reasons say "Some checks did not finish in time; this answer uses what was known."

Binding the account to signupRequestId happens before that clock starts, and the event is stored after it, so a slow lookup never costs you the record.

The token from /check

token is optional. It is the token that /check (the browser script on your sign-up page) gave this player at sign-up, and it lends the event that check's browser signals: the time zone and the automation hints. Without a token the event is screened from server-side data only (the body, the bound sign-up and the account's history), and those two signals are unknown.

  • Only the sign-up token works. It can be used only within its 10-minute life, and only if your backend has not already redeemed it with /siteverify. In practice that means an event sent right after sign-up, for example a welcome bonus claimed at once.
  • Never run /check on your bonus or cashier pages. Each /check records a new sign-up screening: it counts against your quota and creates false links between the player and their own earlier sign-up.
  • An event spends the token. The unused /check token of your account is redeemed by the event, so a later POST /api/v1/siteverify with the same token answers success: false with TOKEN_ALREADY_USED. If you need both, call /siteverify first and screen the event without the token.
  • A token of the other mode (a test token with a live key, or the other way round) is left unused and ignored. An unknown, expired or already used token is ignored too: the event is still answered.
  • A token is single-use for 10 minutes, as for /siteverify.

Retries and timeouts

  • A retried request is a second event. There is no idempotency key: a request you send again is screened and stored again, counts again against your quota, and counts as another withdrawal toward WITHDRAWAL_VELOCITY. Do not retry after a timeout; wait for the webhook instead.
  • After a timeout, the webhook decides. If the SDK timed out and failed open (you let the claim or withdrawal through), a later bonus.held or withdrawal.held webhook for that event is authoritative: the event was held, so stop or reverse the payout and wait for its *.approved or *.rejected.

Held events and review

  • A held live event has a reviewId (rev_…). It creates one review, counted once, and sends one bonus.held or withdrawal.held webhook.
  • It waits in the console under Review › Held, next to held sign-ups, with a Bonus claim or Withdrawal badge, the amount, the reasons and the account id masked to its first 2 characters and ***.
  • GET /api/v1/reviews/{reviewId} answers for both kinds, with the same fields: GET /api/v1/reviews/{reviewId}.
  • A review nobody decides within 72 hours is rejected with decisionReason: "expired". No label is written for that.
  • Rejecting marks the event's sign-up as fraud. Approving marks it legit, unless the sign-up was blocked or already carries a fraud mark from any source: an approval never clears one, whether it is the approval of an event or of the held sign-up itself. An event whose account has no sign-up bound writes no label.

See Hold and review for the flow.

Webhooks

Eight events: bonus.allowed, bonus.held, bonus.approved, bonus.rejected and withdrawal.allowed, withdrawal.held, withdrawal.approved, withdrawal.rejected. They are signed and retried like the others: see Webhooks.

  • They go to every enabled endpoint, whatever its subscriptions. An endpoint that subscribes to the registration events only receives them too.
  • *.allowed and *.held are sent when the event is screened (live events only). Nothing is sent for a BLOCKED event. *.approved and *.rejected are sent when the review is decided or expires.
  • Deliveries can arrive in any order. Each delivery is sent and retried on its own, so bonus.approved or withdrawal.approved can arrive before the *.held of the same event. Key your handler on reviewId and requestId, not on the order.
  • The payload carries your own accountId, the amount and the reasons. It has no IP address and no User-Agent.

bonus.allowed, bonus.held, withdrawal.allowed, withdrawal.held: data

FieldTypeRequiredDescription
eventstringyesValues: bonus.allowed, bonus.held, withdrawal.allowed, withdrawal.held
requestIdstringyesThe requestId the event's answer carried.matches ^EVT-[0-9A-F]{16}$
reviewIdstringnoHeld events only.matches ^rev_
accountIdstringyesThe accountId you sent.
kindstringyesValues: bonus, withdrawal
decisionstringyesNever BLOCKED: a blocked event sends no webhook.Values: ALLOWED, QUARANTINED
actionstringyesallow for an allowed event, hold for a held one.Values: allow, hold
recommendedActionstringyesAs in the answer.Values: allow, review, deny_bonus, deny
riskScorenumberyes0 (lowest risk) to 100.0 to 100
reasonsarray of stringyesPlain-language reasons for the result.
triggersarray of stringyesThe signal codes of the answer (see the answer of the event endpoints).
amountnumber or nullyesThe amount you sent, or null.
currencystring or nullyesThe currency you sent, in upper case, or null.
linkedLinked or nullyesThe linked-accounts summary, as in the answer.
linked.groupIdstring or nullyesThe linked-accounts group of this screening, as the console names it. null when there are only possible links (they never form a group). Groups can merge later, and then the merged group keeps one of the ids, so a later screening can report another id for the same people. In rare cases (the links could not be stored) the console has no group with this id.matches ^lg_[A-Za-z0-9_-]{16}$
linked.sizeintegeryesThe number of screenings in the group, this one included. With only possible links: 1 plus the number of possibly linked screenings.at least 1
linked.strengthstringyesstrong when at least one link is strong, otherwise possible.Values: strong, possible
linked.linkedToFraudbooleanyesTrue when at least one strong link goes to a screening that was BLOCKED or that you reported as fraud (feedback, a rejected review or a mark in the console). It is the condition of the Linked-to-fraud rule (Settings › Rules, off by default), and is reported whether the rule is on or off.

bonus.approved, bonus.rejected, withdrawal.approved, withdrawal.rejected: data

FieldTypeRequiredDescription
eventstringyesValues: bonus.approved, bonus.rejected, withdrawal.approved, withdrawal.rejected
reviewIdstringyesmatches ^rev_
requestIdstring or nullyesThe requestId (EVT-…) the held event's answer carried.
accountIdstring or nullyesThe accountId you sent.
kindstringyesValues: bonus, withdrawal
statusstringyesValues: approved, rejected
decisionReasonstring or nullyesoperator: decided in the console. expired: nobody decided in time (rejected).Values: operator, expired, null
decidedAtstring (date-time) or nullyes
amountnumber or nullyes
currencystring or nullyes

Test mode

A test key behaves as it does for sign-ups: events are screened and stored (shown with "Show test data"), but they never count against the quota, and they create no review, webhook, label or alert and no linked-accounts links. Test events only meet test accounts. See Test mode.

Two magic account ids give a fixed answer without any lookups, with test keys only. The match is exact, and each applies to its own endpoint; on the other endpoint, or with a live key, they are ordinary account ids.

accountIdEndpointdecisionactionrecommendedActionriskScoretriggers
abuser@test/events/bonusALLOWEDallowdeny_bonus40BONUS_ALREADY_CLAIMED_IN_GROUP, TEST_MAGIC_ADDRESS
payout-shared@test/events/withdrawalQUARANTINEDholdreview35PAYOUT_SHARED, TEST_MAGIC_ADDRESS

The held answer of payout-shared@test has no reviewId: test mode creates no review.

Quota, limits and errors

  • Each live event counts once against your monthly quota, like a /verify screening, and live answers carry the X-Quota-* headers. Over the quota, the event endpoints answer 402 QUOTA_EXCEEDED. Test keys never count.
  • Live events are limited per key, like /siteverify: the key's own per-minute limit (1,200 for a live key as created, 600 for a key without a limit of its own), one bucket for both endpoints. Test keys use a separate bucket of 300 per minute for your account, again for both endpoints together.
  • Event calls with a test key also count toward the daily test cap of 10,000 per UTC day (then 429 TEST_LIMIT), the one that /verify and /check use.
  • If the event was screened but could not be stored you get 500 PERSISTENCE_FAILURE, and nothing is counted. Decide in advance whether to pay or wait; see Errors and HTTP codes.

What InlineShield keeps

  • Each event is stored for your account: your accountId as you sent it, the kind, the amount and currency, the bonusType or payout type, the IP address, User-Agent, the keyed payout hash and the result.
  • After 180 days (the default retention period) the IP address, User-Agent and payout hash are removed from the event; the result stays.
  • The console shows the account id masked. Account ids and payout data are never written to InlineShield's logs.
  • See Data and privacy for everything else.