# sources

Source: https://docs.korala.ai/risk/sources

---

# Risk evidence sources

You can use external facts and Korala verification results in a risk policy.
Korala retains evidence from both paths. Each receipt identifies
the source of the fact. You choose which sources your policy accepts and remain
responsible for deciding whether their claims provide enough evidence.

All endpoints below are relative to `/api/v1`. See the
[risk API overview](https://docs.korala.ai/risk) for authentication, subjects, idempotency and limits.

## External and internal evidence

| Path | How facts arrive | Source identity |
| --- | --- | --- |
| Registered external system | An authorized API key submits a declared fact | `registered:<source-registration-id>` |
| Native Korala verification | The risk engine reads policy-selected fields from a linked session | Source ID returned by the verification input catalog |

You register sources for external systems. Native verification sources
do not have a registration or `sourceRegistrationId`: select their source ID from the verification input catalog.

## Register an external source

In the dashboard, open **Risk → Sources**, choose the test or live environment,
then select **Register source**. Enter a source key, declare the facts it may
submit and select its permitted API keys. Owners and administrators can register
sources; other workspace members can inspect their configuration and history.
Create API keys in **Settings → API keys** first. Korala does not display key
secrets on the source page.

An organization owner/admin uses a dashboard JWT to register a source. An API
key cannot create registrations or change their key bindings. For test resources,
the JWT request includes `X-Risk-Mode: test`; otherwise it uses live mode.

```http
POST /api/v1/risk/sources
Authorization: Bearer <owner-or-admin-jwt>
Idempotency-Key: register-purchase-system-001
Content-Type: application/json

{
  "key": "purchase_system",
  "facts": [{ "key": "purchase.amount", "type": "number" }],
  "apiKeyIds": ["<active-api-key-id>"]
}
```

Replace placeholders with your UUIDs. For `apiKeyIds`, use the `id` from the API-key
record, rather than the `keyId` used in HMAC headers. Keys must belong to the same organization
and test/live mode, and must not be revoked or expired. The source starts enabled.
The response wraps the registration as `{ id, resource: "sources", data }`.
Use its `id` for submissions and `registered:<id>` for policy acceptance.

Declare 1–100 unique facts with type `string`, `number`, `boolean` or `list`.
A list also requires a scalar `itemType`, for example
`{ "key": "account.channels", "type": "list", "itemType": "string" }`.
Source and fact keys use lowercase letters, digits, underscores, dots and hyphens,
start with a letter, and are at most 80 characters. The key and fact schema are
immutable; register a new source to change them. A source binds at most 50 keys.

## Submit a fact for a subject

Use a bound API key and the standard [HMAC authentication](https://docs.korala.ai/guides/authentication).
The subject must belong to that organization and environment.

```http
POST /api/v1/risk/subjects/<subject-id>/evidence
Idempotency-Key: purchase-123-amount
Content-Type: application/json

{
  "sourceRegistrationId": "<source-registration-id>",
  "fact": "purchase.amount",
  "status": "available",
  "value": 1000,
  "observedAt": "2026-10-10T12:00:00Z",
  "providerRef": "purchase-123"
}
```

Use the observation time, which cannot be in the future. `expiresAt` is
optional. An available value must match the registered fact type; lists must
match their declared item type. `unavailable` and `revoked` require `value: null`.
Keep structured payloads and document images in your evidence storage and use
an opaque `providerRef`; Korala accepts scalar values and scalar lists here.

Korala retains the fact and assigns its source and submission history. Provenance records the registration ID, binding revision,
submitting API key, adapter version and `assertion` evidence class. You cannot
set this attribution by supplying `source` or `provenance` in the request.

The SDK exposes `client.risk.addEvidence(subjectId, body, idempotencyKey)`.
Repeat the same request with the same key to retry. A changed request or
principal with the same key returns 409. A new receipt supersedes the previous
receipt for that subject, fact and source; Korala retains the previous record.

## Submit several facts together

Use a batch when an external system sends several properties for one subject,
for example country, account age and email verification. Declare each property
as a source fact first. Send 1–100 distinct facts from that source in one request:

```http
POST /api/v1/risk/subjects/<subject-id>/evidence/batch
Idempotency-Key: profile-123-revision-2
Content-Type: application/json

{
  "sourceRegistrationId": "<source-registration-id>",
  "items": [
    {
      "fact": "profile.country",
      "status": "available",
      "value": "US",
      "observedAt": "2026-10-10T12:00:00Z"
    },
    {
      "fact": "profile.email_verified",
      "status": "available",
      "value": false,
      "observedAt": "2026-10-10T12:00:00Z"
    }
  ]
}
```

Korala accepts the whole batch or rejects it without recording any of its facts.
The response contains `{ id, subjectId, items }`, with evidence receipts in
request order. Each receipt keeps its own source attribution and history.
A batch supports one subject and one source; use separate requests for others.
The limit is 16 KiB per item and 256 KiB for the batch, measured as UTF-8 JSON.

Use `client.risk.addEvidenceBatch(subjectId, body, idempotencyKey)` in the SDK.
Keep the same key, API credential and body when retrying, including item order.
Korala returns the original batch ID and receipt IDs, even if another request
has since replaced those facts. A different body or submitting credential with
the same key returns 409. Send corrections with a new key. If Korala rejects
an uncommitted batch, you can correct it and reuse the key.

Korala requests a refresh for enabled monitoring after accepting the batch.
An assessment reads all the committed facts together; acceptance itself does
not approve the subject. Subscribers can receive individual
`risk.evidence.recorded` events and a `risk.evidence.batch_recorded` event.
Webhook delivery may repeat or arrive out of order; follow the
[risk push integration guide](./webhooks) when processing it.

## Accept the source in a policy

Add an entry to your policy's `inputs` array:

```json
{
  "key": "purchase.amount",
  "type": "number",
  "acceptedSources": ["registered:<source-registration-id>"],
  "maxAgeSeconds": 3600
}
```

Copy the exact lowercase source identity. You must update the policy to accept
it after registration. Korala records submissions as assertions by that source;
it does not verify the purchase or obtain an attestation from a provider.
The risk engine checks types, availability, conflicting values and freshness.

## Rotate keys, disable a source and inspect history

Open a source in **Risk → Sources** to replace its permitted keys or pause new
submissions. During rotation, add the replacement key, switch your integration,
then remove the old key. Saving replaces the selected keys. Pausing does not
invalidate evidence or assessments already recorded.

If another administrator changes the permissions while you edit, reload the
bindings before saving your changes. **Reload bindings and discard edits** reads
the current configuration and discards your unsaved selection. The source page
also shows a paginated history of registration and permissions changes.

Read `GET /risk/sources/{id}` to obtain its current `revision`, then replace the
bindings using an owner/admin JWT and a new idempotency key:

```json
{
  "expectedRevision": 1,
  "apiKeyIds": ["<replacement-api-key-id>"],
  "enabled": true
}
```

Send this body to `POST /risk/sources/{id}/bindings`. The key list replaces the
whole list. A stale revision returns 409. To disable submissions, set
`enabled: false`; a disabled source may have an empty key list. An enabled source
must have at least one key.

You can rotate keys without changing the source identity or policy acceptance.
Korala retains historical receipts. Disabling a source blocks new submissions;
it does not revoke existing evidence. Korala returns the original receipt for an
identical retry after rotation or disabling if the caller can authenticate.
Korala refuses authentication with revoked or expired credentials.

List registrations through `GET /risk/sources`. Inspect binding history through
`GET /risk/events?resourceId=<source-registration-id>`. Follow `nextOffset` for
both lists. Korala records the complete bindings and revision in the audit feed
on source creation and binding changes. The SDK provides `createSource` and
`updateSourceBindings` for the corresponding governance calls.

## Korala KYC and POA sessions

For native evidence, link the session to the risk subject and select verification
fields from `GET /risk/verification-inputs` when
[authoring the policy](./policy-authoring). Copy the returned binding and source;
do not submit the session results again as external evidence.

Korala collects the selected fields as risk evidence before assessment and policy
selection, including automatic reassessment. It
retains references to the originating session, workflow run and step. You can retrieve
original results and document references through the session and workflow APIs;
risk receipts exclude raw images and complete step outputs. It reuses
unchanged observations and writes a new revision
when an observation changes. You choose the fields to synchronize through the
policy's verification bindings.

Extraction and enrichment facts require explicit approval of the completed
attempt or completed session. Supported completed capture, check, validate and
risk outputs can inform risk while review is pending; they do not approve the
identity. Missing, failed, stale or unsupported evidence cannot satisfy an input.
A newer linked attempt cannot fall back to an older favorable result.

Source identity pins the workflow definition revision and interpretation, while
receipt references identify the actual attempt. Selecting a new workflow revision
requires a new policy binding. Session changes request refreshes for enabled
monitoring; background reconciliation also checks for missed updates. Neither
path promises instantaneous evaluation. Assessment snapshots preserve the exact
evidence used for the historical result.
