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

# Instructions Over the API

> Create, update, and remove Data Protector instructions from your own systems.

[Instructions](/agents/instructions) are the part of Velatir most worth automating. They are the rules
that are specific to your organisation, which means they are also the ones that go stale — a codename
stops being secret, a supplier relationship ends, a matter closes — and nobody thinks to remove them.

If another system already knows when that happens, it can keep Velatir in step.

<Note>
  Instructions belong to the **Data Protector**. `agentType` must be `"DataProtection"`; other agents do not
  take instructions, and sending one is rejected with `400`.
</Note>

## Endpoints

All five sit under your organisation. Everything below assumes the `X-API-Key` header from
[Authentication](/api/authentication), and `{organisationId}` is the id you got from `GET /organisations`.

| Method   | Path                                                           |
| -------- | -------------------------------------------------------------- |
| `POST`   | `/organisations/{organisationId}/instructions`                 |
| `GET`    | `/organisations/{organisationId}/instructions`                 |
| `GET`    | `/organisations/{organisationId}/instructions/{instructionId}` |
| `PATCH`  | `/organisations/{organisationId}/instructions/{instructionId}` |
| `DELETE` | `/organisations/{organisationId}/instructions/{instructionId}` |

## The Body of an Instruction

These are the same parts described in [Instructions](/agents/instructions), in their wire form.

| Field            | Type          | Notes                                                                                                       |
| ---------------- | ------------- | ----------------------------------------------------------------------------------------------------------- |
| `agentType`      | string        | Required. Always `"DataProtection"`.                                                                        |
| `title`          | string        | Required. Up to 200 characters. This is what a reviewer sees in the dashboard.                              |
| `trigger`        | object        | Required. See below.                                                                                        |
| `action`         | string        | Required. `"Allow"` or `"Block"`.                                                                           |
| `criticality`    | string        | Required. `"Low"` or `"High"`. High escalates to a person.                                                  |
| `categoryId`     | uuid          | Optional. The data category this relates to.                                                                |
| `usedBy`         | array of uuid | Optional. Agents to assign it to on creation. List yours with `GET /organisations/{organisationId}/agents`. |
| `allowRedaction` | boolean       | Optional, defaults to `false`. Only valid with a `Regex` or `StringMatch` trigger.                          |

### Triggers

The `trigger` object is tagged by its `type` field.

<Tabs>
  <Tab title="Semantic">
    Matches by meaning. Best when the wording will vary.

    ```json theme={null}
    { "type": "Semantic", "text": "Customer is discussing their account balance" }
    ```
  </Tab>

  <Tab title="Regex">
    Matches a pattern. Exact and fast, best for structured data.

    ```json theme={null}
    { "type": "Regex", "pattern": "AKIA[0-9A-Z]{16}", "isCaseSensitive": true }
    ```

    `isCaseSensitive` defaults to `true`.
  </Tab>

  <Tab title="String match">
    Matches an exact list of terms. Best for codenames and identifiers.

    ```json theme={null}
    { "type": "StringMatch", "values": ["Project Aurora"], "isCaseSensitive": false }
    ```

    `isCaseSensitive` defaults to `true`.
  </Tab>
</Tabs>

## Worked Example: Keeping Confidential Codenames in Step

A common pattern is a rule whose whole lifetime is decided elsewhere. Unannounced work is a good example:
while a project is confidential its codename should not reach an AI service, and on the day it launches
that rule is just noise that will sit in your dashboard forever.

Your roadmap tool already knows both dates. Have it call Velatir.

### When the project becomes confidential

```bash theme={null}
curl -X POST https://api.velatir.com/organisations/$ORG_ID/instructions \
  -H "X-API-Key: $VELATIR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "agentType": "DataProtection",
    "title": "Block unannounced codename: Project Aurora",
    "trigger": {
      "type": "StringMatch",
      "values": ["Project Aurora"],
      "isCaseSensitive": false
    },
    "action": "Block",
    "criticality": "High"
  }'
```

Velatir answers `201 Created` with the instruction, including the id you will need later:

```json theme={null}
{
  "id": "8c41d0b6-3e92-4f77-b5a1-27de6a09c8f3",
  "agentType": "DataProtection",
  "title": "Block unannounced codename: Project Aurora",
  "trigger": {
    "type": "StringMatch",
    "values": ["Project Aurora"],
    "isCaseSensitive": false
  },
  "action": "Block",
  "allowRedaction": false,
  "criticality": "High",
  "categoryId": null,
  "assessmentId": null,
  "usedBy": [],
  "createdAt": "2026-09-14T09:12:44.117Z",
  "updatedAt": "2026-09-14T09:12:44.117Z"
}
```

Store `id` against the project in your own system. That is the handle you delete with.

<Tip>
  An instruction created with no `usedBy` exists but is not yet assigned to an agent. Pass the agent ids in
  `usedBy` on creation if you want it enforcing straight away, or assign it in the dashboard under
  **Agents → Instructions**.
</Tip>

### When the project launches

```bash theme={null}
curl -X DELETE https://api.velatir.com/organisations/$ORG_ID/instructions/$INSTRUCTION_ID \
  -H "X-API-Key: $VELATIR_API_KEY"
```

A successful delete returns `204 No Content`, and the instruction is removed from every agent it was
assigned to. The rule stops applying to new activity from that point. Traces it already acted on are
unaffected — deleting an instruction does not rewrite history.

### If the rule changes rather than ends

`PATCH` takes any subset of the fields. To soften a block to an escalation without touching anything
else:

```bash theme={null}
curl -X PATCH https://api.velatir.com/organisations/$ORG_ID/instructions/$INSTRUCTION_ID \
  -H "X-API-Key: $VELATIR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "criticality": "Low" }'
```

Omitted fields are left alone. Sending `"categoryId": null` explicitly clears the category.

## Listing and Finding Instructions

`GET /organisations/{organisationId}/instructions` returns a page of instructions:

```json theme={null}
{
  "items": [],
  "totalCount": 0,
  "page": 1,
  "pageSize": 20
}
```

Narrow it with query parameters: `search`, `triggerType`, `action`, `agentId`, and `agentType`. Page with
`page` and `pageSize` — `pageSize` defaults to 20 and is capped at 100.

<Warning>
  Velatir does not de-duplicate instructions, and creating one is not idempotent. A retried `POST` produces
  a second identical instruction rather than returning the first. If your integration retries, record the
  id you got back and check for it before creating again.
</Warning>

## Limits

An agent holds at most **100 instructions**. Creating one with a `usedBy` that would take an agent past
that returns `409 Conflict`, naming the agent that is full.

This is worth designing for if you create an instruction per record. A pattern that adds rules but never
removes them will reach the ceiling; the codename example above stays well clear of it because each rule
is deleted when the project launches.

## Making It Safe to Re-Run

A few habits make an integration like this survive contact with real systems:

* **Store the instruction id** alongside the record that caused it. Searching by title to find it again
  breaks the first time someone edits the title in the dashboard.
* **Treat `404` on delete as success.** It means the instruction is already gone, which is the state you
  wanted.
* **Handle `409` as a real condition, not a retry.** Nothing that returns it becomes true by trying
  again — the agent is full, or the assessment already produced an instruction.
* **Name the key for the system that holds it.** Every change it makes is attributed to that name in the
  audit trail.

***

<CardGroup cols={2}>
  <Card title="Instructions" icon="fingerprint" href="/agents/instructions">
    What instructions are and how they behave.
  </Card>

  <Card title="Data Protector" icon="shield-check" href="/agents/data-protector">
    The agent that applies them.
  </Card>
</CardGroup>
