SKOOR Risk Money movement. Skoored by AI.

API reference · 2026-09-17

Every dollar that moves, scored, decided, on the record.

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.

Base URLhttps://skoor.ai/risk
AuthAuthorization: Bearer <operator token or API key> proves the tenant; X-Operator: <who is acting> names the person and is written on every decision. Placeholder key in the examples: test_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.
Machine-readableopenapi.json (OpenAPI 3.1) · Getting started
Coverage54 operations across 20 objects: 54 live, 0 planned with the keys, webhooks and sandbox forks.

Program

A business that banks on the tenant.

A program is a fintech platform on the bank's rails: payroll, remittance, marketplace, lending. Column identifies it by the webhook endpoint that delivered the event. Its declared activity (volume, rails, countries) is what the Skoor reads as "expected". Programs are created by the platform admin; a tenant reads them through reports, and rescoring runs per program.

Program

FieldTypeNotes
idstring (uuid)
tenantIdstring (uuid)The bank this program banks on.
slugstringStable, URL-safe name; unique per tenant.
namestring
webhookEndpointIdstring | nullColumn Webhook-Endpoint-Id header that identifies the platform delivering events.
declaredMonthlyVolumeCentsstring | nullWhat the program declared at onboarding; the Skoor reads it as expected activity. Integer cents as a string, or null.
declaredRailsstring[]Rails declared at onboarding.
declaredCountriesstring[]Countries declared at onboarding (default ["US"]).
programReserveAccountIdstring | nullReserve account whose balance backs the reserve-coverage KRI.
source"column" | "simulated"Where the program's events come from. Simulated programs are labelled everywhere.
createdAtstring (date-time)

No dedicated endpoint. This object is read through the endpoints of the objects that reference it.

Transfer

A dollar moving on a rail, with its Transaction Risk Skoor.

Every transfer the bank's webhooks describe, on any rail, keyed by Column's id. The Transaction Risk Skoor (trs-v1) is a number from 0 to 100 stored on the row with its band, version, n and confidence; null means too little history, never zero risk. Bands: clear 0–29, review 30–69, hold 70–100. Transfers are written by ingest; the API rescores them and reads them inside reports and pre-flights.

Transfer

FieldTypeNotes
idstringColumn transfer id, any rail.
tenantIdstring (uuid)
programIdstring (uuid)
rail"ach" | "wire" | "realtime" | "book" | "intl_wire" | "check" | "card"
direction"incoming" | "outgoing"
type"CREDIT" | "DEBIT" | nullACH type, null on other rails.
amountCentsstringInteger cents as a string (bigint-safe).
currencystringISO 4217, default USD.
statusstringColumn's status enum, upper-cased.
entityIdstring | null
accountIdstring | null
counterpartyIdstring | null
counterpartyCountrystring | nullISO 3166-1 alpha-2.
secCodestring | nullACH SEC code.
returnCodestring | nullACH return code when returned (R01…).
returnedAtstring | null (date-time)
effectiveAtstring | null (date-time)
columnCreatedAtstring (date-time)When the bank created it.
skoorinteger | nullTransaction Risk Skoor 0–100; null until scored or when history is too thin.
skoorBand"clear" | "review" | "hold" | "unscored" | null
skoorVersionstring | nullScorer version, e.g. trs-v1.
skoorNinteger | nullObservations behind the baselines.
skoorConfidencenumber | null0–1; null when the Skoor is null.
skoorInputsobject | null
skooredAtstring | null (date-time)
rawobjectThe bank's own object as last delivered.
updatedAtstring (date-time)

Reason

FieldTypeNotes
idstringStable reason id, e.g. TRS-ENT-01.
signalstringThe signal code the scorer emitted.
weightnumberPoints added.
hardboolean
sentencestringWhat a compliance officer reads.
detail (optional)string

SkoorSignal

FieldTypeNotes
codestring
weightnumber
hard (optional)boolean
detail (optional)string

Endpoints

post/api/skoor/backfill/{programId}

Rescore every transfer on a program

Recomputes the Transaction Risk Skoor for every transfer on the program with the current scorer. The bearer must be the operator token of the tenant that owns the program; X-Operator is not required.

AuthBearer (program's tenant); no X-Operator
OperationTransfer · POST /api/skoor/backfill/{programId}

Parameters

NameInTypeNotes
programIdpathstring (uuid)Program id.

Responses

200 — How many transfers were scored. object

FieldTypeNotes
scoredinteger

401 — Bearer is not the owning tenant's operator token. Error

FieldTypeNotes
errorstringWhat went wrong, in one sentence.
requestId (optional)stringPresent once the gateway is live: the X-Request-Id of the failed request.

Example

curl -s -X POST "https://skoor.ai/risk/api/skoor/backfill/a1e0c2d4-6b7f-4d8e-9a3b-0c1d2e3f4a55" \
  -H "Authorization: Bearer test_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"

Entity

A person or business on a program, with its Entity Risk Skoor.

The customer behind a transfer: verification state, screening state and PEP status are read from the bank verbatim. The Entity Risk Skoor (ers-v1) is the latest actor_skoors row for the entity; it appears in reports, on pre-flight answers through the limit ladder, and in signed proofs.

Entity

FieldTypeNotes
idstringColumn entity id.
tenantIdstring (uuid)
programIdstring (uuid)
type"PERSON" | "BUSINESS"
namestring
verificationStatus"UNVERIFIED" | "PENDING" | "MANUAL_REVIEW" | "VERIFIED" | "DENIED"
isHighRiskboolean
pepStatus"not_checked" | "no" | "yes" | "potential"
requiresScreeningboolean
reviewReasonsstring[]
lastScreenedAtstring | null (date-time)
countryCodestring | null
legalTypestring | null
expectedPaymentRailsstring[]
rawobjectThe bank's own object as last delivered.
createdAtstring (date-time)
updatedAtstring (date-time)

Endpoints

post/api/skoor/actors/backfill/{programId}

Rescore every actor on a program

Recomputes the Entity, Counterparty and Agent Risk Skoors for every actor on the program and re-runs the actor detectors. Operator token of the owning tenant; X-Operator is not required.

AuthBearer (program's tenant); no X-Operator
OperationEntity · POST /api/skoor/actors/backfill/{programId}

Parameters

NameInTypeNotes
programIdpathstring (uuid)Program id.

Responses

200 — Actors scored, by kind (agents are counted inside entities or counterparties as well). object

FieldTypeNotes
entitiesinteger
counterpartiesinteger
agentsinteger

401 — Bearer is not the owning tenant's operator token. Error

FieldTypeNotes
errorstringWhat went wrong, in one sentence.
requestId (optional)stringPresent once the gateway is live: the X-Request-Id of the failed request.

Example

curl -s -X POST "https://skoor.ai/risk/api/skoor/actors/backfill/a1e0c2d4-6b7f-4d8e-9a3b-0c1d2e3f4a55" \
  -H "Authorization: Bearer test_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"

Counterparty

The other side of a transfer, with its Counterparty Risk Skoor.

Routing number, last four and country as the bank delivered them; first seen when the program first paid or was paid by it. The Counterparty Risk Skoor (crs-v1) rolls up returns and first-time signals. There is no dedicated counterparty endpoint: counterparties are read through actor reports, the limit ladder, pre-flight answers and proofs.

Counterparty

FieldTypeNotes
idstringColumn counterparty id.
tenantIdstring (uuid)
programIdstring (uuid)
namestring | null
routingNumberstring | null
accountLast4string | null
countryCodestring | null
rawobjectThe bank's own object as last delivered.
firstSeenAtstring (date-time)First transfer with this counterparty on the program.

No dedicated endpoint. This object is read through the endpoints of the objects that reference it.

Agent

An AI agent moving money, imported from the public scan index.

On-chain agents are imported as entities on the program agents and scored with the Agent Risk Skoor (ars-v1) on the same bands. Import pulls agents, their compliance profiles and their settlements from the scan index; an hourly refresh runs for tenants that have the program.

Agent

FieldTypeNotes
entityIdstringThe entity row the agent is stored as (program agents).
namestring
chainIdinteger | null
agentIdstring | nullOn-chain agent id from the scan index.
addressstring | nullWallet address.
verificationStatusstring
screeningStatusstring | nullLatest compliance-profile screening status, or null when never screened.
lastScreenedAtstring | null (date-time)
settlementsintegerTransfers imported for this agent.
volumeCentsstringSum of imported transfers. Integer cents as a string (bigint-safe).
skoorinteger | nullAgent Risk Skoor (ars-v1); null until history is enough.
bandName"clear" | "review" | "hold" | "unscored"
ninteger
confidencenumber | null
versionstring | null

ImportResult

FieldTypeNotes
programIdstring (uuid)
agentsinteger
profilesinteger
settlementsScannedinteger
transfersinteger
counterpartiesinteger
agentCounterpartiesinteger

Endpoints

get/api/agents

Imported agents with their Skoors

AuthBearer + X-Operator
OperationAgent · GET /api/agents

Parameters

NameInTypeNotes
tenant (optional)querystringTenant slug. Optional: the bearer token already proves the tenant. Pass it to be explicit.

Responses

200 — Up to 500 agents on the tenant's agent program, newest update first. object

FieldTypeNotes
programIdstring (uuid) | null
lastImportAtstring (date-time) | null
agentsAgent[]

401 — Bearer token missing or unknown. Error

FieldTypeNotes
errorstringWhat went wrong, in one sentence.
requestId (optional)stringPresent once the gateway is live: the X-Request-Id of the failed request.

Example

curl -s "https://skoor.ai/risk/api/agents?tenant=demo" \
  -H "Authorization: Bearer test_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
  -H "X-Operator: you@bank.example"

post/api/agents/import

Import agents from the scan index

AuthBearer + X-Operator
OperationAgent · POST /api/agents/import

Parameters

NameInTypeNotes
tenant (optional)querystringTenant slug. Optional: the bearer token already proves the tenant. Pass it to be explicit.
Idempotency-Key (optional)headerstring1–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) · object

FieldTypeNotes
limit (optional)integerAgents to import, 1–200 (default 50).
chainId (optional)integer
offset (optional)integer
txLimit (optional)integerExplorer settlements to scan for matches.
withProfiles (optional)booleanFetch the compliance profile per agent.

Responses

201 — What was imported. ImportResult

FieldTypeNotes
programIdstring (uuid)
agentsinteger
profilesinteger
settlementsScannedinteger
transfersinteger
counterpartiesinteger
agentCounterpartiesinteger

401 — Bearer token missing or unknown. Error

FieldTypeNotes
errorstringWhat went wrong, in one sentence.
requestId (optional)stringPresent once the gateway is live: the X-Request-Id of the failed request.

502 — Scan index unavailable; detail carries the upstream message. Error

FieldTypeNotes
errorstringWhat went wrong, in one sentence.
requestId (optional)stringPresent once the gateway is live: the X-Request-Id of the failed request.

Example

curl -s -X POST "https://skoor.ai/risk/api/agents/import?tenant=demo" \
  -H "Authorization: Bearer test_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
  -H "X-Operator: you@bank.example" \
  -H "Content-Type: application/json" \
  -d '{"limit":50,"withProfiles":true}'

Alert

What a detector opened, routed automated or reviewed, on a decision clock.

Detectors open alerts from scored transfers and from KRI snapshots; the policy routes each one automated or reviewed when it opens and stamps a due time by SLA tier. The alert carries its evidence, the Skoor and band at the time, and whether a hard signal was present. Alerts are decided on the dashboard and inside Claude (connector tool decide_alert); the API lists the ones past their clock and exports them on the record.

Alert

FieldTypeNotes
idstring (uuid)
tenantIdstring (uuid)
programIdstring (uuid)
detectorstringWhich detector opened it, e.g. structuring_pattern, or rule:<id>.
severity"low" | "medium" | "high"
subjectType"transfer" | "entity" | "program" | "counterparty" | "account"
subjectIdstring
transferIdstring | null
summarystring
evidenceobjectWhat the detector saw; carries routeReason, autoHold and escalation when they apply.
skoorAtAlertinteger | null
bandAtAlert"clear" | "review" | "hold" | "unscored" | null
hardSignalbooleanA hard signal always routes to a person.
status"open" | "auto_closed" | "closed" | "held" | "escalated"
route"automated" | "reviewed" | nullDecided by the policy when opened.
policyVersionstring | null
openedAtstring (date-time)
closedAtstring | null (date-time)
dedupeKeystring | null
dueAtstring | null (date-time)Decision clock: when a person must have decided by.
escalatedAtstring | null (date-time)Stamped once when the clock ran out.
slaTier"auto" | "standard" | "complex" | "appeal" | null

OverdueAlert

FieldTypeNotes
idstring (uuid)
programIdstring (uuid)
detectorstring
severitystring
statusstring
routestring | null
slaTierstring | null
openedAtstring (date-time)
dueAtstring | null (date-time)
escalatedAtstring | null (date-time)
summarystring

Endpoints

get/api/clocks/overdue

Alerts past their decision clock

AuthBearer + X-Operator
OperationAlert · GET /api/clocks/overdue

Parameters

NameInTypeNotes
tenant (optional)querystringTenant slug. Optional: the bearer token already proves the tenant. Pass it to be explicit.

Responses

200 — Waiting alerts (open, held, escalated) whose due time has passed, oldest due first, with the clock policy in force. object

FieldTypeNotes
asOfstring (date-time)
policyClockPolicy
countinteger
alertsOverdueAlert[]

401 — Bearer token missing or unknown. Error

FieldTypeNotes
errorstringWhat went wrong, in one sentence.
requestId (optional)stringPresent once the gateway is live: the X-Request-Id of the failed request.

Example

curl -s "https://skoor.ai/risk/api/clocks/overdue?tenant=demo" \
  -H "Authorization: Bearer test_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
  -H "X-Operator: you@bank.example"

Disposition

A signed decision on an alert, hash-chained per tenant.

Every decision on an alert — by the policy alone or by a person — is a disposition with the narrative that was signed, the draft's recommendation and confidence, whether the person overturned the draft, and a hash chained to the previous disposition on the tenant. Dispositions are written on the dashboard and through the connector; the API verifies and exports the chain.

Disposition

FieldTypeNotes
idstring (uuid)
tenantIdstring (uuid)
alertIdstring (uuid)
decidedBystringpolicy:<version> when the platform decided alone; operator:<name> when a person signed.
decision"close" | "hold" | "escalate" | "release" | "no_action"
narrativestringThe examiner-shaped narrative that was signed.
narrativeModelstring | nullModel alias that drafted it; null when template or human-written.
recommendationstring | nullWhat the draft recommended.
confidencenumber | nullDraft confidence; never present on a template.
policyVersionstring
overturnsDraftboolean | nullTrue when the person disagreed with the draft.
prevHashstring | nullHash of the previous disposition on the tenant chain.
hashstringsha256(prevHash | canonical(body)).
createdAtstring (date-time)

No dedicated endpoint. This object is read through the endpoints of the objects that reference it.

Action

A request through the bank's own API, attributed and chained.

Clear or cancel a held transfer, pause a card, suspend a card account, freeze an account, send a request to a program, assemble a periodic review. freeze_account, request_to_program and suspend_card_account need a second, distinct approver; the requester approving is recorded as refused. Execution uses the tenant's own Column key (test_ only in this build); a tenant without a key gets a recorded dry run that says so.

Action

FieldTypeNotes
idstring (uuid)
tenantIdstring (uuid)
programIdstring (uuid)
alertIdstring | null (uuid)
type"clear_hold" | "cancel_transfer" | "pause_card" | "suspend_card_account" | "freeze_account" | "request_to_program" | "periodic_review"
targetTypestringWhat the action acts on (transfer, account, card, program…).
targetIdstring
requestedBystringThe X-Operator who requested it.
approvedBystring | nullSecond, distinct approver for freeze_account, request_to_program, suspend_card_account.
status"requested" | "approved" | "executed" | "failed" | "refused"
columnResponseobject | null
errorstring | null
executedAtstring | null (date-time)
prevHashstring | null
hashstringChained at insert.
createdAtstring (date-time)

Endpoints

post/api/actions

Request an action

Writes the chained request attributed to X-Operator. Single-approver actions execute at once through the bank's API (or a recorded dry run); two-approver actions wait for approve.

AuthBearer + X-Operator
OperationAction · POST /api/actions

Parameters

NameInTypeNotes
tenant (optional)querystringTenant slug. Optional: the bearer token already proves the tenant. Pass it to be explicit.
Idempotency-Key (optional)headerstring1–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

FieldTypeNotes
programIdstring (uuid)
alertId (optional)string (uuid)
type"clear_hold" | "cancel_transfer" | "pause_card" | "suspend_card_account" | "freeze_account" | "request_to_program" | "periodic_review"
targetTypestringWhat the action acts on.
targetIdstring

Responses

201 — The action row; status is requested (two approvers), executed, or failed. Action

FieldTypeNotes
idstring (uuid)
tenantIdstring (uuid)
programIdstring (uuid)
alertIdstring | null (uuid)
type"clear_hold" | "cancel_transfer" | "pause_card" | "suspend_card_account" | "freeze_account" | "request_to_program" | "periodic_review"
targetTypestringWhat the action acts on (transfer, account, card, program…).
targetIdstring
requestedBystringThe X-Operator who requested it.
approvedBystring | nullSecond, distinct approver for freeze_account, request_to_program, suspend_card_account.
status"requested" | "approved" | "executed" | "failed" | "refused"
columnResponseobject | null
errorstring | null
executedAtstring | null (date-time)
prevHashstring | null
hashstringChained at insert.
createdAtstring (date-time)

400 — Validation failed. Error

FieldTypeNotes
errorstringWhat went wrong, in one sentence.
requestId (optional)stringPresent once the gateway is live: the X-Request-Id of the failed request.

401 — Bearer token missing or unknown. Error

FieldTypeNotes
errorstringWhat went wrong, in one sentence.
requestId (optional)stringPresent once the gateway is live: the X-Request-Id of the failed request.

Example

curl -s -X POST "https://skoor.ai/risk/api/actions?tenant=demo" \
  -H "Authorization: Bearer test_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
  -H "X-Operator: you@bank.example" \
  -H "Content-Type: application/json" \
  -d '{"programId":"a1e0c2d4-6b7f-4d8e-9a3b-0c1d2e3f4a55","alertId":"5f3c1c8e-2a44-4c0b-9d0f-2b0a2f6e7c11","type":"request_to_program","targetType":"program","targetId":"a1e0c2d4-6b7f-4d8e-9a3b-0c1d2e3f4a55"}'

post/api/actions/{id}/approve

Approve a two-approver action

The approver defaults to X-Operator. The requester approving their own action is recorded as refused (200 with status refused), never executed.

AuthBearer + X-Operator
OperationAction · POST /api/actions/{id}/approve

Parameters

NameInTypeNotes
idpathstring (uuid)Action id.
tenant (optional)querystringTenant slug. Optional: the bearer token already proves the tenant. Pass it to be explicit.
Idempotency-Key (optional)headerstring1–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) · object

