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

# `PATCH /v1/me/webhooks/{id}` — partial update of a subscription
keeping its `id` (and so its `webhook_deliveries` history) intact.

> The previous "delete + re-create" workflow orphaned the audit
trail under a new id. This endpoint keeps continuity for
dashboards and replay.

Returns the full webhook row after the update. 404 if the id
belongs to a different user (no existence-leak).



## OpenAPI

````yaml https://api.pegana.xyz/openapi.json patch /v1/me/webhooks/{id}
openapi: 3.1.0
info:
  title: Pegana API
  description: >-
    The peg-risk oracle for Solana. Read real-time peg state, history, alerts,
    and delivery health across 26 active mainnet assets spanning 6 classes —
    LSTs, fiat / CDP / delta-neutral / yield-bearing / FX stables, and a
    leveraged synthetic. Some assets are flagged `monitoring_only` (beta): their
    alerts are live but the calibration is provisional. Public read endpoints
    require no API key but ARE rate-limited per IP (300/min global, 10/min on
    /v1/audit.csv); exceeding the limit returns 429 with a Retry-After header.
    User-scoped /v1/me/* routes require a JWT obtained via Telegram Login. Every
    state transition emits a public receipt at /v1/audit/{id} with the
    methodology version, frozen inputs, and an on-chain SPL Memo commit (SAS
    deferred per ADR-0004 — SPL Memo gives 90% of the value). EVERY error
    response across the API is JSON — the envelope is `{error, message, asset?}`
    (the `ApiError` schema); branch on the stable `error` code, never on
    `message`. Malformed request bodies surface as `{error:"invalid_body"}`. Any
    endpoint may also return 500 `{error:"internal"}` on an unexpected server
    error.
  contact:
    name: Rafael Souza
    email: raffxweb3@gmail.com
  license:
    name: MIT
    identifier: MIT
  version: 0.1.0
servers:
  - url: https://api.pegana.xyz
    description: Production
security: []
tags:
  - name: Health
    description: Liveness and readiness probes
  - name: Assets
    description: Public read access to asset state, history, and metadata
  - name: Alerts
    description: Global feed of state transitions
  - name: Stats
    description: Aggregate counters and delivery health
  - name: Auth
    description: Telegram Login Widget → JWT
  - name: Me
    description: Authenticated user profile and preferences
  - name: Subscriptions
    description: User alert subscriptions
  - name: Webhooks
    description: User-managed Ed25519-signed webhooks
  - name: WebSocket
    description: Live state stream
  - name: audit
    description: >-
      Public receipts for alerts — methodology version, inputs frozen, replay
      bundles. All endpoints public, no auth, cacheable. ADR-0006 four-state
      response on /v1/audit/:id (200/202/404/410); ADR-0014 bounds (90-day max,
      100-row max, 50k-row cap).
  - name: methodology
    description: >-
      Public lifecycle status of the active methodology — version + status
      (active/deprecated/broken) + optional fix_url when broken. Consumed by
      web/app/api/methodology to power the home-page trust strip.
  - name: peg
    description: >-
      Mint-keyed peg-signal feed for tokens.xyz / aggregator consumption.
      Public, no auth, cacheable. Honest beta status via `calibration` +
      `monitoring_only`; freshness via `as_of` + `stale`.
paths:
  /v1/me/webhooks/{id}:
    patch:
      tags:
        - Webhooks
      summary: |-
        `PATCH /v1/me/webhooks/{id}` — partial update of a subscription
        keeping its `id` (and so its `webhook_deliveries` history) intact.
      description: |-
        The previous "delete + re-create" workflow orphaned the audit
        trail under a new id. This endpoint keeps continuity for
        dashboards and replay.

        Returns the full webhook row after the update. 404 if the id
        belongs to a different user (no existence-leak).
      operationId: patch_webhook
      parameters:
        - name: id
          in: path
          description: Webhook subscription id
          required: true
          schema:
            type: string
            format: uuid
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PatchWebhook'
        required: true
      responses:
        '200':
          description: Updated webhook
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Webhook'
        '400':
          description: Validation error (bad url, threshold out of range)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError'
        '401':
          description: Caller is not authenticated
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError'
        '404':
          description: Webhook subscription not found under caller's user
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError'
      security:
        - telegram_jwt: []
components:
  schemas:
    PatchWebhook:
      type: object
      properties:
        channel_type:
          type:
            - string
            - 'null'
          description: |-
            Override the channel flavor (`"generic"`|`"discord"`|`"slack"`) when
            auto-detection guessed wrong. Omit to leave unchanged.
        is_active:
          type:
            - boolean
            - 'null'
          description: |-
            Toggle active state without deleting the row. Useful for
            pause-then-resume during receiver maintenance.
        threshold_bps:
          type:
            - integer
            - 'null'
          format: int32
          description: |-
            Replace the alert threshold (1..10000 bps). Omit to leave
            unchanged.
        url:
          type:
            - string
            - 'null'
          description: |-
            Replace the destination URL. SSRF + length guards re-applied;
            omit to leave unchanged.
    Webhook:
      type: object
      required:
        - id
        - url
        - asset
        - threshold_bps
        - is_active
        - channel_type
      properties:
        asset:
          type: string
        channel_type:
          type: string
          description: >-
            `"generic"` | `"discord"` | `"slack"`. Auto-detected from the URL on

            create when omitted; overridable via PATCH. Decides how alerts
            render

            (Discord embed / Slack attachment / generic signed JSON).
        id:
          type: string
          format: uuid
        is_active:
          type: boolean
        threshold_bps:
          type: integer
          format: int32
        url:
          type: string
    ApiError:
      type: object
      required:
        - error
        - message
      properties:
        asset:
          type:
            - string
            - 'null'
          description: The offending asset symbol, present only for asset-scoped errors.
        error:
          type: string
          description: Stable, machine-readable code. Branch on this, never on `message`.
        message:
          type: string
          description: >-
            Human-readable explanation. NOT stable — for humans/logs; do not
            parse.
  securitySchemes:
    telegram_jwt:
      type: http
      scheme: bearer
      bearerFormat: JWT
      description: >-
        Session JWT issued by `POST /v1/auth/telegram` (Telegram Login Widget)
        or `POST /v1/auth/magic/consume`. Send as `Authorization: Bearer <jwt>`.

````