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

# Replay every delivery that did not arrive, by filter

> Top-level alias: POST /admin/api/deliveries/replay. Write role required. The recovery after a receiver outage: replays every delivery in the project that matches the filter and did not arrive - 'dead', or 'failed' with no retry scheduled (one still being retried is left to its retry). Only the latest delivery of each event to each destination matches (an event's original and a failed replay of it are one event, sent once), and only while the event is stored; deliveries to a deleted destination, or to a customer-portal destination whose portal no longer exposes the dataset, never match. Oldest first. Bounded per call: queues up to limit and reports how many still match, so call again while more is true; a replayed delivery stops matching, so repeating (or racing) a call never sends one twice. Each replay is a new delivery (event_id <record>#replay:<n>) charged to the monthly delivery allowance, like a single replay: the call queues as many as the allowance grants and stops there. Audited once per call as replay_deliveries.



## OpenAPI

````yaml /openapi.json post /admin/api/projects/{pid}/deliveries/replay
openapi: 3.1.0
info:
  title: Hookie API
  version: 1.0.0
  summary: Capture, route and deliver webhooks.
  description: >-
    Hookie exposes three HTTP surfaces.


    **Ingest + streaming** is the public plane. A path-form endpoint URL
    authenticates by its own high-entropy slug — there is no key, token or
    cookie — and an `ik_live_…` ingest key authenticates the `/v1/*` routes.
    Whenever the URL names a dataset the payload is stored whole into it;
    project mapping rules are evaluated on exactly one shape, `POST
    /v1/ingest/{ingest_key}` with no dataset segment.


    **The customer portal API** is a separate, token-authed surface for your end
    customers, scoped to one portal and one customer.


    **The admin API** drives the console. It accepts either a browser session or
    an OAuth bearer token from a connected coding agent — the same routes, the
    same validation, the same audit trail. An agent's authority is its user's
    workspace role narrowed to the scopes granted in the console.


    **A refused agent is told how to proceed.** An agent's grant is one rung of
    a ladder — `hookie:read` < `hookie:write` < `hookie:manage` — and a call
    above it answers 403 with `code: "agent_scope_insufficient"`, the
    `required_scope`, the `granted_scopes` and a `manage_url` to the console
    page where its user widens it. When the user's own role is the limit the
    code is `insufficient_role` and there is no link, because no grant can
    exceed the user; billing, platform admin, managing connected agents and
    destination signing secrets answer `agent_not_permitted` at every scope. A
    human in the console still reads `Insufficient role`.


    **A suspended workspace** (an operator action, for abuse or non-payment)
    answers every surface with 403 and `reason: "workspace_suspended"`: ingest
    (endpoint URLs and ingest keys), `/v1/data` and `/v1/stream` refuse before
    anything is stored or counted, and every admin API write — console, OAuth
    agent or hosted MCP alike — is refused except billing, platform admin,
    connected-agent management and the POST-bodied searches. The customer portal
    API is read-only in the same way: destination writes answer 403
    `workspace_suspended`. Reads keep working. Nothing is sent while it is
    suspended: pending deliveries are paused, workflow runs already under way
    are paused, open `/v1/stream` connections are closed, and cron triggers,
    database sources and WebSocket listeners stop. Reinstating sends the paused
    deliveries, resumes the paused workflow runs where they stopped, and resumes
    the rest; no stored data is lost.


    This document is generated from the request handlers and adversarially
    verified against them.
  contact:
    name: Hookie
    url: https://hookie.ai
  license:
    name: Proprietary
    url: https://hookie.ai/legal/terms
servers:
  - url: https://app.hookie.ai
    description: Production
  - url: https://app.preview.hookie.ai
    description: Preview
security: []
tags:
  - name: Ingest
    description: Send events to Hookie.
  - name: Streaming
    description: Tail events in real time over SSE or WebSocket.
  - name: Portal
    description: Token-authed surface for your end customers.
  - name: Projects
    description: Projects and their settings.
  - name: Routing
    description: Rules, endpoints, datasets and records.
  - name: Delivery
    description: Destinations, deliveries and replay.
  - name: Workflows
    description: Multi-step workflows, triggers and AI agents.
  - name: Observability
    description: Search, correlation, stats and the audit log.
  - name: Workspace
    description: Members, connected agents, SSO and billing.
  - name: Data API
    description: >-
      Read-only access to the datasets and columns a project exposes, with
      per-project keys.
  - name: Broadcast Logs
    description: >-
      Forward a project's logs and traces over OTLP/HTTP (JSON) to Datadog,
      Grafana Cloud, an OpenTelemetry Collector, PostHog or Sentry.
paths:
  /admin/api/projects/{pid}/deliveries/replay:
    post:
      tags:
        - Deliveries
      summary: Replay every delivery that did not arrive, by filter
      description: >-
        Top-level alias: POST /admin/api/deliveries/replay. Write role required.
        The recovery after a receiver outage: replays every delivery in the
        project that matches the filter and did not arrive - 'dead', or 'failed'
        with no retry scheduled (one still being retried is left to its retry).
        Only the latest delivery of each event to each destination matches (an
        event's original and a failed replay of it are one event, sent once),
        and only while the event is stored; deliveries to a deleted destination,
        or to a customer-portal destination whose portal no longer exposes the
        dataset, never match. Oldest first. Bounded per call: queues up to limit
        and reports how many still match, so call again while more is true; a
        replayed delivery stops matching, so repeating (or racing) a call never
        sends one twice. Each replay is a new delivery (event_id
        <record>#replay:<n>) charged to the monthly delivery allowance, like a
        single replay: the call queues as many as the allowance grants and stops
        there. Audited once per call as replay_deliveries.
      operationId: replayDeliveries
      parameters:
        - name: pid
          in: path
          required: true
          description: Project id (UUID) belonging to the caller workspace.
          schema:
            type: string
      requestBody:
        required: false
        content:
          application/json:
            schema:
              type: object
              properties:
                destination_id:
                  type: string
                  description: Only deliveries to this destination.
                status:
                  description: >-
                    dead, failed, or both (the default). A list, or a
                    comma-separated string. Any other status is a 400.
                  oneOf:
                    - type: array
                      items:
                        type: string
                        enum:
                          - dead
                          - failed
                    - type: string
                since:
                  type: string
                  format: date-time
                  description: Only deliveries created at or after this time.
                until:
                  type: string
                  format: date-time
                  description: Only deliveries created at or before this time.
                limit:
                  type: integer
                  minimum: 1
                  maximum: 500
                  default: 100
                  description: The most to queue in this call.
                dry_run:
                  type: boolean
                  description: >-
                    Answer {matched, dry_run: true} without queueing or charging
                    anything - what a confirmation states.
      responses:
        '200':
          description: One bounded pass (or, with dry_run, the count alone).
          content:
            application/json:
              schema:
                oneOf:
                  - type: object
                    required:
                      - queued
                      - matched
                      - remaining
                      - more
                    properties:
                      queued:
                        type: integer
                        description: New deliveries queued by this call.
                      matched:
                        type: integer
                        description: Deliveries matching the filter when the call began.
                      remaining:
                        type: integer
                        description: Deliveries still matching after it.
                      more:
                        type: boolean
                        description: 'remaining > 0: call again to continue.'
                      stopped:
                        type: string
                        enum:
                          - delivery_allowance_exhausted
                        description: Present when the allowance ran out part-way.
                      error:
                        type: string
                        description: 'With stopped: the allowance message.'
                  - type: object
                    required:
                      - matched
                      - dry_run
                    properties:
                      matched:
                        type: integer
                      dry_run:
                        type: boolean
                        const: true
        '400':
          description: >-
            status names something other than dead or failed, since or until is
            not an ISO 8601 timestamp, destination_id is not a string, or limit
            is not a whole number from 1 to 500.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          description: >-
            Not signed in, or the agent bearer token is invalid, unknown or
            revoked.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '403':
          description: >-
            Insufficient role (write capability required: owner, admin or
            developer), or a cookie-authenticated mutation missing the
            X-Requested-With: fetch header. For an OAuth-connected agent the
            body also carries `code`, `required_scope` and `granted_scopes`,
            plus `manage_url` when widening its grant in Settings → Connected
            agents would let the call through (see Error).
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '429':
          description: >-
            The month's delivery allowance is used up and nothing could be
            queued (reason: delivery_allowance_exhausted; the body also carries
            queued: 0, matched and remaining).
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '500':
          description: Internal error.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '503':
          description: >-
            The replays could not be put on the delivery queue. Those not queued
            were withdrawn and not charged (queued says how many did go); try
            again.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
      security:
        - session: []
        - bearerAuth: []
components:
  schemas:
    Error:
      type: object
      required:
        - error
      properties:
        error:
          type: string
          description: Human-readable reason.
        retry_after:
          type: integer
          description: Seconds to wait. Present on 429.
        upgrade_url:
          type: string
          format: uri
          description: Present only on a monthly-quota 429.
        reason:
          type: string
          description: >-
            Machine-readable reason, where the status alone is ambiguous.
            `workspace_suspended` (403): the workspace is suspended;
            `delivery_allowance_exhausted` (429): a replay was refused because
            the monthly delivery allowance is used. `endpoint_disabled` (503):
            the endpoint is switched off; `handshake_failed`: a provider
            URL-verification challenge could not be answered (#193).
        code:
          type: string
          enum:
            - agent_scope_insufficient
            - insufficient_role
            - agent_not_permitted
          description: >-
            Present on a 403 to an OAuth-connected agent.
            `agent_scope_insufficient`: the agent's grant is below what the call
            needs, and widening it would let the call through (see
            `required_scope` and `manage_url`). `insufficient_role`: the user's
            OWN workspace role does not allow the call, so no grant can — a
            workspace owner or admin has to change the role.
            `agent_not_permitted`: no connected agent may do this at any scope
            (billing, platform admin, managing connected agents, a destination's
            signing secret).
        required_scope:
          type: string
          enum:
            - hookie:read
            - hookie:write
            - hookie:manage
          description: 'With `code`: the scope the refused call needs.'
        granted_scopes:
          type: array
          items:
            type: string
          description: 'With `code`: the scopes the agent''s grant holds now.'
        manage_url:
          type: string
          format: uri
          description: >-
            With `code: "agent_scope_insufficient"` only: the console's Settings
            → Connected agents page, opened on this agent
            (`…/#/settings/agents?client=<client_id>`), where its user widens
            the grant. The change applies on the agent's next request.
  securitySchemes:
    session:
      type: apiKey
      in: cookie
      name: hookie_session
      description: >-
        The console's sealed session cookie, set by WorkOS AuthKit. Mutating
        requests also require the `X-Requested-With` CSRF header.
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: JWT
      description: >-
        An OAuth 2.1 access token from a connected agent, audienced at the
        `/mcp` resource URI. The agent acts as its user, with that user's role
        narrowed to the granted `hookie:*` scopes. No CSRF header is required —
        a bearer token is not ambient credentials.

````