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

# Drain the caller's inbox (unary work-cycle pull)

> Drain the calling agent's own inbox without holding a stream open — the work-cycle pull for a non-resident agent (serverless, cron, orchestrated node).

Items are paged by the monotonic `delivery_seq` (the delivery sequence), returning only commit-stable rows, and are acknowledged through a server-persisted cursor: pass the response's `window_high` back as `ack` on the next call once you have handled the page. Pull and ack ride ONE round trip — the `ack` is applied before this call's page is read, so it advances the floor for the very page it is sent with. An un-acked item is redelivered on the next pull (at-least-once), and a stale or lower `ack` is a no-op — the cursor never rewinds.

The response is a page envelope. Set `limit` to what your harness can inject in one turn, handle the whole page, and `ack` its `window_high`; if you can only handle part of it, `ack` the largest sequence all of whose items you handled. `has_more` says whether another page is already waiting.

The body carries NO agent id: the inbox drained is always the authenticated caller's. Within a page, items are presented highest-`score` first (a display ordering only — it never affects which items are in the page, nor the cursor). Because the page unit is the DELIVERY, a diff that was delivered to this agent twice (e.g. via an agent node and a beacon it owns) appears twice; de-duplicate on `diff.id` if you need observable exactly-once.

Charged against the `Inbox` rate-limit bucket.



## OpenAPI

````yaml /api-reference/openapi.json post /v1/inbox:pull
openapi: 3.0.3
info:
  title: HyphaeDB REST API
  version: 1.0.0
  description: >-
    The unary REST surface over the HyphaeDB internal service facade. JSON over
    HTTP/1.1; every endpoint is a thin translator onto the same facade that gRPC
    and MCP speak. `recall` and `query` return fully hydrated nodes in rank
    order (nearest-first); no similarity score rides the wire — the array order
    conveys the rank.


    All endpoints except `GET /healthz` require an API key supplied in the
    `x-hyphae-key` header. Errors use a uniform envelope: `{ "error": { "code":
    string, "message": string } }`.


    This document covers the unary REST endpoints only. The WebSocket gossip
    stream (`GET /v1/inbox`) and the MCP-over-HTTP endpoint (`POST /mcp`) are
    separate surfaces not described here, and `GET /openapi.json` serves this
    document itself.
servers:
  - url: http://localhost:8080
    description: Local HyphaeDB server
security:
  - ApiKeyAuth: []
tags:
  - name: Memory
    description: Store and retrieve knowledge cells.
  - name: Sessions
    description: Open and close working sessions.
  - name: Beacons
    description: Standing interest beacons for gossip routing.
  - name: Inbox
    description: 'The gossip inbox: the unary work-cycle drain for non-resident agents.'
  - name: Scenes
    description: Scene listing, lookup, and consolidation.
  - name: Health
    description: Liveness probe.
paths:
  /v1/inbox:pull:
    post:
      tags:
        - Inbox
      summary: Drain the caller's inbox (unary work-cycle pull)
      description: >-
        Drain the calling agent's own inbox without holding a stream open — the
        work-cycle pull for a non-resident agent (serverless, cron, orchestrated
        node).


        Items are paged by the monotonic `delivery_seq` (the delivery sequence),
        returning only commit-stable rows, and are acknowledged through a
        server-persisted cursor: pass the response's `window_high` back as `ack`
        on the next call once you have handled the page. Pull and ack ride ONE
        round trip — the `ack` is applied before this call's page is read, so it
        advances the floor for the very page it is sent with. An un-acked item
        is redelivered on the next pull (at-least-once), and a stale or lower
        `ack` is a no-op — the cursor never rewinds.


        The response is a page envelope. Set `limit` to what your harness can
        inject in one turn, handle the whole page, and `ack` its `window_high`;
        if you can only handle part of it, `ack` the largest sequence all of
        whose items you handled. `has_more` says whether another page is already
        waiting.


        The body carries NO agent id: the inbox drained is always the
        authenticated caller's. Within a page, items are presented
        highest-`score` first (a display ordering only — it never affects which
        items are in the page, nor the cursor). Because the page unit is the
        DELIVERY, a diff that was delivered to this agent twice (e.g. via an
        agent node and a beacon it owns) appears twice; de-duplicate on
        `diff.id` if you need observable exactly-once.


        Charged against the `Inbox` rate-limit bucket.
      operationId: pullInbox
      requestBody:
        required: false
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PullInboxBody'
      responses:
        '200':
          description: The page of deliveries plus the acknowledged cursor after this call.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PullInboxResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '415':
          $ref: '#/components/responses/UnsupportedMediaType'
        '422':
          $ref: '#/components/responses/UnprocessableContent'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/ServerError'
        '503':
          $ref: '#/components/responses/Unavailable'
