Agent Patterns
An agent's capability is decided entirely by the permissions on the credential it carries. Designing one is therefore mostly deciding what to grant — and that is easier against known shapes than from a blank page.
Grant a pattern's permissions and the agent can do that job and nothing else.
A "read-only" agent provisioned with entity.read alone cannot count records
and cannot detect duplicates, because neither operation reads an entity:
- Counting is a statistics operation and needs
statistics.read. - Duplicate detection runs the matching engine and needs
entity.match.
Both fail closed, so the symptom is a refusal rather than a wrong answer — but it is a confusing refusal if you believed reading was all you had granted.
Choosing a pattern
| Pattern | Answers | Changes data |
|---|---|---|
| Read-only analyst | "What is in here, and how good is it?" | No |
| Duplicate triage | "Are these two the same, and should they be joined?" | Yes |
| Bulk loader | "Get this batch in, and tell me what failed." | Yes |
| Privacy responder | "Someone exercised a data right — action it." | Yes |
The patterns compose. An agent may hold any combination; the permissions simply add up.
Read-only analyst
Reads and reports. Cannot change anything, because none of these permissions grant a write.
| Operation | Answers | Permission |
|---|---|---|
countEntities | How many records of a type exist | statistics.read |
searchEntities | Which records match this text or attribute | search.read |
getEntity | One record | entity.read |
getEntity360 | One record with its sources, history, and relationships | entity.read |
listRelationships | How records connect | entity.relationship.read |
getDqScore | The quality score behind a record | dq.score.read |
listAuditEntries | What changed, and who changed it | audit.read |
Masking matters most here: a value the operator may not see is masked before the agent receives it, so the agent cannot report it even if asked directly.
Duplicate triage
Works the clerical review queue — the pairs the engine scored as probable but not certain. See reviewing potential matches for what a reviewer is actually deciding.
| Operation | Does | Permission | Class |
|---|---|---|---|
findDuplicates | Finds probable duplicates of one record | entity.match | Read |
listPotentialMatches | Reads the review queue | potential-match.read | Read |
getPotentialMatch | Reads one pair and its scoring | potential-match.read | Read |
confirmMatch | Confirms a queued pair and joins the records | entity.merge + potential-match.resolve | Edit |
rejectMatch | Records that a pair is not a match | potential-match.resolve | Edit |
snoozeMatch | Defers a pair without deciding it | potential-match.resolve | Edit |
markNotAMatch | Excludes a pair from future scoring | entity.read + potential-match.resolve | Edit |
mergeEntities | Joins records directly, outside the queue | entity.merge + entity.read | Destructive |
unmergeEntities | Reverses a merge, restoring what was absorbed | entity.unmerge + merge-history.read | Destructive |
Resolving a queued pair and merging directly are different permissions. An agent can be allowed to work the queue — where the engine has already proposed the pair and a person can audit the decision — without being able to join two arbitrary records it chose itself.
Bulk loader
Loads batches and reports on them. The load itself runs asynchronously, so the agent submits and then polls.
| Operation | Does | Permission | Class |
|---|---|---|---|
createBulkJob | Submits a batch | bulk-job.create | Destructive |
getBulkJob | Reports progress | bulk-job.read | Read |
getBulkJobResults | Reports what succeeded and what failed | bulk-job.read | Read |
cancelBulkJob | Stops a running job | bulk-job.cancel + bulk-job.read | Edit |
Submitting counts as destructive because the size of the change is not apparent from the call — one submission may touch a single record or a million.
Privacy responder
Handles data-subject requests and consent. Grant it narrowly.
| Operation | Does | Permission | Class |
|---|---|---|---|
listDsrs | Reads outstanding requests | dsr.read | Read |
createDsr | Records a new request | dsr.create + entity.read | Edit |
updateDsrStatus | Moves a request through its states | dsr.update | Edit |
fulfillDsr | Executes the request, including erasure | dsr.update | Destructive |
listConsentRecords | Reads what a subject has consented to | consent-record.read | Read |
withdrawConsentRecord | Withdraws a consent-basis record | consent-record.update | Edit |
An erasure request revokes every consent-basis record for the subject while retaining those held under another lawful basis — a correct outcome, and not one to discover after the fact.
What every pattern shares
| Behaviour | Applies to |
|---|---|
| Two calls to change anything. Every write is a preview, then a separate confirmation carrying a single-use token. The model cannot produce that token itself. | All mutating operations, always |
| The permission is checked on the second call too. Confirming does not bypass anything. | All mutating operations |
getMe needs only me.read. An agent can always report its own effective permissions — including what it lacks. | Every pattern |
| Masking is applied before the agent sees the value. There is no unmasked surface to reach for. | Every pattern |
The Class column is a signal to the client. Read operations change nothing. Edits are reversible. Destructive operations are flagged so a well-behaved client asks a human before proceeding — but that prompt is a client-side courtesy, and a client configured permissively may skip it.
Keep the two apart when you reason about risk. The prompt can be turned off; the two-call contract and the permission check are enforced by the endpoint and cannot be. An agent configured to approve everything is still an agent that can only do what its permissions allow.
Provisioning an agent
| Step | What you do |
|---|---|
| 1 | Decide the pattern, and read off the permissions its tables list |
| 2 | Create a role holding exactly those, and nothing else |
| 3 | Assign it to the credential the agent will carry — a person, or a system token if the agent runs unattended |
| 4 | Ask the agent what it can do. getMe reports its effective permissions, so you verify the grant rather than assuming it |
Step 4 catches both surprises above in a single call.
Discovering the full set
These are representative operations, not the complete catalog. The endpoint publishes its own tool list, each entry naming what it does and the permission it requires. Query it rather than copying the operations into your own configuration.
Next
- Agent Identity — whether an agent borrows an identity or holds one of its own
- AI Security Model — what an agent cannot do, and why
- Connect an Agent — getting one running
- Control Access — how permissions are composed into roles