Skip to Content
RiskOverview and API

Independent risk framework

Risk subjects outlive verification sessions. Attach evidence to a person, business, account or transaction, evaluate a published policy, and retain the assessment and its explanation. A policy key identifies a purpose, such as onboarding or fraud; one subject can have separate current assessments for each.

This API is independent of Korala’s predefined session fraud evaluators. It does not automatically approve identities, change account permissions or deliver payment restrictions.

Authentication and environments

Use the usual HMAC API credentials or dashboard JWT. Every resource belongs to an organization and a test/live environment. API keys are pinned to their mode; a test key cannot read or mutate live resources. Dashboard JWTs default to live; send X-Risk-Mode: test to manage test resources. OAuth assistant connections cannot use these endpoints yet.

Owner/admin JWTs are required to create/publish policies, create/revoke overrides and cancel actions. Ordinary authenticated clients can record evidence, evaluate published policies and acknowledge action execution. These operations remain subject to organization and mode isolation.

Every mutation needs an Idempotency-Key header (1–120 letters, digits, underscores, dots, colons or hyphens). Retry an identical operation with the same key. Reusing a key with different input or a different principal returns 409. Successful retries return the same resource ID and its current data; failed transactions do not consume the key.

Resources

All paths below are relative to /api/v1.

OperationEndpoint
Create a subjectPOST /risk/subjects
Create a draft policy versionPOST /risk/policies
Publish a policyPOST /risk/policies/{id}/publish
Append evidencePOST /risk/subjects/{id}/evidence
Evaluate a policyPOST /risk/subjects/{id}/assessments
Read currentness/effective decisionGET /risk/assessments/{id}/state
Record an overridePOST /risk/assessments/{id}/overrides
Revoke an overridePOST /risk/overrides/{id}/revoke
Update an actionPOST /risk/actions/{id}/transitions
List recordsGET /risk/{resource}
Read a recordGET /risk/{resource}/{id}

Resources are subjects, policies, evidence, assessments, overrides, actions and events. Responses wrap the record as { id, resource, data }. List responses contain items and nextOffset; follow non-null offsets for pages of at most 100 records and 1 MiB. A page can be shorter because of its byte budget; always follow nextOffset, not the item count. Evidence, assessments, overrides, actions and events support ?subjectId=.... Policies and subjects do not support that filter.

The OpenAPI schemas describe each record’s data. Assessment data includes the policy snapshot, evaluation time, sequence, evidence revision, previous assessment ID, exact input snapshots, score, band and factor/rule results.

Subjects and evidence

Create a subject with a kind and your externalId. The combination is unique within the organization/environment. Use stable customer/account identifiers; do not put names or raw identity documents in the external ID.

Evidence is a typed scalar or a list of scalars:

{ "fact": "monthly_usage_units", "status": "available", "value": 32, "observedAt": "2026-09-27T09:00:00Z", "expiresAt": "2026-10-27T09:00:00Z", "providerRef": "report-123" }

Observation times cannot be in the future. For unavailable or revoked evidence, value must be null. Only an available observation can have a non-null value. Evidence values are limited to strings, finite numbers, booleans and lists of those types; structured provider payloads belong in your evidence storage, referenced by providerRef.

The source is assigned by the server as api_key:<database-key-id> or user:<user-id>. Read it from the resulting evidence record and explicitly accept that source in a policy. A submitted provider name does not establish trust. Key rotation currently requires updating accepted sources in a new policy version.

New evidence supersedes the latest receipt for the same subject/fact/source. The original record remains. A later receipt with an older observation time still supersedes the previous receipt, so clients must deliberately reconcile late events before submission. Revocation or unavailability never falls back to older favourable evidence. Accepted sources that disagree make an input unresolved. New evidence marks prior assessments stale.

Resource limits

Oversized operations return 413. Limits are measured as UTF-8 JSON bytes: 64 KiB for a normalized policy request, 16 KiB for an evidence request, 256 KiB across selected evidence rows (including stored metadata), and 512 KiB for the evaluation result. Full-record reads are capped at 768 KiB. Lists have a 1 MiB budget. These aggregate limits also apply when many small requests have accumulated evidence; a rejected assessment leaves no partial assessment/actions/audit event and does not consume its retry key.