FieldTypeNotes
approverId (optional)stringOverrides X-Operator as the approver of record.

Responses

200 — The action row after approval: approved / executed / failed / refused. Action

FieldTypeNotes
idstring (uuid)
tenantIdstring (uuid)
programIdstring (uuid)
alertIdstring | null (uuid)
type"clear_hold" | "cancel_transfer" | "pause_card" | "suspend_card_account" | "freeze_account" | "request_to_program" | "periodic_review"
targetTypestringWhat the action acts on (transfer, account, card, program…).
targetIdstring
requestedBystringThe X-Operator who requested it.
approvedBystring | nullSecond, distinct approver for freeze_account, request_to_program, suspend_card_account.
status"requested" | "approved" | "executed" | "failed" | "refused"
columnResponseobject | null
errorstring | null
executedAtstring | null (date-time)
prevHashstring | null
hashstringChained at insert.
createdAtstring (date-time)

401 — Bearer token missing or unknown. Error

FieldTypeNotes
errorstringWhat went wrong, in one sentence.
requestId (optional)stringPresent once the gateway is live: the X-Request-Id of the failed request.

404 — Not this tenant's action. Error

FieldTypeNotes
errorstringWhat went wrong, in one sentence.
requestId (optional)stringPresent once the gateway is live: the X-Request-Id of the failed request.

Example

curl -s -X POST "https://skoor.ai/risk/api/actions/5f3c1c8e-2a44-4c0b-9d0f-2b0a2f6e7c11/approve?tenant=demo" \
  -H "Authorization: Bearer test_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
  -H "X-Operator: you@bank.example" \
  -H "Content-Type: application/json" \
  -d '{"approverId":"second.approver@bank.example"}'

Pre-flight

Score a proposed transfer before origination.

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

