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

# Create a webhook

> Base URL `https://compute.x402layer.cc/pods/v1`. Auth is `X-API-Key` only. This is the PROGRAMMATIC surface and is NOT the same as the dashboard pod routes (`POST /pods`, `PATCH /pods/{id}/settings`, ...), which take a wallet signature or a browser session and are free to change. Every response carries `x-request-id`.

HTTPS only. **The signing secret is returned once and never again** - an endpoint secret that can be fetched is one an attacker with read access can forge deliveries with. If you lose it, rotate the endpoint.

Omit `event_types` to receive everything. An EMPTY array is refused, because a subscription to nothing is a webhook that silently never fires.

Limit: 10 webhooks per account.

**Verify every delivery against the RAW body.** Deliveries carry:

```
x-sgl-signature: t=<unix>,v1=<hmac-sha256 of "<t>.<raw body>">
x-sgl-event-type: pod.status.changed
x-sgl-event-id: <uuid>
```

Parsing and re-encoding JSON changes key order and spacing, and the signature covers the bytes we sent - re-serialise and a genuine delivery fails to verify. Both SDKs ship a helper (`verifyPodWebhook`, `verify_pod_webhook`) that also enforces a 300-second timestamp window. The window is not optional: the timestamp is inside the signed string precisely so a captured delivery cannot be replayed later, and without a window an old capture still verifies.

**Failed deliveries back off** at 1, 5, 15, 60, 180, 360 and 720 minutes, then stop. When we give up, `disabled_by_us_at` is set - deliberately distinct from `enabled: false`, which is you turning it off. From the outside both look like silence.

Rate limit: the shared write bucket, 60 requests/minute per account.



## OpenAPI

````yaml /api-reference/openapi.json post /pods/v1/webhooks
openapi: 3.1.0
info:
  title: x402 Singularity Layer API
  description: >-
    OpenAPI-backed reference for marketplace discovery, payment routes,
    webhooks, wallet-first auth, agent endpoints, and ERC-8004 flows.
  version: 1.0.0
servers:
  - url: https://api.x402layer.cc
security: []
tags:
  - name: Marketplace
    description: Public discovery and listing lookup
  - name: Public Endpoints
    description: Public endpoint metadata and hosted checkout context
  - name: Public Payment Links
    description: Hosted public payment-link lookup
  - name: Payments
    description: Hosted x402 payment challenge routes
  - name: Receipts
    description: Signed receipt lookup and verification helpers
  - name: Ratings
    description: Public listing ratings and authenticated rating actions
  - name: Webhooks
    description: Seller webhook management API
  - name: Agent Auth
    description: Wallet-first challenge and verification routes
  - name: Agent Endpoints
    description: Create, read, top up, and delete agent endpoints
  - name: ERC-8004
    description: Agent registry and registration lifecycle routes
  - name: Marketplace Agents
    description: Public ERC-8004 marketplace discovery routes
  - name: Compute Catalog
    description: Compute plans, regions, and OS catalog
  - name: Compute Instances
    description: Provision, inspect, extend, and destroy compute instances
  - name: Compute API Keys
    description: API keys for compute agent access
  - name: Fundraiser Campaigns
    description: List, view, create, and edit fundraiser campaigns
  - name: Fundraiser Contributions
    description: Record and list campaign contributions
  - name: Fundraiser Comments
    description: Campaign comment threads
  - name: Fundraiser Media
    description: Campaign image uploads and OG images
  - name: Enterprise
    description: >-
      Enterprise partner configuration, endpoint listing, revenue stats, and
      transaction ledger
  - name: Staking
    description: Agentic $SGL staking
  - name: SGL Grid
    description: >-
      Decentralized, confidential, OpenAI-compatible inference served by
      attested TEE nodes.
  - name: Compute Credits
    description: Prepaid USDC credit balance shared across Machines and Grid.
  - name: Agent Pods
    description: >-
      Deploy and manage hosted agents (Agent Pods), plus the pod's
      OpenAI-compatible adapter for external clients.
  - name: Agent Pods API
    description: >-
      The programmatic `/pods/v1` surface: create, drive and destroy Agent Pods
      with a single `X-API-Key`. Distinct from the Agent Pods dashboard routes,
      which expect a wallet signature or a browser session.
