{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "$id": "/schemas/3.1.0-rc.4/error-details/agent-permission-denied.json",
  "title": "Agent Permission Denied Details",
  "description": "Recommended details shape for `PERMISSION_DENIED` errors when the gate that fired is a non-status per-agent provisioning constraint (`scope: \"agent\"` plus a registered `reason`) — distinct from generic credential-shaped permission failures (no `scope`), from per-account/per-task authorization failures (`SCOPE_INSUFFICIENT`, `READ_ONLY_SCOPE`, `FIELD_NOT_PERMITTED`), and from per-agent commercial-status rejections (`AGENT_SUSPENDED` / `AGENT_BLOCKED`, which are their own discriminator codes and do NOT carry an explicit `scope` field). The shape carries a registered `reason` value identifying the gate; `additionalProperties: false` clamps it so adopters cannot invent ad-hoc per-agent state keys (rate cards, payment terms, credit limit, billing entity, custom reason strings) — full disclosure of the agent's commercial state in a single probe is a per-agent oracle the clamp prevents. New `reason` values MUST be registered in the schema's enum so cross-language SDKs can dispatch without parsing prose; future per-agent state additions belong on new dedicated codes (each with its own clamped details shape), NOT by relaxing this shape. The cross-tenant onboarding oracle clamp and its full channel-coverage requirements are normative in `error-handling.mdx` Per-Agent Authorization Gate — this schema description does not restate them to avoid drift.",
  "type": "object",
  "properties": {
    "scope": {
      "const": "agent",
      "description": "Discriminator that this `PERMISSION_DENIED` was triggered by a per-buyer-agent provisioning gate (as opposed to a generic credential-shaped failure). Registered subset of `enums/error-scope.json` — sellers MUST set this field exactly to `\"agent\"` for this shape, and MUST omit `scope` entirely when responding on the unauthenticated/unestablished-identity path required by the cross-tenant oracle clamp. The `AGENT_SUSPENDED` / `AGENT_BLOCKED` codes (per-agent commercial status) do NOT carry an explicit `scope` field — the code itself is the discriminator on those paths, mirroring `BILLING_NOT_PERMITTED_FOR_AGENT`."
    },
    "reason": {
      "type": "string",
      "enum": ["sandbox_only"],
      "description": "Registered per-agent provisioning gate that fired. `\"sandbox_only\"` — the agent is provisioned for sandbox traffic only and the request was against a non-sandbox account. New `reason` values MUST be added here (the enum is closed by `additionalProperties: false`) so cross-language SDKs can dispatch without parsing prose."
    }
  },
  "required": ["scope", "reason"],
  "additionalProperties": false,
  "examples": [
    {
      "scope": "agent",
      "reason": "sandbox_only"
    }
  ]
}
