> ## Documentation Index
> Fetch the complete documentation index at: https://docs.getbindu.com/llms.txt
> Use this file to discover all available pages before exploring further.

# The gateway's self-published DID document.

> Returns a W3C DID Core v1-compatible document with the gateway's
Ed25519 public key under `authentication[]`. A2A peers that
accept `did_signed` requests fetch this to verify the gateway's
outbound signatures.

**Availability:** only registered when the gateway has a DID
identity configured via env — `BINDU_GATEWAY_DID_SEED`,
`BINDU_GATEWAY_AUTHOR`, and `BINDU_GATEWAY_NAME` all set. When no
identity is loaded this endpoint returns 404.

**Caching:** the gateway's DID is stable across process lifetime
(env-driven); responses carry `Cache-Control: public, max-age=300`
as a defense against bad caches that would otherwise hold the key
indefinitely.

**Content-Type:** `application/did+json` per W3C DID Core, not
plain `application/json`. Some DID resolvers enforce the media
type.

**Auth:** none. Well-known endpoints are public by spec — the
whole point is that any peer can resolve the DID without
credentials.




## OpenAPI

````yaml /gateway-openapi.yaml get /.well-known/did.json
openapi: 3.1.0
info:
  title: Bindu Gateway API
  version: 1.0.0
  summary: >-
    External HTTP surface of the Bindu Gateway — a task-first orchestrator that
    plans over a caller-supplied catalog of A2A agents.
  description: >
    # Bindu Gateway API


    The **Bindu Gateway** sits between an external system (your app, a custom

    frontend, another service) and one or more **Bindu A2A agents**. It takes

    a user question + an agent catalog and returns a streaming plan: the

    gateway's planner LLM decomposes the request, invokes A2A agents via the

    polling protocol, and emits Server-Sent Events in real time.


    Distinct from the per-agent **Bindu Agent API** (see the repo-root

    `openapi.yaml`), which describes what a single `bindufy()`-built agent

    exposes. This spec documents the **gateway** — the orchestrator sitting

    one layer up.


    ---


    ## Mental model: one endpoint, many turns


    Every orchestration goes through `POST /plan`. Inside, the planner LLM

    runs an agentic loop — it calls A2A agents as tools, the results feed

    back into the LLM, and the loop continues up to `max_steps` or until the

    plan resolves.


    Two auxiliary endpoints support health probing and DID-based peer

    authentication:


    | Path | Purpose |

    |---|---|

    | `POST /plan` | Open a new plan or resume an existing session. Streams SSE.
    |

    | `GET /health` | Liveness + cheap config probe. |

    | `GET /.well-known/did.json` | The gateway's own DID document (only when a
    DID identity is configured via env). |


    ---


    ## Request shape


    A `/plan` request carries three things:


    1. **`question`** — the user's natural-language input.

    2. **`agents[]`** — the catalog of A2A peers the planner may call, each
       with an endpoint, authentication descriptor, and list of skills.
       The gateway does **not** host agents; the caller is always the
       source of truth for "what can we reach."
    3. **`preferences`** and **`session_id`** (both optional) — caps and
       continuation handles.

    The shape is stable and additive; unknown top-level keys are accepted

    (forward-compatible `.passthrough()`), but `preferences` keys are strict

    snake_case. Clients sending camelCase preferences will have them

    silently dropped — match the schema below.


    ---


    ## Response shape — Server-Sent Events


    The happy path returns `200 OK` with `Content-Type: text/event-stream`.

    Errors surface in three ways depending on when they occur:


    - **Before streaming starts** (auth failure, invalid JSON, malformed
      request, session creation failure): `401`/`400`/`500` with a JSON
      `{ error, detail? }` body.
    - **During streaming** (planner or tool failure): a single
      `event: error` SSE frame, followed by `event: done`.
    - **Never silent** — every successful plan closes with `event: done`
      (empty payload). Consumers should treat the absence of `done` as
      an incomplete stream.

    SSE events emitted during a plan, in typical order:


    | Event | When | Purpose |

    |---|---|---|

    | `session` | Once, before the plan starts | Carries session identifiers so
    clients can correlate. |

    | `plan` | Once, when the planner starts its first turn | Announces plan_id.
    |

    | `text.delta` | Many (streaming planner output) | Incremental text chunks
    for the final assistant message. |

    | `task.started` | Per A2A tool call | The planner decided to call a peer
    agent. |

    | `task.artifact` | Per A2A tool call | The peer returned an artifact,
    wrapped in a `<remote_content>` envelope. |

    | `task.finished` | Per A2A tool call | Terminal state of the peer call. |

    | `final` | Once, at the end | Stop reason + usage counters. |

    | `error` | Only on failure during streaming | Human-readable message. |

    | `done` | Always last | Empty marker so clients can close cleanly. |


    ---


    ## Recipes (internal)


    The gateway supports **progressive-disclosure recipes** — markdown

    playbooks the planner lazy-loads when a task matches (e.g.,

    "multi-agent research", "payment-required flow"). Recipes are operator-

    authored and not part of this HTTP API surface: they live in

    `gateway/recipes/` and are injected automatically into the planner's

    system prompt as metadata, with the body fetched on demand via an

    internal `load_recipe` tool.


    You cannot upload, list, or invoke recipes via the HTTP API; they

    influence the planner's behavior transparently. See the gateway README

    §Recipes for authoring details.


    ---


    ## A2A protocol pass-through


    The gateway speaks A2A (JSON-RPC 2.0 over HTTP) to every peer in

    `agents[]` — `message/send` + `tasks/get` polling, with DID signature

    verification when configured. A2A task states (`submitted`, `working`,

    `input-required`, `auth-required`, `payment-required`, `completed`,

    `failed`, `canceled`) flow through to the planner; terminal states

    become `task.finished` events, non-terminal states can surface as

    planner text or trigger recipe-based handling (e.g., surfacing a

    `payment-required` URL to the user).


    See the Bindu Agent API spec (`openapi.yaml` at the repo root) for the

    full A2A protocol surface.
  contact:
    name: Bindu Team
    url: https://docs.getbindu.com/
  license:
    name: Apache-2.0
