---
title: "Data governance"
url: /trust/governance
---

# Data governance

> How a product treats the data it holds: classes on fields, controller and processor, a generated DPA, people's requests, customer-held keys and the data chain.

Data governance says what each field holds, who answers for it, and whether it is masked, logged, sent to a model, kept or erased.

**A person asks to be erased.** A person's request.

1. Received: by the Customer.
2. Verified (a check): who is asking. Or refused: its reason.
3. Found (personal fields): every record.
4. Held (reviewed): a legal hold. Or listed: its reason.
5. Erased (the data chain): and upstream.

The Customer decides; the product finds, holds and erases, and every upstream product hears it.

## What each field is, and who answers for it
A data class on a field changes what every part of the product does with it. Nothing else in the file has to say so.

| Family | What it does |
| --- | --- |
| Data classes | `class` on a field: `personal`, `sensitive`, `payment`, `health` or `privileged`; [Model and records](/model/types.md) types it, and the one decision reads it |
| Roles for the data | Each Customer is the controller of its own records, you are its processor, and APIs.Dev is yours; you control your own sales record and account data |
| The DPA | A data processing agreement generated per product from the file: the classes it holds, its regions, retention and sub-processors, versioned and accepted like [any agreement](/commerce/agreements.md) |
| Sub-processors | APIs.Dev, Stripe, each model provider the router may use, and each upstream product with its own list, generated and printed beside the terms |
| Subject requests | A person asks to find, export, correct or erase what the product holds about them; the Customer decides, and the product does it across every record |
| Retention | Per Noun and per class, deleting on schedule unless a legal hold stops it; history keeps that a value existed, never the value |
| Customer-held keys | On its own [isolation](/identity/tenancy.md#families), a Customer holds the key to its [Namespace](/reference/platform.md#namespace) in its own key service; revoking it leaves the records unreadable |
| The model path | Per class: `allow`, `redact` (tokens in, values restored after) or `refuse`; only providers that keep nothing, and never training on a Customer's data |
| The audit log | Every change, and every read of a `sensitive`, `health` or `privileged` field, as a projection each Customer queries and streams to its own SIEM |
| The data chain | What each hop of [composition](/how-it-works.md#composition) carries, under which licence, residency and processor terms; an erasure follows it upstream |

| Class | Masked for | Logs and traces | The model path | Edge cache and export |
| --- | --- | --- | --- | --- |
| `personal` | Roles without `see` on its Noun | Redacted | As `privacy.models` says | Never cached; exported to its Customer |
| `sensitive` | The same, and encrypted per Namespace | Redacted | `redact` unless the file allows it | Never cached; exported to its Customer |
| `payment` | Everyone: only Stripe holds the number | Never written | `refuse` | Last four digits only |
| `health` | The same as sensitive, and every read audited | Redacted | `refuse` unless the provider signs for health data | Never cached; exported with its audit |
| `privileged` | Everyone outside the case's wall | Redacted | Only inside the case, kept in no memory | Exported only by a person with the case's Role |

### Example
A contact's email is personal, the notes are sensitive, the product acts as each Customer's processor, and the model router redacts both.

```ts title="definition.ts"
import { define } from 'apis.dev'

/** Deals for each Customer, kicked off when won. */
export default define({
  name: 'pipeline', domain: 'pipeline.apis.dev',
  Company: { name: 'string', domain: 'url!#', deals: '<-Deal.company' },
  Deal: {
    title: 'string', amount: 'decimal(12,2)', company: '->Company.deals',
    contact: { type: 'email?', class: 'personal' },
    notes:   { type: 'markdown?', class: 'sensitive' },
    stage: { open: [], qualify: 'opened', win: 'qualified', lose: ['opened', 'qualified'] },
  },
  privacy: {
    role: 'processor', keys: 'customer', requests: '30d',
    models: { personal: 'redact', sensitive: 'redact' },
  },
  offer: { Team: { month: 49, seats: 5, seat: 9 }, 'Deal.open': 0.001 },
})
```

A Customer's admin reads a finished erasure: what was found, what a hold kept, and which upstream product erased its copy.

```http title="Response"
HTTP/1.1 200 OK
Content-Type: application/json
X-Cost: price=0, currency="USD"
X-Request-Id: req_6a0f2d93c18e7b45a9d3

{
  "id": "sr_1",
  "kind": "erase",
  "state": "done",
  "subject": { "email": "dana@acme.com" },
  "found": { "Deal": 3, "Company": 0, "events": 41 },
  "held": [{ "record": "deal_2", "reason": "legal hold" }],
  "upstream": [{ "host": "companyfacts.apis.dev", "state": "erased" }],
  "due": "2026-11-26T00:00:00Z"
}
```

That is `GET /subject-requests/sr_1`, `getSubjectRequest` over MCP and `pipeline subject-requests get sr_1` in the CLI.

## Requests, keys and lists, state by state
Every move is a Verb a Customer's admin or its agent makes on the product's own [Interfaces](/every-product.md).

Your operator reads each request that reaches the product with `SubjectRequest.list` on the [Operator plane](/trust/planes.md).

| Thing | States | What moves it |
| --- | --- | --- |
| A subject request | `received`, `verifying`, `finding`, `ready`, then `done` or `refused`; `extended` once, with notice | The Customer's admin; the person's identity check; the deadline in `privacy.requests` |
| An erasure | `requested`, `erasing`, then `erased` and `propagated`; any record under a hold stays `held` | The request; every upstream product on the data chain answers with its own state |
| A correction | `requested`, `reviewed`, then `applied` or `declined` | The Customer decides; an applied correction is a change in history, Stated by the person |
| A customer-held key | `active`, `rotating`, then `revoked` | The Customer's own key service; a revoke makes the Namespace unreadable at once, and a restore of the key reverses it |
| A sub-processor change | `announced`, `objection_window`, `effective` | A deploy that adds a model provider or an upstream product; each Customer gets the notice its DPA names |
| A DPA version | `offered`, then `accepted` or `objected` | A deploy that changes classes, regions, retention or sub-processors; acceptance follows the agreement rules |

## What Customers, people and reviewers meet
- **A Customer's admin.** Its DPA and sub-processor list, every person's request in one queue, its own key if it holds one, and an audit log it queries.
- **A person in the records.** A request made through the Customer, answered within the deadline, with what was found and what a hold kept.
- **A security reviewer.** Each field's class, where it goes, which models see it and how, and the chain of products behind this one.
- **An agent.** Masked fields stay masked in every tool, and a redacted value never reaches its model.

---

Every field carries its class from the file to the model router, so what is masked, logged, kept and erased follows one declaration.