Skip to content
POST /v1/eventsRequest access
API reference

Load your history

Bring your existing customers and transactions, including the fraud your team has already confirmed. Sieve replays them in order, learns from them, and starts with a probability model calibrated on your own fraud.

Backfill is recorded, never acted on

  • Each event is scored as of when it happened, seeing only the history before it.
  • Rule hits, patterns and decisions are recorded, so rule precision, backtests and the probability model learn from them.
  • No alerts, cases, webhooks, emails or account holds, and no inline screening. Backfilled events are not billed.
  • Re-running an import is safe: events are deduplicated by idempotency_key, or by their content when there isn’t one.
  • History can go back five years (configurable on self-hosted installs).

From your core banking export

Export customers and transactions as CSV (or NDJSON) from your core system or warehouse, T24, Finacle, Flexcube, Mambu or anything else. Describe the columns once in a mapping file, check it with --dry-run, then load.

scripts/import.mjs
# 1. Check the mapping without sending anything
node scripts/import.mjs --file customers.csv --map core-banking-customers.json --dry-run

# 2. Customers first, then their history (oldest first is handled for you)
SIEVE_KEY=sv_live_… node scripts/import.mjs --file customers.csv    --map core-banking-customers.json    --base https://sieve.yourbank.local
SIEVE_KEY=sv_live_… node scripts/import.mjs --file transactions.csv --map core-banking-transactions.json --base https://sieve.yourbank.local
core-banking-transactions.json
{
  "type": { "column": "TXN_CODE", "map": { "WDL": "payout.requested", "*": "transaction" } },
  "fields": {
    "customer_id": "CUSTOMER_NO",
    "idempotency_key": "TXN_REF",
    "occurred_at": { "column": "BOOKING_DATE", "format": "DD-MMM-YYYY HH:mm:ss" },
    "amount":      { "column": "AMOUNT_LCY", "number": true, "abs": true },
    "direction":   { "column": "DR_CR", "map": { "D": "out", "C": "in" } },
    "destination.account": { "column": "CONTRA_ACCOUNT", "when": { "column": "TXN_CODE", "in": ["WDL"] } }
  },
  "label": { "column": "FRAUD_FLAG", "map": { "Y": "fraud", "N": null } },
  "skip":  { "column": "RECORD_STATUS", "in": ["REVERSED"] }
}

Dates may be ISO 8601, epoch, or a pattern such as DD-MMM-YYYY; times without a zone are read in East Africa Time unless you pass --tz (e.g. --tz +01:00, West Africa Time, for Nigerian exports). Map your fraud flag to label: every "fraud" row becomes a confirmed verdict. Example mappings ship in scripts/import-examples/.

The batch endpoint

The script calls POST /v1/batch, which you can also call from your own ETL: up to 2,000 events per request, processed oldest first. mode: "live" instead treats the batch exactly like individual live events, which suits end-of-day files.

POST /v1/batch
curl https://sieve.yourbank.local/v1/batch \
  -H "Authorization: Bearer $SIEVE_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "mode": "backfill",
    "events": [
      { "type": "transaction", "customer_id": "1002005", "occurred_at": "2026-02-11T08:14:00+03:00",
        "amount": 42000, "currency": "KES", "direction": "in", "method": "mpesa", "idempotency_key": "FT100231" },
      { "type": "payout.requested", "customer_id": "1002005", "occurred_at": "2026-02-11T14:02:00+03:00",
        "amount": 212000, "balance": 213900, "destination": { "type": "mpesa", "account": "254700999111" },
        "idempotency_key": "FT100238", "label": "fraud" }
    ]
  }'

The response reports how many events were accepted, already loaded or rejected, with a reason for each rejected row, and how the history would have been decided. The probability model refits automatically once the import pauses.