Liaison & Binding Contracts

Open a liaison session, mint a signed pre-flight plan, and commit a scoped, capability-bounded completion under a binding contract.

A liaison wraps a multi-step agent task in a binding pre-flight contract. The agent opens a session, mints a signed plan that fixes a cost ceiling, a prompt fingerprint, and — as of Increment 1 — the exact models and tools the committed request may use, then commits the completion by replaying the plan_id. Guardian middleware verifies the HMAC signature and enforces every field of the contract before the request executes.

Endpoints

MethodPathAuthDescription
POST/v1/liaison/openAPI KeyOpen a negotiation session, mint a signed liaison_id
POST/v1/liaison/{liaison_id}/planAPI KeyMint a signed pre-flight contract (the binding plan)
GET/v1/liaison/{liaison_id}/eventsAPI KeySSE downlink of routing decisions / trace envelopes
POST/v1/liaison/{liaison_id}/closeAPI KeyFinalize the liaison, emit cost summary + trace digest

Mint a plan

curl "https://api.brainstormrouter.com/v1/liaison/<liaison_id>/plan" \
  -H "Authorization: Bearer br_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "messages": [{ "role": "user", "content": "summarize this doc" }],
    "model": "anthropic/claude-sonnet-4",
    "max_tokens": 1024,
    "allowed_models": ["anthropic/claude-sonnet-4"],
    "allowed_tools": ["web_search"]
  }'

Request fields

FieldTypeDescription
messagesarrayOpenAI-shaped messages. Required.
modelstring?Preferred canonical provider/model-id. If omitted, BR auto-selects the cheapest viable model.
max_tokensinteger?Output cap used in the cost estimate (1–128000).
cost_slacknumber?Slack on top of the estimate (default 0.10).
max_cost_usdnumber?Hard cap the agent will accept; a higher estimate returns 402 ceiling_exceeds_max.
ttl_secondsinteger?Plan validity window (default 60, max 300).
allowed_modelsstring[]?Capability scope — which models the committed request may resolve to. See below.
allowed_toolsstring[]?Capability scope — which tools the committed request may invoke. See below.

Capability scope: allowed_models and allowed_tools

Both fields are signed into the plan contract (HMAC-SHA256, envelope version v: 2) alongside the cost ceiling and prompt fingerprint, so they cannot be tampered with after the plan is minted. They are enforced by Guardian at commit time, before the request executes.

The semantics are deliberately fail-closed — omission can never widen access:

