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

# Revoke a webhook subscription

> Soft-revoke a subscription. After revocation, no further events are delivered to its URL. The row is retained for audit purposes; you can still fetch it with `GET /v1/webhooks/{id}` and see its `revoked_at` timestamp.

Revoking an already-revoked subscription is a no-op that returns `{"status": "already_revoked"}`.

### Revoke a webhook subscription

**Method:** `DELETE` | **Endpoint:** `/v1/webhooks/{subscription_id}`

**Description:** Stop sending events to a subscription. Revoking twice is safe.

**Authentication:** API key in the `Authorization` header: `Authorization: Bearer ib_pk_<64 hex>`. Create keys in the dashboard under **Settings → API access**.

#### Request Body

None. The path parameter `subscription_id` is the subscription's ID (a UUID).

```bash cURL theme={null}
curl -X DELETE https://api.ibubbl.com/v1/webhooks/4a5b6c7d-8e9f-0a1b-2c3d-4e5f6a7b8c9d \
  -H "Authorization: Bearer $IBUBBL_API_KEY"
```

```typescript TypeScript theme={null}
const subscriptionId = "4a5b6c7d-8e9f-0a1b-2c3d-4e5f6a7b8c9d";

const res = await fetch(`https://api.ibubbl.com/v1/webhooks/${subscriptionId}`, {
  method: "DELETE",
  headers: { Authorization: `Bearer ${process.env.IBUBBL_API_KEY}` },
  signal: AbortSignal.timeout(10_000),
});

const data = await res.json();
if (!res.ok) throw new Error(`${res.status} ${data.detail?.error}`);
console.log(data.status); // "revoked" or "already_revoked"
```

```python Python theme={null}
import os

import requests

API_KEY = os.environ["IBUBBL_API_KEY"]

subscription_id = "4a5b6c7d-8e9f-0a1b-2c3d-4e5f6a7b8c9d"

res = requests.delete(
    f"https://api.ibubbl.com/v1/webhooks/{subscription_id}",
    headers={"Authorization": f"Bearer {API_KEY}"},
    timeout=10,
)
data = res.json()
if not res.ok:
    raise RuntimeError(f"{res.status_code} {data['detail']['error']}")
print(data["status"])  # "revoked" or "already_revoked"
```

```json 200 OK theme={null}
{
  "status": "revoked"
}
```

```json 404 Not Found theme={null}
{
  "detail": {
    "error": "not_found",
    "message": "Webhook subscription not found"
  }
}
```

#### Responses

**200 OK**

```json theme={null}
{
  "status": "revoked"
}
```

A subscription that was already revoked returns `{"status": "already_revoked"}`.

**401 Unauthorized**

```json theme={null}
{
  "detail": {
    "error": "unauthorized",
    "message": "Invalid or revoked API key"
  }
}
```

Other `message` values: `Authorization Bearer token required`, `API key required (use Authorization: Bearer ib_pk_…)`, `Invalid API key format`, `Customer account not found`.

**403 Forbidden**

```json theme={null}
{
  "detail": {
    "error": "forbidden",
    "message": "Account is suspended"
  }
}
```

Your iBubbl account is suspended or cancelled (`message` is then `Account is cancelled`). Every API-key endpoint returns this.

**404 Not Found**

```json theme={null}
{
  "detail": {
    "error": "not_found",
    "message": "Webhook subscription not found"
  }
}
```

Also returned for a malformed ID and for a subscription that belongs to another account.


## OpenAPI

````yaml DELETE /v1/webhooks/{subscription_id}
openapi: 3.1.0
info:
  title: iBubbl API
  version: v1
  description: >-
    The iBubbl REST API. Send iMessages, manage contacts, and subscribe to
    webhook events from your backend.


    Authenticate every request with a Bearer API key (`ib_pk_<hex>`) — generate
    one from your dashboard's API Access settings. See the
    [Authentication](/authentication) guide.
servers:
  - url: https://api.ibubbl.com
security:
  - BearerAuth: []
tags:
  - name: Messages
  - name: Contacts
  - name: Conversations
  - name: Webhooks
  - name: Inbound Events
paths:
  /v1/webhooks/{subscription_id}:
    delete:
      tags:
        - Webhooks
      summary: Revoke a webhook subscription
      description: >-
        Soft-revoke a subscription. After revocation, no further events are
        delivered to its URL. The row is retained for audit purposes; you can
        still fetch it with `GET /v1/webhooks/{id}` and see its `revoked_at`
        timestamp.


        Revoking an already-revoked subscription is a no-op that returns
        `{"status": "already_revoked"}`.
      operationId: v1_revoke_webhook_v1_webhooks__subscription_id__delete
      parameters:
        - name: subscription_id
          in: path
          required: true
          schema:
            type: string
            title: Subscription Id
      responses:
        '200':
          description: Revocation result.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WebhookRevokeResponse'
        '401':
          description: Missing or invalid API key.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError'
        '403':
          description: '`forbidden`: your iBubbl account is suspended or cancelled.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError'
        '404':
          description: Subscription not found.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError'
components:
  schemas:
    WebhookRevokeResponse:
      properties:
        status:
          type: string
          enum:
            - revoked
            - already_revoked
          title: Status
          description: >-
            `revoked` if this call performed the revocation; `already_revoked`
            if the subscription was already revoked.
      type: object
      required:
        - status
      title: WebhookRevokeResponse
      example:
        status: revoked
    ApiError:
      properties:
        detail:
          allOf:
            - $ref: '#/components/schemas/ApiErrorDetail'
          description: >-
            Error envelope. Every `/v1` error response wraps a stable `error`
            code and a human-readable `message` in `detail`.
      type: object
      required:
        - detail
      title: ApiError
      example:
        detail:
          error: validation_error
          message: >-
            Exactly one of contact_id, to, chat_guid, or participant_phones is
            required
    ApiErrorDetail:
      properties:
        error:
          type: string
          title: Error
          description: >-
            Stable machine-readable error code. Use this for branching logic in
            your integration; the human-readable `message` is subject to change.
            See the Error Reference for the catalog.
          examples:
            - validation_error
        message:
          type: string
          title: Message
          description: Human-readable description of what went wrong.
          examples:
            - >-
              Exactly one of contact_id, to, chat_guid, or participant_phones is
              required
      type: object
      required:
        - error
        - message
      title: ApiErrorDetail
  securitySchemes:
    BearerAuth:
      type: http
      scheme: bearer
      bearerFormat: ib_pk_<64 hex>
      description: >-
        Bearer API key. Format: `Authorization: Bearer ib_pk_<64 hex>` (70
        characters). Generate keys from Dashboard → Settings → API Access.

````

This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.