FieldTypeNotes
preflightIdstring (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.
skoorinteger | null
band"clear" | "review" | "hold" | "unscored"
versionstring
ninteger
confidencenumber | null
reasonsReason[]
limitsHitLimitHit[]
degradedbooleanTrue when a feature source the answer leans on is missing or stale.
degradedReasonsstring[]Why it is degraded (empty on a replayed answer).
latencyMsinteger
idempotentbooleanTrue when the answer was replayed from the idempotency key.
programIdstring (uuid)

PreflightRequest

FieldTypeNotes
programId (optional)string (uuid)One of programId or programSlug is required.
programSlug (optional)string
idempotencyKey (optional)stringSame key on the same tenant returns the same answer (200 instead of 201).
transferobject
transfer.rail"ach" | "wire" | "realtime" | "book" | "intl_wire" | "check" | "card"
transfer.direction"outgoing" | "incoming"
transfer.type (optional)"CREDIT" | "DEBIT" | null
transfer.amountCentsstringInteger cents as a string.
transfer.currency (optional)stringDefault USD.
transfer.entityId (optional)string
transfer.counterpartyId (optional)string
transfer.counterparty (optional)objectDescribe a counterparty not on file; matched by routing + last4, else by name, else scored as a first-time stranger.
transfer.counterparty.namestring
transfer.counterparty.routingNumber (optional)string
transfer.counterparty.accountLast4 (optional)string
transfer.counterparty.countryCode (optional)string
transfer.effectiveOn (optional)string
transfer.description (optional)string

PreflightDetail

FieldTypeNotes
preflightIdstring (uuid)
decision"clear" | "review" | "hold"
skoorinteger | null
band"clear" | "review" | "hold" | "unscored"
versionstring
ninteger
confidencenumber | null
reasonsReason[]
limitsHitLimitHit[]
degradedboolean
degradedReasonsstring[]
latencyMsinteger
idempotentboolean
programIdstring (uuid)
requestobjectThe request as submitted plus operator and resolved {entityId, counterpartyId, counterpartyCountry}.
matchedTransferIdstring | nullSet when the transfer later landed and was matched back (same program, counterparty, amount, within 24h).
createdAtstring (date-time)

PreflightStats

FieldTypeNotes
windowDaysinteger
countinteger
byDecisionobject
byDecision.clearinteger
byDecision.reviewinteger
byDecision.holdinteger
latencyMsobject
latencyMs.p50number | null
latencyMs.p90number | null
latencyMs.ninteger
matchedobjectShare of pre-flights matched to a landed transfer.
matched.valuenumber | nullnull when nothing was measured
matched.nintegerobservations behind the value
degradedobjectShare of degraded answers.
degraded.valuenumber | nullnull when nothing was measured
degraded.nintegerobservations behind the value
gradedobjectMatched pre-flights whose transfer later received a bad/ok label.
graded.ninteger
graded.holdAndBadinteger
graded.holdAndOkinteger
graded.notHoldAndBadinteger
graded.notHoldAndOkinteger
graded.holdPrecisionnumber | null
graded.holdRecallnumber | null

LimitHit

FieldTypeNotes
limitstring<actor>.<kind>: entity|counterparty × per_transaction|daily|monthly.
actor"counterparty" | "entity"
actorIdstring
band"clear" | "review" | "hold" | "unscored"
capstringThe cap at that band. Integer cents as a string (bigint-safe).
observedstringAlready moved in the window (0 for per_transaction). Integer cents as a string (bigint-safe).
proposedstringThis 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.

AuthBearer + X-Operator
OperationPre-flight · POST /api/preflight

Parameters

NameInTypeNotes
tenant (optional)querystringTenant slug. Optional: the bearer token already proves the tenant. Pass it to be explicit.
Idempotency-Key (optional)headerstring1–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

FieldTypeNotes
programId (optional)string (uuid)One of programId or programSlug is required.
programSlug (optional)string
idempotencyKey (optional)stringSame key on the same tenant returns the same answer (200 instead of 201).
transferobject
transfer.rail"ach" | "wire" | "realtime" | "book" | "intl_wire" | "check" | "card"
transfer.direction"outgoing" | "incoming"
transfer.type (optional)"CREDIT" | "DEBIT" | null
transfer.amountCentsstringInteger cents as a string.
transfer.currency (optional)stringDefault USD.
transfer.entityId (optional)string
transfer.counterpartyId (optional)string
transfer.counterparty (optional)objectDescribe a counterparty not on file; matched by routing + last4, else by name, else scored as a first-time stranger.
transfer.counterparty.namestring
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

FieldTypeNotes
preflightIdstring (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.
skoorinteger | null
band"clear" | "review" | "hold" | "unscored"
versionstring
ninteger
confidencenumber | null
reasonsReason[]
limitsHitLimitHit[]
degradedbooleanTrue when a feature source the answer leans on is missing or stale.
degradedReasonsstring[]Why it is degraded (empty on a replayed answer).
latencyMsinteger
idempotentbooleanTrue when the answer was replayed from the idempotency key.
programIdstring (uuid)

201 — Scored and recorded. Preflight

FieldTypeNotes
preflightIdstring (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.
skoorinteger | null
band"clear" | "review" | "hold" | "unscored"
versionstring
ninteger
confidencenumber | null
reasonsReason[]
limitsHitLimitHit[]
degradedbooleanTrue when a feature source the answer leans on is missing or stale.
degradedReasonsstring[]Why it is degraded (empty on a replayed answer).
latencyMsinteger
idempotentbooleanTrue when the answer was replayed from the idempotency key.
programIdstring (uuid)

400 — Validation failed; error lists the paths. Error

FieldTypeNotes
errorstringWhat went wrong, in one sentence.
requestId (optional)stringPresent once the gateway is live: the X-Request-Id of the failed request.

401 — Bearer token missing or unknown. Error

FieldTypeNotes
errorstringWhat went wrong, in one sentence.
requestId (optional)stringPresent once the gateway is live: the X-Request-Id of the failed request.

404 — Unknown program on this tenant. Error

FieldTypeNotes
errorstringWhat went wrong, in one sentence.
requestId (optional)stringPresent 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

AuthBearer + X-Operator
OperationPre-flight · GET /api/preflight/stats

Parameters

NameInTypeNotes
tenant (optional)querystringTenant slug. Optional: the bearer token already proves the tenant. Pass it to be explicit.
days (optional)queryintegerWindow in days, 1–365 (default 30).

Responses

200 — Counts by decision, latency percentiles, matched and degraded shares, and the graded confusion counts. PreflightStats

FieldTypeNotes
windowDaysinteger
countinteger
byDecisionobject
byDecision.clearinteger
byDecision.reviewinteger
byDecision.holdinteger
latencyMsobject
latencyMs.p50number | null
latencyMs.p90number | null
latencyMs.ninteger
matchedobjectShare of pre-flights matched to a landed transfer.
matched.valuenumber | nullnull when nothing was measured
matched.nintegerobservations behind the value
degradedobjectShare of degraded answers.
degraded.valuenumber | nullnull when nothing was measured
degraded.nintegerobservations behind the value
gradedobjectMatched pre-flights whose transfer later received a bad/ok label.
graded.ninteger
graded.holdAndBadinteger
graded.holdAndOkinteger
graded.notHoldAndBadinteger
graded.notHoldAndOkinteger
graded.holdPrecisionnumber | null
graded.holdRecallnumber | null

401 — Bearer token missing or unknown. Error

FieldTypeNotes
errorstringWhat went wrong, in one sentence.
requestId (optional)stringPresent 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

AuthBearer + X-Operator
OperationPre-flight · GET /api/preflight/{id}

Parameters

NameInTypeNotes
idpathstring (uuid)Pre-flight id.
tenant (optional)querystringTenant 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

FieldTypeNotes
preflightIdstring (uuid)
decision"clear" | "review" | "hold"
skoorinteger | null
band"clear" | "review" | "hold" | "unscored"
versionstring
ninteger
confidencenumber | null
reasonsReason[]
limitsHitLimitHit[]
degradedboolean
degradedReasonsstring[]
latencyMsinteger
idempotentboolean
programIdstring (uuid)
requestobjectThe request as submitted plus operator and resolved {entityId, counterpartyId, counterpartyCountry}.
matchedTransferIdstring | nullSet when the transfer later landed and was matched back (same program, counterparty, amount, within 24h).
createdAtstring (date-time)

401 — Bearer token missing or unknown. Error

FieldTypeNotes
errorstringWhat went wrong, in one sentence.
requestId (optional)stringPresent once the gateway is live: the X-Request-Id of the failed request.

404 — Unknown pre-flight on this tenant. Error

FieldTypeNotes
errorstringWhat went wrong, in one sentence.
requestId (optional)stringPresent 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"

KRI snapshot

Program KRIs over a rolling window, rolled into the Program Risk Skoor.

Per program: ACH return rates against NACHA thresholds, reserve coverage, review and hold aging, overdraft and freeze events, velocity, counterparty concentration, verification denial rate, card decline mix. Each KRI carries value | null, n, threshold and a status; the Program Risk Skoor (prs-v1) is null until at least three KRIs have n above zero. Snapshots run every 15 minutes, after every 200 transfers, and on demand.

KriSnapshot

FieldTypeNotes
idstring (uuid)
tenantIdstring (uuid)
programIdstring (uuid)
asOfstring (date-time)
windowDaysinteger
krisRecord<string, KriValue>One KriValue per KRI name (ach_overall_return_rate, reserve_coverage, …).
programSkoorinteger | nullProgram Risk Skoor (prs-v1); null until at least three KRIs have n > 0.
programSkoorNinteger | null
programSkoorVersionstring | null
createdAtstring (date-time)

KriValue

FieldTypeNotes
valuenumber | nullnull = not measured.
ninteger
unit"ratio" | "count" | "cents" | "days" | "hours"
threshold (optional)number | null
status"ok" | "watch" | "breach" | "unmeasured"
note (optional)string

Endpoints

post/api/kri/snapshot/{programId}

Take a KRI snapshot now

Computes every KRI for the program over its window, stores the snapshot, and emits kri.snapshot so the program detectors run. Operator token of the owning tenant; X-Operator is not required.

AuthBearer (program's tenant); no X-Operator
OperationKRI snapshot · POST /api/kri/snapshot/{programId}

Parameters

NameInTypeNotes
programIdpathstring (uuid)Program id.

Responses

200 — The new snapshot id. Read it back inside the program report. object

FieldTypeNotes
snapshotIdstring (uuid)

401 — Bearer is not the owning tenant's operator token. Error

FieldTypeNotes
errorstringWhat went wrong, in one sentence.
requestId (optional)stringPresent once the gateway is live: the X-Request-Id of the failed request.

404 — Program not found. Error

FieldTypeNotes
errorstringWhat went wrong, in one sentence.
requestId (optional)stringPresent once the gateway is live: the X-Request-Id of the failed request.

Example

curl -s -X POST "https://skoor.ai/risk/api/kri/snapshot/a1e0c2d4-6b7f-4d8e-9a3b-0c1d2e3f4a55" \
  -H "Authorization: Bearer test_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"

Policy

The automated-or-reviewed line, decision clocks, the limit ladder, program autonomy.

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

FieldTypeNotes
idstring (uuid)
tenantIdstring (uuid)
programIdstring | null (uuid)null = tenant-wide; set = a program's autonomy tier / limits.
versionstring
bodyobjectPolicyBody, ClockPolicy, LimitLadder or a program autonomy body, by version prefix.
activeboolean
createdAtstring (date-time)

PolicyBody

FieldTypeNotes
versionstring
autoClosableDetectorsstring[]Detectors the platform may close alone.
autoCloseBands"clear" | "review" | "hold" | "unscored"[]v1: clear only.
minProgramNintegerHistory floor for any automated decision.
alwaysReviewedstring[]
twoApproverActions"clear_hold" | "cancel_transfer" | "pause_card" | "suspend_card_account" | "freeze_account" | "request_to_program" | "periodic_review"[]
holdBandAutoHoldbooleanHold band → held before anyone decides.

ClockPolicy

Hours a decision may wait, by SLA tier. Defaults 1 / 24 / 72 / 168.

FieldTypeNotes
autoHoursnumber
standardHoursnumber
complexHoursnumber
appealHoursnumber

LimitLadder

FieldTypeNotes
counterpartyLadder
entityLadder
rescreenDaysobjectRe-screen cadence by band.
rescreenDays.clearinteger
rescreenDays.reviewinteger
rescreenDays.holdinteger
rescreenDays.unscoredinteger

LimitEvaluation

FieldTypeNotes
hitsLimitHit[]
hardbooleanTrue when any hit is hard (an actor in the hold band).
bandsobject
bands.entity"clear" | "review" | "hold" | "unscored" | null
bands.counterparty"clear" | "review" | "hold" | "unscored" | null
rescreenobject
rescreen.entityobject | null
rescreen.counterpartyobject | null
ladderLimitLadder

ProgramAutonomy

FieldTypeNotes
programIdstring (uuid)
tier"supervised" | "standard" | "trusted"
versionstring | nullActive program version; null = policy v1 only.
grantedstring[]
effectivePolicyBody
eligibilityDetectorEligibility[]
lastChangeAtstring | null (date-time)
nextChangeAllowedAtstring | null (date-time)Cadence: one widening per week.
historyobject[]
history[].versionstring
history[].createdAtstring (date-time)
history[].bodyProgramPolicyBody
history[].activeboolean

Endpoints

get/api/limits/ladder

The tenant's limit ladder

AuthBearer + X-Operator
OperationPolicy · GET /api/limits/ladder

Parameters

NameInTypeNotes
tenant (optional)querystringTenant 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

FieldTypeNotes
counterpartyLadder
entityLadder
rescreenDaysobjectRe-screen cadence by band.
rescreenDays.clearinteger
rescreenDays.reviewinteger
rescreenDays.holdinteger
rescreenDays.unscoredinteger

401 — Bearer token missing or unknown. Error

FieldTypeNotes
errorstringWhat went wrong, in one sentence.
requestId (optional)stringPresent 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

AuthBearer + X-Operator
OperationPolicy · GET /api/limits/evaluate

Parameters

NameInTypeNotes
tenant (optional)querystringTenant slug. Optional: the bearer token already proves the tenant. Pass it to be explicit.
programId (optional)querystring (uuid)Program id.
amountCents (optional)querystringInteger cents.
entityId (optional)querystringEntity to evaluate, if any.
counterpartyId (optional)querystringCounterparty 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

FieldTypeNotes
hitsLimitHit[]
hardbooleanTrue when any hit is hard (an actor in the hold band).
bandsobject
bands.entity"clear" | "review" | "hold" | "unscored" | null
bands.counterparty"clear" | "review" | "hold" | "unscored" | null
rescreenobject
rescreen.entityobject | null
rescreen.counterpartyobject | null
ladderLimitLadder

400 — programId and amountCents required. Error

FieldTypeNotes
errorstringWhat went wrong, in one sentence.
requestId (optional)stringPresent once the gateway is live: the X-Request-Id of the failed request.

401 — Bearer token missing or unknown. Error

FieldTypeNotes
errorstringWhat went wrong, in one sentence.
requestId (optional)stringPresent 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

AuthBearer + X-Operator
OperationPolicy · GET /api/autonomy/{programId}

Parameters

NameInTypeNotes
programIdpathstring (uuid)Program id.
tenant (optional)querystringTenant 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

FieldTypeNotes
programIdstring (uuid)
tier"supervised" | "standard" | "trusted"
versionstring | nullActive program version; null = policy v1 only.
grantedstring[]
effectivePolicyBody
eligibilityDetectorEligibility[]
lastChangeAtstring | null (date-time)
nextChangeAllowedAtstring | null (date-time)Cadence: one widening per week.
historyobject[]
history[].versionstring
history[].createdAtstring (date-time)
history[].bodyProgramPolicyBody
history[].activeboolean

401 — Bearer token missing or unknown. Error

FieldTypeNotes
errorstringWhat went wrong, in one sentence.
requestId (optional)stringPresent once the gateway is live: the X-Request-Id of the failed request.

404 — Unknown program on this tenant. Error

FieldTypeNotes
errorstringWhat went wrong, in one sentence.
requestId (optional)stringPresent 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.

AuthBearer + X-Operator
OperationPolicy · POST /api/autonomy/{programId}/activate

Parameters

NameInTypeNotes
programIdpathstring (uuid)Program id.
tenant (optional)querystringTenant slug. Optional: the bearer token already proves the tenant. Pass it to be explicit.
Idempotency-Key (optional)headerstring1–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

FieldTypeNotes
detectorstring
approverIdstring

Responses

201 — The new version and tier. object

FieldTypeNotes
versionstring
tier"supervised" | "standard" | "trusted"
grantedstring[]

400 — detector and approverId required. Error

FieldTypeNotes
errorstringWhat went wrong, in one sentence.
requestId (optional)stringPresent once the gateway is live: the X-Request-Id of the failed request.

401 — Bearer token missing or unknown. Error

FieldTypeNotes
errorstringWhat went wrong, in one sentence.
requestId (optional)stringPresent once the gateway is live: the X-Request-Id of the failed request.

404 — Unknown program on this tenant. Error

FieldTypeNotes
errorstringWhat went wrong, in one sentence.
requestId (optional)stringPresent once the gateway is live: the X-Request-Id of the failed request.

409 — Refused: not eligible, same approver, cadence, or never-automate. Error

FieldTypeNotes
errorstringWhat went wrong, in one sentence.
requestId (optional)stringPresent 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.

AuthBearer + X-Operator
OperationPolicy · POST /api/autonomy/{programId}/demote

Parameters

NameInTypeNotes
programIdpathstring (uuid)Program id.
tenant (optional)querystringTenant 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

FieldTypeNotes
demotedstring[]
versionstring | null

401 — Bearer token missing or unknown. Error

FieldTypeNotes
errorstringWhat went wrong, in one sentence.
requestId (optional)stringPresent once the gateway is live: the X-Request-Id of the failed request.

404 — Unknown program on this tenant. Error

FieldTypeNotes
errorstringWhat went wrong, in one sentence.
requestId (optional)stringPresent 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

The rule studio: draft → backtest → activate with two approvers → live alerts.

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

FieldTypeNotes
idstringStable id, lowercase.
namestring
scope"transfer" | "entity" | "counterparty" | "program"Default transfer; only transfer-scoped rules fire today.
programIdstring | null (uuid)null = every program on the tenant.
whenRuleCondition[]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

FieldTypeNotes
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

FieldTypeNotes
keystring
ruleIdstring
atstring (date-time)
daysinteger
scannedintegerTransfers replayed (capped at 1000).
sampledbooleanTrue when the cap truncated the window.
matchedinteger
matchedSharenumber | null
wouldHoldinteger
wouldAlertinteger
overlapWithExistingAlertsinteger
byBandRecord<string, integer>
sampleTransferIdsstring[]

RulesVersion

FieldTypeNotes
idstring
versionstring
rulesRule[]
createdAtstring (date-time)
activeboolean
note (optional)string

Endpoints

get/api/rules

List rules with their last backtest

AuthBearer + X-Operator
OperationRule · GET /api/rules

Parameters

NameInTypeNotes
tenant (optional)querystringTenant 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

FieldTypeNotes
rulesRule & object[]
fieldsstring[]
operatorsstring[]

401 — Bearer token missing or unknown. Error

FieldTypeNotes
errorstringWhat went wrong, in one sentence.
requestId (optional)stringPresent 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

AuthBearer + X-Operator
OperationRule · POST /api/rules

Parameters

NameInTypeNotes
tenant (optional)querystringTenant slug. Optional: the bearer token already proves the tenant. Pass it to be explicit.
Idempotency-Key (optional)headerstring1–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

FieldTypeNotes
idstring
namestring
scope (optional)"transfer" | "entity" | "counterparty" | "program"
programId (optional)string | null (uuid)
whenRuleCondition[]
action (optional)"alert" | "hold" | "review" | "log"
severity (optional)"low" | "medium" | "high"
note (optional)string

Responses

201 — The rule as stored (status draft). Rule

FieldTypeNotes
idstringStable id, lowercase.
namestring
scope"transfer" | "entity" | "counterparty" | "program"Default transfer; only transfer-scoped rules fire today.
programIdstring | null (uuid)null = every program on the tenant.
whenRuleCondition[]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

FieldTypeNotes
errorstringWhat went wrong, in one sentence.
requestId (optional)stringPresent once the gateway is live: the X-Request-Id of the failed request.

401 — Bearer token missing or unknown. Error

FieldTypeNotes
errorstringWhat went wrong, in one sentence.
requestId (optional)stringPresent once the gateway is live: the X-Request-Id of the failed request.

409 — The rule is active; pause it before editing. Error

FieldTypeNotes
errorstringWhat went wrong, in one sentence.
requestId (optional)stringPresent 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

AuthBearer + X-Operator
OperationRule · POST /api/rules/{id}/backtest

Parameters

NameInTypeNotes
idpathstringRule id.
tenant (optional)querystringTenant slug. Optional: the bearer token already proves the tenant. Pass it to be explicit.

Request body (application/json) · object

FieldTypeNotes
days (optional)integerWindow 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

FieldTypeNotes
keystring
ruleIdstring
atstring (date-time)
daysinteger
scannedintegerTransfers replayed (capped at 1000).
sampledbooleanTrue when the cap truncated the window.
matchedinteger
matchedSharenumber | null
wouldHoldinteger
wouldAlertinteger
overlapWithExistingAlertsinteger
byBandRecord<string, integer>
sampleTransferIdsstring[]

401 — Bearer token missing or unknown. Error

FieldTypeNotes
errorstringWhat went wrong, in one sentence.
requestId (optional)stringPresent once the gateway is live: the X-Request-Id of the failed request.

404 — Rule not found. Error

FieldTypeNotes
errorstringWhat went wrong, in one sentence.
requestId (optional)stringPresent 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.

AuthBearer + X-Operator
OperationRule · POST /api/rules/{id}/activate

Parameters

NameInTypeNotes
idpathstringRule id.
tenant (optional)querystringTenant slug. Optional: the bearer token already proves the tenant. Pass it to be explicit.
Idempotency-Key (optional)headerstring1–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

FieldTypeNotes
approverIdstring

Responses

200 — The active rule, the rules version written, and the action id recorded on the chain (null when none). object

FieldTypeNotes
ruleRule
versionRulesVersion
actionIdstring | null

401 — Bearer token missing or unknown. Error

FieldTypeNotes
errorstringWhat went wrong, in one sentence.
requestId (optional)stringPresent once the gateway is live: the X-Request-Id of the failed request.

404 — Rule not found. Error

FieldTypeNotes
errorstringWhat went wrong, in one sentence.
requestId (optional)stringPresent 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

FieldTypeNotes
errorstringWhat went wrong, in one sentence.
requestId (optional)stringPresent 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

AuthBearer + X-Operator
OperationRule · POST /api/rules/{id}/pause

Parameters

NameInTypeNotes
idpathstringRule id.
tenant (optional)querystringTenant slug. Optional: the bearer token already proves the tenant. Pass it to be explicit.

Responses

200 — The rule with status paused. Rule

FieldTypeNotes
idstringStable id, lowercase.
namestring
scope"transfer" | "entity" | "counterparty" | "program"Default transfer; only transfer-scoped rules fire today.
programIdstring | null (uuid)null = every program on the tenant.
whenRuleCondition[]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

FieldTypeNotes
errorstringWhat went wrong, in one sentence.
requestId (optional)stringPresent once the gateway is live: the X-Request-Id of the failed request.

404 — Rule not found. Error

FieldTypeNotes
errorstringWhat went wrong, in one sentence.
requestId (optional)stringPresent 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

The six numbers the platform reports on itself.

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

FieldTypeNotes
asOfstring (date-time)
windowDaysinteger
calibrationobjectPer band: labelled transfers that went bad ÷ labelled transfers.
calibration.clearMeasured
calibration.reviewMeasured
calibration.holdMeasured
calibration.unscoredMeasured
coverageobject
coverage.transfersMeasured
coverage.programsMeasured
holdBandobject
holdBand.precisionMeasured
holdBand.recallMeasured
holdBand.tpinteger
holdBand.fpinteger
holdBand.fninteger
autoClosedMeasured
overturnedMeasured
timeToDecisionHoursobject
timeToDecisionHours.automatedobjectClosed alerts routed automated.
timeToDecisionHours.automated.mediannumber | null
timeToDecisionHours.automated.p90number | null
timeToDecisionHours.automated.ninteger
timeToDecisionHours.reviewedobjectClosed alerts routed reviewed.
timeToDecisionHours.reviewed.mediannumber | null
timeToDecisionHours.reviewed.p90number | null
timeToDecisionHours.reviewed.ninteger
approvedWithoutWaitingobjectClear-band transfers that never raised an alert.
approvedWithoutWaiting.countinteger
approvedWithoutWaiting.centsstringInteger cents as a string (bigint-safe).
approvedWithoutWaiting.nintegerScored transfers.
eventCoverageobjectEvent families understood ÷ families known.
eventCoverage.valuenumber | null
eventCoverage.ninteger
eventCoverage.seenstring[]

Measured

A number with its n; value is null when nothing was measured.

FieldTypeNotes
valuenumber | nullnull when nothing was measured
nintegerobservations behind the value

Endpoints

get/api/metrics

The six numbers for the tenant

AuthBearer + X-Operator
OperationMetrics · GET /api/metrics

Parameters

NameInTypeNotes
tenant (optional)querystringTenant slug. Optional: the bearer token already proves the tenant. Pass it to be explicit.
days (optional)queryintegerWindow in days, 1–365 (default 30).

Responses

200 — Every metric with its n; also persisted as today's daily snapshot. Metrics

FieldTypeNotes
asOfstring (date-time)
windowDaysinteger
calibrationobjectPer band: labelled transfers that went bad ÷ labelled transfers.
calibration.clearMeasured
calibration.reviewMeasured
calibration.holdMeasured
calibration.unscoredMeasured
coverageobject
coverage.transfersMeasured
coverage.programsMeasured
holdBandobject
holdBand.precisionMeasured
holdBand.recallMeasured
holdBand.tpinteger
holdBand.fpinteger
holdBand.fninteger
autoClosedMeasured
overturnedMeasured
timeToDecisionHoursobject
timeToDecisionHours.automatedobjectClosed alerts routed automated.
timeToDecisionHours.automated.mediannumber | null
timeToDecisionHours.automated.p90number | null
timeToDecisionHours.automated.ninteger
timeToDecisionHours.reviewedobjectClosed alerts routed reviewed.
timeToDecisionHours.reviewed.mediannumber | null
timeToDecisionHours.reviewed.p90number | null
timeToDecisionHours.reviewed.ninteger
approvedWithoutWaitingobjectClear-band transfers that never raised an alert.
approvedWithoutWaiting.countinteger
approvedWithoutWaiting.centsstringInteger cents as a string (bigint-safe).
approvedWithoutWaiting.nintegerScored transfers.
eventCoverageobjectEvent families understood ÷ families known.
eventCoverage.valuenumber | null
eventCoverage.ninteger
eventCoverage.seenstring[]

401 — Bearer token missing or unknown. Error

FieldTypeNotes
errorstringWhat went wrong, in one sentence.
requestId (optional)stringPresent 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.

AuthBearer + X-Operator
OperationMetrics · GET /api/governance

Parameters

NameInTypeNotes
tenant (optional)querystringTenant slug. Optional: the bearer token already proves the tenant. Pass it to be explicit.
days (optional)queryintegerWindow in days, 1–365 (default 30).

Responses

200 — The record. GovernanceRecord

FieldTypeNotes
recordVersionstringgovernance-v1
asOfstring (date-time)
tenantstring
simulatedboolean
windowDaysinteger
inventoryobject[]
inventory[].idstring
inventory[].namestring
inventory[].versionstring
inventory[].kind"deterministic" | "llm" | "rules"
inventory[].decidesboolean
inventory[].purposestring
inventory[].inputsstring
inventory[].outputstring
inventory[].whereUsedstring
inventory[].validationstring
inventory[].evidencestring
boundaryobjectPolicy 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.
validationobjectEval 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.
monitoringMetrics
changeControlobjectStored policy versions (scope, version, active, createdAt), rule versions and the activation gate, the hash-chain report, and how the schema changes.
oversightobjectAttribution 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.
dataobjectEvent coverage with the event types seen, the null rule, the no-fabrication test, retention.
mappingobject[]
mapping[].framework"SR 11-7" | "NIST AI RMF"
mapping[].sectionstring
mapping[].requirementstring
mapping[].whereInSkoorstring
mapping[].evidencestring

401 — Bearer token missing or unknown. Error

FieldTypeNotes
errorstringWhat went wrong, in one sentence.
requestId (optional)stringPresent 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

AuthNone (public)
OperationMetrics · GET /api/public/metrics

Parameters

NameInTypeNotes
days (optional)queryintegerWindow 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

FieldTypeNotes
tenantstring
simulatedconst true
asOfstring (date-time)
windowDaysinteger
calibrationobjectPer band: labelled transfers that went bad ÷ labelled transfers.
calibration.clearMeasured
calibration.reviewMeasured
calibration.holdMeasured
calibration.unscoredMeasured
coverageobject
coverage.transfersMeasured
coverage.programsMeasured
holdBandobject
holdBand.precisionMeasured
holdBand.recallMeasured
holdBand.tpinteger
holdBand.fpinteger
holdBand.fninteger
autoClosedMeasured
overturnedMeasured
timeToDecisionHoursobject
timeToDecisionHours.automatedobjectClosed alerts routed automated.
timeToDecisionHours.automated.mediannumber | null
timeToDecisionHours.automated.p90number | null
timeToDecisionHours.automated.ninteger
timeToDecisionHours.reviewedobjectClosed alerts routed reviewed.
timeToDecisionHours.reviewed.mediannumber | null
timeToDecisionHours.reviewed.p90number | null
timeToDecisionHours.reviewed.ninteger
approvedWithoutWaitingobjectClear-band transfers that never raised an alert.
approvedWithoutWaiting.countinteger
approvedWithoutWaiting.centsstringInteger cents as a string (bigint-safe).
approvedWithoutWaiting.nintegerScored transfers.
eventCoverageobjectEvent families understood ÷ families known.
eventCoverage.valuenumber | null
eventCoverage.ninteger
eventCoverage.seenstring[]

404 — No demo tenant on this deployment. Error

FieldTypeNotes
errorstringWhat went wrong, in one sentence.
requestId (optional)stringPresent 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

Actor, program and weekly reports, plus the examiner record.

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.

FieldTypeNotes
kindconst "actor"
type"entity" | "counterparty" | "agent"
idstring
namestring | null
programIdstring (uuid)
generatedAtstring (date-time)
currentobject | null
historySkoorPoint[]
transitionsBandTransition[]
signalTrendsobject | null
transfersTransfersSummary
alertsobject[]
alerts[].idstring (uuid)
alerts[].detectorstring
alerts[].statusstring
alerts[].routestring | null
alerts[].openedAtstring (date-time)
alerts[].summarystring
dispositionsobject[]
dispositions[].alertIdstring (uuid)
dispositions[].decidedBystring
dispositions[].decisionstring
dispositions[].overturnsDraftboolean | null
dispositions[].createdAtstring (date-time)

ProgramReport

FieldTypeNotes
kindconst "program"
idstring (uuid)
namestring
generatedAtstring (date-time)
currentobject | null
historyobject[]
history[].asOfstring (date-time)
history[].programSkoorinteger | null
history[].band"clear" | "review" | "hold" | "unscored"
history[].ninteger
transitionsBandTransition[]
krisRecord<string, KriValue>
kriDeltasobject[]
kriDeltas[].namestring
kriDeltas[].fromnumber | null
kriDeltas[].tonumber | null
kriDeltas[].deltanumber | null
kriDeltas[].statusFromstring | null
kriDeltas[].statusTostring
kriDeltas[].ninteger
rankobject | null
transfersTransfersSummary
alertsByDetectorobject[]
alertsByDetector[].detectorstring
alertsByDetector[].routestring | null
alertsByDetector[].countinteger

WeeklyReport

FieldTypeNotes
kindconst "weekly"
keystringweekly:<programId>:<week>
programIdstring (uuid)
programNamestring
isoWeekstringe.g. 2026-W37
fromstring (date-time)
tostring (date-time)
generatedAtstring (date-time)
programSkoorobject
programSkoor.currentinteger | null
programSkoor.priorinteger | null
programSkoor.bandstring
programSkoor.ninteger
krisobject[]
kris[].namestring
kris[].valuenumber | null
kris[].priornumber | null
kris[].deltanumber | null
kris[].statusstring
kris[].priorStatusstring | null
kris[].ninteger
kris[].unitstring
kris[].thresholdnumber | null
alertsobject
alerts.totalinteger
alerts.byDetectorobject[]
alerts.byDetector[].detectorstring
alerts.byDetector[].routestring | null
alerts.byDetector[].countinteger
alerts.byStatusRecord<string, integer>
enteredHoldobject[]
enteredHold[].typestring
enteredHold[].idstring
enteredHold[].skoorinteger | null
enteredHold[].ninteger
enteredHold[].atstring (date-time)
dispositionsobject
dispositions.totalinteger
dispositions.byPlatforminteger
dispositions.byPersoninteger
dispositions.overturnedinteger
timeToDecisionHoursobjectClosed alerts in the week.
timeToDecisionHours.mediannumber | null
timeToDecisionHours.p90number | null
timeToDecisionHours.ninteger
transfersobject
transfers.countinteger
transfers.volumeCentsstringInteger cents as a string (bigint-safe).
transfers.returnsobject
transfers.returns.anyinteger
transfers.returns.unauthorizedinteger
transfers.bandsRecord<string, integer>
summarystringPlain-text summary written from a template, never a model.

ChainReport

FieldTypeNotes
okboolean
checkedinteger
firstBreakAtobject | null
chainsobject
chains.dispositionsobject
chains.dispositions.okboolean
chains.dispositions.checkedinteger
chains.actionsobject
chains.actions.okboolean
chains.actions.checkedinteger

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.

FieldTypeNotes
kind"policy" | "alert" | "disposition" | "action"

Endpoints

get/api/reports/actor/{type}/{id}

Actor report

AuthBearer + X-Operator
OperationReport · GET /api/reports/actor/{type}/{id}

Parameters

NameInTypeNotes
typepath"entity" | "counterparty" | "agent"entity, counterparty or agent.
idpathstringActor id.
tenant (optional)querystringTenant 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

FieldTypeNotes
kindconst "actor"
type"entity" | "counterparty" | "agent"
idstring
namestring | null
programIdstring (uuid)
generatedAtstring (date-time)
currentobject | null
historySkoorPoint[]
transitionsBandTransition[]
signalTrendsobject | null
transfersTransfersSummary
alertsobject[]
alerts[].idstring (uuid)
alerts[].detectorstring
alerts[].statusstring
alerts[].routestring | null
alerts[].openedAtstring (date-time)
alerts[].summarystring
dispositionsobject[]
dispositions[].alertIdstring (uuid)
dispositions[].decidedBystring
dispositions[].decisionstring
dispositions[].overturnsDraftboolean | null
dispositions[].createdAtstring (date-time)

400 — type must be entity, counterparty, or agent. Error

FieldTypeNotes
errorstringWhat went wrong, in one sentence.
requestId (optional)stringPresent once the gateway is live: the X-Request-Id of the failed request.

401 — Bearer token missing or unknown. Error

FieldTypeNotes
errorstringWhat went wrong, in one sentence.
requestId (optional)stringPresent once the gateway is live: the X-Request-Id of the failed request.

404 — Unknown actor on this tenant. Error

FieldTypeNotes
errorstringWhat went wrong, in one sentence.
requestId (optional)stringPresent 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

AuthBearer + X-Operator
OperationReport · GET /api/reports/program/{id}

Parameters

NameInTypeNotes
idpathstring (uuid)Program id.
tenant (optional)querystringTenant 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

FieldTypeNotes
kindconst "program"
idstring (uuid)
namestring
generatedAtstring (date-time)
currentobject | null
historyobject[]
history[].asOfstring (date-time)
history[].programSkoorinteger | null
history[].band"clear" | "review" | "hold" | "unscored"
history[].ninteger
transitionsBandTransition[]
krisRecord<string, KriValue>
kriDeltasobject[]
kriDeltas[].namestring
kriDeltas[].fromnumber | null
kriDeltas[].tonumber | null
kriDeltas[].deltanumber | null
kriDeltas[].statusFromstring | null
kriDeltas[].statusTostring
kriDeltas[].ninteger
rankobject | null
transfersTransfersSummary
alertsByDetectorobject[]
alertsByDetector[].detectorstring
alertsByDetector[].routestring | null
alertsByDetector[].countinteger

401 — Bearer token missing or unknown. Error

FieldTypeNotes
errorstringWhat went wrong, in one sentence.
requestId (optional)stringPresent once the gateway is live: the X-Request-Id of the failed request.

404 — Unknown program on this tenant. Error

FieldTypeNotes
errorstringWhat went wrong, in one sentence.
requestId (optional)stringPresent 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

AuthBearer + X-Operator
OperationReport · GET /api/reports/weekly/{programId}

Parameters

NameInTypeNotes
programIdpathstring (uuid)Program id.
tenant (optional)querystringTenant slug. Optional: the bearer token already proves the tenant. Pass it to be explicit.
week (optional)querystringISO 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

FieldTypeNotes
kindconst "weekly"
keystringweekly:<programId>:<week>
programIdstring (uuid)
programNamestring
isoWeekstringe.g. 2026-W37
fromstring (date-time)
tostring (date-time)
generatedAtstring (date-time)
programSkoorobject
programSkoor.currentinteger | null
programSkoor.priorinteger | null
programSkoor.bandstring
programSkoor.ninteger
krisobject[]
kris[].namestring
kris[].valuenumber | null
kris[].priornumber | null
kris[].deltanumber | null
kris[].statusstring
kris[].priorStatusstring | null
kris[].ninteger
kris[].unitstring
kris[].thresholdnumber | null
alertsobject
alerts.totalinteger
alerts.byDetectorobject[]
alerts.byDetector[].detectorstring
alerts.byDetector[].routestring | null
alerts.byDetector[].countinteger
alerts.byStatusRecord<string, integer>
enteredHoldobject[]
enteredHold[].typestring
enteredHold[].idstring
enteredHold[].skoorinteger | null
enteredHold[].ninteger
enteredHold[].atstring (date-time)
dispositionsobject
dispositions.totalinteger
dispositions.byPlatforminteger
dispositions.byPersoninteger
dispositions.overturnedinteger
timeToDecisionHoursobjectClosed alerts in the week.
timeToDecisionHours.mediannumber | null
timeToDecisionHours.p90number | null
timeToDecisionHours.ninteger
transfersobject
transfers.countinteger
transfers.volumeCentsstringInteger cents as a string (bigint-safe).
transfers.returnsobject
transfers.returns.anyinteger
transfers.returns.unauthorizedinteger
transfers.bandsRecord<string, integer>
summarystringPlain-text summary written from a template, never a model.

401 — Bearer token missing or unknown. Error

FieldTypeNotes
errorstringWhat went wrong, in one sentence.
requestId (optional)stringPresent once the gateway is live: the X-Request-Id of the failed request.

404 — Unknown program. Error

FieldTypeNotes
errorstringWhat went wrong, in one sentence.
requestId (optional)stringPresent 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

AuthBearer + X-Operator
OperationReport · GET /api/record/verify

Parameters

NameInTypeNotes
tenant (optional)querystringTenant 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

FieldTypeNotes
okboolean
checkedinteger
firstBreakAtobject | null
chainsobject
chains.dispositionsobject
chains.dispositions.okboolean
chains.dispositions.checkedinteger
chains.actionsobject
chains.actions.okboolean
chains.actions.checkedinteger

401 — Bearer token missing or unknown. Error

FieldTypeNotes
errorstringWhat went wrong, in one sentence.
requestId (optional)stringPresent 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)

AuthBearer + X-Operator
OperationReport · GET /api/record/export

Parameters

NameInTypeNotes
tenant (optional)querystringTenant slug. Optional: the bearer token already proves the tenant. Pass it to be explicit.
from (optional)querystring (date-time)Start of the range (ISO 8601). Default 30 days ago.
to (optional)querystring (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

FieldTypeNotes
kind"policy" | "alert" | "disposition" | "action"

400 — Bad date. Error

FieldTypeNotes
errorstringWhat went wrong, in one sentence.
requestId (optional)stringPresent once the gateway is live: the X-Request-Id of the failed request.

401 — Bearer token missing or unknown. Error

FieldTypeNotes
errorstringWhat went wrong, in one sentence.
requestId (optional)stringPresent 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"

Proof

A signed Skoor another bank can verify offline.

An EdDSA-signed compact token carrying what the platform observed about one actor at one moment: the Skoor, its band, the version, n, confidence and the reason ids. Unscored actors cannot be proved (409): there is no proof of a number that does not exist. The public key is published as a JWKS.

ProofClaims

FieldTypeNotes
issstringIssuer: the platform URL.
substring<type>:<id>
tenantstringTenant slug.
programstring | nullProgram id.
skoorinteger
bandstring
versionstring
ninteger
confidencenumber | null
reasonsstring[]Reason-code ids only.
simulated (optional)const truePresent only when the underlying data is simulated.
iatinteger
expinteger24 hours after iat.
jtistringThe actor_skoors row id.

ProofVerification

FieldTypeNotes
validboolean
reason (optional)stringmalformed | unsupported alg | unknown kid | bad signature | expired | no skoor in claims | token required
claims (optional)ProofClaims
expired (optional)boolean

Jwks

FieldTypeNotes
keysobject[]
keys[].ktystring
keys[].crvstring
keys[].xstring
keys[].kidstring
keys[].useconst "sig"
keys[].algconst "EdDSA"

Endpoints

post/api/proofs/actor

Issue a signed proof for an actor

AuthBearer + X-Operator
OperationProof · POST /api/proofs/actor

Parameters

NameInTypeNotes
tenant (optional)querystringTenant slug. Optional: the bearer token already proves the tenant. Pass it to be explicit.
Idempotency-Key (optional)headerstring1–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

FieldTypeNotes
type"entity" | "counterparty" | "agent"
idstring

Responses

201 — A 24-hour EdDSA token, its claims, and where the public key lives. object

FieldTypeNotes
tokenstringCompact JWT (header.payload.signature), alg EdDSA.
claimsProofClaims
jwksUrlstring

400 — type (entity|counterparty|agent) and id required. Error

FieldTypeNotes
errorstringWhat went wrong, in one sentence.
requestId (optional)stringPresent once the gateway is live: the X-Request-Id of the failed request.

401 — Bearer token missing or unknown. Error

FieldTypeNotes
errorstringWhat went wrong, in one sentence.
requestId (optional)stringPresent once the gateway is live: the X-Request-Id of the failed request.

404 — No Skoor on record for the actor. Error

FieldTypeNotes
errorstringWhat went wrong, in one sentence.
requestId (optional)stringPresent once the gateway is live: the X-Request-Id of the failed request.

409 — The actor is unscored; a proof is never issued for a null Skoor. Error

FieldTypeNotes
errorstringWhat went wrong, in one sentence.
requestId (optional)stringPresent once the gateway is live: the X-Request-Id of the failed request.

Example

curl -s -X POST "https://skoor.ai/risk/api/proofs/actor?tenant=demo" \
  -H "Authorization: Bearer test_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
  -H "X-Operator: you@bank.example" \
  -H "Content-Type: application/json" \
  -d '{"type":"entity","id":"enti_sim_acme"}'

get/api/proofs/verify

Verify a proof

Checks the signature against the platform key, the expiry and the presence of a Skoor. No database access; anyone may call it.

AuthNone (public)
OperationProof · GET /api/proofs/verify

Parameters

NameInTypeNotes
token (optional)querystringThe compact token.

Responses

200 — valid with the claims, or invalid with the reason. ProofVerification

FieldTypeNotes
validboolean
reason (optional)stringmalformed | unsupported alg | unknown kid | bad signature | expired | no skoor in claims | token required
claims (optional)ProofClaims
expired (optional)boolean

400 — token required. ProofVerification

FieldTypeNotes
validboolean
reason (optional)stringmalformed | unsupported alg | unknown kid | bad signature | expired | no skoor in claims | token required
claims (optional)ProofClaims
expired (optional)boolean

Example

curl -s "https://skoor.ai/risk/api/proofs/verify?token=eyJhbGciOiJFZERTQSIs%E2%80%A6"

get/.well-known/skoor-risk-jwks.json

The public key set

AuthNone (public)
OperationProof · GET /.well-known/skoor-risk-jwks.json

Parameters

None.

Responses

200 — JWKS with the current Ed25519 key (cached 5 minutes). Jwks

FieldTypeNotes
keysobject[]
keys[].ktystring
keys[].crvstring
keys[].xstring
keys[].kidstring
keys[].useconst "sig"
keys[].algconst "EdDSA"

Example

curl -s "https://skoor.ai/risk/.well-known/skoor-risk-jwks.json"

Ingest

The inbound Column webhook.

Column delivers events here, one endpoint per program. The raw body is verified against the program's signing secret (Column-Signature, HMAC-SHA256 hex); an invalid signature stores nothing. Deduplicated by event id, safe under redelivery, answered inside the 10-second budget; scoring, detection and triage run after the response.

ColumnEvent

FieldTypeNotes
idstringColumn event id; redeliveries carry the same id and are deduplicated.
created_atstring
typestringe.g. ach.outgoing_transfer.returned
data (optional)objectThe bank's object.

Endpoints

post/webhooks/column/{key}

Receive a Column event

Raw body, 2 MB limit, any content type. Verified before anything is parsed; stored, deduplicated by event id, answered at once. Writes are awaited; scoring, detection and triage run after the response. Column registers this URL itself when a tenant connects its key.

AuthNone (public)
OperationIngest · POST /webhooks/column/{key}

Parameters

NameInTypeNotes
keypathstringThe program slug (or the Column webhook endpoint id).
Column-SignatureheaderstringHMAC-SHA256 of the raw body with the program's webhook secret, hex.
Webhook-Endpoint-Id (optional)headerstringColumn's endpoint id; resolves the program before the path key.

Request body (application/json, required) · ColumnEvent

FieldTypeNotes
idstringColumn event id; redeliveries carry the same id and are deduplicated.
created_atstring
typestringe.g. ach.outgoing_transfer.returned
data (optional)objectThe bank's object.

Responses

200 — Stored (or a redelivery). object

FieldTypeNotes
receivedboolean
duplicateboolean

400 — Invalid JSON or an event without id, created_at and type. Error

FieldTypeNotes
errorstringWhat went wrong, in one sentence.
requestId (optional)stringPresent once the gateway is live: the X-Request-Id of the failed request.

401 — Invalid signature; nothing stored. Error

FieldTypeNotes
errorstringWhat went wrong, in one sentence.
requestId (optional)stringPresent once the gateway is live: the X-Request-Id of the failed request.

404 — Unknown program. Error

FieldTypeNotes
errorstringWhat went wrong, in one sentence.
requestId (optional)stringPresent once the gateway is live: the X-Request-Id of the failed request.

Example

curl -s -X POST "https://skoor.ai/risk/webhooks/column/northwind-payroll" \
  -H "Content-Type: application/json" \
  -d '{"id":"evnt_2Zx9…","created_at":"2026-09-17T12:00:00Z","type":"ach.outgoing_transfer.returned","data":{"id":"acht_…","return_details":[{"return_code":"R10"}]}}'

Column signs the body; a hand-made call needs the program's secret: -H "Column-Signature: $(printf %s "$BODY" | openssl dgst -sha256 -hmac "$WEBHOOK_SECRET" | sed 's/^.* //')".

Connector

Skoor Risk inside Claude (MCP over streamable HTTP).

A minimal MCP server: JSON-RPC 2.0 over POST, request/response only. Tools: list_queue, get_alert, decide_alert, skoor_preflight, program_report, actor_report, weekly_report, metrics. Every write goes through the same services as the dashboard, so the record is identical.

Endpoints

get/connector/manifest.json

Connector manifest

AuthNone (public)
OperationConnector · GET /connector/manifest.json

Parameters

None.

Responses

200 — Name, version, transport, MCP URL, protocol version, auth shape and tool names. object

FieldTypeNotes
namestring
versionstring
descriptionstring
transportconst "streamable-http"
urlstring
protocolVersionstring
authobject
auth.typeconst "bearer"
auth.headerstring
auth.extraHeadersstring[]
auth.notestring
toolsstring[]

Example

curl -s "https://skoor.ai/risk/connector/manifest.json"

post/mcp

MCP JSON-RPC endpoint

Streamable HTTP, request/response only: initialize, tools/list, tools/call. Notifications (no id) are answered 202 with no body. Body limit 1 MB. Protocol version 2025-06-18.

AuthBearer + X-Operator
OperationConnector · POST /mcp

Parameters

NameInTypeNotes
tenant (optional)querystringTenant slug. Optional: the bearer token already proves the tenant. Pass it to be explicit.

Request body (application/json, required) · object

FieldTypeNotes
jsonrpcconst "2.0"
id (optional)string | integer | null
methodstring
params (optional)object

Responses

200 — A JSON-RPC response (or a batch of them). object

FieldTypeNotes
jsonrpcconst "2.0"
idstring | integer | null
result (optional)object
error (optional)object
error.codeinteger
error.messagestring
error.data (optional)object

202 — Notification accepted; no body.

400 — X-Operator header required. Error

FieldTypeNotes
errorstringWhat went wrong, in one sentence.
requestId (optional)stringPresent once the gateway is live: the X-Request-Id of the failed request.

401 — Bearer token missing or unknown. Error

FieldTypeNotes
errorstringWhat went wrong, in one sentence.
requestId (optional)stringPresent once the gateway is live: the X-Request-Id of the failed request.

Example

curl -s -X POST "https://skoor.ai/risk/mcp?tenant=demo" \
  -H "Authorization: Bearer test_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
  -H "X-Operator: you@bank.example" \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"list_queue","arguments":{"route":"reviewed","limit":5}}}'

get/mcp

No server-initiated streams

AuthNone (public)
OperationConnector · GET /mcp

Parameters

None.

Responses

405 — Streamable HTTP: POST JSON-RPC to this URL. Error

FieldTypeNotes
errorstringWhat went wrong, in one sentence.
requestId (optional)stringPresent once the gateway is live: the X-Request-Id of the failed request.

Example

curl -s "https://skoor.ai/risk/mcp"

Key

API keys: test_ and live_.

Keys let a program or a bank integration call the API without the operator token. A test key may only touch simulated programs and the sandbox; a live key only Column-sourced programs (403 either way). The secret is shown once at creation and only its sha256 is stored. For a key, the operator on the record is key:<prefix> unless an X-Operator header names a person.

ApiKey

Only the sha256 of the secret is stored; the secret is shown once at creation.

FieldTypeNotes
idstring (uuid)
namestring
mode"test" | "live"test_ keys may only touch simulated programs and the sandbox; live_ keys touch Column programs.
prefixstringFirst 12 characters of the secret, for display.
programIdstring | null (uuid)null = every program on the tenant.
scopesstring[][] = all scopes the mode allows.
lastUsedAtstring | null (date-time)
revokedAtstring | null (date-time)
createdAtstring (date-time)

Endpoints

post/api/keys

Create an API key

live keys may be created only when the tenant's Column environment is live or the operator passes confirmLive: true. The secret (test_/live_ + 32 base64url characters) is returned exactly once.

AuthBearer + X-Operator
OperationKey · POST /api/keys

Parameters

NameInTypeNotes
tenant (optional)querystringTenant slug. Optional: the bearer token already proves the tenant. Pass it to be explicit.
Idempotency-Key (optional)headerstring1–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

FieldTypeNotes
namestring
mode"test" | "live"
programId (optional)string (uuid) | null
scopes (optional)string[]
confirmLive (optional)boolean

Responses

201 — The key with its secret, shown once. object

FieldTypeNotes
idstring (uuid)
namestring
mode"test" | "live"
prefixstring
secretstring

400 — Validation failed. Error

FieldTypeNotes
errorstringWhat went wrong, in one sentence.
requestId (optional)stringPresent once the gateway is live: the X-Request-Id of the failed request.

401 — Bearer token missing or unknown. Error

FieldTypeNotes
errorstringWhat went wrong, in one sentence.
requestId (optional)stringPresent once the gateway is live: the X-Request-Id of the failed request.

Example

curl -s -X POST "https://skoor.ai/risk/api/keys?tenant=demo" \
  -H "Authorization: Bearer test_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
  -H "X-Operator: you@bank.example" \
  -H "Content-Type: application/json" \
  -d '{"name":"Northwind integration","mode":"test","programId":null}'

get/api/keys

List keys (never the secret)

AuthBearer + X-Operator
OperationKey · GET /api/keys

Parameters

NameInTypeNotes
tenant (optional)querystringTenant slug. Optional: the bearer token already proves the tenant. Pass it to be explicit.

Responses

200 — Every key on the tenant, including revoked ones. Array of ApiKey

FieldTypeNotes
idstring (uuid)
namestring
mode"test" | "live"test_ keys may only touch simulated programs and the sandbox; live_ keys touch Column programs.
prefixstringFirst 12 characters of the secret, for display.
programIdstring | null (uuid)null = every program on the tenant.
scopesstring[][] = all scopes the mode allows.
lastUsedAtstring | null (date-time)
revokedAtstring | null (date-time)
createdAtstring (date-time)

401 — Bearer token missing or unknown. Error

FieldTypeNotes
errorstringWhat went wrong, in one sentence.
requestId (optional)stringPresent once the gateway is live: the X-Request-Id of the failed request.

Example

curl -s "https://skoor.ai/risk/api/keys?tenant=demo" \
  -H "Authorization: Bearer test_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
  -H "X-Operator: you@bank.example"

delete/api/keys/{id}

Revoke a key

AuthBearer + X-Operator
OperationKey · DELETE /api/keys/{id}

Parameters

NameInTypeNotes
idpathstring (uuid)Key id.
tenant (optional)querystringTenant slug. Optional: the bearer token already proves the tenant. Pass it to be explicit.

Responses

200 — Revoked; the key stops working at once. ApiKey

FieldTypeNotes
idstring (uuid)
namestring
mode"test" | "live"test_ keys may only touch simulated programs and the sandbox; live_ keys touch Column programs.
prefixstringFirst 12 characters of the secret, for display.
programIdstring | null (uuid)null = every program on the tenant.
scopesstring[][] = all scopes the mode allows.
lastUsedAtstring | null (date-time)
revokedAtstring | null (date-time)
createdAtstring (date-time)

401 — Bearer token missing or unknown. Error

FieldTypeNotes
errorstringWhat went wrong, in one sentence.
requestId (optional)stringPresent once the gateway is live: the X-Request-Id of the failed request.

404 — Not this tenant's key. Error

FieldTypeNotes
errorstringWhat went wrong, in one sentence.
requestId (optional)stringPresent once the gateway is live: the X-Request-Id of the failed request.

Example

curl -s -X DELETE "https://skoor.ai/risk/api/keys/5f3c1c8e-2a44-4c0b-9d0f-2b0a2f6e7c11?tenant=demo" \
  -H "Authorization: Bearer test_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
  -H "X-Operator: you@bank.example"

Webhook

Outbound signed webhooks.

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

FieldTypeNotes
idstring (uuid)
programIdstring | null (uuid)null = every program on the tenant.
urlstringhttps 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.
enabledboolean
descriptionstring | null
createdBystring
createdAtstring (date-time)

WebhookDelivery

FieldTypeNotes
idstring (uuid)
endpointIdstring (uuid)
event"alert.opened" | "disposition.signed" | "action.created" | "action.approved" | "preflight.scored" | "transfer.scored" | "label.recorded" | "kri.snapshot" | "ping"
eventIdstringStable id of the emitted event, for the receiver's dedupe.
payloadobject{ id, type, createdAt, tenant, program, data }
status"pending" | "delivered" | "failed" | "exhausted"
attemptsinteger
nextAttemptAtstring (date-time)now + min(2^attempts minutes, 6h) after a failure.
lastStatusCodeinteger | null
lastErrorstring | null
deliveredAtstring | null (date-time)
createdAtstring (date-time)

Endpoints

post/api/webhooks

Register an endpoint

AuthBearer + X-Operator
OperationWebhook · POST /api/webhooks

Parameters

NameInTypeNotes
tenant (optional)querystringTenant slug. Optional: the bearer token already proves the tenant. Pass it to be explicit.
Idempotency-Key (optional)headerstring1–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

FieldTypeNotes
urlstringhttps 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

FieldTypeNotes
idstring (uuid)
urlstring
eventsstring[]
secretstring

400 — Validation failed. Error

FieldTypeNotes
errorstringWhat went wrong, in one sentence.
requestId (optional)stringPresent once the gateway is live: the X-Request-Id of the failed request.

401 — Bearer token missing or unknown. Error

FieldTypeNotes
errorstringWhat went wrong, in one sentence.
requestId (optional)stringPresent 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

AuthBearer + X-Operator
OperationWebhook · GET /api/webhooks

Parameters

NameInTypeNotes
tenant (optional)querystringTenant 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

FieldTypeNotes
idstring (uuid)
programIdstring | null (uuid)null = every program on the tenant.
urlstringhttps 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.
enabledboolean
descriptionstring | null
createdBystring
createdAtstring (date-time)

401 — Bearer token missing or unknown. Error

FieldTypeNotes
errorstringWhat went wrong, in one sentence.
requestId (optional)stringPresent 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.

AuthBearer + X-Operator
OperationWebhook · GET /api/webhooks/events

Parameters

NameInTypeNotes
tenant (optional)querystringTenant slug. Optional: the bearer token already proves the tenant. Pass it to be explicit.

Responses

200 — Event names and example payloads. object

FieldTypeNotes
events (optional)string[]
examples (optional)object

401 — Bearer token missing or unknown. Error

FieldTypeNotes
errorstringWhat went wrong, in one sentence.
requestId (optional)stringPresent 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

AuthBearer + X-Operator
OperationWebhook · DELETE /api/webhooks/{id}

Parameters

NameInTypeNotes
idpathstring (uuid)Endpoint id.
tenant (optional)querystringTenant slug. Optional: the bearer token already proves the tenant. Pass it to be explicit.

Responses

200 — Disabled. WebhookEndpoint

FieldTypeNotes
idstring (uuid)
programIdstring | null (uuid)null = every program on the tenant.
urlstringhttps 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.
enabledboolean
descriptionstring | null
createdBystring
createdAtstring (date-time)

401 — Bearer token missing or unknown. Error

FieldTypeNotes
errorstringWhat went wrong, in one sentence.
requestId (optional)stringPresent once the gateway is live: the X-Request-Id of the failed request.

404 — Not this tenant's endpoint. Error

FieldTypeNotes
errorstringWhat went wrong, in one sentence.
requestId (optional)stringPresent 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

AuthBearer + X-Operator
OperationWebhook · GET /api/webhooks/{id}/deliveries

Parameters

NameInTypeNotes
idpathstring (uuid)Endpoint id.
tenant (optional)querystringTenant slug. Optional: the bearer token already proves the tenant. Pass it to be explicit.
limit (optional)queryintegerNewest first.

Responses

200 — Deliveries, latest first. Array of WebhookDelivery

FieldTypeNotes
idstring (uuid)
endpointIdstring (uuid)
event"alert.opened" | "disposition.signed" | "action.created" | "action.approved" | "preflight.scored" | "transfer.scored" | "label.recorded" | "kri.snapshot" | "ping"
eventIdstringStable id of the emitted event, for the receiver's dedupe.
payloadobject{ id, type, createdAt, tenant, program, data }
status"pending" | "delivered" | "failed" | "exhausted"
attemptsinteger
nextAttemptAtstring (date-time)now + min(2^attempts minutes, 6h) after a failure.
lastStatusCodeinteger | null
lastErrorstring | null
deliveredAtstring | null (date-time)
createdAtstring (date-time)

401 — Bearer token missing or unknown. Error

FieldTypeNotes
errorstringWhat went wrong, in one sentence.
requestId (optional)stringPresent once the gateway is live: the X-Request-Id of the failed request.

404 — Not this tenant's endpoint. Error

FieldTypeNotes
errorstringWhat went wrong, in one sentence.
requestId (optional)stringPresent 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

AuthBearer + X-Operator
OperationWebhook · POST /api/webhooks/{id}/deliveries/{deliveryId}/redeliver

Parameters

NameInTypeNotes
idpathstring (uuid)Endpoint id.
deliveryIdpathstring (uuid)Delivery id.
tenant (optional)querystringTenant slug. Optional: the bearer token already proves the tenant. Pass it to be explicit.

Responses

200 — Reset to pending; attempts unchanged. WebhookDelivery

FieldTypeNotes
idstring (uuid)
endpointIdstring (uuid)
event"alert.opened" | "disposition.signed" | "action.created" | "action.approved" | "preflight.scored" | "transfer.scored" | "label.recorded" | "kri.snapshot" | "ping"
eventIdstringStable id of the emitted event, for the receiver's dedupe.
payloadobject{ id, type, createdAt, tenant, program, data }
status"pending" | "delivered" | "failed" | "exhausted"
attemptsinteger
nextAttemptAtstring (date-time)now + min(2^attempts minutes, 6h) after a failure.
lastStatusCodeinteger | null
lastErrorstring | null
deliveredAtstring | null (date-time)
createdAtstring (date-time)

401 — Bearer token missing or unknown. Error

FieldTypeNotes
errorstringWhat went wrong, in one sentence.
requestId (optional)stringPresent once the gateway is live: the X-Request-Id of the failed request.

404 — Not this tenant's delivery. Error

FieldTypeNotes
errorstringWhat went wrong, in one sentence.
requestId (optional)stringPresent 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

AuthBearer + X-Operator
OperationWebhook · POST /api/webhooks/{id}/test

Parameters

NameInTypeNotes
idpathstring (uuid)Endpoint id.
tenant (optional)querystringTenant slug. Optional: the bearer token already proves the tenant. Pass it to be explicit.

Responses

200 — A ping delivery enqueued. WebhookDelivery

FieldTypeNotes
idstring (uuid)
endpointIdstring (uuid)
event"alert.opened" | "disposition.signed" | "action.created" | "action.approved" | "preflight.scored" | "transfer.scored" | "label.recorded" | "kri.snapshot" | "ping"
eventIdstringStable id of the emitted event, for the receiver's dedupe.
payloadobject{ id, type, createdAt, tenant, program, data }
status"pending" | "delivered" | "failed" | "exhausted"
attemptsinteger
nextAttemptAtstring (date-time)now + min(2^attempts minutes, 6h) after a failure.
lastStatusCodeinteger | null
lastErrorstring | null
deliveredAtstring | null (date-time)
createdAtstring (date-time)

401 — Bearer token missing or unknown. Error

FieldTypeNotes
errorstringWhat went wrong, in one sentence.
requestId (optional)stringPresent once the gateway is live: the X-Request-Id of the failed request.

404 — Not this tenant's endpoint. Error

FieldTypeNotes
errorstringWhat went wrong, in one sentence.
requestId (optional)stringPresent 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

Simulation endpoints for test_ keys.

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

FieldTypeNotes
eventsstring[]Ids of the Column-shaped events stored (source = simulated).
transferId (optional)string
alertIdsstring (uuid)[]
skoorobject | 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.

AuthBearer + X-Operator
OperationSandbox · POST /api/sandbox/transfers

Parameters

NameInTypeNotes
tenant (optional)querystringTenant slug. Optional: the bearer token already proves the tenant. Pass it to be explicit.
Idempotency-Key (optional)headerstring1–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

FieldTypeNotes
programId (optional)string (uuid)The simulated program. One of programId / programSlug is required.
programSlug (optional)stringThe simulated program by slug.
rail"ach" | "wire" | "book" | "realtime" | "card"
direction"incoming" | "outgoing"
amountCentsinteger | stringInteger 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)stringcard only, e.g. suspected_fraud

Responses

200 — What the pipeline produced. object

FieldTypeNotes
programstringProgram slug.
eventsstring[]Ids of the Column-shaped events stored, in order.
transferIdstring
transferSandboxTransfer
skoorSkoor
alertIdsstring (uuid)[]
alertsSandboxAlert[]
labelsSandboxLabel[]

401 — Bearer token missing or unknown. Error

FieldTypeNotes
errorstringWhat went wrong, in one sentence.
requestId (optional)stringPresent 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

FieldTypeNotes
errorstringWhat went wrong, in one sentence.
requestId (optional)stringPresent 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.

AuthBearer + X-Operator
OperationSandbox · POST /api/sandbox/transfers/{id}/return

Parameters

NameInTypeNotes
idpathstringid
tenant (optional)querystringTenant slug. Optional: the bearer token already proves the tenant. Pass it to be explicit.
Idempotency-Key (optional)headerstring1–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

FieldTypeNotes
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

FieldTypeNotes
programstringProgram slug.
eventsstring[]Ids of the Column-shaped events stored, in order.
transferIdstring
transferSandboxTransfer
skoorSkoor
alertIdsstring (uuid)[]
alertsSandboxAlert[]
labelsSandboxLabel[]

401 — Bearer token missing or unknown. Error

FieldTypeNotes
errorstringWhat went wrong, in one sentence.
requestId (optional)stringPresent 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

FieldTypeNotes
errorstringWhat went wrong, in one sentence.
requestId (optional)stringPresent 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.

AuthBearer + X-Operator
OperationSandbox · POST /api/sandbox/transfers/{id}/dispute

Parameters

NameInTypeNotes
idpathstringid
tenant (optional)querystringTenant slug. Optional: the bearer token already proves the tenant. Pass it to be explicit.
Idempotency-Key (optional)headerstring1–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

FieldTypeNotes
reason (optional)"fraud" | "not_received" | "duplicate" | "other"
at (optional)string (date-time)

Responses

200 — What the pipeline produced. object

FieldTypeNotes
programstringProgram slug.
eventsstring[]Ids of the Column-shaped events stored, in order.
transferIdstring
transferSandboxTransfer
skoorSkoor
alertIdsstring (uuid)[]
alertsSandboxAlert[]
labelsSandboxLabel[]

401 — Bearer token missing or unknown. Error

FieldTypeNotes
errorstringWhat went wrong, in one sentence.
requestId (optional)stringPresent 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

FieldTypeNotes
errorstringWhat went wrong, in one sentence.
requestId (optional)stringPresent 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.

AuthBearer + X-Operator
OperationSandbox · POST /api/sandbox/entities

Parameters

NameInTypeNotes
tenant (optional)querystringTenant slug. Optional: the bearer token already proves the tenant. Pass it to be explicit.
Idempotency-Key (optional)headerstring1–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

FieldTypeNotes
programId (optional)string (uuid)The simulated program. One of programId / programSlug is required.
programSlug (optional)stringThe simulated program by slug.
type"PERSON" | "BUSINESS"
namestring
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.purposestring
agent.scope (optional)string[]
at (optional)string (date-time)Entity created_at. Defaults to now.

Responses

200 — What the pipeline produced. object

FieldTypeNotes
programstring
eventsstring[]
entityIdstring
entitySandboxEntity

401 — Bearer token missing or unknown. Error

FieldTypeNotes
errorstringWhat went wrong, in one sentence.
requestId (optional)stringPresent 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

FieldTypeNotes
errorstringWhat went wrong, in one sentence.
requestId (optional)stringPresent 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).