components:
  schemas:
    PullInboxBody:
      type: object
      description: >-
        The body for `POST /v1/inbox:pull`. Every field is optional — `{}` means
        "drain from my stored cursor, at the server's page cap, acknowledging
        nothing". There is deliberately NO agent id field: the caller is the
        authenticated principal.
      properties:
        since:
          type: integer
          format: int64
          minimum: 0
          description: >-
            Explicit page floor: return deliveries whose `delivery_seq` is
            strictly greater than this, OVERRIDING the stored ack cursor. Omit
            for the normal case (resume from the cursor; from the beginning if
            the agent has never acked).
        ack:
          type: integer
          format: int64
          minimum: 0
          description: >-
            Acknowledge every delivery up to this `delivery_seq` — normally the
            `window_high` of the previous response. Monotonic: a value at or
            below the stored watermark is a no-op, never a rewind, and the
            server clamps it to the newest sequence the same call examined, so
            it can never skip past deliveries the server did not look at. The
            acknowledgement is applied BEFORE this call's page is read, so it
            also advances this call's page floor — that is what makes
            pull-and-ack one round trip.
        limit:
          type: integer
          format: int32
          minimum: 0
          description: >-
            Requested page size. Omitted or `0` means the server's
            `pull.max_batch`; a larger value is clamped down to it. Size this to
            whatever your harness can actually inject in one turn (Claude Code
            caps hook output at 10 000 characters, Codex at roughly 2 500
            tokens) so you can handle a whole page and acknowledge `window_high`
            cleanly.
    PullInboxResponse:
      type: object
      description: >-
        A page ENVELOPE, not a bare list: `items` is what you may see,
        `window_high` is how far you may acknowledge, and `has_more` is whether
        another page is waiting. The three answer different questions and none
        is derivable from the others.
      required:
        - items
        - cursor
        - window_high
        - has_more
      properties:
        items:
          type: array
          description: >-
            The page, highest-`delivery.score` first (an intra-page display
            ordering).
          items:
            $ref: '#/components/schemas/SeqInboxItem'
        cursor:
          type: integer
          format: int64
          description: >-
            The acknowledged watermark AFTER this call (`0` = nothing
            acknowledged yet). Unchanged when the request carried no `ack`;
            compare it with the `ack` you sent to detect one that was clamped or
            ignored.
        window_high:
          type: integer
          format: int64
          description: >-
            The top of the delivery window this call examined — the highest
            `delivery_seq` your next call may `ack`. Handled every item? Send
            this back as `ack`. Handled only some (the normal case under a hook
            injection cap)? Send back the largest sequence all of whose items
            you handled — because the page is presented score-first, that is
            generally NOT the newest item you received. This is deliberately NOT
            the maximum `delivery_seq` in `items`: it is greater whenever your
            read scope filtered a delivery out of the window, and acknowledging
            your newest ITEM instead would make the cursor re-scan those rows on
            every pull.
        has_more:
          type: boolean
          description: >-
            Whether a further commit-stable delivery exists above `window_high`
            right now — i.e. whether pulling again immediately would examine
            more. It describes the delivery window, not `items`: under a narrow
            read scope a `true` can be followed by a page with no items.
    SeqInboxItem:
      type: object
      description: One delivered diff plus the sequence the cursor tracks.
      required:
        - delivery_seq
        - diff
        - delivery
      properties:
        delivery_seq:
          type: integer
          format: int64
          description: The delivery sequence — the page order key and the unit of `ack`.
        diff:
          type: object
          description: >-
            The delivered `MemoryDiff` (the gossip payload: content, embedding,
            energy, provenance).
        delivery:
          type: object
          description: >-
            The `DeliveryRecord` for this delivery (score, hop count, remaining
            energy, delivered-at).
    Error:
      type: object
      description: The uniform error envelope returned for every non-2xx response.
      required:
        - error
      properties:
        error:
          type: object
          required:
            - code
            - message
          properties:
            code:
              type: string
              description: A stable, machine-readable error code.
              enum:
                - not_found
                - validation
                - dimension_mismatch
                - unauthorized
                - forbidden
                - conflict
                - transaction
                - quota_exceeded
                - rate_limited
                - timeout
                - unavailable
                - storage
                - embedding
                - config
                - internal
            message:
              type: string
              description: >-
                A redaction-safe, human-readable description (never embeds
                secrets).
  responses:
    BadRequest:
      description: >-
        The request was malformed or failed validation (codes: `validation`,
        `dimension_mismatch`).
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    Unauthorized:
      description: 'Missing or invalid `x-hyphae-key` (code: `unauthorized`).'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    Forbidden:
      description: 'Authenticated but not permitted (code: `forbidden`).'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    UnsupportedMediaType:
      description: >-
        The request body was sent with a non-JSON `Content-Type` (code:
        `validation`). Send `Content-Type: application/json`.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    UnprocessableContent:
      description: >-
        The JSON body parsed but did not match the expected shape — a missing
        required field or a wrong field type (code: `validation`).
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    RateLimited:
      description: >-
        Quota exhausted or rate limited (codes: `quota_exceeded`,
        `rate_limited`). A `rate_limited` response sets the `Retry-After` header
        (in seconds).
      headers:
        Retry-After:
          description: Seconds to wait before retrying (only on `rate_limited`).
          schema:
            type: integer
            minimum: 1
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    ServerError:
      description: 'An internal error (codes: `embedding`, `config`, `internal`).'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    Unavailable:
      description: 'A dependency was unavailable (codes: `unavailable`, `storage`).'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
  securitySchemes:
    ApiKeyAuth:
      type: apiKey
      in: header
      name: x-hyphae-key
      description: >-
        The API key issued for the calling agent. Required on every endpoint
        except `GET /healthz`.

````