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

# Derivations API

> Manage attribute derivation rules through the /derivations endpoints

A **derivation rule** describes how the value of one Rosetta Stone attribute is computed from another — for example, a rule from `raw_email` to `sha256_hashed_email` might map the target's `value` property to `SHA2(NORMALIZE_EMAIL($source.value), 256)`. Once a rule exists, queries can resolve the target attribute from the source without a stored [mapping](/reference/rosetta-stone/mapping-types).

The `/derivations` endpoints let you list, fetch, create, update, and delete rules. Read access follows collaborator sharing; create, update, and delete are restricted to admins on the company that owns the rule.

## Concepts

### Source, target, and transformation

Every rule has one **source** attribute (read through `$source` inside the transformation) and one **target** attribute (the value the rule produces). The `mapping` field on the request holds the transformation and mirrors [mapping types](/reference/rosetta-stone/mapping-types) with two differences:

* Only `value_mapping` and `object_mapping` (as `RawValueMapping` and `RawObjectMapping`) are accepted. `cached_mapping` is rejected because there is no source dataset to look values up from.
* Expressions reference the source attribute's shape through `$source` instead of source-column names. For an `object` source, `$source.<property>` reads a property; for a primitive source, `$source` is the value.

Enum constraints and validation expressions on the target attribute do not restrict the transformation, matching the behavior of a dataset mapping.

### Validation

Every transformation is validated before the rule is written. The API parses each expression, compiles it against a synthetic single-column table built from the source attribute, and type-checks the result against the target attribute using the same rules as a dataset mapping. Every required target property must be covered; unknown properties are rejected. If several problems are present in one mapping, they come back together as a single `400`.

Non-deterministic and dynamic functions like `CURRENT_DATE`, `CURRENT_TIMESTAMP`, and `RAND` are permitted. A derivation expression runs when a query resolves the rule, so a rule from birthdate to age in years is correct as of each query that uses it.

### Cycle warnings

A rule from `normalized_email` to `raw_email` alongside one going the other way is a pair of correct rules, so a create or update that closes a cycle in the derivation graph is written anyway. The response returns a `cyclic_derivation` warning listing the attribute ids in the cycle. Cycle detection is bounded: only cycles of up to six attributes are reported, and at most ten come back per response. List and get responses do not compute warnings.

### Permissions

| Action         | Grant on `attribute_derivations` | Ownership requirement                                                                                     |
| -------------- | -------------------------------- | --------------------------------------------------------------------------------------------------------- |
| List, get      | `read`                           | The rule is owned by your company or shared with it through collaborators.                                |
| Create         | `admin`                          | The request's `company_id` must equal your company's. The target attribute must grant your company `map`. |
| Update, delete | `admin`                          | The rule must be owned by your company.                                                                   |

A rule your company cannot see returns `404`, not `403`. Only Narrative can share a rule broadly, with `all` or with an `exclusion` set on `collaborators.use`.

## Endpoints

### `GET /derivations`

List the derivation rules your company can use. Filter with repeated `source_attribute_id` and `target_attribute_id` query parameters; both accept multiple ids and combine as `AND`.

```bash theme={null}
curl -H "Authorization: Bearer $TOKEN" \
  "https://api.narrative.io/derivations?source_attribute_id=101&target_attribute_id=202"
```

The response is `{ "records": [ ... ] }`. Warnings are empty on reads.

### `GET /derivations/{derivation_id}`

Fetch a single rule by id. Returns `404` if the rule is not usable by your company.

### `POST /derivations`

Create a rule. Requires an `admin` grant on `attribute_derivations`. The transformation is validated before the rule is written.

```json theme={null}
{
  "name": "raw_email_to_sha256",
  "description": "Hashes the raw email address with SHA-256.",
  "source_attribute_id": 101,
  "target_attribute_id": 202,
  "company_id": 42,
  "mapping": {
    "type": "value_mapping",
    "expression": "SHA2(NORMALIZE_EMAIL($source.value), 256)"
  },
  "lossy": true,
  "imprecise": false,
  "fidelity_note": "Hashing is one-way, so the original address cannot be recovered.",
  "collaborators": { "use": { "type": "none" } },
  "cost": 1,
  "active": true
}
```

Returns `201` with the created rule and any `warnings`.

Error responses:

| Status | Meaning                                                                                                                                                                                                                                                    |
| ------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `400`  | The transformation fails to parse, compile, or type-check against the target; a required target property is uncovered; an unknown property is present; a `cached_mapping` was submitted; the source and target ids are equal; or another field is invalid. |
| `403`  | The caller is not an admin, the rule's `company_id` differs from the caller's, the collaborators are not allowed, or the target attribute does not grant the caller's company `map`.                                                                       |
| `409`  | Another of the company's rules already uses the same `name`, or already covers the same `(source_attribute_id, target_attribute_id)` pair.                                                                                                                 |

