Commands — io.best.agents.commands

Commands are intents to change a domain service. They are sent to the service by any caller — a Process Manager, an AI agent, a UI, or another service. The service validates, queues, and processes them asynchronously.

Normative reference: SPEC.md — Commands. Canonical schema: commands.json; envelope: cloudEvent.json.

Caller
Any Caller
app · agent · LLM
POST /commands
BEST Endpoint
Validates & Queues
201 Accepted (async)
Async processing
Service
Domain Handler
processes · emits event

POST /commands is a behaviour endpoint, not a resource collection. Sending a command is not creating a "command resource" — it is expressing an intent. /commands is a single entry point for every operation the service accepts; what happens is determined entirely by the envelope type, not the HTTP verb or the URL. In REST you manipulate resources; in BEST you invoke named operations and observe the facts they produce.

Commands ride the CloudEvents 1.0 envelope — see SPEC.md — Wire Format for the field table and examples.

HTTP API

Method Path Description
GET /commands Catalogue of all available command types and their schema URIs
POST /commands Send a command (CloudEvent). Validates, queues, returns 201
GET /commands/{schema}/{version} JSON Schema document for one command type and version

The catalogue (GET /commands)

Each entry: schema (kebab-case name, the {schema} path segment — distinct from the PascalCase envelope type), version, dataschema (resolvable URI, the exact value to put on the command envelope), optional description, optional workflows (recipe cross-links), optional impact (high-impact annotation — see below).

{
  "commands": [
    {
      "schema": "propose-counter",
      "version": "1.0",
      "dataschema": "https://api.example.com/commands/propose-counter/1.0",
      "description": "Propose a counter-offer in a contract negotiation"
    }
  ]
}

Individual command types are domain data, not capabilities — they never appear as manifest capability entries.

Ingestion (POST /commands)

  1. Validate required envelope attributes.
  2. Look up the schema in the server's own catalogue — the inbound dataschema is a selector, not a location; servers MUST NOT fetch a caller-supplied URI (SSRF — see Security).
  3. Validate data against that schema. Schema selection, authorisation, and dispatch MUST key on the same identifier.
  4. Valid → durably queue, return 201 with { "id": ..., "correlationId": ... }. Invalid → 400.

The envelope id is the idempotency key: duplicates are rejected within a retention window; same id with a different payload → 409. type is the routing key; source must never be the sole routing key. 201 (durably recorded, processing will happen) is the target; use 202 only when the implementation cannot durably enqueue before responding.

Correlation

The correlationid envelope attribute (a CloudEvents extension, lowercase on the wire) ties a command to everything it causes. The caller may set it; when omitted, the server adopts the command's id. The 201 response echoes the effective value as correlationId, every resulting event must carry it, and follow-up commands in the same process should propagate it. Retrieve results with GET /events?correlationId=... (history) and GET /events/stream?correlationId=... (push). See Design Decisions — Command Result Retrieval.

Schema documents (GET /commands/{schema}/{version})

Returns the raw JSON Schema for one command version — the canonical target of the catalogue's dataschema URI. 404 for unknown name or version.

The document may declare produces: an array of PascalCase event types the command can raise, e.g. ["CounterProposed", "NegotiationFailed"]. Failure outcomes are ordinary events in that list; the naming convention (*Failed) is service-defined. Silent failures are handled client-side via timeout — services should document expected processing times and always publish a failure event rather than silently dropping a command.

When the service publishes workflows, the document should also carry the operation's workflows cross-link array — the same ids the catalogue entry carries (see Workflows — Discoverability).

High-impact annotations (impact)

A command that moves money, destroys data, or cannot be undone may carry an impact annotation — on its catalogue entry and, identically, as a top-level member of its schema document:

"impact": {
  "categories": ["financial"],
  "confirmation": "required",
  "warning": "Places a real order on your broker account. Capital is at risk."
}

categories names the kind of impact (financial, destructive, irreversible, compliance — open vocabulary; unknown values are treated as high-impact). confirmation: "required" means a consumer acting on behalf of a human must not submit the command without explicit, per-submission confirmation from that human; "recommended" allows proceeding under a durable prior authorization. warning is text the consumer should surface, substantially intact, before asking.

The annotation is the discovery half of the high-impact controls in Security: it tells well-behaved consumers what to do, and never replaces the server-side control — a server cannot rely on clients honoring it. Normative reference: SPEC.md — Impact Annotations.

spec/v0.9.9