Compliance APIs for emerging-market commodity trade.

REST API reference. All endpoints under /api/v1/* with consistent { ok, data | error } shape. 29 endpoints across 11 SKU surfaces. Sandbox keys in 60 seconds; production tier via Stripe + sales-led enterprise.

Running a compliance agent? Paste the block below into its instructions. The pre-screen needs no key and no account.

OilFlow — counterparty compliance for physical-commodity trade.

Free fraud-cluster pre-screen (no key, no account):
  curl -sX POST https://oilflow.us/api/public/check \
    -H 'Content-Type: application/json' \
    -d '{"query":"<counterparty name>"}'
  10 requests/hour per IP. "email" is optional; omit it to stay anonymous.

Model Context Protocol (no install, no key for 4 tools):
  https://oilflow.us/api/mcp
  claude mcp add --transport http oilflow https://oilflow.us/api/mcp
  Keyless tools: cluster_check, predeal_preview (5/day/IP),
                 verify_receipt, request_sandbox_key

Machine-readable API spec:
  https://oilflow.us/openapi.yaml   (auth: Authorization: Bearer <key>)

Full 7-step dossier, $25, no account:
  https://oilflow.us/dossier

Coverage limits — state these to the user, they change the answer:
  - 8 sanctions lists: OFAC SDN, OFAC Consolidated, UN, EU, UK HMT,
    Canada SEMA, AU DFAT, Swiss SECO.
  - PEP screening is NOT shipped. Coverage is sanctions-only: a
    politically exposed person who is not also sanctioned is not flagged.
  - No functioning US domestic UBO registry post-March-2025, so
    beneficial-ownership coverage is partial and every result says so.
  - The free check queries the fraud-cluster registry only. It is an
    honest pre-screen, not a regulatory KYC.

The same index, in the format agents fetch by convention, is at /llms.txt.

Provide an API key on every request via Authorization: Bearer <key> or X-Api-Key header. Sandbox keys are read-only; production keys carry per-scope authorization.

Get a sandbox key at /sandbox or production access via [email protected].

GET · regulatory + clusters/check60–120 req / 60s
POST · kyc screen + rescreen30 req / 60s
POST · ubo / lc / reports10 req / 60s
GET · audit summary60 req / 60s
Sandbox keys (hard cap)100 calls / day

POST ubo / lc / reports limits are lower because each call runs heavy synthesis. Production tier quotas raise these per SKU; see /pricing.

Regulatory Matrix API · BETA

/api/v1/regulatory/*

GET

/api/v1/regulatory/countries

List all jurisdictions

Returns the 235 jurisdictions covered by the OilFlow regulatory matrix, each with the full per-product rule table (allowed / restricted / blocked + contextual notes).

Example request

curl -H "Authorization: Bearer <key>" \
  https://oilflow.us/api/v1/regulatory/countries

Example response

{
  "ok": true,
  "data": {
    "count": 235,
    "version": "v1-beta",
    "countries": [
      {
        "slug": "algeria",
        "country": "Algeria",
        "rules": {
          "crude":   { "status": "restricted", "notes": "Sonatrach controls upstream..." },
          "refined": { "status": "restricted", "notes": "Sonatrach controls refining..." }
        }
      }
    ]
  }
}
  • Cached at edge for 1 hour (ISR).
GET

/api/v1/regulatory/countries/:slug

Get jurisdiction detail

Returns the full rule set for a single jurisdiction. The slug is the lowercase, hyphenated country name (e.g. 'pakistan', 'saudi-arabia').

Parameters

  • slugstring (path)required

    Lowercase, hyphenated country name.

Example request

curl -H "Authorization: Bearer <key>" \
  https://oilflow.us/api/v1/regulatory/countries/pakistan

Example response

{
  "ok": true,
  "data": {
    "slug": "pakistan",
    "country": "Pakistan",
    "rules": {
      "crude":   { "status": "restricted", "notes": "..." },
      "refined": { "status": "allowed",    "notes": "..." }
    }
  }
}
GET

/api/v1/regulatory/check

Run a tradability check

Quick tradability check for a (country, product, listing_type) tuple. Returns whether the deal would be allowed and the specific blockers if not.

Parameters

  • countrystringrequired

    Country name (full or ISO-2).

  • productstringrequired

    Product label or category.

  • listing_type'supply' | 'demand'

    Default: 'demand'.

Example request

curl -H "Authorization: Bearer <key>" \
  "https://oilflow.us/api/v1/regulatory/check?country=Kenya&product=crude&listing_type=demand"

Example response

{
  "ok": true,
  "data": {
    "country": "Kenya",
    "product": "crude",
    "allowed": false,
    "blockers": [{ "reason": "Private buyers in Kenya cannot independently import crude oil (OTS-only)." }]
  }
}
GET

/api/v1/regulatory/products

List product categories

Normalized product categories used by the matrix and accepted by /check.

Example request

curl -H "Authorization: Bearer <key>" \
  https://oilflow.us/api/v1/regulatory/products

Example response

{
  "ok": true,
  "data": {
    "count": 5,
    "products": [
      { "category": "crude",   "label": "Crude oil" },
      { "category": "refined", "label": "Refined products" },
      { "category": "lpg",     "label": "LPG" },
      { "category": "lng",     "label": "LNG" },
      { "category": "bitumen", "label": "Bitumen" }
    ]
  }
}

Counterparty KYC-as-API · EARLY ACCESS

/api/v1/kyc/*

POST

/api/v1/kyc/screen

Screen a counterparty

Run the 7-step KYC pipeline on a counterparty. Synchronous v0 checks: scam-cluster blocklist + regulatory tradability. Returns a screening_run_id; subscribe via Supabase realtime to stream per-step events.

Request body

  • company_namestringrequired

    Counterparty legal name.

  • countrystring

    Jurisdiction for the regulatory check.

  • productstring

    Product for the regulatory check.

  • directorsstring[]

    Names of directors to cross-check against the blocklist.

  • metadataobject

    Free-form metadata for your records.

Example request

curl -X POST -H "Authorization: Bearer <key>" \
  -H "Content-Type: application/json" \
  -d '{"company_name":"SSW Kafcima","country":"Singapore","directors":["Simar Chahal"]}' \
  https://oilflow.us/api/v1/kyc/screen

Example response

{
  "ok": true,
  "data": {
    "screening_run_id": "00000000-...",
    "verdict": "fail",
    "verdict_reasoning": "Confirmed cluster blocklist match: Simar Chahal.",
    "checks": { "cluster_blocklist": { "status": "fail" }, "regulatory_tradability": { "status": "pass" } }
  }
}
  • verdict='fail' on confirmed cluster match OR regulatory tradability fail.
  • Subscribe to realtime channel `pipeline:{screening_run_id}` for live step events.
POST

/api/v1/kyc/rescreen

Queue a re-screen for a known entity

Asynchronously re-runs the full 7-step pipeline on a previously-screened entity. Returns 202 with a new screening_run_id. Continuous re-screen fires automatically every 90 days; this endpoint is for ad-hoc re-runs.

Request body

  • entity_iduuidrequired

    ID from a prior /api/v1/kyc/screen run.

Example request

curl -X POST -H "Authorization: Bearer <key>" \
  -H "Content-Type: application/json" \
  -d '{"entity_id":"00000000-0000-0000-0000-000000000001"}' \
  https://oilflow.us/api/v1/kyc/rescreen

Example response

{ "ok": true, "data": { "screening_run_id": "...", "status": "queued", "queued_at": "2026-06-02T..." } }
  • 202 Accepted. Subscribe to webhook event `kyc.rescreen_completed` for the final verdict.

Scam Cluster Intelligence Feed · EARLY ACCESS

/api/v1/clusters/*

GET

/api/v1/clusters

List clusters

All verified-fraudulent counterparty clusters from the OilFlow scam blocklist. Filterable by severity, country, and timestamp.

Parameters

  • severity'confirmed' | 'likely' | 'suspected'

    Filter by severity tier.

  • countrystring

    Filter by entity country.

  • sinceISO timestamp

    Only return clusters added after this time.

Example request

curl -H "Authorization: Bearer <key>" \
  "https://oilflow.us/api/v1/clusters?severity=confirmed"

Example response

{ "ok": true, "data": { "count": 1, "clusters": [{ "entity_name": "Simar Chahal", "severity": "confirmed", ... }] } }
GET

/api/v1/clusters/check

Check an entity

Fast lookup: is this entity in the cluster blocklist? Integrate into intake to short-circuit obvious frauds before full KYC.

Parameters

  • entitystringrequired

    Entity name to check.

  • countrystring

    Filter to matches in this country.

Example request

curl -H "Authorization: Bearer <key>" \
  "https://oilflow.us/api/v1/clusters/check?entity=Simar%20Chahal"

Example response

{ "ok": true, "data": { "matched": true, "matches": [{ "severity": "confirmed", ... }], "highest_severity": "confirmed" } }

Webhooks · LIVE

/api/v1/webhooks/*

GET

/api/v1/webhooks

List webhook subscriptions

Returns the caller's active subscriptions plus the catalog of valid event types.

Example request

curl -H "Authorization: Bearer <key>" https://oilflow.us/api/v1/webhooks

Example response

{ "ok": true, "data": { "subscriptions": [...], "valid_events": ["kyc.match_detected", "cluster.entity_added", "watchlist.match_detected", ...] } }
POST

/api/v1/webhooks

Create a subscription

Subscribe a URL to one or more event types. HMAC-signed delivery with exponential-backoff retry and DLQ. delivery_format='slack'|'teams' renders payloads as Block Kit / Adaptive Card; 'raw' is the default HMAC JSON.

Request body

  • urlstringrequired

    HTTPS URL to POST events to (or a Slack/Teams incoming-webhook URL).

  • eventsstring[]required

    Event types to subscribe to. See GET /webhooks for the catalog.

  • delivery_format'raw' | 'slack' | 'teams'

    Default: 'raw' (HMAC-signed JSON).

  • descriptionstring

    Human-readable label.

Example request

curl -X POST -H "Authorization: Bearer <key>" \
  -H "Content-Type: application/json" \
  -d '{"url":"https://hooks.slack.com/services/...","events":["kyc.match_detected","cluster.entity_added"],"delivery_format":"slack"}' \
  https://oilflow.us/api/v1/webhooks

Example response

{ "ok": true, "data": { "id": "wh_...", "secret_preview": "..." } }
  • HMAC secret displayed once on create. Store it in your secret manager.
DELETE

/api/v1/webhooks/:id

Revoke a subscription

Soft-deletes the subscription. Stops future deliveries; existing DLQ entries are retained for audit.

Example request

curl -X DELETE -H "Authorization: Bearer <key>" https://oilflow.us/api/v1/webhooks/wh_...

Example response

{ "ok": true }
GET

/api/v1/webhooks/events

Recent delivery log

Returns the caller's last 50 webhook delivery attempts with status, attempt count, and HTTP response code. Used to audit a flaky endpoint.

Example request

curl -H "Authorization: Bearer <key>" https://oilflow.us/api/v1/webhooks/events

Example response

{ "ok": true, "data": { "events": [{ "id": "...", "status": "delivered", "attempt_count": 1, "response_code": 200 }] } }
POST

/api/v1/webhooks/events/:id/replay

Replay a failed delivery

Re-queues a specific event for delivery. Useful after fixing a receiver bug.

Example request

curl -X POST -H "Authorization: Bearer <key>" https://oilflow.us/api/v1/webhooks/events/evt_.../replay

Example response

{ "ok": true, "data": { "queued_at": "..." } }

Regulator Reports · LIVE

/api/v1/reports/*

GET

/api/v1/reports

List your generated reports

Returns paginated list of regulator-ready report packages your team has generated. Filterable by template_id and status.

Parameters

  • template_idstring

    e.g. 'fatf_rec10', 'fincen_cdd', 'eu_6amld', 'mas_626', 'fca_sysc18', 'ofsi'.

  • status'queued' | 'ready' | 'failed'

    Filter by status.

Example response

{ "ok": true, "data": { "reports": [{ "id": "...", "template_id": "fatf_rec10", "status": "ready" }] } }
POST

/api/v1/reports

Generate a report

Synthesises a regulator-ready evidence package from a previous KYC run. PDF rendered server-side; webhook `report.ready` fires when complete.

Request body

  • entity_iduuidrequired

    Source KYC run.

  • template_idstringrequired

    Framework template (fatf_rec10, fincen_cdd, eu_6amld, mas_626, fca_sysc18, ofsi).

Example request

curl -X POST -H "Authorization: Bearer <key>" \
  -H "Content-Type: application/json" \
  -d '{"entity_id":"...","template_id":"fatf_rec10"}' \
  https://oilflow.us/api/v1/reports

Example response

{ "ok": true, "data": { "id": "rpt_...", "status": "queued" } }
GET

/api/v1/reports/:id

Fetch a report

Returns report metadata + signed-URL download link for the PDF. Append ?include_cover_letter=true for a Claude-Haiku-synthesised regulator-officer-voice cover letter, cached after first request.

Parameters

  • include_cover_letterboolean

    Append a draft cover letter (cached after first fetch).

Example response

{ "ok": true, "data": { "report": {...}, "download_url": "https://...", "cover_letter": { "text": "...", "cached": false } } }
GET

/api/v1/reports/:id/audit

Report audit trail

Returns the append-only audit trail for a report (generation events, signed-URL fetches, regenerate triggers). Bank regulators ask for this directly.

Example response

{ "ok": true, "data": { "events": [{ "kind": "generated", "at": "..." }, { "kind": "downloaded", "at": "..." }] } }

Adverse Media · LIVE

/api/v1/adverse-media/*

GET

/api/v1/adverse-media/entities

List monitored entities

Your active monitoring list. Each entity is swept across 11 languages daily; matches fire `adverse_media.match_detected` webhooks.

Example response

{ "ok": true, "data": { "entities": [{ "id": "...", "entity_name": "Acme Trading FZE", "languages": ["en","ar"] }] } }
POST

/api/v1/adverse-media/entities

Add an entity to monitoring

Begins daily multilingual sweeps for the named entity. Idempotent on (member_id, entity_name).

Request body

  • entity_namestringrequired

    Legal name.

  • aliasesstring[]

    Other names to track.

  • languagesstring[]

    ISO codes; default: ['en']. Supported: en, ar, ru, ur, fa, zh, fr, es, de, pt, tr.

  • jurisdictionsstring[]

    Country filter.

Example response

{ "ok": true, "data": { "id": "mon_..." } }
DELETE

/api/v1/adverse-media/entities/:id

Stop monitoring an entity

Pauses daily sweeps. Soft-delete; historical findings retained.

Example response

{ "ok": true }
GET

/api/v1/adverse-media/findings

List findings

Returns adverse-media findings for your monitored entities. Filterable by entity, severity, language, and time window.

Parameters

  • entity_iduuid

    Filter by monitored entity.

  • severity'low' | 'medium' | 'high' | 'critical'

    Minimum severity.

  • sinceISO timestamp

    Findings discovered after this time.

Example response

{ "ok": true, "data": { "findings": [{ "id": "...", "severity": "high", "language": "ar", "source_url": "..." }] } }

UBO Graph · LIVE

/api/v1/ubo/*

POST

/api/v1/ubo/screen

Screen a UBO graph

Traverses ownership N hops from a root entity, screens every node + edge against the 8 sanctions lists + cluster blocklist, and runs shell-company detection on each entity. Returns a graph_id; the full graph fetches via /ubo/graph/{id}.

Request body

  • root_entity_namestringrequired

    Top of the ownership tree.

  • root_jurisdictionstring

    Root jurisdiction (helps narrow ambiguous matches).

  • max_hopsinteger

    Default: 3.

Example response

{ "ok": true, "data": { "graph_id": "...", "status": "queued" } }
GET

/api/v1/ubo/graph/:id

Fetch a UBO graph

Returns the screened ownership graph: nodes, edges, sanctions matches, shell-company verdicts, and aggregate risk score.

Example response

{ "ok": true, "data": { "nodes": [...], "edges": [...], "shell_company_verdicts": {...}, "aggregate_risk": "medium" } }

LC Validation · LIVE

/api/v1/lc/*

POST

/api/v1/lc/validate

Validate an LC + invoice + B/L

Cross-document discrepancy detection: UCP 600 rule engine + amount/currency/port/expiry consistency between letter of credit, commercial invoice, and bill of lading.

Request body

  • lcobjectrequired

    Letter of credit terms (amount, currency, beneficiary, ports, expiry_date).

  • invoiceobjectrequired

    Commercial invoice (amount, currency, seller, presentation_date).

  • blobjectrequired

    Bill of lading (currency, port_of_loading, issue_date, shipped_on_board).

Example response

{ "ok": true, "data": { "report_id": "lc_...", "discrepancies": [{ "rule": "UCP 600 Art. 14(d)", "severity": "major", "detail": "..." }] } }
GET

/api/v1/lc/reports/:id

Fetch an LC validation report

Returns the persisted validation report with discrepancies, UCP rule citations, and signed-URL PDF download.

Example response

{ "ok": true, "data": { "report": {...}, "download_url": "https://..." } }

Customer Watchlists · LIVE

/api/v1/watchlists/*

POST

/api/v1/watchlists

Upload a counterparty watchlist (CSV)

Ingests a CSV of counterparties you want continuously screened against new sanctions deltas, cluster upgrades, and adverse-media findings. Matches fire `watchlist.match_detected` webhooks. CSV header: entity_name,aliases,jurisdictions (aliases + jurisdictions pipe-separated within the cell).

Request body

  • namestringrequired

    Watchlist name.

  • descriptionstring

    Free-form label.

  • csvstringrequired

    CSV body; max 50K rows per upload.

Example response

{ "ok": true, "data": { "id": "wl_...", "name": "...", "entries_count": 1247 } }
GET

/api/v1/watchlists/:id

Fetch a watchlist + recent matches

Returns the watchlist + the first 25 entries + the 50 most recent match events.

Example response

{ "ok": true, "data": { "watchlist": {...}, "sample_entries": [...], "recent_matches": [{ "matched_at": "...", "source": "adverse_media" }] } }

Audit Export · LIVE

/api/v1/audit/*

GET

/api/v1/audit

Export your audit trail

Returns your own api_request_log rows for compliance-evidence submission. Three modes via the `format` parameter: 'json' (full rows, paginated), 'csv' (download), 'summary' (aggregate counts by SKU scope — used by the dashboard SpendForecast card).

Parameters

  • format'json' | 'csv' | 'summary'

    Default: 'json'.

  • daysinteger

    1–365; default 90.

  • page_sizeinteger

    1–10000; default 10000.

  • cursorISO timestamp

    From a prior page's last logged_at. Pages are strictly older than the cursor; no duplicates if new rows land mid-export.

Example response

{ "ok": true, "data": { "days": 90, "count": 9842, "rows": [...], "next_cursor": "2026-04-21T15:02:11Z" } }
  • Customer-scoped to your member_id (or tenant if you're on SKU #4 Workflow Suite).
  • Page by passing the prior response's next_cursor; null = end of stream.

Sandbox Keys · PUBLIC

/api/v1/keys/*

POST

/api/v1/keys/sandbox

Issue a sandbox API key

Unauthenticated. Issues a 7-day, 100 calls/day, read-only key scoped to /api/v1/regulatory/* + /api/v1/clusters/check. Use it to evaluate the API without signing up. Rate-limited by IP (3/h) and email (5/day).

Request body

  • emailstringrequired

    Your work email.

  • companystring

    Company name.

  • use_casestring

    Optional context.

Example request

curl -X POST -H "Content-Type: application/json" \
  -d '{"email":"[email protected]","company":"Example Bank","use_case":"Evaluate /regulatory/check for Saudi crude"}' \
  https://oilflow.us/api/v1/keys/sandbox

Example response

{ "key": "oilflow_sk_...", "key_id": "...", "environment": "sandbox", "scopes": ["regulatory","cluster"], "expires_at": "...", "daily_call_cap": 100 }
  • Welcome email fires on issuance with curl gallery + dashboard link.

Sandbox now, production by Friday.

Issue a sandbox key, hit the curl examples above, then upgrade in the dashboard. Enterprise scoping at your pace.