paths:
  /pods/v1/webhooks:
    servers:
      - url: https://compute.x402layer.cc
    post:
      tags:
        - Agent Pods API
      summary: Create a webhook
      description: >-
        Base URL `https://compute.x402layer.cc/pods/v1`. Auth is `X-API-Key`
        only. This is the PROGRAMMATIC surface and is NOT the same as the
        dashboard pod routes (`POST /pods`, `PATCH /pods/{id}/settings`, ...),
        which take a wallet signature or a browser session and are free to
        change. Every response carries `x-request-id`.


        HTTPS only. **The signing secret is returned once and never again** - an
        endpoint secret that can be fetched is one an attacker with read access
        can forge deliveries with. If you lose it, rotate the endpoint.


        Omit `event_types` to receive everything. An EMPTY array is refused,
        because a subscription to nothing is a webhook that silently never
        fires.


        Limit: 10 webhooks per account.


        **Verify every delivery against the RAW body.** Deliveries carry:


        ```

        x-sgl-signature: t=<unix>,v1=<hmac-sha256 of "<t>.<raw body>">

        x-sgl-event-type: pod.status.changed

        x-sgl-event-id: <uuid>

        ```


        Parsing and re-encoding JSON changes key order and spacing, and the
        signature covers the bytes we sent - re-serialise and a genuine delivery
        fails to verify. Both SDKs ship a helper (`verifyPodWebhook`,
        `verify_pod_webhook`) that also enforces a 300-second timestamp window.
        The window is not optional: the timestamp is inside the signed string
        precisely so a captured delivery cannot be replayed later, and without a
        window an old capture still verifies.


        **Failed deliveries back off** at 1, 5, 15, 60, 180, 360 and 720
        minutes, then stop. When we give up, `disabled_by_us_at` is set -
        deliberately distinct from `enabled: false`, which is you turning it
        off. From the outside both look like silence.


        Rate limit: the shared write bucket, 60 requests/minute per account.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - url
              properties:
                url:
                  type: string
                  format: uri
                  description: >-
                    HTTPS only. Credentials in the URL, localhost,
                    `.internal`/`.local` hosts and private IP literals are
                    refused.
                event_types:
                  type: array
                  nullable: true
                  items:
                    type: string
                    enum:
                      - pod.created
                      - pod.active
                      - pod.destroyed
                      - pod.destroy_failed
                      - pod.action.queued
                      - pod.status.changed
                      - pod.renewed
                      - pod.renewal_failed
                      - pod.expiring
                      - pod.backup.completed
                      - pod.backup.failed
                  description: Omit for every event. An empty array is refused.
      responses:
        '201':
          description: Webhook created. `secret` is shown once.
          headers:
            x-request-id:
              description: >-
                Correlation id for this request. A caller-supplied
                `x-request-id` (8-64 chars of `A-Za-z0-9._-`) is echoed back;
                otherwise one is generated. Quote it in support requests.
              schema:
                type: string
          content:
            application/json:
              schema:
                type: object
                properties:
                  webhook:
                    allOf:
                      - $ref: '#/components/schemas/PodV1Webhook'
                      - type: object
                        properties:
                          secret:
                            type: string
                            description: The signing secret. SHOWN ONCE.
                          secret_note:
                            type: string
        '400':
          description: >-
            `invalid_request` - bad url, non-HTTPS url, unreachable host, or an
            unknown/empty `event_types` (`details.valid` lists the types).
          headers:
            x-request-id:
              description: >-
                Correlation id for this request. A caller-supplied
                `x-request-id` (8-64 chars of `A-Za-z0-9._-`) is echoed back;
                otherwise one is generated. Quote it in support requests.
              schema:
                type: string
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PodV1Error'
        '401':
          description: '`unauthorized` - missing or invalid `X-API-Key`.'
          headers:
            x-request-id:
              description: >-
                Correlation id for this request. A caller-supplied
                `x-request-id` (8-64 chars of `A-Za-z0-9._-`) is echoed back;
                otherwise one is generated. Quote it in support requests.
              schema:
                type: string
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PodV1Error'
        '409':
          description: '`conflict` - this account already has 10 webhooks. Delete one first.'
          headers:
            x-request-id:
              description: >-
                Correlation id for this request. A caller-supplied
                `x-request-id` (8-64 chars of `A-Za-z0-9._-`) is echoed back;
                otherwise one is generated. Quote it in support requests.
              schema:
                type: string
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PodV1Error'
        '429':
          description: >-
            `rate_limited` - too many requests. `details.retry_after_seconds`
            gives the bucket window.
          headers:
            x-request-id:
              description: >-
                Correlation id for this request. A caller-supplied
                `x-request-id` (8-64 chars of `A-Za-z0-9._-`) is echoed back;
                otherwise one is generated. Quote it in support requests.
              schema:
                type: string
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PodV1Error'
      security:
        - podsApiKey: []
