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

# Delete a webhook

> Delete an owned subscription and stop future callback attempts once deletion is observed. Requests already in progress may finish. Deleted subscriptions are no longer available through management endpoints.



## OpenAPI

````yaml https://data.gravia.trade/ delete /v1/webhooks/{id}
openapi: 3.0.3
info:
  title: Gravia Data API
  version: 0.3.0
  contact:
    name: Gravia Data API docs
    url: https://docs.gravia.trade
  description: >
    HTTP and WebSocket API for Polymarket analytics: crypto up/down

    markets and their reference prices, the market category catalog, curated

    per-user activity feeds, trade markouts and counterparty breakdowns,

    positions and portfolio performance, profiles, taker tiers, and a live

    transaction stream.


    ## Base URL


    Send every request to `https://data.gravia.trade`. Application endpoints are

    versioned in the URL under `/api/v1`, and versioned responses carry an

    `X-API-Version: v1` header. `/health` sits outside the versioned surface and

    needs no credentials.


    Webhook management uses `/v1/webhooks` with the same `Authorization`

    header as other endpoints. Supported events are `trader_new_trade` and

    `close_to_bond`. See Webhooks for subscription management and callbacks.


    ## Authentication


    Pass your API key verbatim in the `Authorization` header. There is no

    `Bearer` prefix and no other scheme:


    ```http

    GET /api/v1/users/profile?user=0xabc… HTTP/1.1

    Host: data.gravia.trade

    Authorization: <your-api-key>

    ```


    WebSocket endpoints also accept `?api_key=<your-api-key>`, because browsers

    cannot set request headers on a WebSocket handshake. The value is redacted

    from access logs and usage records, but prefer the header wherever you can

    set one. A missing or unrecognized key returns `401` with the

    `unauthorized` code.


    ## Rate limits


    Limits are enforced per API key over sliding windows, with a global default

    and optional per-endpoint policies. Every successful response reports the

    currently most restrictive policy:


    | Header | Meaning |

    | --- | --- |

    | `RateLimit-Limit` | Requests allowed in that policy's window |

    | `RateLimit-Remaining` | Requests still available |

    | `RateLimit-Reset` | Whole seconds until capacity returns |


    Exceeding a policy returns `429` with the `rate_limit_exceeded` code and a

    `Retry-After` header in whole seconds. Back off for that long rather than

    retrying immediately.


    WebSocket streams also enforce a concurrent connection allowance
    independently

    per API key and stream. Exceeding the allowance returns `429` with the

    distinct `stream_limit_reached` code. That is a concurrency limit, not a

    rate limit — close an existing stream instead of backing off.


    ## Errors


    Every non-2xx response uses the same envelope:


    ```json

    {
      "error": {
        "code": "invalid_request",
        "message": "user is required",
        "request_id": "01f6c90f31d24db3a25c7dba6ca071ee"
      }
    }

    ```


    Branch on `code`, never on `message`. Codes are stable; messages are not.

    The `request_id` also appears in the `X-Request-ID` response header of every

    request, successful or not — quote it when reporting a problem. You may send

    your own `X-Request-ID` to correlate with your logs.


    ## Identifying users


    User endpoints take a `user` value that may be a wallet address or a

    Polymarket username. Addresses resolve through Safe proxy and owner

    relationships to the canonical address the warehouse keys on, so a proxy and

    its owner return the same data. An unknown user returns `404` with the

    `profile_not_found` code.


    The live transaction stream is the exception: it accepts raw addresses only

    and rejects usernames. Resolve a username once through a REST endpoint, then

    stream on the address it returns.


    ## Identifying markets


    Market filters accept a mix of condition IDs, token IDs, market slugs, and

    `category:<dotted-slug>` selectors in a single request. Selectors are ORed

    together, matched exactly, and do not expand to category parents or

    children. See each endpoint's `market_id` parameter for its exact limits.


    ## Pagination


    Paginated endpoints use opaque keyset cursors. Read `page.next_cursor` from

    a response and send it back as `cursor` to fetch the following page;

    `page.has_more` tells you whether another page exists. Cursors encode sort

    position, so keep every other query parameter identical while paging.


    ## Stability


    Operations declare their lifecycle stage in the `x-feature-stage` extension.

    Everything published here is `stable`: fields may be added, but existing

    fields and semantics will not change or disappear without a new API version.

    Treat unrecognized response fields as forward-compatible additions rather

    than errors.
servers:
  - url: https://data.gravia.trade
    description: Production server
security:
  - APIKey: []