AuthBearer + X-Operator
OperationSandbox · POST /api/sandbox/entities/{id}/verification

Parameters

NameInTypeNotes
idpathstringid
tenant (optional)querystringTenant slug. Optional: the bearer token already proves the tenant. Pass it to be explicit.
Idempotency-Key (optional)headerstring1–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

FieldTypeNotes
programId (optional)string (uuid)The simulated program. One of programId / programSlug is required.
programSlug (optional)stringThe simulated program by slug.
status"VERIFIED" | "MANUAL_REVIEW" | "DENIED"
at (optional)string (date-time)

Responses

200 — What the pipeline produced. object

FieldTypeNotes
programstring
eventsstring[]
entityIdstring
entitySandboxEntity

401 — Bearer token missing or unknown. Error

FieldTypeNotes
errorstringWhat went wrong, in one sentence.
requestId (optional)stringPresent 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

FieldTypeNotes
errorstringWhat went wrong, in one sentence.
requestId (optional)stringPresent 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.

AuthBearer + X-Operator
OperationSandbox · POST /api/sandbox/counterparties

Parameters

NameInTypeNotes
tenant (optional)querystringTenant slug. Optional: the bearer token already proves the tenant. Pass it to be explicit.
Idempotency-Key (optional)headerstring1–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

FieldTypeNotes
programId (optional)string (uuid)The simulated program. One of programId / programSlug is required.
programSlug (optional)stringThe simulated program by slug.
namestring
country (optional)string
routingNumber (optional)string
at (optional)string (date-time)

