> ## Documentation Index
> Fetch the complete documentation index at: https://docs.lerian.studio/llms.txt
> Use this file to discover all available pages before exploring further.

# System Facts API overview

> What System Facts records, the four steps to integrate a producer, and the conventions every operation shares: tenant, idempotency, paging, sensitivity and errors.

System Facts is the shared record of what happened in your business flows. Each service that takes part in a flow, called a producer, sends an immutable fact when a business moment happens. An order is placed. A payment settles.

System Facts checks every fact against the contract its producer registered, stores it once, and links the facts that share a reference into one flow. Then you can ask what happened to order `ORD-2026-000123` and get every fact of that flow, from every producer, in one answer.

A stored fact never changes. System Facts records and correlates. It does not edit history.

## Concepts

***

| Term | Meaning |
| :- | :- |
| Producer | A service that sends facts, such as `checkout-api` or `payments-api`. |
| Namespace | The dotted name a producer sends its facts under, such as `acme.checkout`. The first service that registers a contract for a producer and namespace becomes their owner. |
| Fact type | The name of one kind of business moment, such as `checkout.order_placed`. |
| Contract | One semantic version of a fact type, with the JSON Schema of its payload and the references its facts carry. |
| Reference type | A named kind of business identifier, such as `acme.order_id`. You register it once, with its scope, correlation, sensitivity and searchable settings. |
| Reference | One identifier a fact carries: a reference type, a value and a scope. For example, `acme.order_id` `ORD-2026-000123` with scope `storeId: store-sp-01`. |
| Subject | The business entity a fact is about, given as a type and an id, such as `checkout.order` `ORD-2026-000123`. |
| Fact | One immutable record that a business moment happened. It is sent as an envelope with its subject, its references and its payload (`data`). |
| Flow | The group of facts linked by their references. A `flowAggregationId` identifies it. |

A reference type with `correlation: exact` links its fact to every fact that carries the same reference. When one fact links two flows, they merge into one. The absorbed flow id still resolves to the surviving flow.

A reference type registered with `searchable: true` can list every fact that carries it.

## Integrate in four steps

***

**1. Register the reference types**

Register each kind of identifier your facts carry with `PUT /v1/reference-types/{type}`, such as `acme.order_id` and `acme.payment_id`. Do this from your deploy pipeline, before any contract uses the type.

```bash theme={null}
curl -X PUT "$SYSTEM_FACTS_URL/v1/reference-types/acme.order_id" \
  -H "Authorization: Bearer $SYSTEM_FACTS_BEARER_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "scope": ["storeId"],
    "sensitivity": "none",
    "correlation": "exact",
    "searchable": true,
    "description": "Order number assigned by the checkout service, unique within a store"
  }'
```

**2. Register a contract version**

Register a version of each fact type with `PUT /v1/contracts/{producer}/{namespace}/{type}/{schemaVersion}`. Do this from your deploy pipeline, before the producer rolls out. System Facts accepts facts only after the registration succeeds.

**3. Send facts**

Send one envelope per business moment with `POST /v1/facts`. Use the producer's service credential for the tenant.

```bash theme={null}
curl -X POST "$SYSTEM_FACTS_URL/v1/facts" \
  -H "Authorization: Bearer $SYSTEM_FACTS_BEARER_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "envelopeVersion": 1,
    "factId": "01a15493-ba10-7b21-9d44-3c1f8e2a9b01",
    "producer": "checkout-api",
    "namespace": "acme.checkout",
    "type": "checkout.order_placed",
    "schemaVersion": "1.0.0",
    "occurredAt": "2026-10-19T14:32:10Z",
    "subject": { "type": "checkout.order", "id": "ORD-2026-000123" },
    "references": [
      {
        "type": "acme.order_id",
        "value": "ORD-2026-000123",
        "scope": { "storeId": "store-sp-01" }
      }
    ],
    "data": { "amount": "129.90", "currency": "BRL" }
  }'
```

A first send answers `201`.

```json theme={null}
{
  "factId": "01a15493-ba10-7b21-9d44-3c1f8e2a9b01",
  "producer": "checkout-api",
  "flowAggregationId": "01a15493-bdf8-7c55-b0e2-6f4a1d9e7c33",
  "duplicate": false,
  "ingestedAt": "2026-10-19T14:32:11Z"
}
```

**4. Read the flows**

Read a flow by its id with `GET /v1/flows/{flowAggregationId}`. Find the one flow of a reference with `GET /v1/flows/by-reference`. List every fact that carries a searchable reference with `GET /v1/flows`.