Policies

Create a version with { key, definition }, inspect it, and publish it. Drafts and published definitions are immutable through this API; creating another version under the same key assigns the next version number. Assessments pin an explicit published policyVersionId. Publishing a new version does not silently reevaluate subjects or replace their historical policy.

The definition has these fields:

FieldMeaning
inputsRequired facts, types, accepted sources and maxAgeSeconds
applicabilityOptional groups of conditions that select which subjects the policy covers; a subject must match all conditions in at least one group
factorsNamed factors with ordered condition/point cases, first/highest match, weight and optional default points
aggregationSum or maximum of weighted factor contributions
floor, ceilingBounds applied after aggregation
bandsOrdered IDs and lower bounds, each with an effect
rulesConjunctive conditions with reasons, optional forced band and effects
missingEvidenceawaiting_evidence or review
reviewAfterSecondsAssessment freshness deadline; monitored customers are reassessed when it elapses

Conditions support scalar equality (eq), membership (in), list membership (contains), numeric gt, gte, lt, lte, and comparison with another input (eq_fact, neq_fact). For input comparisons, value names the other input key. Types must agree with the declared input. Rules contain all; use separate rules for alternative conditions. Arbitrary scripts, expressions and network lookups are not allowed.

Factor match: first selects the first matching case. highest takes the highest matched point value once, useful when an account declares multiple access methods. Weights multiply factor points before aggregation. If no case matches and there is no explicit defaultPoints, evaluation is incomplete.

Bands must start at zero and have strictly increasing lower bounds. They use inclusive lower/exclusive upper boundaries; the final band is unbounded. A score is not a probability. Do not compare scores across unrelated policies.

Effects contain a decision (none, review, restrict, reject) and an array of named actions. Decision precedence is reject, restrict, review, none. Forced bands only increase risk. Known mandatory rules still apply when other inputs are missing. An incomplete assessment has a null numeric score. none means this policy does not require a restriction; it is not KYC approval.

All policy limits and required properties are documented in the generated CreateRiskPolicyDto schema. Nested group trees, maintained country-risk tables and general arithmetic expressions are not supported in this version.

Assessments and current decisions

Call the assessment endpoint with { policyVersionId, trigger }. The service evaluates persisted evidence and atomically stores its snapshot, explanation, required actions and audit event. Replaying an assessment means using its stored policy, inputs and evaluation time with the recorded evaluator version.

Historical GET always returns the original assessment. Before making a new downstream decision, call its /state endpoint. It reports current: false and effectiveDecision: reassessment_required if newer evidence arrived, a newer assessment exists for the policy key, or a freshness/review deadline passed. Refresh by explicitly creating a new assessment.

Risk changes support signed push notifications through webhooks. Enable automatic reevaluation by assigning a published policy through the monitoring endpoint below. On a notification, fetch current action/assessment state before making a new decision. Do not treat an old saved assessment or an action request as proof of current authorization.

Human decisions and action history

An owner/admin may override a current assessment with decision, a nonempty reason and an expiresAt within one year. The calculated score/band stays unchanged. Incomplete evidence cannot be overridden to none. New evidence invalidates the currentness of the assessment and the applicability of its override. Only the most recent override may apply; revoking or expiring it does not reactivate earlier overrides.

Actions start as requested. Consumers acknowledge them, carry out their own authorized operation, then report completion. Send status, expectedRevision and reason for each transition. Stale revisions return 409.

requested → acknowledged → completed │ │ └──→ failed ←─┘ │ └──→ requested (explicit retry) requested / acknowledged / failed → cancelled (owner/admin only)

Completed and cancelled actions are terminal. A new low-risk assessment does not automatically cancel earlier requirements. Each assessment can produce its own action: reconcile existing open actions for the subject before performing duplicate external work. Acknowledgements are client assertions, not independent proof that a payment system applied a restriction.

events records authenticated actors and reasons for publication, evidence changes, assessments, overrides and action transitions in the same transaction as the change. It is application-level history, not a cryptographically sealed audit log. There are no record deletion endpoints or configurable retention jobs yet; organization deletion cascades its risk data.

TypeScript client