Responses

200 — What the pipeline produced. object

FieldTypeNotes
programstring
counterpartyIdstring
counterpartySandboxCounterparty

401 — Bearer token missing or unknown. Error

FieldTypeNotes
errorstringWhat went wrong, in one sentence.
requestId (optional)stringPresent 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

FieldTypeNotes
errorstringWhat went wrong, in one sentence.
requestId (optional)stringPresent 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.

AuthBearer + X-Operator
OperationSandbox · POST /api/sandbox/patterns/{name}

Parameters

NameInTypeNotes
namepathstringname
tenant (optional)querystringTenant slug. Optional: the bearer token already proves the tenant. Pass it to be explicit.
Idempotency-Key (optional)headerstring1–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

FieldTypeNotes
programId (optional)string (uuid)The simulated program. One of programId / programSlug is required.
programSlug (optional)stringThe simulated program by slug.
entityId (optional)string
counterpartyId (optional)string
at (optional)string (date-time)

Responses

200 — What the pipeline produced. object

FieldTypeNotes
programstring
pattern"structuring" | "just_below_threshold" | "velocity" | "round_trip" | "layering" | "dormant_reactivation" | "burst_then_dormant"
expectedDetectorstring
eventsstring[]
transferIdsstring[]
entityIdstring
entityIdsstring[]Entities the play created.
counterpartyIdsstring[]
alertIdsstring (uuid)[]
alertsSandboxAlert[]
snapshotId (optional)string (uuid)burst_then_dormant only: the KRI snapshot taken.

401 — Bearer token missing or unknown. Error

FieldTypeNotes
errorstringWhat went wrong, in one sentence.
requestId (optional)stringPresent 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

FieldTypeNotes
errorstringWhat went wrong, in one sentence.
requestId (optional)stringPresent 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.

AuthBearer + X-Operator
OperationSandbox · POST /api/sandbox/reset

Parameters

NameInTypeNotes
tenant (optional)querystringTenant slug. Optional: the bearer token already proves the tenant. Pass it to be explicit.
Idempotency-Key (optional)headerstring1–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

FieldTypeNotes
programId (optional)string (uuid)The simulated program. One of programId / programSlug is required.
programSlug (optional)stringThe simulated program by slug.

Responses

200 — What the pipeline produced. object

FieldTypeNotes
programstring
resetboolean
countsRecord<string, integer>

401 — Bearer token missing or unknown. Error

FieldTypeNotes
errorstringWhat went wrong, in one sentence.
requestId (optional)stringPresent 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

FieldTypeNotes
errorstringWhat went wrong, in one sentence.
requestId (optional)stringPresent 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}}}'