The same API the dashboard and the Claude connector use. One page per object; every endpoint with its parameters, request and response fields, and a curl you can paste against the demo tenant. Every score is null until measured and always carries its n; money is integer cents as strings.
Pre-flight
The same features, scorer and bands as a landed transfer, answered inside the bank's decision window and recorded so it can be graded against the outcome once the transfer lands and is matched back (same program, counterparty and amount within 24 hours). degraded is true when a source the answer leans on is missing or stale, so a thin answer is never mistaken for a confident one.
Preflight
| Field | Type | Notes |
|---|
| preflightId | string (uuid) | |
| decision | "clear" | "review" | "hold" | hold = hold band, hard signal or hard limit hit; review = review band, unscored or any soft limit hit; clear otherwise. |
| skoor | integer | null | |
| band | "clear" | "review" | "hold" | "unscored" | |
| version | string | |
| n | integer | |
| confidence | number | null | |
| reasons | Reason[] | |
| limitsHit | LimitHit[] | |
| degraded | boolean | True when a feature source the answer leans on is missing or stale. |
| degradedReasons | string[] | Why it is degraded (empty on a replayed answer). |
| latencyMs | integer | |
| idempotent | boolean | True when the answer was replayed from the idempotency key. |
| programId | string (uuid) | |
PreflightRequest
| Field | Type | Notes |
|---|
| programId (optional) | string (uuid) | One of programId or programSlug is required. |
| programSlug (optional) | string | |
| idempotencyKey (optional) | string | Same key on the same tenant returns the same answer (200 instead of 201). |
| transfer | object | |
| transfer.rail | "ach" | "wire" | "realtime" | "book" | "intl_wire" | "check" | "card" | |
| transfer.direction | "outgoing" | "incoming" | |
| transfer.type (optional) | "CREDIT" | "DEBIT" | null | |
| transfer.amountCents | string | Integer cents as a string. |
| transfer.currency (optional) | string | Default USD. |
| transfer.entityId (optional) | string | |
| transfer.counterpartyId (optional) | string | |
| transfer.counterparty (optional) | object | Describe a counterparty not on file; matched by routing + last4, else by name, else scored as a first-time stranger. |
| transfer.counterparty.name | string | |
| transfer.counterparty.routingNumber (optional) | string | |
| transfer.counterparty.accountLast4 (optional) | string | |
| transfer.counterparty.countryCode (optional) | string | |
| transfer.effectiveOn (optional) | string | |
| transfer.description (optional) | string | |
PreflightDetail
| Field | Type | Notes |
|---|
| preflightId | string (uuid) | |
| decision | "clear" | "review" | "hold" | |
| skoor | integer | null | |
| band | "clear" | "review" | "hold" | "unscored" | |
| version | string | |
| n | integer | |
| confidence | number | null | |
| reasons | Reason[] | |
| limitsHit | LimitHit[] | |
| degraded | boolean | |
| degradedReasons | string[] | |
| latencyMs | integer | |
| idempotent | boolean | |
| programId | string (uuid) | |
| request | object | The request as submitted plus operator and resolved {entityId, counterpartyId, counterpartyCountry}. |
| matchedTransferId | string | null | Set when the transfer later landed and was matched back (same program, counterparty, amount, within 24h). |
| createdAt | string (date-time) | |
PreflightStats
| Field | Type | Notes |
|---|
| windowDays | integer | |
| count | integer | |
| byDecision | object | |
| byDecision.clear | integer | |
| byDecision.review | integer | |
| byDecision.hold | integer | |
| latencyMs | object | |
| latencyMs.p50 | number | null | |
| latencyMs.p90 | number | null | |
| latencyMs.n | integer | |
| matched | object | Share of pre-flights matched to a landed transfer. |
| matched.value | number | null | null when nothing was measured |
| matched.n | integer | observations behind the value |
| degraded | object | Share of degraded answers. |
| degraded.value | number | null | null when nothing was measured |
| degraded.n | integer | observations behind the value |
| graded | object | Matched pre-flights whose transfer later received a bad/ok label. |
| graded.n | integer | |
| graded.holdAndBad | integer | |
| graded.holdAndOk | integer | |
| graded.notHoldAndBad | integer | |
| graded.notHoldAndOk | integer | |
| graded.holdPrecision | number | null | |
| graded.holdRecall | number | null | |
LimitHit
| Field | Type | Notes |
|---|
| limit | string | <actor>.<kind>: entity|counterparty × per_transaction|daily|monthly. |
| actor | "counterparty" | "entity" | |
| actorId | string | |
| band | "clear" | "review" | "hold" | "unscored" | |
| cap | string | The cap at that band. Integer cents as a string (bigint-safe). |
| observed | string | Already moved in the window (0 for per_transaction). Integer cents as a string (bigint-safe). |
| proposed | string | This amount. Integer cents as a string (bigint-safe). |
| severity | "hard" | "soft" | |
Endpoints
post/api/preflight
Score a proposed transfer
Answers clear / review / hold for a transfer that has not been originated yet, and records the answer. 201 on a new answer; 200 when idempotencyKey replays a stored one (then idempotent: true and degradedReasons is empty). Body limit 64 KB.
| Auth | Bearer + X-Operator |
|---|
| Operation | Pre-flight · POST /api/preflight |
|---|
Parameters
| Name | In | Type | Notes |
|---|
| tenant (optional) | query | string | Tenant slug. Optional: the bearer token already proves the tenant. Pass it to be explicit. |
| Idempotency-Key (optional) | header | string | 1–255 characters. A replay with the same body returns the stored status and body with Idempotent-Replayed: true; a different body under the same key is 409. |
Request body (application/json, required) · PreflightRequest
| Field | Type | Notes |
|---|
| programId (optional) | string (uuid) | One of programId or programSlug is required. |
| programSlug (optional) | string | |
| idempotencyKey (optional) | string | Same key on the same tenant returns the same answer (200 instead of 201). |
| transfer | object | |
| transfer.rail | "ach" | "wire" | "realtime" | "book" | "intl_wire" | "check" | "card" | |
| transfer.direction | "outgoing" | "incoming" | |
| transfer.type (optional) | "CREDIT" | "DEBIT" | null | |
| transfer.amountCents | string | Integer cents as a string. |
| transfer.currency (optional) | string | Default USD. |
| transfer.entityId (optional) | string | |
| transfer.counterpartyId (optional) | string | |
| transfer.counterparty (optional) | object | Describe a counterparty not on file; matched by routing + last4, else by name, else scored as a first-time stranger. |
| transfer.counterparty.name | string | |
| transfer.counterparty.routingNumber (optional) | string | |
| transfer.counterparty.accountLast4 (optional) | string | |
| transfer.counterparty.countryCode (optional) | string | |
| transfer.effectiveOn (optional) | string | |
| transfer.description (optional) | string | |
Responses
200 — Replayed from the idempotency key. Preflight
| Field | Type | Notes |
|---|
| preflightId | string (uuid) | |
| decision | "clear" | "review" | "hold" | hold = hold band, hard signal or hard limit hit; review = review band, unscored or any soft limit hit; clear otherwise. |
| skoor | integer | null | |
| band | "clear" | "review" | "hold" | "unscored" | |
| version | string | |
| n | integer | |
| confidence | number | null | |
| reasons | Reason[] | |
| limitsHit | LimitHit[] | |
| degraded | boolean | True when a feature source the answer leans on is missing or stale. |
| degradedReasons | string[] | Why it is degraded (empty on a replayed answer). |
| latencyMs | integer | |
| idempotent | boolean | True when the answer was replayed from the idempotency key. |
| programId | string (uuid) | |
201 — Scored and recorded. Preflight
| Field | Type | Notes |
|---|
| preflightId | string (uuid) | |
| decision | "clear" | "review" | "hold" | hold = hold band, hard signal or hard limit hit; review = review band, unscored or any soft limit hit; clear otherwise. |
| skoor | integer | null | |
| band | "clear" | "review" | "hold" | "unscored" | |
| version | string | |
| n | integer | |
| confidence | number | null | |
| reasons | Reason[] | |
| limitsHit | LimitHit[] | |
| degraded | boolean | True when a feature source the answer leans on is missing or stale. |
| degradedReasons | string[] | Why it is degraded (empty on a replayed answer). |
| latencyMs | integer | |
| idempotent | boolean | True when the answer was replayed from the idempotency key. |
| programId | string (uuid) | |
400 — Validation failed; error lists the paths. Error
| Field | Type | Notes |
|---|
| error | string | What went wrong, in one sentence. |
| requestId (optional) | string | Present once the gateway is live: the X-Request-Id of the failed request. |
401 — Bearer token missing or unknown. Error
| Field | Type | Notes |
|---|
| error | string | What went wrong, in one sentence. |
| requestId (optional) | string | Present once the gateway is live: the X-Request-Id of the failed request. |
404 — Unknown program on this tenant. Error
| Field | Type | Notes |
|---|
| error | string | What went wrong, in one sentence. |
| requestId (optional) | string | Present once the gateway is live: the X-Request-Id of the failed request. |
Example
curl -s -X POST "https://skoor.ai/risk/api/preflight?tenant=demo" \
-H "Authorization: Bearer test_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
-H "X-Operator: you@bank.example" \
-H "Content-Type: application/json" \
-d '{"programSlug":"northwind-payroll","idempotencyKey":"pf-2026-09-17-0001","transfer":{"rail":"ach","direction":"outgoing","type":"CREDIT","amountCents":"185000","currency":"USD","entityId":"enti_sim_acme","counterparty":{"name":"Blue Ridge Landscaping","routingNumber":"021000021","accountLast4":"4421","countryCode":"US"},"description":"Payroll 2026-09-19"}}'
Response
{
"preflightId": "5f3c1c8e-2a44-4c0b-9d0f-2b0a2f6e7c11",
"decision": "clear",
"skoor": 12,
"band": "clear",
"version": "trs-v1",
"n": 210,
"confidence": 0.81,
"reasons": [
{
"id": "TRS-CP-01",
"signal": "counterparty.first_time",
"weight": 8,
"hard": false,
"sentence": "This is the first transfer with this counterparty."
}
],
"limitsHit": [],
"degraded": false,
"degradedReasons": [],
"latencyMs": 41,
"idempotent": false,
"programId": "a1e0c2d4-6b7f-4d8e-9a3b-0c1d2e3f4a55"
}
get/api/preflight/stats
Pre-flight statistics for a window
| Auth | Bearer + X-Operator |
|---|
| Operation | Pre-flight · GET /api/preflight/stats |
|---|
Parameters
| Name | In | Type | Notes |
|---|
| tenant (optional) | query | string | Tenant slug. Optional: the bearer token already proves the tenant. Pass it to be explicit. |
| days (optional) | query | integer | Window in days, 1–365 (default 30). |
Responses
200 — Counts by decision, latency percentiles, matched and degraded shares, and the graded confusion counts. PreflightStats
| Field | Type | Notes |
|---|
| windowDays | integer | |
| count | integer | |
| byDecision | object | |
| byDecision.clear | integer | |
| byDecision.review | integer | |
| byDecision.hold | integer | |
| latencyMs | object | |
| latencyMs.p50 | number | null | |
| latencyMs.p90 | number | null | |
| latencyMs.n | integer | |
| matched | object | Share of pre-flights matched to a landed transfer. |
| matched.value | number | null | null when nothing was measured |
| matched.n | integer | observations behind the value |
| degraded | object | Share of degraded answers. |
| degraded.value | number | null | null when nothing was measured |
| degraded.n | integer | observations behind the value |
| graded | object | Matched pre-flights whose transfer later received a bad/ok label. |
| graded.n | integer | |
| graded.holdAndBad | integer | |
| graded.holdAndOk | integer | |
| graded.notHoldAndBad | integer | |
| graded.notHoldAndOk | integer | |
| graded.holdPrecision | number | null | |
| graded.holdRecall | number | null | |
401 — Bearer token missing or unknown. Error
| Field | Type | Notes |
|---|
| error | string | What went wrong, in one sentence. |
| requestId (optional) | string | Present once the gateway is live: the X-Request-Id of the failed request. |
Example
curl -s "https://skoor.ai/risk/api/preflight/stats?tenant=demo&days=30" \
-H "Authorization: Bearer test_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
-H "X-Operator: you@bank.example"
get/api/preflight/{id}
One pre-flight answer with its request
| Auth | Bearer + X-Operator |
|---|
| Operation | Pre-flight · GET /api/preflight/{id} |
|---|
Parameters
| Name | In | Type | Notes |
|---|
| id | path | string (uuid) | Pre-flight id. |
| tenant (optional) | query | string | Tenant slug. Optional: the bearer token already proves the tenant. Pass it to be explicit. |
Responses
200 — The stored answer, the request as submitted, and the matched transfer when one landed. PreflightDetail
| Field | Type | Notes |
|---|
| preflightId | string (uuid) | |
| decision | "clear" | "review" | "hold" | |
| skoor | integer | null | |
| band | "clear" | "review" | "hold" | "unscored" | |
| version | string | |
| n | integer | |
| confidence | number | null | |
| reasons | Reason[] | |
| limitsHit | LimitHit[] | |
| degraded | boolean | |
| degradedReasons | string[] | |
| latencyMs | integer | |
| idempotent | boolean | |
| programId | string (uuid) | |
| request | object | The request as submitted plus operator and resolved {entityId, counterpartyId, counterpartyCountry}. |
| matchedTransferId | string | null | Set when the transfer later landed and was matched back (same program, counterparty, amount, within 24h). |
| createdAt | string (date-time) | |
401 — Bearer token missing or unknown. Error
| Field | Type | Notes |
|---|
| error | string | What went wrong, in one sentence. |
| requestId (optional) | string | Present once the gateway is live: the X-Request-Id of the failed request. |
404 — Unknown pre-flight on this tenant. Error
| Field | Type | Notes |
|---|
| error | string | What went wrong, in one sentence. |
| requestId (optional) | string | Present once the gateway is live: the X-Request-Id of the failed request. |
Example
curl -s "https://skoor.ai/risk/api/preflight/5f3c1c8e-2a44-4c0b-9d0f-2b0a2f6e7c11?tenant=demo" \
-H "Authorization: Bearer test_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
-H "X-Operator: you@bank.example"
Policy
Policy is versioned data, never code: which detectors the platform may close alone and in which band, the history floor, which actions need two approvers, how long each SLA tier may wait, the per-band caps for entities and counterparties, and a per-program autonomy tier that widens only after the labelled set shows a detector is safe (and narrows on its own when overturns rise). Every alert and disposition records the policy version in force.
Policy
| Field | Type | Notes |
|---|
| id | string (uuid) | |
| tenantId | string (uuid) | |
| programId | string | null (uuid) | null = tenant-wide; set = a program's autonomy tier / limits. |
| version | string | |
| body | object | PolicyBody, ClockPolicy, LimitLadder or a program autonomy body, by version prefix. |
| active | boolean | |
| createdAt | string (date-time) | |
PolicyBody
| Field | Type | Notes |
|---|
| version | string | |
| autoClosableDetectors | string[] | Detectors the platform may close alone. |
| autoCloseBands | "clear" | "review" | "hold" | "unscored"[] | v1: clear only. |
| minProgramN | integer | History floor for any automated decision. |
| alwaysReviewed | string[] | |
| twoApproverActions | "clear_hold" | "cancel_transfer" | "pause_card" | "suspend_card_account" | "freeze_account" | "request_to_program" | "periodic_review"[] | |
| holdBandAutoHold | boolean | Hold band → held before anyone decides. |
ClockPolicy
Hours a decision may wait, by SLA tier. Defaults 1 / 24 / 72 / 168.
| Field | Type | Notes |
|---|
| autoHours | number | |
| standardHours | number | |
| complexHours | number | |
| appealHours | number | |
LimitLadder
| Field | Type | Notes |
|---|
| counterparty | Ladder | |
| entity | Ladder | |
| rescreenDays | object | Re-screen cadence by band. |
| rescreenDays.clear | integer | |
| rescreenDays.review | integer | |
| rescreenDays.hold | integer | |
| rescreenDays.unscored | integer | |
LimitEvaluation
| Field | Type | Notes |
|---|
| hits | LimitHit[] | |
| hard | boolean | True when any hit is hard (an actor in the hold band). |
| bands | object | |
| bands.entity | "clear" | "review" | "hold" | "unscored" | null | |
| bands.counterparty | "clear" | "review" | "hold" | "unscored" | null | |
| rescreen | object | |
| rescreen.entity | object | null | |
| rescreen.counterparty | object | null | |
| ladder | LimitLadder | |
ProgramAutonomy
| Field | Type | Notes |
|---|
| programId | string (uuid) | |
| tier | "supervised" | "standard" | "trusted" | |
| version | string | null | Active program version; null = policy v1 only. |
| granted | string[] | |
| effective | PolicyBody | |
| eligibility | DetectorEligibility[] | |
| lastChangeAt | string | null (date-time) | |
| nextChangeAllowedAt | string | null (date-time) | Cadence: one widening per week. |
| history | object[] | |
| history[].version | string | |
| history[].createdAt | string (date-time) | |
| history[].body | ProgramPolicyBody | |
| history[].active | boolean | |
Endpoints
get/api/limits/ladder
The tenant's limit ladder
| Auth | Bearer + X-Operator |
|---|
| Operation | Policy · GET /api/limits/ladder |
|---|
Parameters
| Name | In | Type | Notes |
|---|
| tenant (optional) | query | string | Tenant slug. Optional: the bearer token already proves the tenant. Pass it to be explicit. |
Responses
200 — Per-band caps for counterparties and entities and the re-screen cadence; the default ladder when the tenant has not written one. LimitLadder
| Field | Type | Notes |
|---|
| counterparty | Ladder | |
| entity | Ladder | |
| rescreenDays | object | Re-screen cadence by band. |
| rescreenDays.clear | integer | |
| rescreenDays.review | integer | |
| rescreenDays.hold | integer | |
| rescreenDays.unscored | integer | |
401 — Bearer token missing or unknown. Error
| Field | Type | Notes |
|---|
| error | string | What went wrong, in one sentence. |
| requestId (optional) | string | Present once the gateway is live: the X-Request-Id of the failed request. |
Example
curl -s "https://skoor.ai/risk/api/limits/ladder?tenant=demo" \
-H "Authorization: Bearer test_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
-H "X-Operator: you@bank.example"
get/api/limits/evaluate
Evaluate an amount against the ladder
| Auth | Bearer + X-Operator |
|---|
| Operation | Policy · GET /api/limits/evaluate |
|---|
Parameters
| Name | In | Type | Notes |
|---|
| tenant (optional) | query | string | Tenant slug. Optional: the bearer token already proves the tenant. Pass it to be explicit. |
| programId (optional) | query | string (uuid) | Program id. |
| amountCents (optional) | query | string | Integer cents. |
| entityId (optional) | query | string | Entity to evaluate, if any. |
| counterpartyId (optional) | query | string | Counterparty to evaluate, if any. |
Responses
200 — Limit hits (hard when an actor is in the hold band), the actors' bands, their re-screen cadence and the ladder used. LimitEvaluation
| Field | Type | Notes |
|---|
| hits | LimitHit[] | |
| hard | boolean | True when any hit is hard (an actor in the hold band). |
| bands | object | |
| bands.entity | "clear" | "review" | "hold" | "unscored" | null | |
| bands.counterparty | "clear" | "review" | "hold" | "unscored" | null | |
| rescreen | object | |
| rescreen.entity | object | null | |
| rescreen.counterparty | object | null | |
| ladder | LimitLadder | |
400 — programId and amountCents required. Error
| Field | Type | Notes |
|---|
| error | string | What went wrong, in one sentence. |
| requestId (optional) | string | Present once the gateway is live: the X-Request-Id of the failed request. |
401 — Bearer token missing or unknown. Error
| Field | Type | Notes |
|---|
| error | string | What went wrong, in one sentence. |
| requestId (optional) | string | Present once the gateway is live: the X-Request-Id of the failed request. |
Example
curl -s "https://skoor.ai/risk/api/limits/evaluate?tenant=demo&programId=a1e0c2d4-6b7f-4d8e-9a3b-0c1d2e3f4a55&amountCents=185000&entityId=enti_sim_acme&counterpartyId=cpty_sim_1" \
-H "Authorization: Bearer test_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
-H "X-Operator: you@bank.example"
get/api/autonomy/{programId}
A program's autonomy tier and eligibility
| Auth | Bearer + X-Operator |
|---|
| Operation | Policy · GET /api/autonomy/{programId} |
|---|
Parameters
| Name | In | Type | Notes |
|---|
| programId | path | string (uuid) | Program id. |
| tenant (optional) | query | string | Tenant slug. Optional: the bearer token already proves the tenant. Pass it to be explicit. |
Responses
200 — Tier, granted detectors, the effective policy, per-detector eligibility from the last 30 days of person decisions, and the version history. ProgramAutonomy
| Field | Type | Notes |
|---|
| programId | string (uuid) | |
| tier | "supervised" | "standard" | "trusted" | |
| version | string | null | Active program version; null = policy v1 only. |
| granted | string[] | |
| effective | PolicyBody | |
| eligibility | DetectorEligibility[] | |
| lastChangeAt | string | null (date-time) | |
| nextChangeAllowedAt | string | null (date-time) | Cadence: one widening per week. |
| history | object[] | |
| history[].version | string | |
| history[].createdAt | string (date-time) | |
| history[].body | ProgramPolicyBody | |
| history[].active | boolean | |
401 — Bearer token missing or unknown. Error
| Field | Type | Notes |
|---|
| error | string | What went wrong, in one sentence. |
| requestId (optional) | string | Present once the gateway is live: the X-Request-Id of the failed request. |
404 — Unknown program on this tenant. Error
| Field | Type | Notes |
|---|
| error | string | What went wrong, in one sentence. |
| requestId (optional) | string | Present once the gateway is live: the X-Request-Id of the failed request. |
Example
curl -s "https://skoor.ai/risk/api/autonomy/a1e0c2d4-6b7f-4d8e-9a3b-0c1d2e3f4a55?tenant=demo" \
-H "Authorization: Bearer test_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
-H "X-Operator: you@bank.example"
post/api/autonomy/{programId}/activate
Grant a detector to the automated side
Needs an eligible detector (≥10 person decisions in 30 days, ≥90% matching the draft, none of the never-automate set) and a second approver distinct from X-Operator; at most one widening per week. Writes a new program policy version on the record.
| Auth | Bearer + X-Operator |
|---|
| Operation | Policy · POST /api/autonomy/{programId}/activate |
|---|
Parameters
| Name | In | Type | Notes |
|---|
| programId | path | string (uuid) | Program id. |
| tenant (optional) | query | string | Tenant slug. Optional: the bearer token already proves the tenant. Pass it to be explicit. |
| Idempotency-Key (optional) | header | string | 1–255 characters. A replay with the same body returns the stored status and body with Idempotent-Replayed: true; a different body under the same key is 409. |
Request body (application/json, required) · object
| Field | Type | Notes |
|---|
| detector | string | |
| approverId | string | |
Responses
201 — The new version and tier. object
| Field | Type | Notes |
|---|
| version | string | |
| tier | "supervised" | "standard" | "trusted" | |
| granted | string[] | |
400 — detector and approverId required. Error
| Field | Type | Notes |
|---|
| error | string | What went wrong, in one sentence. |
| requestId (optional) | string | Present once the gateway is live: the X-Request-Id of the failed request. |
401 — Bearer token missing or unknown. Error
| Field | Type | Notes |
|---|
| error | string | What went wrong, in one sentence. |
| requestId (optional) | string | Present once the gateway is live: the X-Request-Id of the failed request. |
404 — Unknown program on this tenant. Error
| Field | Type | Notes |
|---|
| error | string | What went wrong, in one sentence. |
| requestId (optional) | string | Present once the gateway is live: the X-Request-Id of the failed request. |
409 — Refused: not eligible, same approver, cadence, or never-automate. Error
| Field | Type | Notes |
|---|
| error | string | What went wrong, in one sentence. |
| requestId (optional) | string | Present once the gateway is live: the X-Request-Id of the failed request. |
Example
curl -s -X POST "https://skoor.ai/risk/api/autonomy/a1e0c2d4-6b7f-4d8e-9a3b-0c1d2e3f4a55/activate?tenant=demo" \
-H "Authorization: Bearer test_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
-H "X-Operator: you@bank.example" \
-H "Content-Type: application/json" \
-d '{"detector":"repeat_pattern","approverId":"second.approver@bank.example"}'
post/api/autonomy/{programId}/demote
Run the demotion check now
Narrows the automated side for any granted detector whose overturn rate in the window is above 10%. The same check runs every 15 minutes.
| Auth | Bearer + X-Operator |
|---|
| Operation | Policy · POST /api/autonomy/{programId}/demote |
|---|
Parameters
| Name | In | Type | Notes |
|---|
| programId | path | string (uuid) | Program id. |
| tenant (optional) | query | string | Tenant slug. Optional: the bearer token already proves the tenant. Pass it to be explicit. |
Responses
200 — Detectors demoted (empty when nothing changed) and the version written, if any. object
| Field | Type | Notes |
|---|
| demoted | string[] | |
| version | string | null | |
401 — Bearer token missing or unknown. Error
| Field | Type | Notes |
|---|
| error | string | What went wrong, in one sentence. |
| requestId (optional) | string | Present once the gateway is live: the X-Request-Id of the failed request. |
404 — Unknown program on this tenant. Error
| Field | Type | Notes |
|---|
| error | string | What went wrong, in one sentence. |
| requestId (optional) | string | Present once the gateway is live: the X-Request-Id of the failed request. |
Example
curl -s -X POST "https://skoor.ai/risk/api/autonomy/a1e0c2d4-6b7f-4d8e-9a3b-0c1d2e3f4a55/demote?tenant=demo" \
-H "Authorization: Bearer test_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
-H "X-Operator: you@bank.example"
Rule
A bank authors rules as data over the same features the scorer uses. A rule never changes a Skoor: it opens an alert (detector rule:<id>) that always waits for a person. Activation needs a backtest against the tenant's own last 90 days that is younger than 24 hours, a second approver, and writes a versioned rules policy on the record chain.
Rule
| Field | Type | Notes |
|---|
| id | string | Stable id, lowercase. |
| name | string | |
| scope | "transfer" | "entity" | "counterparty" | "program" | Default transfer; only transfer-scoped rules fire today. |
| programId | string | null (uuid) | null = every program on the tenant. |
| when | RuleCondition[] | AND of conditions; at least one. |
| action | "alert" | "hold" | "review" | "log" | |
| severity | "low" | "medium" | "high" | |
| status | "draft" | "testing" | "active" | "paused" | "archived" | |
| note (optional) | string | |
RuleCondition
| Field | Type | Notes |
|---|
| field | "amountCents" | "rail" | "direction" | "type" | "status" | "counterpartyCountry" | "cardDecisionReason" | "entity.verificationStatus" | "entity.pepStatus" | "entity.isHighRisk" | "entity.requiresScreening" | "entity.screeningAgeDays" | "entity.count30d" | "velocity24hCount" | "velocity24hSumCents" | "entity.priorTransfers" | "entity.priorReturns" | "entity.achDebits60d" | "entity.unauthorizedReturns60d" | "structuringWindowCount" | "counterparty.firstTime" | "counterparty.n" | "counterparty.priorReturns" | "counterparty.priorUnauthorizedReturns" | "program.n90d" | "program.medianCents" | "program.p95Cents" | "program.dailyAvgCents30d" | "program.declaredMonthlyVolumeCents" | "program.unauthorizedReturnRate60d" | "declared.countries" | "typology.roundTrip" | "typology.layeringHops" | "typology.dormantDays" | "typology.justBelowThreshold" | A fixed vocabulary over the same features the scorer uses. |
| op | "gt" | "gte" | "lt" | "lte" | "eq" | "neq" | "in" | "not_in" | "between" | "is_null" | "not_null" | |
| value (optional) | string | number | boolean | null | string | number | boolean | null[] | Scalar (string, number, boolean, null) or an array of scalars for in / not_in / between; omitted for is_null / not_null. |
Backtest
| Field | Type | Notes |
|---|
| key | string | |
| ruleId | string | |
| at | string (date-time) | |
| days | integer | |
| scanned | integer | Transfers replayed (capped at 1000). |
| sampled | boolean | True when the cap truncated the window. |
| matched | integer | |
| matchedShare | number | null | |
| wouldHold | integer | |
| wouldAlert | integer | |
| overlapWithExistingAlerts | integer | |
| byBand | Record<string, integer> | |
| sampleTransferIds | string[] | |
RulesVersion
| Field | Type | Notes |
|---|
| id | string | |
| version | string | |
| rules | Rule[] | |
| createdAt | string (date-time) | |
| active | boolean | |
| note (optional) | string | |
Endpoints
get/api/rules
List rules with their last backtest
| Auth | Bearer + X-Operator |
|---|
| Operation | Rule · GET /api/rules |
|---|
Parameters
| Name | In | Type | Notes |
|---|
| tenant (optional) | query | string | Tenant slug. Optional: the bearer token already proves the tenant. Pass it to be explicit. |
Responses
200 — Every rule on the tenant, each with its latest backtest, plus the field vocabulary and operators the DSL accepts. object
| Field | Type | Notes |
|---|
| rules | Rule & object[] | |
| fields | string[] | |
| operators | string[] | |
401 — Bearer token missing or unknown. Error
| Field | Type | Notes |
|---|
| error | string | What went wrong, in one sentence. |
| requestId (optional) | string | Present once the gateway is live: the X-Request-Id of the failed request. |
Example
curl -s "https://skoor.ai/risk/api/rules?tenant=demo" \
-H "Authorization: Bearer test_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
-H "X-Operator: you@bank.example"
post/api/rules
Create or update a draft rule
| Auth | Bearer + X-Operator |
|---|
| Operation | Rule · POST /api/rules |
|---|
Parameters
| Name | In | Type | Notes |
|---|
| tenant (optional) | query | string | Tenant slug. Optional: the bearer token already proves the tenant. Pass it to be explicit. |
| Idempotency-Key (optional) | header | string | 1–255 characters. A replay with the same body returns the stored status and body with Idempotent-Replayed: true; a different body under the same key is 409. |
Request body (application/json, required) · RuleDraft
| Field | Type | Notes |
|---|
| id | string | |
| name | string | |
| scope (optional) | "transfer" | "entity" | "counterparty" | "program" | |
| programId (optional) | string | null (uuid) | |
| when | RuleCondition[] | |
| action (optional) | "alert" | "hold" | "review" | "log" | |
| severity (optional) | "low" | "medium" | "high" | |
| note (optional) | string | |
Responses
201 — The rule as stored (status draft). Rule
| Field | Type | Notes |
|---|
| id | string | Stable id, lowercase. |
| name | string | |
| scope | "transfer" | "entity" | "counterparty" | "program" | Default transfer; only transfer-scoped rules fire today. |
| programId | string | null (uuid) | null = every program on the tenant. |
| when | RuleCondition[] | AND of conditions; at least one. |
| action | "alert" | "hold" | "review" | "log" | |
| severity | "low" | "medium" | "high" | |
| status | "draft" | "testing" | "active" | "paused" | "archived" | |
| note (optional) | string | |
400 — Invalid rule; issues lists the Zod problems. Error
| Field | Type | Notes |
|---|
| error | string | What went wrong, in one sentence. |
| requestId (optional) | string | Present once the gateway is live: the X-Request-Id of the failed request. |
401 — Bearer token missing or unknown. Error
| Field | Type | Notes |
|---|
| error | string | What went wrong, in one sentence. |
| requestId (optional) | string | Present once the gateway is live: the X-Request-Id of the failed request. |
409 — The rule is active; pause it before editing. Error
| Field | Type | Notes |
|---|
| error | string | What went wrong, in one sentence. |
| requestId (optional) | string | Present once the gateway is live: the X-Request-Id of the failed request. |
Example
curl -s -X POST "https://skoor.ai/risk/api/rules?tenant=demo" \
-H "Authorization: Bearer test_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
-H "X-Operator: you@bank.example" \
-H "Content-Type: application/json" \
-d '{"id":"large-first-time-wire","name":"Large wire to a first-time counterparty","when":[{"field":"rail","op":"eq","value":"wire"},{"field":"counterparty.firstTime","op":"eq","value":true},{"field":"amountCents","op":"gte","value":1000000}],"action":"alert","severity":"high"}'
post/api/rules/{id}/backtest
Backtest a rule against the tenant's own transfers
| Auth | Bearer + X-Operator |
|---|
| Operation | Rule · POST /api/rules/{id}/backtest |
|---|
Parameters
| Name | In | Type | Notes |
|---|
| id | path | string | Rule id. |
| tenant (optional) | query | string | Tenant slug. Optional: the bearer token already proves the tenant. Pass it to be explicit. |
Request body (application/json) · object
| Field | Type | Notes |
|---|
| days (optional) | integer | Window in days (default 90). |
Responses
200 — What the rule would have matched: scanned (capped at 1000, then sampled), matched, share, would-hold, overlap with existing alerts, by band, and sample transfer ids. Backtest
| Field | Type | Notes |
|---|
| key | string | |
| ruleId | string | |
| at | string (date-time) | |
| days | integer | |
| scanned | integer | Transfers replayed (capped at 1000). |
| sampled | boolean | True when the cap truncated the window. |
| matched | integer | |
| matchedShare | number | null | |
| wouldHold | integer | |
| wouldAlert | integer | |
| overlapWithExistingAlerts | integer | |
| byBand | Record<string, integer> | |
| sampleTransferIds | string[] | |
401 — Bearer token missing or unknown. Error
| Field | Type | Notes |
|---|
| error | string | What went wrong, in one sentence. |
| requestId (optional) | string | Present once the gateway is live: the X-Request-Id of the failed request. |
404 — Rule not found. Error
| Field | Type | Notes |
|---|
| error | string | What went wrong, in one sentence. |
| requestId (optional) | string | Present once the gateway is live: the X-Request-Id of the failed request. |
Example
curl -s -X POST "https://skoor.ai/risk/api/rules/large-first-time-wire/backtest?tenant=demo" \
-H "Authorization: Bearer test_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
-H "X-Operator: you@bank.example" \
-H "Content-Type: application/json" \
-d '{"days":90}'
post/api/rules/{id}/activate
Activate a rule with a second approver
Needs a backtest younger than 24 hours and an approver distinct from X-Operator. Writes a new rules version and chains a periodic_review action (target rule, <id>@<version>) on the rule's program, else the tenant's first program.
| Auth | Bearer + X-Operator |
|---|
| Operation | Rule · POST /api/rules/{id}/activate |
|---|
Parameters
| Name | In | Type | Notes |
|---|
| id | path | string | Rule id. |
| tenant (optional) | query | string | Tenant slug. Optional: the bearer token already proves the tenant. Pass it to be explicit. |
| Idempotency-Key (optional) | header | string | 1–255 characters. A replay with the same body returns the stored status and body with Idempotent-Replayed: true; a different body under the same key is 409. |
Request body (application/json, required) · object
| Field | Type | Notes |
|---|
| approverId | string | |
Responses
200 — The active rule, the rules version written, and the action id recorded on the chain (null when none). object
| Field | Type | Notes |
|---|
| rule | Rule | |
| version | RulesVersion | |
| actionId | string | null | |
401 — Bearer token missing or unknown. Error
| Field | Type | Notes |
|---|
| error | string | What went wrong, in one sentence. |
| requestId (optional) | string | Present once the gateway is live: the X-Request-Id of the failed request. |
404 — Rule not found. Error
| Field | Type | Notes |
|---|
| error | string | What went wrong, in one sentence. |
| requestId (optional) | string | Present once the gateway is live: the X-Request-Id of the failed request. |
409 — No fresh backtest, same approver, or the rule is archived. Error
| Field | Type | Notes |
|---|
| error | string | What went wrong, in one sentence. |
| requestId (optional) | string | Present once the gateway is live: the X-Request-Id of the failed request. |
Example
curl -s -X POST "https://skoor.ai/risk/api/rules/large-first-time-wire/activate?tenant=demo" \
-H "Authorization: Bearer test_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
-H "X-Operator: you@bank.example" \
-H "Content-Type: application/json" \
-d '{"approverId":"second.approver@bank.example"}'
post/api/rules/{id}/pause
Pause an active rule
| Auth | Bearer + X-Operator |
|---|
| Operation | Rule · POST /api/rules/{id}/pause |
|---|
Parameters
| Name | In | Type | Notes |
|---|
| id | path | string | Rule id. |
| tenant (optional) | query | string | Tenant slug. Optional: the bearer token already proves the tenant. Pass it to be explicit. |
Responses
200 — The rule with status paused. Rule
| Field | Type | Notes |
|---|
| id | string | Stable id, lowercase. |
| name | string | |
| scope | "transfer" | "entity" | "counterparty" | "program" | Default transfer; only transfer-scoped rules fire today. |
| programId | string | null (uuid) | null = every program on the tenant. |
| when | RuleCondition[] | AND of conditions; at least one. |
| action | "alert" | "hold" | "review" | "log" | |
| severity | "low" | "medium" | "high" | |
| status | "draft" | "testing" | "active" | "paused" | "archived" | |
| note (optional) | string | |
401 — Bearer token missing or unknown. Error
| Field | Type | Notes |
|---|
| error | string | What went wrong, in one sentence. |
| requestId (optional) | string | Present once the gateway is live: the X-Request-Id of the failed request. |
404 — Rule not found. Error
| Field | Type | Notes |
|---|
| error | string | What went wrong, in one sentence. |
| requestId (optional) | string | Present once the gateway is live: the X-Request-Id of the failed request. |
Example
curl -s -X POST "https://skoor.ai/risk/api/rules/large-first-time-wire/pause?tenant=demo" \
-H "Authorization: Bearer test_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
-H "X-Operator: you@bank.example"
Metrics
Calibration per band, coverage, hold-band precision and recall, auto-closed and overturned rates, time to decision by route, and dollars approved without waiting — each { value | null, n } over a window (default 30 days), persisted daily. The demo tenant's figures are public and labelled simulated.
Metrics
| Field | Type | Notes |
|---|
| asOf | string (date-time) | |
| windowDays | integer | |
| calibration | object | Per band: labelled transfers that went bad ÷ labelled transfers. |
| calibration.clear | Measured | |
| calibration.review | Measured | |
| calibration.hold | Measured | |
| calibration.unscored | Measured | |
| coverage | object | |
| coverage.transfers | Measured | |
| coverage.programs | Measured | |
| holdBand | object | |
| holdBand.precision | Measured | |
| holdBand.recall | Measured | |
| holdBand.tp | integer | |
| holdBand.fp | integer | |
| holdBand.fn | integer | |
| autoClosed | Measured | |
| overturned | Measured | |
| timeToDecisionHours | object | |
| timeToDecisionHours.automated | object | Closed alerts routed automated. |
| timeToDecisionHours.automated.median | number | null | |
| timeToDecisionHours.automated.p90 | number | null | |
| timeToDecisionHours.automated.n | integer | |
| timeToDecisionHours.reviewed | object | Closed alerts routed reviewed. |
| timeToDecisionHours.reviewed.median | number | null | |
| timeToDecisionHours.reviewed.p90 | number | null | |
| timeToDecisionHours.reviewed.n | integer | |
| approvedWithoutWaiting | object | Clear-band transfers that never raised an alert. |
| approvedWithoutWaiting.count | integer | |
| approvedWithoutWaiting.cents | string | Integer cents as a string (bigint-safe). |
| approvedWithoutWaiting.n | integer | Scored transfers. |
| eventCoverage | object | Event families understood ÷ families known. |
| eventCoverage.value | number | null | |
| eventCoverage.n | integer | |
| eventCoverage.seen | string[] | |
Measured
A number with its n; value is null when nothing was measured.
| Field | Type | Notes |
|---|
| value | number | null | null when nothing was measured |
| n | integer | observations behind the value |
Endpoints
get/api/metrics
The six numbers for the tenant
| Auth | Bearer + X-Operator |
|---|
| Operation | Metrics · GET /api/metrics |
|---|
Parameters
| Name | In | Type | Notes |
|---|
| tenant (optional) | query | string | Tenant slug. Optional: the bearer token already proves the tenant. Pass it to be explicit. |
| days (optional) | query | integer | Window in days, 1–365 (default 30). |
Responses
200 — Every metric with its n; also persisted as today's daily snapshot. Metrics
| Field | Type | Notes |
|---|
| asOf | string (date-time) | |
| windowDays | integer | |
| calibration | object | Per band: labelled transfers that went bad ÷ labelled transfers. |
| calibration.clear | Measured | |
| calibration.review | Measured | |
| calibration.hold | Measured | |
| calibration.unscored | Measured | |
| coverage | object | |
| coverage.transfers | Measured | |
| coverage.programs | Measured | |
| holdBand | object | |
| holdBand.precision | Measured | |
| holdBand.recall | Measured | |
| holdBand.tp | integer | |
| holdBand.fp | integer | |
| holdBand.fn | integer | |
| autoClosed | Measured | |
| overturned | Measured | |
| timeToDecisionHours | object | |
| timeToDecisionHours.automated | object | Closed alerts routed automated. |
| timeToDecisionHours.automated.median | number | null | |
| timeToDecisionHours.automated.p90 | number | null | |
| timeToDecisionHours.automated.n | integer | |
| timeToDecisionHours.reviewed | object | Closed alerts routed reviewed. |
| timeToDecisionHours.reviewed.median | number | null | |
| timeToDecisionHours.reviewed.p90 | number | null | |
| timeToDecisionHours.reviewed.n | integer | |
| approvedWithoutWaiting | object | Clear-band transfers that never raised an alert. |
| approvedWithoutWaiting.count | integer | |
| approvedWithoutWaiting.cents | string | Integer cents as a string (bigint-safe). |
| approvedWithoutWaiting.n | integer | Scored transfers. |
| eventCoverage | object | Event families understood ÷ families known. |
| eventCoverage.value | number | null | |
| eventCoverage.n | integer | |
| eventCoverage.seen | string[] | |
401 — Bearer token missing or unknown. Error
| Field | Type | Notes |
|---|
| error | string | What went wrong, in one sentence. |
| requestId (optional) | string | Present once the gateway is live: the X-Request-Id of the failed request. |
Example
curl -s "https://skoor.ai/risk/api/metrics?tenant=demo&days=30" \
-H "Authorization: Bearer test_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
-H "X-Operator: you@bank.example"
get/api/governance
The model-risk record (how the AI is governed)
Generated from the live record and the running code, in the shape of SR 11-7 and NIST AI RMF: model inventory, the automated-or-reviewed boundary per program, eval-set validation and live calibration, the six metrics, change control (policy and rule versions, hash chain), human oversight (attribution, clocks, overturns, the language model's role), data rules, and a requirement-to-evidence mapping. Every number carries n; unmeasured is null. Rendered for people at /governance.
| Auth | Bearer + X-Operator |
|---|
| Operation | Metrics · GET /api/governance |
|---|
Parameters
| Name | In | Type | Notes |
|---|
| tenant (optional) | query | string | Tenant slug. Optional: the bearer token already proves the tenant. Pass it to be explicit. |
| days (optional) | query | integer | Window in days, 1–365 (default 30). |
Responses
200 — The record. GovernanceRecord
| Field | Type | Notes |
|---|
| recordVersion | string | governance-v1 |
| asOf | string (date-time) | |
| tenant | string | |
| simulated | boolean | |
| windowDays | integer | |
| inventory | object[] | |
| inventory[].id | string | |
| inventory[].name | string | |
| inventory[].version | string | |
| inventory[].kind | "deterministic" | "llm" | "rules" | |
| inventory[].decides | boolean | |
| inventory[].purpose | string | |
| inventory[].inputs | string | |
| inventory[].output | string | |
| inventory[].whereUsed | string | |
| inventory[].validation | string | |
| inventory[].evidence | string | |
| boundary | object | Policy version, auto-closable detectors and bands, minimum program history, always-reviewed and never-automated detectors, two-approver actions, hold-band rule, bands, autonomy eligibility, and each program's tier, version and granted detectors. |
| validation | object | Eval reports for the transaction Skoor, typologies and actor Skoors (cases, band accuracy, precision/recall with n, failures by case id), failuresTotal, and live calibration, hold-band precision/recall and coverage. |
| monitoring | Metrics | |
| changeControl | object | Stored policy versions (scope, version, active, createdAt), rule versions and the activation gate, the hash-chain report, and how the schema changes. |
| oversight | object | Attribution rule, decision clocks, decisions in window (total, by a person, under policy, overturned drafts, overturn rate with n), second-approver actions, the language model's role, model and whether it is configured. |
| data | object | Event coverage with the event types seen, the null rule, the no-fabrication test, retention. |
| mapping | object[] | |
| mapping[].framework | "SR 11-7" | "NIST AI RMF" | |
| mapping[].section | string | |
| mapping[].requirement | string | |
| mapping[].whereInSkoor | string | |
| mapping[].evidence | string | |
401 — Bearer token missing or unknown. Error
| Field | Type | Notes |
|---|
| error | string | What went wrong, in one sentence. |
| requestId (optional) | string | Present once the gateway is live: the X-Request-Id of the failed request. |
Example
curl -s "https://skoor.ai/risk/api/governance?tenant=demo&days=30" \
-H "Authorization: Bearer test_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
-H "X-Operator: you@bank.example"
get/api/public/metrics
The demo tenant's six numbers, public
| Auth | None (public) |
|---|
| Operation | Metrics · GET /api/public/metrics |
|---|
Parameters
| Name | In | Type | Notes |
|---|
| days (optional) | query | integer | Window in days, 1–90 (default 30; only the 30-day answer is cached for 60 s). |
Responses
200 — Metrics for the demo tenant, labelled simulated. object & Metrics
| Field | Type | Notes |
|---|
| tenant | string | |
| simulated | const true | |
| asOf | string (date-time) | |
| windowDays | integer | |
| calibration | object | Per band: labelled transfers that went bad ÷ labelled transfers. |
| calibration.clear | Measured | |
| calibration.review | Measured | |
| calibration.hold | Measured | |
| calibration.unscored | Measured | |
| coverage | object | |
| coverage.transfers | Measured | |
| coverage.programs | Measured | |
| holdBand | object | |
| holdBand.precision | Measured | |
| holdBand.recall | Measured | |
| holdBand.tp | integer | |
| holdBand.fp | integer | |
| holdBand.fn | integer | |
| autoClosed | Measured | |
| overturned | Measured | |
| timeToDecisionHours | object | |
| timeToDecisionHours.automated | object | Closed alerts routed automated. |
| timeToDecisionHours.automated.median | number | null | |
| timeToDecisionHours.automated.p90 | number | null | |
| timeToDecisionHours.automated.n | integer | |
| timeToDecisionHours.reviewed | object | Closed alerts routed reviewed. |
| timeToDecisionHours.reviewed.median | number | null | |
| timeToDecisionHours.reviewed.p90 | number | null | |
| timeToDecisionHours.reviewed.n | integer | |
| approvedWithoutWaiting | object | Clear-band transfers that never raised an alert. |
| approvedWithoutWaiting.count | integer | |
| approvedWithoutWaiting.cents | string | Integer cents as a string (bigint-safe). |
| approvedWithoutWaiting.n | integer | Scored transfers. |
| eventCoverage | object | Event families understood ÷ families known. |
| eventCoverage.value | number | null | |
| eventCoverage.n | integer | |
| eventCoverage.seen | string[] | |
404 — No demo tenant on this deployment. Error
| Field | Type | Notes |
|---|
| error | string | What went wrong, in one sentence. |
| requestId (optional) | string | Present once the gateway is live: the X-Request-Id of the failed request. |
Example
curl -s "https://skoor.ai/risk/api/public/metrics?days=30"
Report
Reports are built from rows the platform already persisted: the current Skoor with its reasons, history and band transitions, transfers in the window, alerts and dispositions. The record is two hash chains per tenant (dispositions, actions); verification recomputes every hash and reports the first break, and the export is JSON lines an examiner can walk for a date range.
ActorReport
No percentiles across banks; n and confidence say how much history stands behind the number.
| Field | Type | Notes |
|---|
| kind | const "actor" | |
| type | "entity" | "counterparty" | "agent" | |
| id | string | |
| name | string | null | |
| programId | string (uuid) | |
| generatedAt | string (date-time) | |
| current | object | null | |
| history | SkoorPoint[] | |
| transitions | BandTransition[] | |
| signalTrends | object | null | |
| transfers | TransfersSummary | |
| alerts | object[] | |
| alerts[].id | string (uuid) | |
| alerts[].detector | string | |
| alerts[].status | string | |
| alerts[].route | string | null | |
| alerts[].openedAt | string (date-time) | |
| alerts[].summary | string | |
| dispositions | object[] | |
| dispositions[].alertId | string (uuid) | |
| dispositions[].decidedBy | string | |
| dispositions[].decision | string | |
| dispositions[].overturnsDraft | boolean | null | |
| dispositions[].createdAt | string (date-time) | |
ProgramReport
| Field | Type | Notes |
|---|
| kind | const "program" | |
| id | string (uuid) | |
| name | string | |
| generatedAt | string (date-time) | |
| current | object | null | |
| history | object[] | |
| history[].asOf | string (date-time) | |
| history[].programSkoor | integer | null | |
| history[].band | "clear" | "review" | "hold" | "unscored" | |
| history[].n | integer | |
| transitions | BandTransition[] | |
| kris | Record<string, KriValue> | |
| kriDeltas | object[] | |
| kriDeltas[].name | string | |
| kriDeltas[].from | number | null | |
| kriDeltas[].to | number | null | |
| kriDeltas[].delta | number | null | |
| kriDeltas[].statusFrom | string | null | |
| kriDeltas[].statusTo | string | |
| kriDeltas[].n | integer | |
| rank | object | null | |
| transfers | TransfersSummary | |
| alertsByDetector | object[] | |
| alertsByDetector[].detector | string | |
| alertsByDetector[].route | string | null | |
| alertsByDetector[].count | integer | |
WeeklyReport
| Field | Type | Notes |
|---|
| kind | const "weekly" | |
| key | string | weekly:<programId>:<week> |
| programId | string (uuid) | |
| programName | string | |
| isoWeek | string | e.g. 2026-W37 |
| from | string (date-time) | |
| to | string (date-time) | |
| generatedAt | string (date-time) | |
| programSkoor | object | |
| programSkoor.current | integer | null | |
| programSkoor.prior | integer | null | |
| programSkoor.band | string | |
| programSkoor.n | integer | |
| kris | object[] | |
| kris[].name | string | |
| kris[].value | number | null | |
| kris[].prior | number | null | |
| kris[].delta | number | null | |
| kris[].status | string | |
| kris[].priorStatus | string | null | |
| kris[].n | integer | |
| kris[].unit | string | |
| kris[].threshold | number | null | |
| alerts | object | |
| alerts.total | integer | |
| alerts.byDetector | object[] | |
| alerts.byDetector[].detector | string | |
| alerts.byDetector[].route | string | null | |
| alerts.byDetector[].count | integer | |
| alerts.byStatus | Record<string, integer> | |
| enteredHold | object[] | |
| enteredHold[].type | string | |
| enteredHold[].id | string | |
| enteredHold[].skoor | integer | null | |
| enteredHold[].n | integer | |
| enteredHold[].at | string (date-time) | |
| dispositions | object | |
| dispositions.total | integer | |
| dispositions.byPlatform | integer | |
| dispositions.byPerson | integer | |
| dispositions.overturned | integer | |
| timeToDecisionHours | object | Closed alerts in the week. |
| timeToDecisionHours.median | number | null | |
| timeToDecisionHours.p90 | number | null | |
| timeToDecisionHours.n | integer | |
| transfers | object | |
| transfers.count | integer | |
| transfers.volumeCents | string | Integer cents as a string (bigint-safe). |
| transfers.returns | object | |
| transfers.returns.any | integer | |
| transfers.returns.unauthorized | integer | |
| transfers.bands | Record<string, integer> | |
| summary | string | Plain-text summary written from a template, never a model. |
ChainReport
| Field | Type | Notes |
|---|
| ok | boolean | |
| checked | integer | |
| firstBreakAt | object | null | |
| chains | object | |
| chains.dispositions | object | |
| chains.dispositions.ok | boolean | |
| chains.dispositions.checked | integer | |
| chains.actions | object | |
| chains.actions.ok | boolean | |
| chains.actions.checked | integer | |
RecordLine
One JSON object per line. policy first ({version, source: tenant|default, from, to}); then every Alert, Disposition and Action in the range, in time order, with all their fields.
| Field | Type | Notes |
|---|
| kind | "policy" | "alert" | "disposition" | "action" | |
Endpoints
get/api/reports/actor/{type}/{id}
Actor report
| Auth | Bearer + X-Operator |
|---|
| Operation | Report · GET /api/reports/actor/{type}/{id} |
|---|
Parameters
| Name | In | Type | Notes |
|---|
| type | path | "entity" | "counterparty" | "agent" | entity, counterparty or agent. |
| id | path | string | Actor id. |
| tenant (optional) | query | string | Tenant slug. Optional: the bearer token already proves the tenant. Pass it to be explicit. |
Responses
200 — The current Skoor with reasons, history, band transitions, signal trends, transfers, alerts and dispositions for the actor. ActorReport
| Field | Type | Notes |
|---|
| kind | const "actor" | |
| type | "entity" | "counterparty" | "agent" | |
| id | string | |
| name | string | null | |
| programId | string (uuid) | |
| generatedAt | string (date-time) | |
| current | object | null | |
| history | SkoorPoint[] | |
| transitions | BandTransition[] | |
| signalTrends | object | null | |
| transfers | TransfersSummary | |
| alerts | object[] | |
| alerts[].id | string (uuid) | |
| alerts[].detector | string | |
| alerts[].status | string | |
| alerts[].route | string | null | |
| alerts[].openedAt | string (date-time) | |
| alerts[].summary | string | |
| dispositions | object[] | |
| dispositions[].alertId | string (uuid) | |
| dispositions[].decidedBy | string | |
| dispositions[].decision | string | |
| dispositions[].overturnsDraft | boolean | null | |
| dispositions[].createdAt | string (date-time) | |
400 — type must be entity, counterparty, or agent. Error
| Field | Type | Notes |
|---|
| error | string | What went wrong, in one sentence. |
| requestId (optional) | string | Present once the gateway is live: the X-Request-Id of the failed request. |
401 — Bearer token missing or unknown. Error
| Field | Type | Notes |
|---|
| error | string | What went wrong, in one sentence. |
| requestId (optional) | string | Present once the gateway is live: the X-Request-Id of the failed request. |
404 — Unknown actor on this tenant. Error
| Field | Type | Notes |
|---|
| error | string | What went wrong, in one sentence. |
| requestId (optional) | string | Present once the gateway is live: the X-Request-Id of the failed request. |
Example
curl -s "https://skoor.ai/risk/api/reports/actor/entity/enti_sim_acme?tenant=demo" \
-H "Authorization: Bearer test_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
-H "X-Operator: you@bank.example"
get/api/reports/program/{id}
Program report
| Auth | Bearer + X-Operator |
|---|
| Operation | Report · GET /api/reports/program/{id} |
|---|
Parameters
| Name | In | Type | Notes |
|---|
| id | path | string (uuid) | Program id. |
| tenant (optional) | query | string | Tenant slug. Optional: the bearer token already proves the tenant. Pass it to be explicit. |
Responses
200 — The Program Risk Skoor with history, KRIs and their deltas, rank inside the bank, transfers and alerts by detector. ProgramReport
| Field | Type | Notes |
|---|
| kind | const "program" | |
| id | string (uuid) | |
| name | string | |
| generatedAt | string (date-time) | |
| current | object | null | |
| history | object[] | |
| history[].asOf | string (date-time) | |
| history[].programSkoor | integer | null | |
| history[].band | "clear" | "review" | "hold" | "unscored" | |
| history[].n | integer | |
| transitions | BandTransition[] | |
| kris | Record<string, KriValue> | |
| kriDeltas | object[] | |
| kriDeltas[].name | string | |
| kriDeltas[].from | number | null | |
| kriDeltas[].to | number | null | |
| kriDeltas[].delta | number | null | |
| kriDeltas[].statusFrom | string | null | |
| kriDeltas[].statusTo | string | |
| kriDeltas[].n | integer | |
| rank | object | null | |
| transfers | TransfersSummary | |
| alertsByDetector | object[] | |
| alertsByDetector[].detector | string | |
| alertsByDetector[].route | string | null | |
| alertsByDetector[].count | integer | |
401 — Bearer token missing or unknown. Error
| Field | Type | Notes |
|---|
| error | string | What went wrong, in one sentence. |
| requestId (optional) | string | Present once the gateway is live: the X-Request-Id of the failed request. |
404 — Unknown program on this tenant. Error
| Field | Type | Notes |
|---|
| error | string | What went wrong, in one sentence. |
| requestId (optional) | string | Present once the gateway is live: the X-Request-Id of the failed request. |
Example
curl -s "https://skoor.ai/risk/api/reports/program/a1e0c2d4-6b7f-4d8e-9a3b-0c1d2e3f4a55?tenant=demo" \
-H "Authorization: Bearer test_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
-H "X-Operator: you@bank.example"
get/api/reports/weekly/{programId}
Weekly program risk report
| Auth | Bearer + X-Operator |
|---|
| Operation | Report · GET /api/reports/weekly/{programId} |
|---|
Parameters
| Name | In | Type | Notes |
|---|
| programId | path | string (uuid) | Program id. |
| tenant (optional) | query | string | Tenant slug. Optional: the bearer token already proves the tenant. Pass it to be explicit. |
| week (optional) | query | string | ISO week (YYYY-Www). Default: the last complete week. |
| rebuild (optional) | query | "1" | Pass 1 to rebuild a stored report. |
Responses
200 — The stored report for the week, built on first request. WeeklyReport
| Field | Type | Notes |
|---|
| kind | const "weekly" | |
| key | string | weekly:<programId>:<week> |
| programId | string (uuid) | |
| programName | string | |
| isoWeek | string | e.g. 2026-W37 |
| from | string (date-time) | |
| to | string (date-time) | |
| generatedAt | string (date-time) | |
| programSkoor | object | |
| programSkoor.current | integer | null | |
| programSkoor.prior | integer | null | |
| programSkoor.band | string | |
| programSkoor.n | integer | |
| kris | object[] | |
| kris[].name | string | |
| kris[].value | number | null | |
| kris[].prior | number | null | |
| kris[].delta | number | null | |
| kris[].status | string | |
| kris[].priorStatus | string | null | |
| kris[].n | integer | |
| kris[].unit | string | |
| kris[].threshold | number | null | |
| alerts | object | |
| alerts.total | integer | |
| alerts.byDetector | object[] | |
| alerts.byDetector[].detector | string | |
| alerts.byDetector[].route | string | null | |
| alerts.byDetector[].count | integer | |
| alerts.byStatus | Record<string, integer> | |
| enteredHold | object[] | |
| enteredHold[].type | string | |
| enteredHold[].id | string | |
| enteredHold[].skoor | integer | null | |
| enteredHold[].n | integer | |
| enteredHold[].at | string (date-time) | |
| dispositions | object | |
| dispositions.total | integer | |
| dispositions.byPlatform | integer | |
| dispositions.byPerson | integer | |
| dispositions.overturned | integer | |
| timeToDecisionHours | object | Closed alerts in the week. |
| timeToDecisionHours.median | number | null | |
| timeToDecisionHours.p90 | number | null | |
| timeToDecisionHours.n | integer | |
| transfers | object | |
| transfers.count | integer | |
| transfers.volumeCents | string | Integer cents as a string (bigint-safe). |
| transfers.returns | object | |
| transfers.returns.any | integer | |
| transfers.returns.unauthorized | integer | |
| transfers.bands | Record<string, integer> | |
| summary | string | Plain-text summary written from a template, never a model. |
401 — Bearer token missing or unknown. Error
| Field | Type | Notes |
|---|
| error | string | What went wrong, in one sentence. |
| requestId (optional) | string | Present once the gateway is live: the X-Request-Id of the failed request. |
404 — Unknown program. Error
| Field | Type | Notes |
|---|
| error | string | What went wrong, in one sentence. |
| requestId (optional) | string | Present once the gateway is live: the X-Request-Id of the failed request. |
Example
curl -s "https://skoor.ai/risk/api/reports/weekly/a1e0c2d4-6b7f-4d8e-9a3b-0c1d2e3f4a55?tenant=demo&week=2026-W37" \
-H "Authorization: Bearer test_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
-H "X-Operator: you@bank.example"
get/api/record/verify
Verify the record chains
| Auth | Bearer + X-Operator |
|---|
| Operation | Report · GET /api/record/verify |
|---|
Parameters
| Name | In | Type | Notes |
|---|
| tenant (optional) | query | string | Tenant slug. Optional: the bearer token already proves the tenant. Pass it to be explicit. |
Responses
200 — Every disposition and action hash recomputed from stored fields; the first break, if any. ChainReport
| Field | Type | Notes |
|---|
| ok | boolean | |
| checked | integer | |
| firstBreakAt | object | null | |
| chains | object | |
| chains.dispositions | object | |
| chains.dispositions.ok | boolean | |
| chains.dispositions.checked | integer | |
| chains.actions | object | |
| chains.actions.ok | boolean | |
| chains.actions.checked | integer | |
401 — Bearer token missing or unknown. Error
| Field | Type | Notes |
|---|
| error | string | What went wrong, in one sentence. |
| requestId (optional) | string | Present once the gateway is live: the X-Request-Id of the failed request. |
Example
curl -s "https://skoor.ai/risk/api/record/verify?tenant=demo" \
-H "Authorization: Bearer test_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
-H "X-Operator: you@bank.example"
Response
{
"ok": true,
"checked": 412,
"firstBreakAt": null,
"chains": {
"dispositions": {
"ok": true,
"checked": 380
},
"actions": {
"ok": true,
"checked": 32
}
}
}
get/api/record/export
Examiner export (JSON lines)
| Auth | Bearer + X-Operator |
|---|
| Operation | Report · GET /api/record/export |
|---|
Parameters
| Name | In | Type | Notes |
|---|
| tenant (optional) | query | string | Tenant slug. Optional: the bearer token already proves the tenant. Pass it to be explicit. |
| from (optional) | query | string (date-time) | Start of the range (ISO 8601). Default 30 days ago. |
| to (optional) | query | string (date-time) | End of the range. Default now. |
Responses
200 — application/x-ndjson, served as an attachment: the policy line first, then alerts, dispositions and actions in time order. RecordLine · application/x-ndjson
| Field | Type | Notes |
|---|
| kind | "policy" | "alert" | "disposition" | "action" | |
400 — Bad date. Error
| Field | Type | Notes |
|---|
| error | string | What went wrong, in one sentence. |
| requestId (optional) | string | Present once the gateway is live: the X-Request-Id of the failed request. |
401 — Bearer token missing or unknown. Error
| Field | Type | Notes |
|---|
| error | string | What went wrong, in one sentence. |
| requestId (optional) | string | Present once the gateway is live: the X-Request-Id of the failed request. |
Example
curl -s "https://skoor.ai/risk/api/record/export?tenant=demo&from=2026-08-18T00%3A00%3A00Z&to=2026-09-17T00%3A00%3A00Z" \
-H "Authorization: Bearer test_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
-H "X-Operator: you@bank.example"
Webhook
Register an https endpoint and receive alert.opened, disposition.signed, action.created, action.approved, preflight.scored, transfer.scored, label.recorded, kri.snapshot as JSON, signed with Skoor-Signature: t=<unix seconds>,v1=<hex hmac-sha256(secret, "<t>.<body>")> plus Skoor-Event and Skoor-Delivery headers. Failed deliveries retry with backoff (2^attempts minutes, capped at 6 hours) up to 10 times, then are marked exhausted. The endpoint secret (whsec_…) is shown once.
WebhookEndpoint
| Field | Type | Notes |
|---|
| id | string (uuid) | |
| programId | string | null (uuid) | null = every program on the tenant. |
| url | string | https only (http://127.0.0.1 and http://localhost in test). |
| events | "alert.opened" | "disposition.signed" | "action.created" | "action.approved" | "preflight.scored" | "transfer.scored" | "label.recorded" | "kri.snapshot" | "ping"[] | [] = every event. |
| enabled | boolean | |
| description | string | null | |
| createdBy | string | |
| createdAt | string (date-time) | |
WebhookDelivery
| Field | Type | Notes |
|---|
| id | string (uuid) | |
| endpointId | string (uuid) | |
| event | "alert.opened" | "disposition.signed" | "action.created" | "action.approved" | "preflight.scored" | "transfer.scored" | "label.recorded" | "kri.snapshot" | "ping" | |
| eventId | string | Stable id of the emitted event, for the receiver's dedupe. |
| payload | object | { id, type, createdAt, tenant, program, data } |
| status | "pending" | "delivered" | "failed" | "exhausted" | |
| attempts | integer | |
| nextAttemptAt | string (date-time) | now + min(2^attempts minutes, 6h) after a failure. |
| lastStatusCode | integer | null | |
| lastError | string | null | |
| deliveredAt | string | null (date-time) | |
| createdAt | string (date-time) | |
Endpoints
post/api/webhooks
Register an endpoint
| Auth | Bearer + X-Operator |
|---|
| Operation | Webhook · POST /api/webhooks |
|---|
Parameters
| Name | In | Type | Notes |
|---|
| tenant (optional) | query | string | Tenant slug. Optional: the bearer token already proves the tenant. Pass it to be explicit. |
| Idempotency-Key (optional) | header | string | 1–255 characters. A replay with the same body returns the stored status and body with Idempotent-Replayed: true; a different body under the same key is 409. |
Request body (application/json, required) · object
| Field | Type | Notes |
|---|
| url | string | https only. |
| events (optional) | "alert.opened" | "disposition.signed" | "action.created" | "action.approved" | "preflight.scored" | "transfer.scored" | "label.recorded" | "kri.snapshot"[] | |
| programId (optional) | string (uuid) | null | |
| description (optional) | string | |
Responses
201 — The endpoint with its signing secret (whsec_…), shown once. object
| Field | Type | Notes |
|---|
| id | string (uuid) | |
| url | string | |
| events | string[] | |
| secret | string | |
400 — Validation failed. Error
| Field | Type | Notes |
|---|
| error | string | What went wrong, in one sentence. |
| requestId (optional) | string | Present once the gateway is live: the X-Request-Id of the failed request. |
401 — Bearer token missing or unknown. Error
| Field | Type | Notes |
|---|
| error | string | What went wrong, in one sentence. |
| requestId (optional) | string | Present once the gateway is live: the X-Request-Id of the failed request. |
Example
curl -s -X POST "https://skoor.ai/risk/api/webhooks?tenant=demo" \
-H "Authorization: Bearer test_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
-H "X-Operator: you@bank.example" \
-H "Content-Type: application/json" \
-d '{"url":"https://bank.example/skoor/webhooks","events":["alert.opened","preflight.scored"]}'
get/api/webhooks
List endpoints
| Auth | Bearer + X-Operator |
|---|
| Operation | Webhook · GET /api/webhooks |
|---|
Parameters
| Name | In | Type | Notes |
|---|
| tenant (optional) | query | string | Tenant slug. Optional: the bearer token already proves the tenant. Pass it to be explicit. |
Responses
200 — Endpoints on the tenant, without secrets. Array of WebhookEndpoint
| Field | Type | Notes |
|---|
| id | string (uuid) | |
| programId | string | null (uuid) | null = every program on the tenant. |
| url | string | https only (http://127.0.0.1 and http://localhost in test). |
| events | "alert.opened" | "disposition.signed" | "action.created" | "action.approved" | "preflight.scored" | "transfer.scored" | "label.recorded" | "kri.snapshot" | "ping"[] | [] = every event. |
| enabled | boolean | |
| description | string | null | |
| createdBy | string | |
| createdAt | string (date-time) | |
401 — Bearer token missing or unknown. Error
| Field | Type | Notes |
|---|
| error | string | What went wrong, in one sentence. |
| requestId (optional) | string | Present once the gateway is live: the X-Request-Id of the failed request. |
Example
curl -s "https://skoor.ai/risk/api/webhooks?tenant=demo" \
-H "Authorization: Bearer test_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
-H "X-Operator: you@bank.example"
get/api/webhooks/events
Event catalogue
The event types an endpoint may subscribe to, with one example payload each.
| Auth | Bearer + X-Operator |
|---|
| Operation | Webhook · GET /api/webhooks/events |
|---|
Parameters
| Name | In | Type | Notes |
|---|
| tenant (optional) | query | string | Tenant slug. Optional: the bearer token already proves the tenant. Pass it to be explicit. |
Responses
200 — Event names and example payloads. object
| Field | Type | Notes |
|---|
| events (optional) | string[] | |
| examples (optional) | object | |
401 — Bearer token missing or unknown. Error
| Field | Type | Notes |
|---|
| error | string | What went wrong, in one sentence. |
| requestId (optional) | string | Present once the gateway is live: the X-Request-Id of the failed request. |
Example
curl -s "https://skoor.ai/risk/api/webhooks/events?tenant=demo" \
-H "Authorization: Bearer test_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
-H "X-Operator: you@bank.example"
delete/api/webhooks/{id}
Disable an endpoint
| Auth | Bearer + X-Operator |
|---|
| Operation | Webhook · DELETE /api/webhooks/{id} |
|---|
Parameters
| Name | In | Type | Notes |
|---|
| id | path | string (uuid) | Endpoint id. |
| tenant (optional) | query | string | Tenant slug. Optional: the bearer token already proves the tenant. Pass it to be explicit. |
Responses
200 — Disabled. WebhookEndpoint
| Field | Type | Notes |
|---|
| id | string (uuid) | |
| programId | string | null (uuid) | null = every program on the tenant. |
| url | string | https only (http://127.0.0.1 and http://localhost in test). |
| events | "alert.opened" | "disposition.signed" | "action.created" | "action.approved" | "preflight.scored" | "transfer.scored" | "label.recorded" | "kri.snapshot" | "ping"[] | [] = every event. |
| enabled | boolean | |
| description | string | null | |
| createdBy | string | |
| createdAt | string (date-time) | |
401 — Bearer token missing or unknown. Error
| Field | Type | Notes |
|---|
| error | string | What went wrong, in one sentence. |
| requestId (optional) | string | Present once the gateway is live: the X-Request-Id of the failed request. |
404 — Not this tenant's endpoint. Error
| Field | Type | Notes |
|---|
| error | string | What went wrong, in one sentence. |
| requestId (optional) | string | Present once the gateway is live: the X-Request-Id of the failed request. |
Example
curl -s -X DELETE "https://skoor.ai/risk/api/webhooks/5f3c1c8e-2a44-4c0b-9d0f-2b0a2f6e7c11?tenant=demo" \
-H "Authorization: Bearer test_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
-H "X-Operator: you@bank.example"
get/api/webhooks/{id}/deliveries
Recent deliveries
| Auth | Bearer + X-Operator |
|---|
| Operation | Webhook · GET /api/webhooks/{id}/deliveries |
|---|
Parameters
| Name | In | Type | Notes |
|---|
| id | path | string (uuid) | Endpoint id. |
| tenant (optional) | query | string | Tenant slug. Optional: the bearer token already proves the tenant. Pass it to be explicit. |
| limit (optional) | query | integer | Newest first. |
Responses
200 — Deliveries, latest first. Array of WebhookDelivery
| Field | Type | Notes |
|---|
| id | string (uuid) | |
| endpointId | string (uuid) | |
| event | "alert.opened" | "disposition.signed" | "action.created" | "action.approved" | "preflight.scored" | "transfer.scored" | "label.recorded" | "kri.snapshot" | "ping" | |
| eventId | string | Stable id of the emitted event, for the receiver's dedupe. |
| payload | object | { id, type, createdAt, tenant, program, data } |
| status | "pending" | "delivered" | "failed" | "exhausted" | |
| attempts | integer | |
| nextAttemptAt | string (date-time) | now + min(2^attempts minutes, 6h) after a failure. |
| lastStatusCode | integer | null | |
| lastError | string | null | |
| deliveredAt | string | null (date-time) | |
| createdAt | string (date-time) | |
401 — Bearer token missing or unknown. Error
| Field | Type | Notes |
|---|
| error | string | What went wrong, in one sentence. |
| requestId (optional) | string | Present once the gateway is live: the X-Request-Id of the failed request. |
404 — Not this tenant's endpoint. Error
| Field | Type | Notes |
|---|
| error | string | What went wrong, in one sentence. |
| requestId (optional) | string | Present once the gateway is live: the X-Request-Id of the failed request. |
Example
curl -s "https://skoor.ai/risk/api/webhooks/5f3c1c8e-2a44-4c0b-9d0f-2b0a2f6e7c11/deliveries?tenant=demo&limit=20" \
-H "Authorization: Bearer test_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
-H "X-Operator: you@bank.example"
post/api/webhooks/{id}/deliveries/{deliveryId}/redeliver
Redeliver
| Auth | Bearer + X-Operator |
|---|
| Operation | Webhook · POST /api/webhooks/{id}/deliveries/{deliveryId}/redeliver |
|---|
Parameters
| Name | In | Type | Notes |
|---|
| id | path | string (uuid) | Endpoint id. |
| deliveryId | path | string (uuid) | Delivery id. |
| tenant (optional) | query | string | Tenant slug. Optional: the bearer token already proves the tenant. Pass it to be explicit. |
Responses
200 — Reset to pending; attempts unchanged. WebhookDelivery
| Field | Type | Notes |
|---|
| id | string (uuid) | |
| endpointId | string (uuid) | |
| event | "alert.opened" | "disposition.signed" | "action.created" | "action.approved" | "preflight.scored" | "transfer.scored" | "label.recorded" | "kri.snapshot" | "ping" | |
| eventId | string | Stable id of the emitted event, for the receiver's dedupe. |
| payload | object | { id, type, createdAt, tenant, program, data } |
| status | "pending" | "delivered" | "failed" | "exhausted" | |
| attempts | integer | |
| nextAttemptAt | string (date-time) | now + min(2^attempts minutes, 6h) after a failure. |
| lastStatusCode | integer | null | |
| lastError | string | null | |
| deliveredAt | string | null (date-time) | |
| createdAt | string (date-time) | |
401 — Bearer token missing or unknown. Error
| Field | Type | Notes |
|---|
| error | string | What went wrong, in one sentence. |
| requestId (optional) | string | Present once the gateway is live: the X-Request-Id of the failed request. |
404 — Not this tenant's delivery. Error
| Field | Type | Notes |
|---|
| error | string | What went wrong, in one sentence. |
| requestId (optional) | string | Present once the gateway is live: the X-Request-Id of the failed request. |
Example
curl -s -X POST "https://skoor.ai/risk/api/webhooks/5f3c1c8e-2a44-4c0b-9d0f-2b0a2f6e7c11/deliveries/a1e0c2d4-6b7f-4d8e-9a3b-0c1d2e3f4a55/redeliver?tenant=demo" \
-H "Authorization: Bearer test_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
-H "X-Operator: you@bank.example"
post/api/webhooks/{id}/test
Send a ping
| Auth | Bearer + X-Operator |
|---|
| Operation | Webhook · POST /api/webhooks/{id}/test |
|---|
Parameters
| Name | In | Type | Notes |
|---|
| id | path | string (uuid) | Endpoint id. |
| tenant (optional) | query | string | Tenant slug. Optional: the bearer token already proves the tenant. Pass it to be explicit. |
Responses
200 — A ping delivery enqueued. WebhookDelivery
| Field | Type | Notes |
|---|
| id | string (uuid) | |
| endpointId | string (uuid) | |
| event | "alert.opened" | "disposition.signed" | "action.created" | "action.approved" | "preflight.scored" | "transfer.scored" | "label.recorded" | "kri.snapshot" | "ping" | |
| eventId | string | Stable id of the emitted event, for the receiver's dedupe. |
| payload | object | { id, type, createdAt, tenant, program, data } |
| status | "pending" | "delivered" | "failed" | "exhausted" | |
| attempts | integer | |
| nextAttemptAt | string (date-time) | now + min(2^attempts minutes, 6h) after a failure. |
| lastStatusCode | integer | null | |
| lastError | string | null | |
| deliveredAt | string | null (date-time) | |
| createdAt | string (date-time) | |
401 — Bearer token missing or unknown. Error
| Field | Type | Notes |
|---|
| error | string | What went wrong, in one sentence. |
| requestId (optional) | string | Present once the gateway is live: the X-Request-Id of the failed request. |
404 — Not this tenant's endpoint. Error
| Field | Type | Notes |
|---|
| error | string | What went wrong, in one sentence. |
| requestId (optional) | string | Present once the gateway is live: the X-Request-Id of the failed request. |
Example
curl -s -X POST "https://skoor.ai/risk/api/webhooks/5f3c1c8e-2a44-4c0b-9d0f-2b0a2f6e7c11/test?tenant=demo" \
-H "Authorization: Bearer test_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
-H "X-Operator: you@bank.example"
Sandbox
Build Column-shaped events against a simulated program and push them through the same ingest, scoring, detection and triage as production, then read the result: the transfer's Skoor with its n, the alerts it opened, the label a return produced. Every endpoint requires a test principal and a program whose source is simulated; a Column-sourced program is refused (403).
SandboxResult
| Field | Type | Notes |
|---|
| events | string[] | Ids of the Column-shaped events stored (source = simulated). |
| transferId (optional) | string | |
| alertIds | string (uuid)[] | |
| skoor | object | null | |
Endpoints
post/api/sandbox/transfers
Simulate a transfer through its Column lifecycle events and score it.
Builds the same event sequence Column would deliver (ACH: initiated → submitted → settled [→ completed]; wire/realtime: initiated → completed; book: completed; card: authorization) and runs scoring, detection and triage before answering. entityId and counterpartyId must already exist on the program; without a counterparty one is minted.
| Auth | Bearer + X-Operator |
|---|
| Operation | Sandbox · POST /api/sandbox/transfers |
|---|
Parameters
| Name | In | Type | Notes |
|---|
| tenant (optional) | query | string | Tenant slug. Optional: the bearer token already proves the tenant. Pass it to be explicit. |
| Idempotency-Key (optional) | header | string | 1–255 characters. A replay with the same body returns the stored status and body with Idempotent-Replayed: true; a different body under the same key is 409. |
Request body (application/json, required) · object
| Field | Type | Notes |
|---|
| programId (optional) | string (uuid) | The simulated program. One of programId / programSlug is required. |
| programSlug (optional) | string | The simulated program by slug. |
| rail | "ach" | "wire" | "book" | "realtime" | "card" | |
| direction | "incoming" | "outgoing" | |
| amountCents | integer | string | Integer cents. A numeric string is accepted for amounts above 2^53. |
| status (optional) | "initiated" | "settled" | "completed" | "manual_review" | "hold" | Final state of the lifecycle. completed records an ok outcome label, as a completed Column transfer does. |
| type (optional) | "CREDIT" | "DEBIT" | ACH only. Defaults to CREDIT outgoing, DEBIT incoming. |
| entityId (optional) | string | |
| counterpartyId (optional) | string | |
| counterpartyCountry (optional) | string | |
| memo (optional) | string | |
| at (optional) | string (date-time) | Column created_at of the transfer. Defaults to now; backdate to build history. |
| cardDecision (optional) | "approved" | "declined" | card only |
| cardDecisionReason (optional) | string | card only, e.g. suspected_fraud |
Responses
200 — What the pipeline produced. object
| Field | Type | Notes |
|---|
| program | string | Program slug. |
| events | string[] | Ids of the Column-shaped events stored, in order. |
| transferId | string | |
| transfer | SandboxTransfer | |
| skoor | Skoor | |
| alertIds | string (uuid)[] | |
| alerts | SandboxAlert[] | |
| labels | SandboxLabel[] | |
401 — Bearer token missing or unknown. Error
| Field | Type | Notes |
|---|
| error | string | What went wrong, in one sentence. |
| requestId (optional) | string | Present once the gateway is live: the X-Request-Id of the failed request. |
403 — Not a test principal, or the program is Column-sourced. Error
| Field | Type | Notes |
|---|
| error | string | What went wrong, in one sentence. |
| requestId (optional) | string | Present once the gateway is live: the X-Request-Id of the failed request. |
Example
curl -s -X POST "https://skoor.ai/risk/api/sandbox/transfers?tenant=demo" \
-H "Authorization: Bearer test_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
-H "X-Operator: you@bank.example" \
-H "Content-Type: application/json" \
-d '{"request":{"programSlug":"northwind-payroll","rail":"ach","direction":"outgoing","type":"DEBIT","amountCents":250000,"entityId":"enti_sb_1a2b3c4d5e6f7081","memo":"vendor payment"},"response":{"program":"northwind-payroll","events":["evnt_sb_m1a2b3c4_5e6f7a8b1","evnt_sb_m1a2b3c4_9c0d1e2f2","evnt_sb_m1a2b3c4_3a4b5c6d3"],"transferId":"acht_sb_9f2c1e0a7b3d4c55","transfer":{"id":"acht_sb_9f2c1e0a7b3d4c55","rail":"ach","direction":"outgoing","type":"DEBIT","amountCents":"250000","currency":"USD","status":"SETTLED","entityId":"enti_sb_1a2b3c4d5e6f7081","counterpartyId":"cpty_sb_0a1b2c3d4e5f6071","counterpartyCountry":"US","returnCode":null,"columnCreatedAt":"2026-09-17T15:04:05.000Z"},"skoor":{"value":null,"band":"unscored","n":0,"version":"trs-v1","confidence":null},"alertIds":[],"alerts":[],"labels":[]}}'
post/api/sandbox/transfers/{id}/return
Return an ACH transfer with a NACHA code; the outcome label follows production rules.
Replays the stored transfer as ach.*_transfer.returned with return_details. Unauthorized codes (R05, R07, R10, R11, R29, R51) record a bad label; NSF and administrative codes record returned. Only ACH transfers can be returned.
| Auth | Bearer + X-Operator |
|---|
| Operation | Sandbox · POST /api/sandbox/transfers/{id}/return |
|---|
Parameters
| Name | In | Type | Notes |
|---|
| id | path | string | id |
| tenant (optional) | query | string | Tenant slug. Optional: the bearer token already proves the tenant. Pass it to be explicit. |
| Idempotency-Key (optional) | header | string | 1–255 characters. A replay with the same body returns the stored status and body with Idempotent-Replayed: true; a different body under the same key is 409. |
Request body (application/json, required) · object
| Field | Type | Notes |
|---|
| code | "R01" | "R02" | "R03" | "R04" | "R05" | "R07" | "R08" | "R10" | "R11" | "R16" | "R29" | "R51" | |
| at (optional) | string (date-time) | |
Responses
200 — What the pipeline produced. object
| Field | Type | Notes |
|---|
| program | string | Program slug. |
| events | string[] | Ids of the Column-shaped events stored, in order. |
| transferId | string | |
| transfer | SandboxTransfer | |
| skoor | Skoor | |
| alertIds | string (uuid)[] | |
| alerts | SandboxAlert[] | |
| labels | SandboxLabel[] | |
401 — Bearer token missing or unknown. Error
| Field | Type | Notes |
|---|
| error | string | What went wrong, in one sentence. |
| requestId (optional) | string | Present once the gateway is live: the X-Request-Id of the failed request. |
403 — Not a test principal, or the program is Column-sourced. Error
| Field | Type | Notes |
|---|
| error | string | What went wrong, in one sentence. |
| requestId (optional) | string | Present once the gateway is live: the X-Request-Id of the failed request. |
Example
curl -s -X POST "https://skoor.ai/risk/api/sandbox/transfers/acht_sandbox_0001/return?tenant=demo" \
-H "Authorization: Bearer test_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
-H "X-Operator: you@bank.example" \
-H "Content-Type: application/json" \
-d '{"request":{"code":"R10"},"response":{"program":"northwind-payroll","events":["evnt_sb_m1a2b3c4_7e8f9a0b4"],"transferId":"acht_sb_9f2c1e0a7b3d4c55","transfer":{"id":"acht_sb_9f2c1e0a7b3d4c55","rail":"ach","direction":"outgoing","type":"DEBIT","amountCents":"250000","currency":"USD","status":"RETURNED","entityId":"enti_sb_1a2b3c4d5e6f7081","counterpartyId":"cpty_sb_0a1b2c3d4e5f6071","counterpartyCountry":"US","returnCode":"R10","columnCreatedAt":"2026-09-17T15:04:05.000Z"},"skoor":{"value":null,"band":"unscored","n":0,"version":"trs-v1","confidence":null},"alertIds":[],"alerts":[],"labels":[{"label":"bad","source":"outcome:return","detail":"R10:unauthorized"}]}}'
post/api/sandbox/transfers/{id}/dispute
Open a cardholder dispute on a card transaction.
Replays the stored card transaction as card.transaction.dispute (event_type: dispute). The transfer row moves to status DISPUTE and is re-scored. Only card transactions can be disputed.
| Auth | Bearer + X-Operator |
|---|
| Operation | Sandbox · POST /api/sandbox/transfers/{id}/dispute |
|---|
Parameters
| Name | In | Type | Notes |
|---|
| id | path | string | id |
| tenant (optional) | query | string | Tenant slug. Optional: the bearer token already proves the tenant. Pass it to be explicit. |
| Idempotency-Key (optional) | header | string | 1–255 characters. A replay with the same body returns the stored status and body with Idempotent-Replayed: true; a different body under the same key is 409. |
Request body (application/json, required) · object
| Field | Type | Notes |
|---|
| reason (optional) | "fraud" | "not_received" | "duplicate" | "other" | |
| at (optional) | string (date-time) | |
Responses
200 — What the pipeline produced. object
| Field | Type | Notes |
|---|
| program | string | Program slug. |
| events | string[] | Ids of the Column-shaped events stored, in order. |
| transferId | string | |
| transfer | SandboxTransfer | |
| skoor | Skoor | |
| alertIds | string (uuid)[] | |
| alerts | SandboxAlert[] | |
| labels | SandboxLabel[] | |
401 — Bearer token missing or unknown. Error
| Field | Type | Notes |
|---|
| error | string | What went wrong, in one sentence. |
| requestId (optional) | string | Present once the gateway is live: the X-Request-Id of the failed request. |
403 — Not a test principal, or the program is Column-sourced. Error
| Field | Type | Notes |
|---|
| error | string | What went wrong, in one sentence. |
| requestId (optional) | string | Present once the gateway is live: the X-Request-Id of the failed request. |
Example
curl -s -X POST "https://skoor.ai/risk/api/sandbox/transfers/acht_sandbox_0001/dispute?tenant=demo" \
-H "Authorization: Bearer test_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
-H "X-Operator: you@bank.example" \
-H "Content-Type: application/json" \
-d '{"request":{"reason":"fraud"},"response":{"program":"northwind-payroll","events":["evnt_sb_m1a2b3c4_1c2d3e4f5"],"transferId":"ctxn_sb_5d6e7f8091a2b3c4","transfer":{"id":"ctxn_sb_5d6e7f8091a2b3c4","rail":"card","direction":"outgoing","type":null,"amountCents":"250000","currency":"USD","status":"DISPUTE","entityId":"enti_sb_1a2b3c4d5e6f7081","counterpartyId":null,"counterpartyCountry":"US","returnCode":null,"columnCreatedAt":"2026-09-17T15:04:05.000Z"},"skoor":{"value":null,"band":"unscored","n":0,"version":"trs-v1","confidence":null},"alertIds":[],"alerts":[],"labels":[]}}'
post/api/sandbox/entities
Onboard an entity through identity.verification events.
Emits identity.verification.created (UNVERIFIED), .pending, then the final status event, seconds apart, and runs the entity Skoor. agent marks the entity as an AI agent acting under an operator.
| Auth | Bearer + X-Operator |
|---|
| Operation | Sandbox · POST /api/sandbox/entities |
|---|
Parameters
| Name | In | Type | Notes |
|---|
| tenant (optional) | query | string | Tenant slug. Optional: the bearer token already proves the tenant. Pass it to be explicit. |
| Idempotency-Key (optional) | header | string | 1–255 characters. A replay with the same body returns the stored status and body with Idempotent-Replayed: true; a different body under the same key is 409. |
Request body (application/json, required) · object
| Field | Type | Notes |
|---|
| programId (optional) | string (uuid) | The simulated program. One of programId / programSlug is required. |
| programSlug (optional) | string | The simulated program by slug. |
| type | "PERSON" | "BUSINESS" | |
| name | string | |
| verificationStatus (optional) | "UNVERIFIED" | "PENDING" | "VERIFIED" | "MANUAL_REVIEW" | "DENIED" | |
| pepStatus (optional) | "not_checked" | "no" | "yes" | "potential" | |
| isHighRisk (optional) | boolean | |
| requiresScreening (optional) | boolean | |
| country (optional) | string | |
| expectedPaymentRails (optional) | string[] | |
| agent (optional) | object | |
| agent.operatorEntityId (optional) | string | |
| agent.purpose | string | |
| agent.scope (optional) | string[] | |
| at (optional) | string (date-time) | Entity created_at. Defaults to now. |
Responses
200 — What the pipeline produced. object
| Field | Type | Notes |
|---|
| program | string | |
| events | string[] | |
| entityId | string | |
| entity | SandboxEntity | |
401 — Bearer token missing or unknown. Error
| Field | Type | Notes |
|---|
| error | string | What went wrong, in one sentence. |
| requestId (optional) | string | Present once the gateway is live: the X-Request-Id of the failed request. |
403 — Not a test principal, or the program is Column-sourced. Error
| Field | Type | Notes |
|---|
| error | string | What went wrong, in one sentence. |
| requestId (optional) | string | Present once the gateway is live: the X-Request-Id of the failed request. |
Example
curl -s -X POST "https://skoor.ai/risk/api/sandbox/entities?tenant=demo" \
-H "Authorization: Bearer test_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
-H "X-Operator: you@bank.example" \
-H "Content-Type: application/json" \
-d '{"request":{"programSlug":"northwind-payroll","type":"PERSON","name":"Dana Whitfield","verificationStatus":"VERIFIED"},"response":{"program":"northwind-payroll","events":["evnt_sb_m1a2b3c4_a1b2c3d46","evnt_sb_m1a2b3c4_e5f6a7b87","evnt_sb_m1a2b3c4_c9d0e1f28"],"entityId":"enti_sb_1a2b3c4d5e6f7081","entity":{"id":"enti_sb_1a2b3c4d5e6f7081","type":"PERSON","name":"Dana Whitfield","verificationStatus":"VERIFIED","isHighRisk":false,"pepStatus":"no","countryCode":"US"}}}'
post/api/sandbox/entities/{id}/verification
Change an entity’s verification decision.
Emits one identity.verification.<status> event for an entity already on the program (DENIED adds sanctions_match, MANUAL_REVIEW adds name_mismatch to review_reasons, as the simulator does).
| Auth | Bearer + X-Operator |
|---|
| Operation | Sandbox · POST /api/sandbox/entities/{id}/verification |
|---|
Parameters
| Name | In | Type | Notes |
|---|
| id | path | string | id |
| tenant (optional) | query | string | Tenant slug. Optional: the bearer token already proves the tenant. Pass it to be explicit. |
| Idempotency-Key (optional) | header | string | 1–255 characters. A replay with the same body returns the stored status and body with Idempotent-Replayed: true; a different body under the same key is 409. |
Request body (application/json, required) · object
| Field | Type | Notes |
|---|
| programId (optional) | string (uuid) | The simulated program. One of programId / programSlug is required. |
| programSlug (optional) | string | The simulated program by slug. |
| status | "VERIFIED" | "MANUAL_REVIEW" | "DENIED" | |
| at (optional) | string (date-time) | |
Responses
200 — What the pipeline produced. object
| Field | Type | Notes |
|---|
| program | string | |
| events | string[] | |
| entityId | string | |
| entity | SandboxEntity | |
401 — Bearer token missing or unknown. Error
| Field | Type | Notes |
|---|
| error | string | What went wrong, in one sentence. |
| requestId (optional) | string | Present once the gateway is live: the X-Request-Id of the failed request. |
403 — Not a test principal, or the program is Column-sourced. Error
| Field | Type | Notes |
|---|
| error | string | What went wrong, in one sentence. |
| requestId (optional) | string | Present once the gateway is live: the X-Request-Id of the failed request. |
Example
curl -s -X POST "https://skoor.ai/risk/api/sandbox/entities/enti_sandbox_0001/verification?tenant=demo" \
-H "Authorization: Bearer test_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
-H "X-Operator: you@bank.example" \
-H "Content-Type: application/json" \
-d '{"request":{"programSlug":"northwind-payroll","status":"DENIED"},"response":{"program":"northwind-payroll","events":["evnt_sb_m1a2b3c4_0a1b2c3d9"],"entityId":"enti_sb_1a2b3c4d5e6f7081","entity":{"id":"enti_sb_1a2b3c4d5e6f7081","type":"PERSON","name":"Dana Whitfield","verificationStatus":"DENIED","isHighRisk":false,"pepStatus":"no","countryCode":"US"}}}'
post/api/sandbox/counterparties
Register a counterparty for later transfers.
Column has no counterparty webhook (counterparties arrive embedded in transfer events), so this writes the row directly with a Column-shaped counterparty object in raw. Use the returned id as counterpartyId on transfers.
| Auth | Bearer + X-Operator |
|---|
| Operation | Sandbox · POST /api/sandbox/counterparties |
|---|
Parameters
| Name | In | Type | Notes |
|---|
| tenant (optional) | query | string | Tenant slug. Optional: the bearer token already proves the tenant. Pass it to be explicit. |
| Idempotency-Key (optional) | header | string | 1–255 characters. A replay with the same body returns the stored status and body with Idempotent-Replayed: true; a different body under the same key is 409. |
Request body (application/json, required) · object
| Field | Type | Notes |
|---|
| programId (optional) | string (uuid) | The simulated program. One of programId / programSlug is required. |
| programSlug (optional) | string | The simulated program by slug. |
| name | string | |
| country (optional) | string | |
| routingNumber (optional) | string | |
| at (optional) | string (date-time) | |
Responses
200 — What the pipeline produced. object
| Field | Type | Notes |
|---|
| program | string | |
| counterpartyId | string | |
| counterparty | SandboxCounterparty | |
401 — Bearer token missing or unknown. Error
| Field | Type | Notes |
|---|
| error | string | What went wrong, in one sentence. |
| requestId (optional) | string | Present once the gateway is live: the X-Request-Id of the failed request. |
403 — Not a test principal, or the program is Column-sourced. Error
| Field | Type | Notes |
|---|
| error | string | What went wrong, in one sentence. |
| requestId (optional) | string | Present once the gateway is live: the X-Request-Id of the failed request. |
Example
curl -s -X POST "https://skoor.ai/risk/api/sandbox/counterparties?tenant=demo" \
-H "Authorization: Bearer test_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
-H "X-Operator: you@bank.example" \
-H "Content-Type: application/json" \
-d '{"request":{"programSlug":"northwind-payroll","name":"Blue Ridge Landscaping","country":"US","routingNumber":"021000021"},"response":{"program":"northwind-payroll","counterpartyId":"cpty_sb_0a1b2c3d4e5f6071","counterparty":{"id":"cpty_sb_0a1b2c3d4e5f6071","name":"Blue Ridge Landscaping","countryCode":"US","routingNumber":"021000021"}}}'
post/api/sandbox/patterns/{name}
Play a typology the detectors know and return the alerts it opened.
Patterns: structuring → structuring_pattern (Four ACH debits of $9,100–$9,900 from one entity inside 24 hours (5 hours apart). Hard signal: the third and fourth are held.); just_below_threshold → amount_just_below_threshold (A single $9,500 ACH debit with no other sub-threshold debit in 24 hours.); velocity → velocity_spike (Seven ACH credits from one entity inside two hours; the sixth onward exceeds five in 24 hours.); round_trip → round_trip (An outgoing ACH to a counterparty three days ago, then the same amount back from it today.); layering → layering_chain (Three similar-amount hops inside 48 hours: entity A → counterparty X, X → the entity, the entity → counterparty Y.); dormant_reactivation → dormant_reactivation (One transfer 75 days ago, silence, then three transfers inside 24 hours.); burst_then_dormant → burst_then_dormant (Six transfers on one day 20 days ago and nothing since; a KRI snapshot is taken so the snapshot-time detector runs.). Without entityId/counterpartyId the play creates its own actors. at is the play's end time (default now); history is placed relative to it.
| Auth | Bearer + X-Operator |
|---|
| Operation | Sandbox · POST /api/sandbox/patterns/{name} |
|---|
Parameters
| Name | In | Type | Notes |
|---|
| name | path | string | name |
| tenant (optional) | query | string | Tenant slug. Optional: the bearer token already proves the tenant. Pass it to be explicit. |
| Idempotency-Key (optional) | header | string | 1–255 characters. A replay with the same body returns the stored status and body with Idempotent-Replayed: true; a different body under the same key is 409. |
Request body (application/json, required) · object
| Field | Type | Notes |
|---|
| programId (optional) | string (uuid) | The simulated program. One of programId / programSlug is required. |
| programSlug (optional) | string | The simulated program by slug. |
| entityId (optional) | string | |
| counterpartyId (optional) | string | |
| at (optional) | string (date-time) | |
Responses
200 — What the pipeline produced. object
| Field | Type | Notes |
|---|
| program | string | |
| pattern | "structuring" | "just_below_threshold" | "velocity" | "round_trip" | "layering" | "dormant_reactivation" | "burst_then_dormant" | |
| expectedDetector | string | |
| events | string[] | |
| transferIds | string[] | |
| entityId | string | |
| entityIds | string[] | Entities the play created. |
| counterpartyIds | string[] | |
| alertIds | string (uuid)[] | |
| alerts | SandboxAlert[] | |
| snapshotId (optional) | string (uuid) | burst_then_dormant only: the KRI snapshot taken. |
401 — Bearer token missing or unknown. Error
| Field | Type | Notes |
|---|
| error | string | What went wrong, in one sentence. |
| requestId (optional) | string | Present once the gateway is live: the X-Request-Id of the failed request. |
403 — Not a test principal, or the program is Column-sourced. Error
| Field | Type | Notes |
|---|
| error | string | What went wrong, in one sentence. |
| requestId (optional) | string | Present once the gateway is live: the X-Request-Id of the failed request. |
Example
curl -s -X POST "https://skoor.ai/risk/api/sandbox/patterns/structuring?tenant=demo" \
-H "Authorization: Bearer test_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
-H "X-Operator: you@bank.example" \
-H "Content-Type: application/json" \
-d '{"request":{"programSlug":"northwind-payroll"},"response":{"program":"northwind-payroll","pattern":"structuring","expectedDetector":"structuring_pattern","events":["evnt_sb_m1a2b3c4_0000000a1","evnt_sb_m1a2b3c4_0000000a2"],"transferIds":["acht_sb_01","acht_sb_02","acht_sb_03","acht_sb_04"],"entityId":"enti_sb_1a2b3c4d5e6f7081","entityIds":["enti_sb_1a2b3c4d5e6f7081"],"counterpartyIds":["cpty_sb_0a1b2c3d4e5f6071"],"alertIds":["0d4c7b6e-2f1a-4e5b-9c8d-7a6b5c4d3e2f"],"alerts":[{"id":"0d4c7b6e-2f1a-4e5b-9c8d-7a6b5c4d3e2f","detector":"structuring_pattern","severity":"high","subjectType":"entity","subjectId":"enti_sb_1a2b3c4d5e6f7081","status":"open","route":"reviewed"}]}}'
post/api/sandbox/reset
Delete a simulated program’s data rows.
Removes labels, dispositions, actions, alerts, actor Skoors, pre-flights, KRI snapshots, events, transfers, counterparties, accounts and entities of the program, in dependency order. Configuration (policies, keys, webhook endpoints) stays. Refused on a Column-sourced program.
| Auth | Bearer + X-Operator |
|---|
| Operation | Sandbox · POST /api/sandbox/reset |
|---|
Parameters
| Name | In | Type | Notes |
|---|
| tenant (optional) | query | string | Tenant slug. Optional: the bearer token already proves the tenant. Pass it to be explicit. |
| Idempotency-Key (optional) | header | string | 1–255 characters. A replay with the same body returns the stored status and body with Idempotent-Replayed: true; a different body under the same key is 409. |
Request body (application/json, required) · object
| Field | Type | Notes |
|---|
| programId (optional) | string (uuid) | The simulated program. One of programId / programSlug is required. |
| programSlug (optional) | string | The simulated program by slug. |
Responses
200 — What the pipeline produced. object
| Field | Type | Notes |
|---|
| program | string | |
| reset | boolean | |
| counts | Record<string, integer> | |
401 — Bearer token missing or unknown. Error
| Field | Type | Notes |
|---|
| error | string | What went wrong, in one sentence. |
| requestId (optional) | string | Present once the gateway is live: the X-Request-Id of the failed request. |
403 — Not a test principal, or the program is Column-sourced. Error
| Field | Type | Notes |
|---|
| error | string | What went wrong, in one sentence. |
| requestId (optional) | string | Present once the gateway is live: the X-Request-Id of the failed request. |
Example
curl -s -X POST "https://skoor.ai/risk/api/sandbox/reset?tenant=demo" \
-H "Authorization: Bearer test_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
-H "X-Operator: you@bank.example" \
-H "Content-Type: application/json" \
-d '{"request":{"programSlug":"northwind-payroll"},"response":{"program":"northwind-payroll","reset":true,"counts":{"labels":1,"dispositions":0,"actions":0,"alerts":2,"actor_skoors":3,"preflights":0,"kri_snapshots":0,"events":12,"transfers":4,"counterparties":1,"accounts":0,"entities":1}}}'