HTTP Transport
HTTP is the primary transport for web-based consumers including the BEST web UI. The HTTP API surface is fully described by the capability endpoints arrays in the discovery manifest — no separate OpenAPI document is required from implementers.
HTTP is the baseline transport — every conformant service exposes it. All requests and responses are application/json (schemas are application/schema+json; SSE is text/event-stream).
POST /commands carries one BEST envelope in the CloudEvents structured content mode. Servers must accept Content-Type: application/cloudevents+json and, for compatibility, application/json; consumers should send the former. With it, a CloudEvents SDK's HTTP sender produces a valid BEST request unchanged.
Path resolution: every capability path is appended to the service's http.endpoint. The leading slash is a separator, not a root-relative indicator:
http.endpoint |
Path | Resolved |
|---|---|---|
https://app.example.com/ |
/commands |
https://app.example.com/commands |
https://api.example.com/tenants/acme |
/commands |
https://api.example.com/tenants/acme/commands |
http.endpoint must be the consumer-facing public address — never an internal backend or service-mesh URL.
Authentication: per the authentication block governing the service (its own, else the root's) — bearer → Authorization: Bearer <token>; apiKey → header or query parameter named in scheme. GET /.well-known/best, the capabilities of a service that declares "type": "none", and the deviceAuthorizationUrl and tokenUrl endpoints need no credential; everything else does when one is declared.
Errors: all endpoints use a consistent body (error.json):
{ "error": { "code": "SCHEMA_NOT_FOUND", "message": "Unknown command schema 'foo'", "details": {} } }
Status codes:
| Status | When |
|---|---|
| 200 | Success with body (queries, event lists, catalogues, schema documents) |
| 201 | Created — command accepted and durably queued |
| 202 | Accepted without durability guarantee (see Ingestion Semantics) |
| 400 | Invalid request body or parameters (schema validation failure), or a well-formed command the service cannot honour in its current state, with a code that names the condition |
| 401 | Missing/invalid credentials (only when the governing authentication.type is not none) |
| 404 | Unknown route, schema name, or version |
| 409 | Conflict — duplicate command id: with a different payload always, with the same payload when the service rejects a duplicate rather than ignoring it |
| 413 | Request body exceeds server limits |
| 415 | POST /commands with a Content-Type the server does not take; the error says which it takes |
| 422 | Semantic error (capability not supported) |
| 429 | A command past the server's pace; Retry-After says when to send again |
| 500 | Internal error |
Behind a proxy
If the implementer routes traffic internally, http.endpoint is the outermost address consumers hit: the public or proxy address.
For a tenant's address, see Multi-Tenancy.
Sending credentials
For oauth2, obtain a token from authentication.tokenUrl, then send Authorization: Bearer <token>. An apiKey in a query parameter carries short-lived exchanged tokens only, never long-lived keys.
The security schemes are formally declared in the OpenAPI securitySchemes component. The constraints on the exchange are in Security — Token Exchange and Query-String Credentials.