Record read consent
Most of what an agent does in ax1om reads aggregates: a model’s readout, its factors, its validation state, the shape of a connection. Ten routes are different, because their responses carry row-level records or named accounts. Those are reachable by an agent only after your org says yes.
Nothing about the browser path changed. A person signed into the product reads their own records exactly as before. This decision is about an agent.
The three properties
Section titled “The three properties”| Property | What it means |
|---|---|
| Opt in | An owner or admin takes an explicit action. Nothing is consented by default, by plan tier, or by any other setting. |
| Absent is off | An org that has never been asked is refused. There is no backfill and no default on. |
| Revocable immediately | The gate reads your answer on every request and caches nothing, so there is no window in which a revoked org is still served records. |
Revoking keeps the record of who last opened the door and when, because that stays true and it is the question an auditor asks afterwards. Granting again restamps the grant and clears the revocation, so the pair never reads as contradictory.
What needs consent
Section titled “What needs consent”Ten reads, grouped by what an agent is trying to do.
| What an agent reads | Route |
|---|---|
| The scored records behind a model | GET /models/{model_id}/records |
| The per-record factors for one CRM record | GET /models/{model_id}/records/{crm_id}/factors |
| A model’s score history across records | GET /models/{model_id}/score-history |
| The score history of one record | GET /score-history/{record_id} |
| A bulk export of scores | GET /models/{model_id}/scores/export |
| A bulk export of training data | GET /models/{model_id}/training-data/export |
| Signal events | GET /signals/events |
| The accounts a timing model covers | GET /timing/accounts |
| One account’s timing reading | GET /timing/account/{account_id} |
| A timing search | GET /timing/search |
Everything else an agent can reach is unaffected. The tools in the MCP reference that return a model’s results, its readout, its validation state, or a score for a record you handed in are not behind this gate.
What is never permitted
Section titled “What is never permitted”GET /orgs/audit-log/export is refused to an agent under any consent. It exports member activity, which is your own users’ email addresses, IP addresses and who did what, rather than CRM records. Every customer-facing word of this consent says record-level read access, so gating an activity log behind it would make your org’s answer mean something it was never asked. The human export is unaffected.
Who can grant it
Section titled “Who can grant it”An owner or admin, and a person rather than an agent.
An agent can read the current state, so that it can tell a refusal from a defect. It cannot grant and it cannot revoke, and that refusal lands before its member’s role is consulted: an agent carries the role of the member it acts for, so an admin’s agent would otherwise have satisfied the check. A consent gate an agent can grant itself is not a gate, and an agent that could revoke could lock an org out of its own consent state. The refusal an agent gets on either write is agent_control_forbidden.
There is no switch for this in the app; an org owner or admin grants it over the API.
What an agent sees when it is refused
Section titled “What an agent sees when it is refused”Status 403, code record_read_consent_required. The code also rides the response as the header x-ax1om-refusal-code, so an agent can branch on it without reading prose. The message is a plain string, relayed to the agent verbatim:
This organization has not granted agents record-level read access, so records are not returned to an agent on this route. This is not a defect and not a restriction we imposed: it is a consent the org has not given yet. An org admin can grant it in Settings > Agent access > Record read access, and can revoke it there at any time.
The message has three jobs, in order: say what was refused, say that it is a decision this org has not made rather than a defect or a limit, and say where the decision gets made.
The setting it names is not one the app renders today, so the grant happens over the API below.
Every refusal is recorded
Section titled “Every refusal is recorded”Each refusal on this gate writes one row to your org’s audit log under the refusal category, carrying the code, the exact message the caller received, whether it arrived over MCP or the API, and the route template. The route is the template rather than the concrete path, because a concrete path would put a CRM record id into the audit trail, which is precisely the identifier the refused route exists to withhold.
A gate that refuses silently is indistinguishable from a broken product. “My agent cannot read my records” and “my agent was told no because nobody granted it access” are the same experience until the trail can tell them apart.
The grant and the revoke are recorded too, under config, as record_read_consent_granted and record_read_consent_revoked. A refusal that is not written never changes the answer the caller got: the write is best effort and can never fail the request it describes.
This is not the spend decision
Section titled “This is not the spend decision”Agent usage billing answers whether an agent may spend your money. This answers whether an agent may see your customers’ records. An org can honestly want either without the other, and an admin flipping one is never construed as having flipped the other, so they are stored separately and their refusals are worded separately. A data-egress refusal that mentioned your bill would be telling you that your privacy posture is a payment problem.
Three endpoints, org-scoped, all returning the same shape.
{ "granted": false, "granted_by": null, "granted_at": null, "revoked_at": null, "reason": null}| Method | Path | Who |
|---|---|---|
GET |
/orgs/record-read-consent |
Any member of the org, and an agent |
POST |
/orgs/record-read-consent/grant |
A human owner or admin |
POST |
/orgs/record-read-consent/revoke |
A human owner or admin |
Both writes take an optional body, {"reason": "..."}, up to 500 characters. It is your own note, recorded verbatim and never written by us: “approved by legal, ticket SEC-4412” is the shape of it. Which endpoint you called is what sets the answer, so no body can mean the opposite of the URL. Both are idempotent.
A member who is not an owner or admin gets 403 with code permission_denied. An agent gets 403 with code agent_control_forbidden.
Three states are readable from that response and they are not two:
- Never decided:
grantedis false andrevoked_atis null. - Granted:
grantedis true, with who and when. - Turned off:
grantedis false andrevoked_atis set, with the record of who last opened the door still present.
- Validation state and deploy policy · the other org-level decision that governs what an agent may do
- MCP server · connecting an agent, and which credential reaches which tool
- How usage accrues · which actions move which meter, and what an agent can spend