### `PATCH /derivations/{derivation_id}`

Update fields on an existing rule. Requires an `admin` grant, and only the owning company can update. Only fields present in the request change; a changed `mapping` runs the same validation as create.

* `source_attribute_id` and `target_attribute_id` are immutable — there is no key for them, and one sent anyway is ignored. Create a new rule instead.
* Setting `fidelity_note` to `null` clears the note. Leaving the key out keeps the current value.
* Unknown keys are rejected.

Returns `200` with the updated rule and any `warnings`.

### `DELETE /derivations/{derivation_id}`

Permanently remove a rule. Requires an `admin` grant, and only the owning company can delete. To take a rule out of use while keeping it, `PATCH` `active` to `false` instead.

## Request and response fields

### Rule fields

<ParamField body="name" type="string" required>
  Name of the rule, unique within the owning company. Up to 256 characters.
</ParamField>

<ParamField body="description" type="string" required>
  What the rule does, for whoever is deciding whether to accept derived data. Up to 2048 characters.
</ParamField>

<ParamField body="source_attribute_id" type="integer" required>
  The attribute the rule reads from through `$source`. Immutable after create.
</ParamField>

<ParamField body="target_attribute_id" type="integer" required>
  The attribute whose value the rule produces. Must differ from `source_attribute_id`. Immutable after create.
</ParamField>

<ParamField body="company_id" type="integer" required>
  The company that owns the rule. Must equal the caller's company on create.
</ParamField>

<ParamField body="mapping" type="object" required>
  The transformation. Same shape as `value_mapping` or `object_mapping` in [Mapping types](/reference/rosetta-stone/mapping-types), except expressions reference `$source` instead of source-column names. `cached_mapping` is not allowed.
</ParamField>

<ParamField body="lossy" type="boolean" required>
  The transformation loses information — for example, hashing.
</ParamField>

<ParamField body="imprecise" type="boolean" required>
  The transformation is approximate — for example, bucketing an age into a range.
</ParamField>

<ParamField body="collaborators" type="object">
  Which other companies may have the rule applied to their data. A rule grants one thing, so there is a single `use` dimension. `all` and `exclusion` are reserved for Narrative-owned rules.

  ```json theme={null}
  { "use": { "type": "inclusion", "company_ids": [17, 92] } }
  ```
</ParamField>

<ParamField body="fidelity_note" type="string | null">
  Explains the `lossy` and `imprecise` flags to whoever is deciding whether to accept derived data. Up to 2048 characters. `PATCH` with `null` clears the note.
</ParamField>

<ParamField body="cost" type="integer" default="1">
  Breaks ties when more than one rule reaches the same target attribute — cheapest first. Multi-step resolution sums the cost of every rule on a path and prefers the cheapest total. Minimum `1`.
</ParamField>

<ParamField body="active" type="boolean" default="true">
  Inactive rules are never applied.
</ParamField>

### Response-only fields

<ResponseField name="id" type="integer">
  Unique identifier of the rule.
</ResponseField>

<ResponseField name="created_at" type="string">
  ISO 8601 timestamp of creation.
</ResponseField>

<ResponseField name="created_by" type="integer">
  Id of the user who created the rule.
</ResponseField>

<ResponseField name="updated_at" type="string">
  ISO 8601 timestamp of the last update.
</ResponseField>

<ResponseField name="updated_by" type="integer">
  Id of the user who last updated the rule.
</ResponseField>

<ResponseField name="warnings" type="array">
  Populated on create and update responses; empty on reads. Each warning is an object; today the only `type` is `cyclic_derivation`, whose `cycle` field lists the attribute ids visited in order (the closing step back to the first id is implied).
</ResponseField>

## Related content

<Columns cols={2}>
  <Card title="Mapping types" icon="shapes" href="/reference/rosetta-stone/mapping-types">
    The mapping shapes derivation transformations reuse.
  </Card>

  <Card title="Transformation functions" icon="function" href="/reference/rosetta-stone/transformation-functions">
    Functions available inside derivation expressions.
  </Card>

  <Card title="Attribute types" icon="tags" href="/reference/rosetta-stone/attribute-types">
    The source and target shapes a rule connects.
  </Card>

  <Card title="How Rosetta Stone works" icon="gears" href="/concepts/rosetta-stone/how-it-works">
    Background on attributes, mappings, and normalization.
  </Card>
</Columns>