servers:
  - url: http://localhost:3774
    description: Local development (default port)
  - url: https://gateway.example.com
    description: Production deployment (replace with your host)
security: []
tags:
  - name: Plan
    description: |
      Open a new plan or resume an existing session. Server-Sent Events
      stream back the planner's turn-by-turn output, tool calls, and
      final answer.
  - name: Health
    description: Liveness and basic configuration probes.
  - name: Identity
    description: |
      The gateway's self-published DID document, for A2A peers that
      need to verify `did_signed` outbound calls. Only exposed when
      the gateway has a DID identity configured via env.
paths:
  /.well-known/did.json:
    get:
      tags:
        - Identity
      summary: The gateway's self-published DID document.
      description: |
        Returns a W3C DID Core v1-compatible document with the gateway's
        Ed25519 public key under `authentication[]`. A2A peers that
        accept `did_signed` requests fetch this to verify the gateway's
        outbound signatures.

        **Availability:** only registered when the gateway has a DID
        identity configured via env — `BINDU_GATEWAY_DID_SEED`,
        `BINDU_GATEWAY_AUTHOR`, and `BINDU_GATEWAY_NAME` all set. When no
        identity is loaded this endpoint returns 404.

        **Caching:** the gateway's DID is stable across process lifetime
        (env-driven); responses carry `Cache-Control: public, max-age=300`
        as a defense against bad caches that would otherwise hold the key
        indefinitely.

        **Content-Type:** `application/did+json` per W3C DID Core, not
        plain `application/json`. Some DID resolvers enforce the media
        type.

        **Auth:** none. Well-known endpoints are public by spec — the
        whole point is that any peer can resolve the DID without
        credentials.
      operationId: getDidDocument
      responses:
        '200':
          description: DID document for the configured gateway identity.
          headers:
            Cache-Control:
              schema:
                type: string
              example: public, max-age=300
          content:
            application/did+json:
              schema:
                $ref: '#/components/schemas/GatewayDidDocument'
              example:
                '@context':
                  - https://www.w3.org/ns/did/v1
                  - https://getbindu.com/ns/v1
                id: >-
                  did:bindu:raahul_at_getbindu_com:gateway:f72ba681-f873-324c-6012-23c4d5b72451
                authentication:
                  - id: >-
                      did:bindu:raahul_at_getbindu_com:gateway:f72ba681-f873-324c-6012-23c4d5b72451#key-1
                    type: Ed25519VerificationKey2020
                    controller: >-
                      did:bindu:raahul_at_getbindu_com:gateway:f72ba681-f873-324c-6012-23c4d5b72451
                    publicKeyBase58: 573SRD1L1EPFaKd77q6TEUGsMatVsn7jdKqPrTKBD9XK
        '404':
          description: No DID identity configured on this gateway instance.
      security: []
components:
  schemas:
    GatewayDidDocument:
      type: object
      required:
        - '@context'
        - id
        - authentication
      description: |
        W3C DID Core v1 document describing the gateway's identity.
        Deliberately omits `created` — the gateway's identity is env-
        driven and stateless, so there's no persisted "first published"
        moment to report (W3C DID Core has `created` as optional).
      properties:
        '@context':
          type: array
          items:
            type: string
          example:
            - https://www.w3.org/ns/did/v1
            - https://getbindu.com/ns/v1
        id:
          type: string
          example: did:bindu:gateway-prod-key-1
        authentication:
          type: array
          items:
            $ref: '#/components/schemas/GatewayVerificationMethod'
    GatewayVerificationMethod:
      type: object
      required:
        - id
        - type
        - controller
        - publicKeyBase58
      properties:
        id:
          type: string
          example: did:bindu:gateway-prod-key-1#key-1
        type:
          type: string
          enum:
            - Ed25519VerificationKey2020
        controller:
          type: string
          example: did:bindu:gateway-prod-key-1
        publicKeyBase58:
          type: string
          description: Ed25519 public key, base58-encoded.
          example: 6MkjQ2r...

````