Security

Every payload is assembled as the calling user — with one deliberate exception.

The guarantee

All record queries run in user mode, so Salesforce enforces on the query itself.

CheckEffect
Object permissionsNo read access, no rows
Field-level securityA field the user can’t see is not returned
Record sharingPrivate records are excluded, including inside related lists and query results

No value is fetched and then hidden, and two users calling the same tool on the same record get different payloads with no per-user configuration.

What a stripped field looks like

Nothing — it is simply absent, and the agent answers from thinner context. The Quality page reports FLS coverage %, the share of designed fields this caller could see, and Issues names stripped fields under fls_strip. A field can also be absent because it is not curated or is below the requested level; only permission strips appear there.

Curation is not security

Removing a field from an entity does not protect it: a user with object access still reaches it through a report, the API, or another entity. Use field-level security for access control and curation for relevance. Curating in a sensitive field is safe, because a user without access still will not receive it.

Which catalog a user gets

What a user can discover is a separate boundary from what data they receive. Record sharing on the domain decides it: a user reads the domains shared to them and no others. That limits discovery only and can never widen data access — two people holding the same domain still get different records. → Domain access

The permission sets

Permission setGrants
ContextWorks AdminWrite access to the configuration objects
ContextWorks Domain ManagerConfiguration rights over entities homed in specific domains. Inert unless domain management is on
ContextWorks UserClass access to the four agent actions, plus read on the domain object so sharing can be resolved. What Agentforce and MCP callers need, and nothing else

None of the three grants access to record data: an admin still sees only the records their own permissions allow, in preview and everywhere else. ContextWorks User exists because Salesforce checks Apex class access when an agent action or MCP tool runs, so assign it to whoever the call runs as. Flow and Apex callers need no permission set at all. → Install and set up

Enrichment sources: the one exception

Enrichment sources run code — Apex, an invocable action, or a flow — and their results are handled two ways:

  • Record results are re-checked. The engine keeps only the IDs, discards every field value the source returned, and re-queries them as the calling user to render the target entity’s list columns. A record or field the caller can’t see never appears, whatever the source saw. Computed per-row values are lost in the refetch — return them as scalar values instead.
  • Scalar values are trusted as returned. A constructed number isn’t a field, so the engine can’t re-check it. Whoever writes a provider owns what its scalar values expose: query as the calling user (WITH USER_MODE) so a total can’t leak to a caller who couldn’t have computed it. See the provider SPI reference.

WARNING

Data 360 DMO reads are not governed by CRM sharing — what such a provider returns is what every caller sees. Fine for aggregate engagement data; decide deliberately.

Error handling

If an enrichment source fails, the engine drops that one element, delivers the rest, and records an element_failure issue on the Issues page. The agent gets a payload without that section, never the exception text.

Debug logging

Time-boxed payload logging stores the exact text a caller received, record data included. A log is shaped by one caller’s permissions, so someone reading the Logs page may see data they could not query themselves. Logging expires by design: it is a debugging tool, not an audit trail.

What’s next