components:
  schemas:
    PodV1Webhook:
      type: object
      properties:
        id:
          type: string
          format: uuid
        url:
          type: string
          format: uri
          description: HTTPS only.
        event_types:
          type: array
          nullable: true
          items:
            type: string
            enum:
              - pod.created
              - pod.active
              - pod.destroyed
              - pod.destroy_failed
              - pod.action.queued
              - pod.status.changed
              - pod.renewed
              - pod.renewal_failed
              - pod.expiring
              - pod.backup.completed
              - pod.backup.failed
          description: Null means every event type.
        enabled:
          type: boolean
          description: False when YOU turned it off.
        disabled_by_us_at:
          type: string
          format: date-time
          nullable: true
          description: >-
            Set when WE gave up after the backoff ladder ran out. Deliberately
            distinct from `enabled: false` - from the outside both look like
            silence, but only one is your doing.
        failure_count:
          type: integer
        last_error:
          type: string
          nullable: true
        last_delivery_at:
          type: string
          format: date-time
          nullable: true
        created_at:
          type: string
          format: date-time
    PodV1Error:
      type: object
      description: >-
        Every `/pods/v1/*` failure has this shape. Branch on `error.code`, never
        on `error.message` - the message is prose and may be reworded.
      properties:
        error:
          type: object
          required:
            - code
            - message
          properties:
            code:
              type: string
              enum:
                - invalid_request
                - unauthorized
                - forbidden
                - not_found
                - conflict
                - limit_exceeded
                - capability_disabled
                - rate_limited
                - not_implemented
                - internal
              description: Stable machine-readable code.
            message:
              type: string
              description: Human-readable explanation.
            details:
              type: object
              description: >-
                Optional structured context, e.g. `{ "code":
                "idempotency_mismatch" }`, `{ "required_scope":
                "pods:wallet:write" }`, `{ "capability": "wallet" }`, `{
                "retry_after_seconds": 60 }`.
  securitySchemes:
    podsApiKey:
      type: apiKey
      in: header
      name: X-API-Key
      description: >-
        A compute API key (`x402c_...`). Mint one in the dashboard under
        Settings -> API Keys. This is the ONLY auth `/pods/v1/*` accepts -
        wallet signatures are bound to method+path+body and do not survive the
        internal hop, so signature callers must use the dashboard routes
        instead. Two extra scopes gate the dangerous powers: `pods:wallet:write`
        (pod wallet money, backup passphrase) and `pods:control:write`
        (connectors, full-power control socket).

````