> ## 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.

# Rename an ingest key, or set its expiry or IP allowlist

> Top-level alias: PATCH /admin/api/ingest-keys/{id}. Write role required. Send at least one of name, expires_at and ip_allowlist. dataset_default and require_signature are fixed at creation. A revoked or expired key cannot be changed (409) - nothing revives a dead credential. On a rotated key, moving expires_at moves the end of the overlap. Audited as update_ingest_key with each change's from and to.



## OpenAPI

````yaml /openapi.json patch /admin/api/projects/{pid}/ingest-keys/{id}
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}/ingest-keys/{id}:
    patch:
      tags:
        - Ingest keys
      summary: Rename an ingest key, or set its expiry or IP allowlist
      description: >-
        Top-level alias: PATCH /admin/api/ingest-keys/{id}. Write role required.
        Send at least one of name, expires_at and ip_allowlist. dataset_default
        and require_signature are fixed at creation. A revoked or expired key
        cannot be changed (409) - nothing revives a dead credential. On a
        rotated key, moving expires_at moves the end of the overlap. Audited as
        update_ingest_key with each change's from and to.
      operationId: updateIngestKey
      parameters:
        - name: pid
          in: path
          required: true
          description: Project id (UUID) belonging to the caller workspace.
          schema:
            type: string
        - name: id
          in: path
          required: true
          description: Ingest key id.
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              minProperties: 1
              additionalProperties: false
              properties:
                name:
                  type: string
                  minLength: 1
                  maxLength: 80
                expires_at:
                  type:
                    - string
                    - 'null'
                  format: date-time
                  description: >-
                    When the key stops authenticating, at ingest and on
                    /v1/stream alike. Must be in the future; null means never. A
                    key past it answers 404 'Unknown ingest key', exactly like a
                    revoked one.
                ip_allowlist:
                  type:
                    - array
                    - 'null'
                  maxItems: 64
                  items:
                    type: string
                    description: An IPv4 or IPv6 address, or a CIDR range.
                  description: >-
                    IP addresses or CIDR ranges allowed to use this credential,
                    validated exactly as the workspace and project allowlists
                    are. [] or null removes the restriction (stored as null). A
                    request must pass every allowlist that is set: this one, the
                    project's and the workspace's. Refused requests get 403
                    'Source IP not allowed' and an ingest_ip_blocked audit row.
                  example:
                    - 203.0.113.0/24
                    - 2001:db8::/32
      responses:
        '200':
          description: Updated. Echoes what changed.
          content:
            application/json:
              schema:
                type: object
                required:
                  - ok
                properties:
                  ok:
                    type: boolean
                    const: true
                  name:
                    type: string
                  expires_at:
                    type:
                      - string
                      - 'null'
                    format: date-time
                  ip_allowlist:
                    type: array
                    items:
                      type: string
        '400':
          description: >-
            Body is not an object, sends no field, sends a field that cannot
            change (dataset_default, require_signature, ...), or a field is
            invalid.
          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. Also `reason: "workspace_suspended"`
            while the workspace is suspended. 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'
        '404':
          description: Key not found in this project.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '409':
          description: The key is revoked or expired.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '500':
          description: Internal error.
          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.

````