Events reference
POST /v1/events: one endpoint, six event types. Only type and customer_id are required; every extra field is a signal.
| Type | Send it when… | Key fields |
|---|---|---|
customer.created | Someone opens an account | customer (name, phone, email, national_id, bvn, dob, nationality, kyc_level) |
customer.updated | They change phone, email, payout account, password, PIN, name, BVN… | changes (required), updated customer fields, destination |
login | Every login attempt, success or failure | login.success, device, location |
transaction | Money moves in or out | direction, amount, method, counterparty, status, asset (crypto) |
payout.requested | Before you release a withdrawal | amount, destination, balance, asset (crypto) |
sim.swap | Your SIM-swap check reports a swap: in Kenya, e.g. Safaricom’s; in Nigeria, the signal your mobile operators or aggregator provide | sim_swapped_at |
Common fields
occurred_at | ISO 8601. Defaults to now. Send it when backfilling history. |
amount | A number in major units: 48000 means KES 48,000 on a Kenyan workspace, 350000 means NGN 350,000 on a Nigerian one. |
currency | ISO 4217. Defaults to your workspace currency. Other currencies are converted to your workspace currency for scoring; the original amount and the rate used are kept and returned in amount. |
counterparty | { id?, name?, phone?, account?, country?, wallet? }: the other side. country (ISO-2) powers the cross-border rules; wallet is the other side’s crypto address (e.g. the source of a deposit). |
method | mpesa, airtel, opay, palmpay, moniepoint, card, bank (bank transfers, including NIP in Nigeria), ussd, pos (a card payment or cash-out at a POS agent), cash, agent, wallet, crypto, other |
asset | Crypto only, optional: { code, amount, network? }, the on-chain amount. amount and currency stay the fiat value. See Crypto withdrawals and deposits. |
destination | { type, account, bank_code?, name?, country?, network? }. Type is mpesa, airtel, opay, palmpay, moniepoint, bank, card, wallet, paybill, till, crypto_wallet or other. bank_code is the bank’s code, e.g. a CBN/NIBSS code such as 058: NUBAN account numbers are only unique within a bank, so send it for Nigerian bank payouts. Account numbers are stored only as keyed hashes. |
customer.bvn | Nigeria, optional: the 11-digit Bank Verification Number. Like national_id (the ID card number in Kenya, the NIN in Nigeria), it links customers who share an identity and feeds the network checks. Stored encrypted. |
changes | For customer.updated: any of phone, email, payout_destination, name, password, pin, national_id, bvn, dob, nationality. |
customer.phone | Local and international forms are folded into one number: Kenyan 07…, 2547… and +2547… are the same number, as are Nigerian 0803…, 234803…, +234803… and +234 0803…. |
device | { id, ip }: any stable device identifier your app has. |
location | { lat, lng }. Powers impossible-travel and far-from-home. |
travel_rule | Crypto only: originator and beneficiary details, stored encrypted. See Crypto & cross-border. |
metadata | Up to 20 of your own keys, shown in the dashboard. |
Crypto withdrawals and deposits
Send crypto movements as ordinary transaction and payout.requested events with method: "crypto". Keep amount and currency as the fiat value (e.g. KES or NGN) so every rule compares like with like, and put the on-chain amount in asset. The asset is stored with the event.
| Field | Type | Description |
|---|---|---|
asset.code | string, required | The ticker: BTC, ETH, USDT… 2–10 letters or digits, uppercased for you. |
asset.amount | number, required | The on-chain amount at full precision, e.g. 0.0153421. Must be positive. |
asset.network | string, optional | The chain it moves on, e.g. bitcoin, ethereum, tron. |
destination.type | crypto_wallet | With the wallet address as account and the chain as network. Addresses are validated (checksums included) and given one canonical spelling, so the same wallet always matches. |
counterparty.wallet | string, optional | For deposits: the wallet the funds came from. |
// A customer withdraws 350.25 USDT on Tron to an exchange-hosted wallet. const d = await sieve.events.create({ type: 'payout.requested', customer_id: 'cus_1001', // The fiat value: every rule scores this (converted to your workspace currency if needed). amount: 350.25, currency: 'USD', method: 'crypto', // The on-chain amount, at full precision. asset: { code: 'USDT', amount: 350.25, network: 'tron' }, destination: { type: 'crypto_wallet', account: 'TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t', network: 'tron', country: 'AE' }, balance: 360, // FATF Travel Rule data (stored encrypted). Required above the threshold. travel_rule: { originator: { name: 'Wanjiru Kamau', account: 'cus_1001', country: 'KE' }, beneficiary: { name: 'Wanjiru Kamau', address: 'TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t' }, beneficiary_vasp: 'Example Exchange FZE', }, }) // d.wallet → { chain: 'tron', valid: true, network_matches: true, sanctioned: false, … } // Deposits are transactions with the same shape; the source wallet goes in counterparty.wallet. await sieve.events.create({ type: 'transaction', customer_id: 'cus_1001', direction: 'in', amount: 128000, currency: 'KES', method: 'crypto', asset: { code: 'BTC', amount: 0.0153421, network: 'bitcoin' }, counterparty: { wallet: 'bc1qar0srrr7xfkvy5l643lydnw9re59gtzzwf5mdq' }, })
Idempotency
Send an Idempotency-Key header (or idempotency_key field). Retrying with the same key returns the original decision instead of counting the event twice, so it’s safe to retry on timeouts.
Other endpoints
const c = await sieve.customers.get('cus_1001') // { status: "held", risk_level: "high", screening: { status: "clear", last_screened_at: "…" }, open_cases: [...] }
// Tell Sieve when it was right or wrong: this sharpens every rule. await sieve.feedback({ event_id: 'evt_W79r7Mh2ywQkhm8baqHr', label: 'fraud' }) // or 'legit' // Late evidence counts too: a chargeback or complaint weeks later overrides an earlier 'legit'. await sieve.feedback({ event_id: 'evt_W79r7Mh2ywQkhm8baqHr', label: 'fraud', source: 'chargeback' })
Errors & limits
Errors are JSON: { "error": "invalid_request", "message": "amount: is required for transaction", "details": [...] }. Invalid keys get 401. The default rate limit is 1,200 requests per minute per key; headers X-RateLimit-Remaining and Retry-After tell you where you stand.