tags:
  - name: Webhooks
    description: >
      # Webhooks


      Webhooks send trade notifications to an HTTPS endpoint you control. Create
      a

      subscription, select an event and its filters, then process callbacks as
      trades

      arrive. Webhook management uses `https://data.gravia.trade/v1/webhooks`.

      The current live trade feed covers finalized Polygon Trades V2
      `OrderFilled`

      trades.


      ## Authentication and ownership


      Use the same API key and header as the other API endpoints. Send the key

      verbatim in `Authorization`, without a `Bearer` prefix:


      ```http

      GET /v1/webhooks/events HTTP/1.1

      Host: data.gravia.trade

      Authorization: <your-api-key>

      ```


      Every management operation requires this header. Query-string credentials
      and

      admin credentials are not accepted. The API key that creates a
      subscription

      owns it; other keys cannot manage it and receive `404` for its ID. A
      webhook ID

      is an identifier, not a credential.


      ## Management endpoints


      | Method | Path | Purpose |

      | --- | --- | --- |

      | `POST` | `/v1/webhooks` | Create a subscription. Returns `201`. |

      | `GET` | `/v1/webhooks` | List subscriptions owned by your API key. |

      | `GET` | `/v1/webhooks/events` | List supported events and their
      available filters. |

      | `GET` | `/v1/webhooks/{id}` | Read a subscription's configuration. |

      | `PUT` | `/v1/webhooks/{id}` | Update configuration, pause, or resume a
      subscription. |

      | `DELETE` | `/v1/webhooks/{id}` | Delete a subscription. Returns `data:
      {"deleted": true}`. |

      | `POST` | `/v1/webhooks/{id}/rotate-secret` | Send `{}` to generate a new
      callback signing secret. |

      | `POST` | `/v1/webhooks/{id}/test` | Send `{}` to request one test
      callback. |

      | `GET` | `/v1/webhooks/{id}/logs` | Read terminal delivery results. |


      ### Create a subscription


      Provide a public HTTPS `url` and an `event`. Optional fields are
      `filters`,

      `secret` (a callback signing secret), and `description`. New subscriptions
      are

      active. Signing secrets are separate from the API key used for management.

      Supply a nonempty `secret` to enable callback signatures.


      ```json

      {
        "url": "https://receiver.example/webhooks/trades",
        "event": "trader_new_trade",
        "secret": "replace-with-your-signing-secret",
        "filters": {
          "min_usd_value": 100
        },
        "description": "Trade alerts"
      }

      ```


      ### List, update, pause, and delete


      Listing uses `limit` (default 20, maximum 100) and `offset` (default 0).

      Optional `status` (`active` or `paused`) and `event` parameters filter the
      list.


      Updates are partial: omitted or null fields leave existing values
      unchanged.

      A supplied `filters` object replaces the entire filter set, so include
      every

      filter you want to retain. `filters: {}` removes filters for
      `trader_new_trade`;

      `close_to_bond` still requires at least one threshold. An empty `secret`

      disables callback signing.


      Use `PUT /v1/webhooks/{id}` with `{"status":"paused"}` to pause or

      `{"status":"active"}` to resume or re-enable a subscription. Deletion and

      pausing prevent new callback attempts once the updated state is observed;

      already-started requests can finish. Deleted subscriptions are no longer

      available through management endpoints.


      ### Rotate signing secrets


      Send `{}` to `POST /v1/webhooks/{id}/rotate-secret`. Save the returned
      `secret`;

      ordinary subscription reads expose only `has_secret`, not the secret
      itself.

      Rotation preserves the webhook ID and management API key. Subsequent
      attempts

      use the new secret once the configuration update is observed;
      already-started

      requests may still use the previous secret.


      ### Test callbacks and delivery logs


      Send `{}` to `POST /v1/webhooks/{id}/test`. A test makes one callback
      attempt,

      including while the subscription is paused, and returns `success`,

      `status_code`, `error`, and `duration_ms`. Its event name has a `_test`
      suffix.

      Tests do not consume `one_shot`, change subscription health, or change
      crossing

      state. A `503` response while waiting for the result does not cancel an
      already

      accepted test.


      Logs use `limit` (default 10, maximum 250), optional `from` and `to`
      timestamps

      in Unix milliseconds, and `pagination_key` for the next page. Pass the
      returned

      `pagination.key` when `pagination.has_more` is true. Results are newest
      first;

      `total` counts entries on that page. Only terminal delivery results are
      shown,

      not each retry attempt or test callback. Recent results may take time to
      appear.


      ### Management responses and errors


      Success responses contain `success`, `message`, `data`, and `info`:


      ```json

      {
        "success": true,
        "message": null,
        "data": {},
        "info": {"version": "1.0.0", "credits_consumed": 0}
      }

      ```


      `data` holds the operation's result. Errors contain `success: false`, a

      `message`, and `data: null`. Common statuses are `400` for invalid input,

      `401` for invalid credentials, `404` for an unavailable or unowned
      subscription,

      `429` for rate limiting, and `503` for temporary service unavailability.

      Use the documented rate-limit headers and `Retry-After` when applicable.


      ## Supported events


      ### New trades: `trader_new_trade`


      Receive a callback for each trade that satisfies the subscription's
      filters.

      Supported filters are `wallet_addresses`, `condition_ids`, `event_slugs`,

      `min_usd_value`, `min_price`, `max_price`, `trade_types`,

      `exclude_shortterm_markets`, and `one_shot`.


      For this event, `min_price` and `max_price` define an inclusive price
      range;

      when both are set, `min_price` must not exceed `max_price`. Price refers
      to the

      traded outcome token's own price. Values within an array use OR; different

      filters use AND. Empty arrays are unrestricted. Wallet and condition IDs
      are

      case-insensitive; event slugs and trade types use exact matching. Missing

      metadata required by a filter prevents a match for that subscription.


      ### Close to bond: `close_to_bond`


      The `close_to_bond` event reports trades whose outcome-token price reaches
      a

      configured high or low threshold. Choose whether to receive every
      qualifying

      trade or only the first trade of each threshold crossing.


      #### Settings


      Set `event` to `close_to_bond` and put these settings inside `filters`:


      | Setting | Meaning | Default |

      | --- | --- | --- |

      | `min_price` | Inclusive high threshold: fire when `price >= min_price`.
      | Omitted. |

      | `max_price` | Inclusive low threshold: fire when `price <= max_price`. |
      Omitted. |

      | `trigger_mode` | `every_trade` or `crossing`. | `every_trade`. |

      | `reset_distance` | Absolute price distance from a threshold required to
      rearm that side. Accepted only with `trigger_mode: "crossing"`. | `0.10`
      in crossing mode. |


      Prices use the range 0 to 1: `0.90` means 90 cents, and a reset distance
      of

      `0.10` means 10 cents, not a 10% change relative to the threshold or
      price.


      At least one threshold is required. Thresholds must be between 0 and 1,

      inclusive. If both are set, `min_price` must be greater than `max_price`;

      they define two alternative trigger zones, not a price range. Actual
      trades

      priced exactly 0 or 1 neither fire nor reset a latch.


      In crossing mode, `reset_distance` must be finite, greater than 0 and less

      than 1. Each configured reset boundary must be strictly between 0 and 1:

      `min_price - reset_distance > 0` for the high side, and

      `max_price + reset_distance < 1` for the low side. Unknown modes, invalid

      boundaries, and a reset distance supplied with every-trade mode are
      rejected.

      `trigger_mode` and `reset_distance` are not supported for
      `trader_new_trade`.


      #### Every-trade mode


      `trigger_mode: "every_trade"` fires for every qualifying trade at or
      beyond

      either configured threshold, including successive trades already in the
      same

      zone. Omitting `trigger_mode` preserves this existing behavior.


      #### Crossing mode


      `trigger_mode: "crossing"` maintains an independent latch for each

      subscription and `position_id`. A subscription covering multiple
      positions,

      a whole market, or an event therefore has separate latches for its
      positions.

      The price is the traded outcome token's own price: a Down token trading at

      95 cents is a high-side match.


      The latch starts armed. The first observed trade already at or beyond a

      threshold fires immediately; an earlier trade outside the zone is not
      required.


      - **High side:** a trade at or above `min_price` fires once. Further
      trades on
        that side are suppressed until a trade at or below
        `min_price - reset_distance` rearms it.
      - **Low side:** a trade at or below `max_price` fires once. Further trades
      on
        that side are suppressed until a trade at or above
        `max_price + reset_distance` rearms it.
      - **Opposite side:** reaching the other configured threshold fires
      immediately
        for that trade and switches the latch to that side, even without an observed
        intermediate reset trade. This takes precedence over silently rearming.

      Rearming alone does not send a callback. Each trade can create at most one

      logical event per subscription. Reset distance is measured from the
      configured

      threshold, not from a moving price peak or trough.


      For high = 90 cents, low = 10 cents, and reset distance = 10 cents:


      | Trade price, in processing order | Crossing mode | Every-trade mode |

      | --- | --- | --- |

      | 91 cents | Fire high; latch high. | Fire high. |

      | 95 cents | No event. | Fire high. |

      | 85 cents | No event; high remains latched. | No event. |

      | 80 cents | Rearm high; no event. | No event. |

      | 90 cents | Fire high again; latch high. | Fire high. |

      | 9 cents | Fire low immediately; latch low. | Fire low. |

      | 5 cents | No event. | Fire low. |

      | 20 cents | Rearm low; no event. | No event. |

      | 10 cents | Fire low again; latch low. | Fire low. |


      #### Create or update a subscription


      Example request body for `POST /v1/webhooks`:


      ```json

      {
        "url": "https://receiver.example/webhooks/bond",
        "event": "close_to_bond",
        "filters": {
          "min_price": 0.90,
          "max_price": 0.10,
          "trigger_mode": "crossing",
          "reset_distance": 0.10
        }
      }

      ```


      Add `position_ids` to `filters` to restrict the subscription to particular

      outcome tokens. Other supported scope filters are `condition_ids`,
      `outcomes`

      (case-sensitive), `position_outcome_indices`, `event_slugs`, and

      `exclude_shortterm_markets`. Values within an array use OR; different
      filters

      use AND. Trades must satisfy these filters both to fire and to rearm.
      Missing

      required metadata prevents either transition for the affected subscription
      until the required metadata is available.


      For an update, supplying `filters` replaces the entire filter object;
      retain

      any thresholds and scope filters you still want. Omitting `trigger_mode`
      in

      that replacement selects every-trade mode. Omitting or passing null for
      the

      whole `filters` field leaves the current filters unchanged.


      Changing the event or filters, or resuming a paused subscription, starts a
      new

      armed state. URL, description, and secret changes preserve latch state.

      Inputs already admitted use their captured configuration;

      delivery still respects the subscription's current status and destination.


      #### Callbacks and ordering


      Both modes use the existing signed callback envelope with

      `event: "close_to_bond"`. The triggering trade's `price`, `position_id`,

      `trade_id`, `bond_side` (`high` or `low`), and configured `threshold`
      identify

      the event. `bond_side` describes the price threshold, not the trade's
      Buy/Sell

      `side`. Switching modes does not change the callback shape.


      The matched side and threshold are preserved through queued work and
      retries.

      One crossing creates one logical event; normal HTTP retries can still
      produce

      multiple delivery attempts. Test callbacks use `close_to_bond_test` and do
      not

      read or change the latch.


      Crossings follow trade processing order, not reconstructed blockchain
      order.

      Only observed trades satisfying the filters affect crossing state. Delayed

      trades can therefore affect a later crossing. Normal retry and
      deduplication

      behavior still applies; receiving the same delivery ID again is not a new
      event.


      Crossing state normally survives a matcher restart. State expires by
      default

      30 days after the last firing or side-switch event; ordinary trades do not

      extend this lifetime. Once it expires, the next qualifying trade can fire
      again

      without an intervening price reset. Loss of stored crossing state can also

      rearm a position.


      ## Receiving and verifying callbacks


      Callbacks are HTTPS POST requests containing `id`, `event`, `data`,
      `timestamp`,

      `webhook_id`, and `attempt`. `data` is the event-specific trade payload
      documented

      in the callback schemas. `timestamp` is in Unix milliseconds. `id`
      identifies a

      logical delivery and remains stable across retries; `attempt` starts at 1.


      Headers include `X-Webhook-ID`, `X-Delivery-ID`, `X-Event-Type`, and
      `X-Attempt`.

      If a signing secret is configured, `X-Webhook-Signature` contains

      `sha256=<hex-digest>`. Verify it by computing HMAC-SHA256 over the exact
      raw

      request body using that secret, then comparing the signatures in constant

      time. Do not parse and reserialize the body before verification.


      Return a `2xx` response after accepting a callback. Other statuses and
      transport

      failures retry up to 10 total attempts, with a one-second retry delay and
      a

      30-second request timeout. Exhausting retries disables the subscription's

      current configuration; fix the receiver and set `status` to `active` to
      resume.


      Delivery is at least once. Deduplicate using the delivery `id`, and do not

      assume callbacks will arrive in trade order. `one_shot: true` applies to
      the

      whole subscription: the first claimed delivery is retried as needed, and a

      successful delivery deletes the subscription. It does not mean one
      callback

      per position or per threshold crossing.
  - name: Markets
    description: |
      Crypto up/down markets, their opening reference prices, and the market
      category catalog.
  - name: Users
    description: |
      Per-user analytics: activity feeds, trade markouts, counterparty
      breakdowns, positions, profiles, category metrics, and taker tiers.
  - name: Streams
    description: |
      Long-lived WebSocket feeds carrying on-chain events as they are decoded.
  - name: Operational
    description: Unauthenticated service checks.