allowed_models — canonical provider/model-id catalog labels the committed request is allowed to resolve to.

  • Omitted ⇒ the plan permits only the model it was minted for (the

resolved model, or BR's auto-selection).

  • ["*"] ⇒ unrestricted (any model). This must be an affirmative opt-in.
  • [] (empty array) ⇒ rejected at mint with 400 allowed_models_empty. A

deny-all-models plan can never execute any model, so it is degenerate; use ["*"] for unrestricted or list at least the pinned model. (This is unlike allowed_tools: [], which is a meaningful deny-all-tools grant — see below.)

  • Any other entry must be a known catalog model in canonical

provider/model-id form — or the synthetic served model brainstorm/sandbox (so a sandbox-mode tenant, served by the sandbox short-circuit rather than the catalog, can mint a least-privilege plan). An unknown label is rejected at mint time with 400 unknown_allowed_model; a literal mixed with other entries is rejected with 400 invalid_allowed_models (the sentinel may only appear alone). Stored labels are normalized to canonical form.

  • Matching is exact-set, not glob: a variant suffix does not satisfy a

non-variant grant (anthropic/claude-sonnet-4 does not match anthropic/claude-sonnet-4:nitro). When a plan is in play, fallback is force-disabled and the grant is enforced against the executed model, not just the requested one.

  • Variant pinning. When the minted model carries a variant suffix (because

the request's model is variant-qualified, or because the grant lists exactly one variant entry for the chosen base model), the variant is preserved end to end: the signed model, the prompt_fingerprint, and the allowed_models grant are all the same canonical provider/model-id:variant label. The plan is then committable only with that exact variant — committing with the base model fails the fingerprint check, and committing with the base label fails the variant-only grant. If an explicit, non-* allowed_models is supplied but the pinned/fingerprinted model is not one of its entries (a genuinely inconsistent grant/model combination), the plan is rejected at mint with 409 plan_grant_inconsistent rather than minting an uncommittable contract.

Sandbox-mode recovery — committing a ["brainstorm/sandbox"] plan. A tenant with zero provider keys is in sandbox mode: every completion is served by the synthetic brainstorm/sandbox model, not the catalog. The least-privilege recovery from a 403 plan_scope_exceeded is to mint a plan with allowed_models: ["brainstorm/sandbox"] — you do not need to grant an unrelated catalog model or ["*"].

A plan still pins a catalog model for its prompt_fingerprint (sandbox mode never executes it; the fingerprint just binds the prompt). So when you commit the plan, the request body's model must equal the plan's signed model (the catalog label returned at mint), or be omitted entirely — anything else fails the fingerprint check with 400 plan_fingerprint_mismatch. With the body model matching, the guardian defers the executed-model decision to the sandbox handler, which grant-checks the served brainstorm/sandbox against your allowed_models and returns the synthetic completion. This holds for both streaming and non-streaming requests. The same ["brainstorm/sandbox"] plan committed once the tenant has added a provider key (no longer in sandbox mode) is denied, because the real executed catalog model is outside the grant.

allowed_tools — tool-name globs the committed request may invoke.

  • Omittedno tools permitted (empty grant []).
  • [] (empty array) ⇒ deny-all tools — a meaningful grant: the model is

allowed, but no tool may be invoked.

  • ["*"] ⇒ unrestricted (any tool).
  • Otherwise, each tool name extracted from the request (across tools[],

legacy functions[], a forced tool_choice, and built-in tool .type such as web_search / computer_ / bash_) must match at least one glob. * matches any run of characters. Tool names are flat identifiers, not paths.

Plan response

{
  "plan_id": "<hex>",
  "liaison_id": "<jwt>",
  "tenant_id": "t_001",
  "model": "anthropic/claude-sonnet-4",
  "ceiling_usd": 0.0012,
  "estimated_cost_usd": 0.0011,
  "allowed_models": ["anthropic/claude-sonnet-4"],
  "allowed_tools": ["web_search"],
  "prompt_fingerprint": "<sha256>",
  "issued_at": 1718000000,
  "ttl_seconds": 60,
  "signature": "<base64url-hmac>",
  "x402_compat": { "scheme": "br-liaison-hmac-v1", "currency": "usd", "amount": 0.0012 }
}

The returned allowed_models / allowed_tools reflect the normalized, fail-closed values that were actually signed into the contract (so an omitted allowed_models comes back as [""], and an omitted allowed_tools comes back as []).

Commit under the contract

Replay the plan on the completion. Both headers are required:

curl "https://api.brainstormrouter.com/v1/chat/completions" \
  -H "Authorization: Bearer br_live_..." \
  -H "X-BR-Plan-Id: <plan_id>" \
  -H "X-BR-Liaison: <liaison_id>" \
  -H "Content-Type: application/json" \
  -d '{ "model": "anthropic/claude-sonnet-4", "messages": [...] }'

Guardian verifies the signature and prompt fingerprint, enforces ceiling_usd as a hard cap, and checks the request's resolved model and every requested tool against the signed grant.

403 plan_scope_exceeded

If the executed model is not in allowed_models, or a requested tool is not permitted by allowed_tools, the commit is rejected with HTTP 403:

{
  "error": {
    "message": "Model \"openai/gpt-4o\" is not permitted by this plan's allowed_models grant",
    "type": "permission_error",
    "code": "plan_scope_exceeded",
    "recovery": {
      "action": "renegotiate",
      "code": "plan_scope_exceeded",
      "docs_url": "/v1/capabilities/liaison.plan/describe",
      "message": "Mint a new plan whose allowed_models / allowed_tools cover this request",
      "reason": "model_outside_grant"
    }
  }
}

A scope denial leaves the plan uncommitted (re-committable until its TTL), so it is not a self-DoS — the caller mints a new plan whose grant covers the request and retries. Every scope denial is written to the tamper-evident completion_audit hash chain as an outcome: "denied" row. Related fail-closed denials a reviewer may encounter: 400 plan_model_unresolved (model does not resolve to a known endpoint), 400 plan_model_ambiguous (a bare model id resolves to more than one endpoint), and 400 plan_tool_unextractable (a tool entry has no nameable identity).