const subject = await client.risk.createSubject( { kind: 'person', externalId: 'customer-123' }, 'customer-123-create', ); const assessment = await client.risk.assess( subject.id, { policyVersionId: publishedPolicyId, trigger: 'onboarding' }, 'customer-123-onboarding-1', ); const state = await client.risk.state(assessment.id);

Use client.risk.inMode('test') for owner/admin JWT sandbox governance and reads. It returns a separate view and leaves client.risk in its default mode. Selecting a contradictory mode with an API key returns 400; the credential continues to determine its environment.

The methods also cover policy publication, evidence ingestion, overrides, action transitions, list and get. Mutation keys are explicit so retries can reuse them. Governance methods need an owner/admin JWT; normal API keys cannot publish a policy or grant themselves override privileges.

Korala check adapter and the next phase

POST /risk/subjects/{id}/session-checks accepts { checkId } for an existing persisted session-level check. It verifies tenant and subject relationships, derives the fact as check.<kind>, and retains the submitting principal as source. The source is the submitting user:<id> or api_key:<id>, which the policy must explicitly accept. A persisted check is not a trusted provider attestation: workflow authors can configure its provider and check kind. The early korala:session-checks source is retired: it is excluded from new assessments, and existing assessments using it require reassessment. Historical results remain available unchanged. Provider failures become unavailable evidence. Review/block flags do not become a clear input. Participant-level checks require a separate adapter and are refused. Imports require a session in the same test/live mode as the risk subject. Test credentials cannot import a live session check.

The adapter imports evidence from an existing session. Configure accepted sources and policy rules for your integration, then validate them in an isolated test environment. Keep proprietary policy definitions and assessment data out of public documentation and shared examples.

Customer compliance history

Open Risk → Customers in the dashboard, select live or test, and choose a subject. The profile shows recorded activity and assessment history. Select an event to inspect its authenticated recorder, reported occurrence, receipt time, evidence and linked assessments. Assessment details show the stored inputs and policy, score contributions, rules, required actions and current applicability.

Your integration can record activity from a form, review process or external service without moving that service into Korala:

const event = await client.risk.recordActivity(subject.id, { activityType: 'information.submitted', category: 'customer', title: 'Supporting information submitted', summary: 'The client collected the requested declaration.', occurredAt: new Date().toISOString(), reference: 'your-submission-reference', }, 'submission-record-001'); await client.risk.assess(subject.id, { policyVersionId: publishedPolicy.id, trigger: 'Review submitted information', eventIds: [event.id], }, 'submission-assessment-001');

Activity reports provide context; they do not create evidence or prove that a provider verified a claim. Submit typed facts through addEvidence or a supported adapter before evaluating them. The server records the authenticated principal; category describes the caller’s report, not an authenticated customer identity. One event can inform several assessments, and one assessment can reference up to 50 distinct events from the same subject and environment.

Use client.risk.journey(subject.id) to read up to 50 events in receipt order. Pass the returned nextCursor as the second argument for older events. Use client.risk.eventAssessments(subject.id, event.id) to inspect linked assessments; follow nextOffset for additional results. Both reads respect response byte limits. Use client.risk.list('subjects', { search: 'customer-reference' }) for a literal, case-insensitive search of external IDs.

The corresponding endpoints are:

OperationEndpoint
Record reported activityPOST /risk/subjects/{id}/activities
Read subject timelineGET /risk/subjects/{id}/journey?before={cursor}
Read linked assessmentsGET /risk/subjects/{id}/events/{eventId}/assessments?offset=0

Mutations require an Idempotency-Key. The service refuses event references or cursors belonging to another subject, organization or environment. API keys fix the environment; dashboard JWT clients can use client.risk.inMode('test').

A policy action can request information such as a source-of-funds declaration. Your workflow chooses how to collect it: your own interface, Korala capabilities, an external provider or manual review. Record requests, submissions and review outcomes separately. Completing a collection action does not approve a customer; submit the resulting evidence and request a new assessment. The framework does not automatically create forms or initiate provider requests.

Configure and operate risk in the dashboard