paths:
  /v1/webhooks/{id}:
    parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
          format: uuid
    delete:
      tags:
        - Webhooks
      summary: Delete a webhook
      description: >-
        Delete an owned subscription and stop future callback attempts once
        deletion is observed. Requests already in progress may finish. Deleted
        subscriptions are no longer available through management endpoints.
      responses:
        '200':
          description: Success
          content:
            application/json:
              example:
                success: true
                message: null
                data:
                  deleted: true
                info:
                  version: 1.0.0
                  credits_consumed: 0
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    enum:
                      - true
                  message:
                    type: string
                    nullable: true
                  data:
                    type: object
                    properties:
                      deleted:
                        type: boolean
                    required:
                      - deleted
                  info:
                    type: object
                    properties:
                      version:
                        type: string
                      credits_consumed:
                        type: integer
                        format: int64
                    required:
                      - version
                      - credits_consumed
                required:
                  - success
                  - message
                  - data
                  - info
          headers:
            RateLimit-Limit:
              $ref: '#/components/headers/WebhookRateLimitLimit'
            RateLimit-Remaining:
              $ref: '#/components/headers/WebhookRateLimitRemaining'
            RateLimit-Reset:
              $ref: '#/components/headers/WebhookRateLimitReset'
        '400':
          $ref: '#/components/responses/WebhookError400'
        '401':
          $ref: '#/components/responses/WebhookError401'
        '404':
          $ref: '#/components/responses/WebhookError404'
        '429':
          $ref: '#/components/responses/WebhookError429'
        '503':
          $ref: '#/components/responses/WebhookError503'
      security:
        - APIKey: []
