← datatap.org

API Documentation

Read your surveillance data, push your market data, and mirror alerts into your SIEM or ticketing system. All endpoints are tenant-scoped by API key; dispositions and approvals always happen inside DataTap.

Authentication

Create keys at Portal → Admin → API keys (shown once, revocable). Send as a bearer token:

curl -H "Authorization: Bearer dtk_..." https://datatap.org/api/v1/cases

Rate limit: 120 requests/minute per key. Unauthorized → 401, throttled → 429.

Read endpoints

EndpointReturns
GET /api/v1/casesCases with dispositions, SLA state, WORM timelines, linked accounts/clusters
GET /api/v1/alertsAlerts with detection facts, narratives, triage state. Filters: ?since=YYYY-MM-DD, ?pattern=, ?status=; pagination ?limit= (≤500) and ?cursor= from the previous page's next_cursor. Cases take ?type= instead of ?pattern=.
POST /api/v1/pretrade/check/batchUp to 200 checks in one call ({"checks": [ ... ]}, each with an optional ref); tenant state loads once, every result carries elapsed_us and over_budget against the 50ms per-check budget.
POST /api/v1/events · quality gateA file whose first 2,000-row sample rejects more than ingest_config.max_reject_pct (default 25%) is quarantined: 422 {"quarantined": true, ...}, nothing written, the Idempotency-Key not consumed — fix the mapping and resend with the same key. ingest_config.px_divisor scales prices exported in cents.
GET /api/v1/packagesAudit packages with hashed manifests, chain hashes, release records

Write endpoint · event ingestion

POST /api/v1/events — JSON-lines body (one object per line, same fields as the drop-copy schema). Send an Idempotency-Key header: retries with the same key return the original validation report without re-ingesting.

curl -X POST https://datatap.org/api/v1/events \
  -H "Authorization: Bearer dtk_..." \
  -H "Idempotency-Key: batch-2026-09-08-001" \
  --data-binary @- <<'EOF'
{"kind":"trade","ts_ms":1200,"market":"CHIEFS-WIN-SB","account":"ACCT-1","side":"buy","px":0.58,"qty":4000}
{"kind":"settlement","ts_ms":86400000,"market":"CHIEFS-WIN-SB","account":"ACCT-1","outcome":"win"}
EOF

Response: {"accepted": n, "rejected": [[line, reason], ...], "markets": [...], "replayed": bool} — rows are accepted whole or rejected whole; accepted events are append-only (WORM posture).

Control endpoint · pre-trade check

POST /api/v1/pretrade/check (needs the ingest scope) — JSON body {"account":"ACCT-1","market":"CHIEFS-WIN-SB","side":"buy","px":0.58,"qty":4000,"yes_px":0.55,"no_px":0.48,"participant":"MM-1"}. Returns decision (allow/block) and every check with its result, detail, and regulatory citation: restricted person (account or resolved cluster), $0.01–$0.99 price bounds, YES+NO parity, cluster position headroom against the tenant's det_cluster_limit, and participant kill-switch state. Wire it into your gateway and the controls the portal shows become the controls your matching engine enforces.

Endpoint index

Every route the API serves, from the same table that generates /api/v1/openapi.json (OpenAPI 3.0, public, import it into Postman or your gateway). Scope: read for GETs, ingest for POSTs.

EndpointScopeWhat it does
GET /api/v1/cases read Cases with dispositions, SLA state, WORM timelines, linked accounts and clusters
GET /api/v1/cases/{id} read One case by id (CASE-/INV- code) with its full timeline, evidence refs and review state
GET /api/v1/alerts read Alerts with detection facts, narratives, threshold distance and triage state
GET /api/v1/alerts/{id} read One alert by id (ALT- code) including its narrative, facts, recurrence and dismissal
GET /api/v1/packages read Audit packages with hashed manifests, chain hashes and release records
GET /api/v1/audit read Admin actions and regulator/demo access views since a cursor (SIEM pull)
GET /api/v1/audit/verify read Walk the hash-chained admin and access ledgers; reports the first broken row, if any
POST /api/v1/events ingest JSON-lines market events (trade/book/settlement/catalyst); Idempotency-Key required; ?dry_run=true validates only
POST /api/v1/accounts ingest JSON-lines account/KYC rows: account, identity, funding, device, subnet, address, restricted, state, country
GET /api/v1/wallets read Wallet↔account map on file with screening flags
POST /api/v1/wallets ingest JSON-lines wallet rows: address, account, chain, flag (clear|mixer-exposure|sanctioned|watch)
GET /api/v1/participants read Market-maker participants (desks, scopes, obligations)
POST /api/v1/participants ingest Create a participant: code, name, scope, trigger_rule
GET /api/v1/rlists read Restricted-list sources with counts and versions
POST /api/v1/rlists ingest Upload a restricted list (CSV body) under a named source
GET /api/v1/markets read Market schedule (open/close/settle) and correlated groups
POST /api/v1/markets/schedule ingest Upsert market schedule rows: {rows: [{market, settle_at, open_at?, close_at?}]}
POST /api/v1/markets/groups ingest Replace correlated-market groups: {groups: {name: [markets]}}
POST /api/v1/uploads/presign ingest Presigned direct-to-bucket upload URL for large backfills
POST /api/v1/ingest/from-bucket ingest Ingest an object already in the drop bucket by key
POST /api/v1/pretrade/check ingest One pre-trade check: allow/block with every rule cited
POST /api/v1/pretrade/check/batch ingest Up to 200 pre-trade checks in one call; per-check elapsed time and budget flag
GET /api/v1/pretrade/decisions read Recorded pre-trade decisions, newest first: rules hit, checks, restricted-list version, latency
GET /api/v1/openapi.json public This document

Webhooks

Register endpoints at Admin → outbound webhooks (https only; signing secret shown once). Each endpoint subscribes to the events it wants — none selected means every event. Every payload is {"event", "at", "data"}; Slack-format endpoints get a Block Kit card instead. Deliveries retry 3× with backoff and are visible (with manual retry) on the Admin screen.

EventFires when · data
alert.created a scan or intraday sweep raised alerts (ids, patterns, subjects, severities)
case.opened a case or investigation was opened (case_id, type, sev, subject, origin)
case.escalated a case was escalated to supervisors (case_id, reason)
case.disposition a disposition was approved (case_id, disposition, approved_by)
sla.breached a 38.156 review clock ran out before disposition (case_id, sla_deadline)
killswitch.triggered a participant kill switch was triggered or re-armed (participant, state, by)
guardrail.changed a control threshold change was approved under four-eyes (rule, old, new, by, approved_by)
package.released an audit package was released (package_id, channel, recipient, approved_by)
ingest.completed an upload or API batch was accepted (source, accepted, rejected, markets, warnings)
ingest.quarantined an upload failed the quality gate and nothing was written (source, rejected, warnings)

Wire ingest.quarantined to the pager that owns your export job, sla.breached to the compliance on-call, killswitch.triggered to the risk desk.

Verifying evidence offline

Every audit-package ZIP carries manifest.json (per-file SHA-256 and the package chain hash). datatap-verify.py recomputes both from the ZIP with the Python standard library and prints PASS/FAIL per file — regulators and counsel can verify a production without an account. GET /api/v1/audit/verify walks the hash-chained admin and access ledgers the same way.

Verify the signature — x-datatap-signature: sha256=HMAC_SHA256(secret, raw_body):

expected = "sha256=" + hex(hmac_sha256(secret, raw_body))
constant_time_compare(expected, request.headers["x-datatap-signature"])

Questions or a format we should support? Get in touch · Data & Usage Guide