Risk → Policies lists policy versions in the selected test/live environment. Owners and administrators can create a version with evidence inputs, accepted sources, factor cases and weights, score bands, required actions, mandatory rules, aggregation and freshness settings. Save to validate a draft; then review and publish it. Copying a saved version creates a new draft. Saved versions are not edited in place. Integrations pin the published version ID; publication does not automatically migrate customers to it.

Risk → Review queue lists required actions across customers, oldest first. Search a customer reference, filter by policy key or task status, and select a task to inspect its originating assessment. Every status change requires a reason and the current revision. Owners/admins can cancel a task; other allowed transitions remain available to workspace members. This is an action queue: a review decision without a configured action does not create a queue entry. A newer assessment with lower risk leaves existing tasks open.

On the customer page, History type separates reported activity, assessments, evidence, required actions and overrides. Korala filters each history on the server before pagination. Assessment history has its own pagination; selecting an activity shows the assessments linked to that exact event. The original result and current applicability are displayed separately.

In assessment details, reviewers can reassess current evidence using the same policy version. An owner/admin can apply or revoke a time-limited override with a reason. Neither operation edits historical scores. Completing a source-of-funds task is not a substitute for submitting evidence and reevaluating it.

Configure push endpoints in Settings → Webhooks, choosing the Risk event group and the matching environment. See risk push integration.

Automatic reassessment

Assign one published version per customer and policy key. Publishing a new version leaves existing assignments unchanged. The customer page provides the same monitor, pause and resume controls as the API.

await korala.risk.configureMonitoring(subjectId, { policyVersionId, enabled: true, }, crypto.randomUUID()); const subscriptions = await korala.risk.monitoring(subjectId);

Use POST /risk/subjects/:id/monitoring with an idempotency key and GET /risk/subjects/:id/monitoring to inspect assignments. To pause, submit the same version with enabled: false. Assigning another published version with the same policy key replaces the monitored version.

The worker checks due subscriptions every five seconds, up to ten per tick. Backlogs and worker downtime increase latency. It reassesses after evidence changes, native verification changes, evidence expiry and reviewAfterSeconds. Unchanged evidence does not create a new result on each tick. Subject evidence revisions also invalidate assessments conservatively when an unrelated fact changes. Activity-only events do not trigger evaluation.

Known mandatory rules run with partial evidence: accepted age 15 can produce decision: reject, status: incomplete and score: null while address evidence is missing. The final numeric score requires all configured inputs. Missing or expired evidence cannot produce an unrestricted complete result.

If a policy’s applicability conditions are missing or do not match, monitoring records applicability_unknown or applicability_not_matched and produces no assessment. Inspect lastError, lastEvaluatedAt and nextCheckAt; the latter is an eligibility time, not a delivery guarantee. evaluation_failed retries after 30 seconds. A database outage can delay that retry further.

Assessment creation, required actions, webhook staging and the monitoring checkpoint commit together. A duplicate worker tick does not duplicate that assessment. A later assessment can request the same action again; consumers must reconcile existing requirements. Webhooks still have at-least-once delivery.

Continue checking current assessment state and enforcing freshness deadlines before authorizing a consequential action. Pausing monitoring does not revoke existing assessments or cancel actions. Overrides retain their own expiry; an expired override alone does not trigger an assessment or notification.

Accepting KYC before the whole session finishes

Native input bindings normally use the completed, approved linked session. An owner/admin may explicitly review a completed workflow attempt while another workflow, such as proof of address, is pending:

POST /risk/subjects/:id/verification-decisions Idempotency-Key: unique-review-command { "sessionWorkflowId": "<linked-workflow-slot-id>", "workflowRunId": "<current-completed-run-id>", "decision": "approved", "reason": "Reviewer accepted this verification attempt" }

Supported decisions are approved, rejected and revoked. This endpoint requires an owner/admin dashboard JWT. API credentials cannot impersonate a Korala verification reviewer; external providers submit evidence through their own authenticated source instead.

The decision belongs to the persisted customer, session workflow and exact run. It does not approve the overall session. A newer attempt cannot inherit it; revocation does not resurrect an earlier approval. Processing completion alone is insufficient. The native adapter still checks workflow version, tenant, mode, output provenance and freshness. An adverse or expired session prevents use of favorable native facts. Each decision appears in the customer’s audit history.

Last updated on