components:
  headers:
    WebhookRateLimitLimit:
      schema:
        type: integer
      description: Allowed requests in the active window.
    WebhookRateLimitRemaining:
      schema:
        type: integer
      description: Requests remaining.
    WebhookRateLimitReset:
      schema:
        type: integer
      description: Seconds until the active window resets.
    WebhookRetryAfter:
      schema:
        type: integer
      description: Seconds to wait before retrying.
  responses:
    WebhookError400:
      description: Invalid request
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/EdgeWebhookError'
      headers:
        RateLimit-Limit:
          $ref: '#/components/headers/WebhookRateLimitLimit'
        RateLimit-Remaining:
          $ref: '#/components/headers/WebhookRateLimitRemaining'
        RateLimit-Reset:
          $ref: '#/components/headers/WebhookRateLimitReset'
    WebhookError401:
      description: Invalid API key
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/EdgeWebhookError'
      headers:
        RateLimit-Limit:
          $ref: '#/components/headers/WebhookRateLimitLimit'
        RateLimit-Remaining:
          $ref: '#/components/headers/WebhookRateLimitRemaining'
        RateLimit-Reset:
          $ref: '#/components/headers/WebhookRateLimitReset'
    WebhookError404:
      description: Webhook not found for this API key
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/EdgeWebhookError'
      headers:
        RateLimit-Limit:
          $ref: '#/components/headers/WebhookRateLimitLimit'
        RateLimit-Remaining:
          $ref: '#/components/headers/WebhookRateLimitRemaining'
        RateLimit-Reset:
          $ref: '#/components/headers/WebhookRateLimitReset'
    WebhookError429:
      description: Rate limit exceeded
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/EdgeWebhookError'
      headers:
        RateLimit-Limit:
          $ref: '#/components/headers/WebhookRateLimitLimit'
        RateLimit-Remaining:
          $ref: '#/components/headers/WebhookRateLimitRemaining'
        RateLimit-Reset:
          $ref: '#/components/headers/WebhookRateLimitReset'
        Retry-After:
          $ref: '#/components/headers/WebhookRetryAfter'
    WebhookError503:
      description: Dependency unavailable
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/EdgeWebhookError'
      headers:
        RateLimit-Limit:
          $ref: '#/components/headers/WebhookRateLimitLimit'
        RateLimit-Remaining:
          $ref: '#/components/headers/WebhookRateLimitRemaining'
        RateLimit-Reset:
          $ref: '#/components/headers/WebhookRateLimitReset'
  schemas:
    EdgeWebhookError:
      type: object
      properties:
        success:
          type: boolean
          enum:
            - false
        message:
          type: string
        data:
          nullable: true
      required:
        - success
        - message
        - data
  securitySchemes:
    APIKey:
      type: apiKey
      in: header
      name: Authorization
      description: Raw database-backed API key.

````