Apex API

Developer

ctxl.Entity is the engine’s public entry point. The agent actions, generated scoped tools, and the Studio preview all go through it.


Entity.load — one record

Returns the assembled payload as a String.

// Default entity for the object, detailed level, markdown
String context = ctxl.Entity.load(recordId);

The object is resolved from the record ID; the level defaults to detailed. Every load serves the entity’s active version; an entity that has never been activated serves its draft.

There are no positional overloads. Everything beyond the one-argument form goes through EntityRequest:

ctxl.EntityRequest req = new ctxl.EntityRequest();
req.recordId      = recordId;
req.entityApiName = 'finance_view';   // or domainApiName — not both
req.level         = 'standard';
req.format        = 'json';
req.toolName      = 'QuarterlyDigest';
String payload = ctxl.Entity.load(req);
FieldTypeNotes
recordIdIdThe record to assemble
recordIdsList<Id>For loadMany. Objects may differ unless an address is pinned
entityApiNameStringA specific entity. Mutually exclusive with domainApiName
objectApiNameStringOptional — resolved from the record ID when omitted
levelStringlist · standard · detailed (default)
formatStringmarkdown (default) · json · bothboth is the JSON payload with the markdown rendering under markdown: one assembly, one metered load
domainApiNameStringRoute through a domain. Mutually exclusive with entityApiName
toolNameStringYour caller’s name, stamped as the telemetry surface

New options land as new EntityRequest fields, not new overloads — build against this form.

Entity.find — a set of records

ctxl.EntityFindRequest req = new ctxl.EntityFindRequest();
req.objectApiName = 'Opportunity';
req.filtersJson   = '[{"fieldName":"IsClosed","operator":"=","values":["false"]},' +
                    ' {"fieldName":"CloseDate","operator":"<=","values":["THIS_QUARTER"]}]';
req.sortBy        = 'Amount DESC';   // ascending without a direction
req.maxRows       = 10;
req.format        = 'markdown';
req.toolName      = 'PipelineReview';
String results = ctxl.Entity.find(req);
FieldTypeNotes
objectApiNameStringThe collection to query. Required unless entityApiName names it
entityApiNameStringExplicit entity. Mutually exclusive with domainApiName
domainApiNameStringRoute through a domain. Requires objectApiName
filtersJsonStringStructured filter list. → Filters
sortByStringField to sort by, ascending unless followed by a direction: Amount DESC. Filterable fields only. Default: last modified, newest first
maxRowsIntegerDefault and cap are org settings (shipped: 20/50); capped requests are noted in the payload
formatStringmarkdown (default) · json · bothboth is the JSON payload with the markdown rendering under markdown: one query, one metered load
toolNameStringTelemetry surface

Rows use the entity’s curated list columns. Totals cover the full matching set, not the returned page — a query capped at 10 rows still reports the true count and sums.

Calling out after a load

Assembly leaves no uncommitted DML, so a caller can load context and then make a callout — an LLM, a Models API request, any HTTP call — in the same transaction. Usage telemetry publishes as a Publish Immediately platform event, which the platform’s callout rule ignores.

This matters for embedded orchestrators, which assemble context and call a model in one transaction by design and have no place to commit in between. It is a property of the package, not of your code: nothing you do at the call site restores it if it is lost.

Entity.write — a curated update

ctxl.EntityWriteRequest req = new ctxl.EntityWriteRequest();
req.recordId    = oppId;
req.changesJson = '{"StageName": "Negotiation/Review", "CloseDate": "2026-09-30"}';
req.confirm     = false;   // preview first — changes nothing
String diff = ctxl.Entity.write(req);
// show the diff, get approval, then the same request with confirm = true
FieldTypeNotes
recordIdIdRequired. The record to update
changesJsonStringJSON object of field API name → new value. Picklists take the API value; dates are yyyy-MM-dd
confirmBooleanfalse/omitted returns the diff and changes nothing; true commits
objectApiNameStringOptional; validated against the ID’s type
entityApiNameStringThe write contract to apply. Default: the object’s default entity. Mutually exclusive with domainApiName
domainApiNameStringRoute through a domain — the domain must allow writes
changeDigestStringThe digest the preview returned, echoed back with confirm = true. Required where confirmation is required
toolNameStringTelemetry surface, recorded in the write log

Effective writability is a strict AND — the org write switch, the domain (when routed through one), the entity’s toggle, the field’s declaration, then the platform’s own FLS, sharing, and validation rules; the DML runs in user mode. Confirmation resolves separately: the (domain, entity) assignment decides when domainApiName is set, the org default when it is not. Where it is required, a commit whose changeDigest is absent or no longer matches the record’s current values is refused. Refusals and validation failures throw ctxl.WriteService.EntityWriteException with a message written for an agent, after logging: every commit, failure, and refused attempt writes durable per-field write-log rows. → Write access

Entity.loadMany — several records

ctxl.EntityRequest req = new ctxl.EntityRequest();
req.recordIds = recordIds;
req.level     = 'list';
String context = ctxl.Entity.loadMany(req);

Assembles each record through the same path as load and joins the payloads with --- separators.

BehaviorDetail
Mixed objectsEach record resolves independently: object from the ID, then that object’s default entity — or the domain’s entity for it when domainApiName is set
Pinned addressentityApiName or objectApiName alongside mixed IDs throws — either names one object
Cap5 records at detailed, 10 otherwise. Past the cap it assembles what fits and appends a visible note
Per-record failureA record that can’t load is named in a trailing note; the rest come back

Use a lean level — detailed multiplies a full 360 by N. For sets, prefer Entity.find: it returns totals.

Error handling

Failures throw ctxl.Entity.EntityException:

try {
    String context = ctxl.Entity.load(recordId);
} catch (ctxl.Entity.EntityException e) {
    // unknown entity, unconfigured object, invalid request shape
}

Two outcomes are not exceptions:

  • A record the caller can’t see returns a not-found payload, not a throw.
  • A domain coverage miss returns a structured answer naming current coverage, so an agent can re-plan.

Element failures never surface here — they degrade to issues.

toolName

Optional. Set it on everything you build — it’s stamped as the telemetry surface, so Usage attributes loads to your caller instead of lumping them under apex.

Governor limits

Assembly is query-bound: one query for the record, one batched query for all metrics, one per related list and reference, plus whatever each enrichment source runs. Calling load in a loop will hit limits — use Entity.find for sets.

Sharing

Every query runs as the calling user, in user mode; calling Entity.load from a without sharing context does not widen what comes back. See Security.

What’s next