```bash theme={null}
curl -G "$SYSTEM_FACTS_URL/v1/flows/by-reference" \
  -H "Authorization: Bearer $SYSTEM_FACTS_BEARER_TOKEN" \
  --data-urlencode "referenceType=acme.order_id" \
  --data-urlencode "referenceValue=ORD-2026-000123" \
  --data-urlencode "storeId=store-sp-01"
```

Every write is idempotent, so a producer can resend after a timeout.

## Authentication and permissions

***

Every request except the public health probes sends `Authorization: Bearer <token>`. Facts and flows belong to the tenant of the token. The reference-type and contract catalogs are shared by every tenant of the deployment.

| Operation | Permission |
| :- | :- |
| `GET /v1/reference-types`, `GET /v1/reference-types/{type}`, `GET /v1/contracts`, `GET /v1/contracts/{producer}/{namespace}/{type}`, `GET /v1/contracts/{producer}/{namespace}/{type}/{schemaVersion}` | `contracts:read` |
| `PUT /v1/reference-types/{type}`, `PUT /v1/contracts/{producer}/{namespace}/{type}/{schemaVersion}` | `contracts:read`, `contracts:write` and `catalog:reserved`. Every catalog name sits under a reserved prefix. |
| `POST /v1/contracts/{producer}/{namespace}/{type}/{schemaVersion}/deprecate` | `contracts:read` and `contracts:write`, plus `catalog:deprecate` for a version whose namespace you do not own |
| `POST /v1/facts` | `facts:read` and `facts:write` |
| `GET /v1/flows/{flowAggregationId}`, `GET /v1/flows/by-reference`, `GET /v1/flows` | `facts:read`. With `facts:reveal`, encrypted values come back in clear form. |

A missing permission answers `403` `SF-0014`. A missing or invalid token answers `401` `SF-0013`.

## Conventions

***

* **Tenant.** The service always takes the tenant from the token. It never reads a tenant from a path, a query parameter or a body.
* **Idempotent writes.** A first registration or send answers `201`. The same request again answers `200` and changes nothing. A fact send adds `duplicate: true`.
* **Immutability.** A contract version, a reference type and a fact never change. Different content under the same identity answers `409`. A reference type can gain a display `mask` once.
* **Paging.** A list answers one page and a `nextCursor`. Send it back as the cursor parameter of the page you want. Treat it as opaque. Stop when it is omitted.
* **Timestamps.** RFC 3339 in UTC, such as `2026-10-19T14:32:10Z`.
* **Size.** A fact envelope is at most 256 KiB by default. A larger one answers `413` `SF-0012`.
* **Names.** The first dotted segment of every namespace and reference-type name must be one of the reserved prefixes of the deployment. The default is `lerian`. These pages use `acme`.

## Sensitivity and protected values

***

The `sensitivity` of a reference type decides how reads show its values. A contract can mark payload fields the same way with `x-sensitivity`.

| Sensitivity | What a read returns |
| :- | :- |
| `none` | The value as sent. |
| `masked` | `****` plus the last 4 characters of a value of 12 or more characters, and `****` alone otherwise. Never the clear value. |
| `hashed` | The keyed digest `hmac-sha256:v1:<hex>`. Never the clear value. |
| `encrypted` | Stored sealed with AES-256-GCM. Returned masked, or in clear form to a caller that holds `facts:reveal`. |

With `mask: document`, a masked value shows `***.456.789-**` for 11 digits and `**.345.678/0001-**` for 14 digits. System Facts refuses any other digit count at send time.

Every class correlates exactly as `none` does, because matching starts from the clear value. Every stored digest is keyed. It is never a plain hash.

A protected value comes back with `protected: true`, and protected payload fields are listed in `protectedFields`. To find a flow by a protected reference, always send the clear value. System Facts digests it and matches the stored digest.

## Errors

***

Every error is `application/problem+json` (RFC 9457) with a stable `code`.

```json theme={null}
{
  "code": "SF-0009",
  "detail": "Verify the identifier: an identifier absorbed by a merge still resolves, an unknown one does not.",
  "instance": "0199c1f07a3e7b219d443c1f8e2a9b01",
  "status": 404,
  "title": "Not Found",
  "type": "https://errors.lerian.studio/v1/SF-0009"
}
```

> **[Error list](/en/reference/platform/system-facts/system-facts-error-list)**
>
> Look up every code, when the service answers it, and whether to retry.

> **[Register a contract version](/en/reference/platform/system-facts/register-a-contract-version)**
>
> Start with the operation every producer runs first.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.