Extension action classes
Cortex ships five canonical action classes — communicate, schedule, remind, inform, shield. That set is sufficient for the founder's executive-assistant use case. It is not sufficient for verticals. A finance customer needs transact. A repos customer needs commit. A coding-agent customer needs code. The extension action class protocol lets you register domain-specific classes that participate in the trust state machine and dispatch protocol identically to canonical ones — without internal enum migrations.
The three tools
action_classes.register({ namespace, name, payload_schema, allowed_modes, ... })— register a new namespaced action class for your tenant.action_classes.list({ scope?, active_only? })— list classes registered in your tenant.action_classes.deregister(class_id)— soft-delete a class. Historical ledger rows preserved.
Namespace convention: {client_namespace}.{action_class}
Every custom class is identified by a namespaced string. The namespace is owned by the registering client_id within the tenant for the tenant's lifetime. Collisions are rejected at registration time with 409 Conflict { code: 'namespace_owned' }.
Tenants are isolated: customer A's acme.transact cannot collide with customer B's acme.transact. Namespace ownership is per-tenant.
Example: register a finance customer's transact class
const { class_id, namespaced_name } = await cortex.tools.call('action_classes.register', {
namespace: 'acme',
name: 'transact',
payload_schema: {
type: 'object',
required: ['amount_cents', 'currency', 'to_account_id'],
properties: {
amount_cents: { type: 'integer', minimum: 1 },
currency: { type: 'string', enum: ['USD', 'EUR', 'GBP'] },
to_account_id: { type: 'string' },
memo: { type: 'string', maxLength: 280 },
},
additionalProperties: false,
},
allowed_modes: ['ask', 'notify'], // no silent execution for financial actions
display_name: 'Transact',
description: 'Send funds from a Plaid-linked account to an authorized recipient.',
legal_effect_classification: 'always', // every transact action is legally significant
});
// → namespaced_name = "acme.transact"
// → trigger envelope action_class field accepts "acme.transact"
// → register a handler for this class via /v1/clients/{client_id}/action_handlersWhat the protocol gives you
- Trust state machine participation. Custom classes seed at Level 1 baseline; per-counterparty graduation works via the same ceremony as canonical classes (typed “Promote” confirmation, evidence summary, audit row).
- Dispatch protocol participation. Register a handler with
action_class: "acme.transact"via /v1/clients/{client_id}/action_handlers; Cortex dispatches signed envelopes to your endpoint the same way it does for canonical classes. - Trigger envelope acceptance. No envelope schema change — ADR-0019 §2's
action_class: stringalready accepts arbitrary strings. - Payload validation at dispatch. Your JSON Schema is canonicalized, content-hashed, and validated against every outbound envelope. Malformed payloads are caught before they reach your handler — dispatch failures surface in the operator dashboard.
- Approval ceremony, your UI. When mode =
"ask", your handler renders the approval ceremony in your app's UI using the envelope payload + reasoning + counterparty fields.
Schema validation: JSON Schema 2020-12 subset
The payload_schema is validated at registration as a subset of JSON Schema 2020-12. Allowed keywords: type, properties, required, additionalProperties, items, enum, oneOf, anyOf, allOf, not, const, format (limited set), minLength, maxLength, minimum, maximum, minItems, maxItems, pattern, description, title, examples. Disallowed: $ref, if/then/else, unevaluatedProperties, arbitrary custom formats. Size cap 32 KB; nesting cap 8 levels.
Quota
Per-tenant active class cap applies (see rate limits & quotas). Inherited canonical classes do not count toward your cap. Over-quota: 429 too_many_classes.
Lifecycle: deregistration is a soft-delete
action_classes.deregister sets deregistered_at on the row but preserves all historical action_ledger entries that reference the class. Re-registration of the same namespaced name within the same tenant requires the same client_id that originally registered it — this prevents accidental namespace handoff between clients.
Cross-references
- Dispatch protocol (ADR-0019) — how extension-class envelopes are signed and delivered.
- Concept: Counterparty depth — trust pairs for custom classes work identically to canonical ones.
- ADR-0022 (full protocol spec); Architecture §4a.8 (Extension Action Classes); Pattern §5.15 (Extension Class Pattern).