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

# Delete Webhook

> Permanently remove a webhook endpoint

## Overview

The Delete Webhook endpoint permanently removes a registered webhook. After deletion, you'll stop receiving notifications for transactions. This action cannot be undone.

<Warning>
  **Permanent Action:** Deleting a webhook is irreversible. Consider [disabling the webhook](/api-reference/webhooks/update) instead if you might need it again later.
</Warning>

## Endpoint

```
DELETE https://webhooks.sbx.moduluslabs.io/v1/webhooks/{id}
```

## Authentication

This endpoint requires [HTTP Basic Authentication](/docs/qr/authentication) using your Secret Key.

```
Authorization: Basic {base64(secret_key:)}
```

## Request

### Path Parameters

<ParamField path="id" type="integer" required>
  The unique identifier of the webhook to delete. You can get this ID from the [Get Webhooks API](/api-reference/webhooks/list) or from the response when you created the webhook.

  **Example:** `123`
</ParamField>

### Headers

| Header          | Value                         | Required |
| --------------- | ----------------------------- | -------- |
| `Authorization` | `Basic {base64(secret_key:)}` | Yes      |

### Request Body

This endpoint does not require a request body.

## Response

### Success Response

**Status Code:** `204 No Content`

Returns an empty response body. The webhook has been successfully deleted.

<Check>
  **Success:** A `204` status code means the webhook was deleted. You'll no longer receive notifications at that URL.
</Check>

<ResponseExample>
  ```text 204 No Content theme={null}
  ```

  ```json 401 Unauthorized theme={null}
  {
    "code": "10000013",
    "error": "Invalid API Key",
    "referenceNumber": "097bf60a-bdab-40bf-b615-3c0eae693b86"
  }
  ```

  ```json 404 Not Found theme={null}
  {
    "code": "20000004",
    "error": "Webhook not found",
    "referenceNumber": "097bf60a-bdab-40bf-b615-3c0eae693b86"
  }
  ```

  ```json 500 Internal Server Error theme={null}
  {
    "code": "10000001",
    "error": "An unexpected error occurred. Please try again later.",
    "referenceNumber": "097bf60a-bdab-40bf-b615-3c0eae693b86"
  }
  ```
</ResponseExample>

### Error Responses

<AccordionGroup>
  <Accordion title="401 Unauthorized" icon="key">
    **Status Code:** `401`

    **Cause:** Invalid or missing authentication credentials

    **Response Example:**

    ```json theme={null}
    {
      "code": "20000001",
      "error": "Invalid or missing secret key",
      "referenceNumber": "abc123-def456-ghi789"
    }
    ```

    **Solution:**

    * Verify your secret key is correct
    * Ensure Authorization header format: `Basic {base64(secret_key:)}`
    * Check you're using the right environment (sandbox vs production)
  </Accordion>

  <Accordion title="404 Not Found" icon="circle-xmark">
    **Status Code:** `404`

    **Cause:** Webhook ID does not exist or was already deleted

    **Response Example:**

    ```json theme={null}
    {
      "code": "20000004",
      "error": "Webhook not found",
      "referenceNumber": "abc123-def456-ghi789"
    }
    ```

    **Solutions:**

    * Verify the webhook ID is correct
    * Use [Get Webhooks API](/api-reference/webhooks/list) to find valid webhook IDs
    * Check if the webhook was already deleted
  </Accordion>

  <Accordion title="500 Internal Server Error" icon="server">
    **Status Code:** `500`

    **Cause:** Unexpected server error

    **Solution:**

    * Retry the request
    * If the issue persists, contact Modulus Labs support
  </Accordion>
</AccordionGroup>

<RequestExample>
  ```bash cURL theme={null}
  curl -X DELETE https://webhooks.sbx.moduluslabs.io/v1/webhooks/123 \
    -u sk_your_secret_key:
  ```

  ```javascript Node.js theme={null}
  const axios = require('axios');

  const SECRET_KEY = process.env.MODULUS_SECRET_KEY;

  async function deleteWebhook(webhookId) {
    try {
      const response = await axios.delete(
        `https://webhooks.sbx.moduluslabs.io/v1/webhooks/${webhookId}`,
        {
          auth: {
            username: SECRET_KEY,
            password: ''
          }
        }
      );

      if (response.status === 204) {
        console.log(` Webhook ${webhookId} deleted successfully`);
        return true;
      }
    } catch (error) {
      if (error.response) {
        console.error(' Error:', error.response.status);
        console.error('Details:', error.response.data);
      } else {
        console.error(' Request failed:', error.message);
      }
      throw error;
    }
  }

  deleteWebhook(123);
  ```

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

  SECRET_KEY = os.getenv('MODULUS_SECRET_KEY')

  def delete_webhook(webhook_id):
      try:
          response = requests.delete(
              f'https://webhooks.sbx.moduluslabs.io/v1/webhooks/{webhook_id}',
              auth=(SECRET_KEY, '')
          )

          response.raise_for_status()

          if response.status_code == 204:
              print(f' Webhook {webhook_id} deleted successfully')
              return True

      except requests.exceptions.HTTPError as e:
          print(f' Error: {e.response.status_code}')
          print('Details:', e.response.json())
          raise
      except requests.exceptions.RequestException as e:
          print(f' Request failed: {str(e)}')
          raise

  if __name__ == '__main__':
      delete_webhook(123)
  ```

  ```php PHP theme={null}
  <?php

  $secretKey = getenv('MODULUS_SECRET_KEY');
  $webhookId = 123;

  $ch = curl_init("https://webhooks.sbx.moduluslabs.io/v1/webhooks/{$webhookId}");
  curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
  curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
  curl_setopt($ch, CURLOPT_USERPWD, $secretKey . ':');

  $response = curl_exec($ch);
  $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
  curl_close($ch);

  if ($httpCode === 204) {
      echo " Webhook {$webhookId} deleted successfully\n";
  } else {
      echo " Error: HTTP $httpCode\n";
      echo "Details: $response\n";
  }
  ```

  ```java Java theme={null}
  import java.net.http.*;
  import java.net.URI;
  import java.util.Base64;

  public class DeleteWebhookExample {
      public static void main(String[] args) throws Exception {
          String secretKey = System.getenv("MODULUS_SECRET_KEY");
          int webhookId = 123;

          // Create HTTP client
          HttpClient client = HttpClient.newHttpClient();

          // Encode credentials
          String auth = secretKey + ":";
          String encodedAuth = Base64.getEncoder().encodeToString(auth.getBytes());

          // Build request
          HttpRequest request = HttpRequest.newBuilder()
              .uri(URI.create("https://webhooks.sbx.moduluslabs.io/v1/webhooks/" + webhookId))
              .header("Authorization", "Basic " + encodedAuth)
              .DELETE()
              .build();

          // Send request
          HttpResponse<String> response = client.send(
              request,
              HttpResponse.BodyHandlers.ofString()
          );

          if (response.statusCode() == 204) {
              System.out.println(" Webhook " + webhookId + " deleted successfully");
          } else {
              System.out.println(" Error: HTTP " + response.statusCode());
              System.out.println("Details: " + response.body());
          }
      }
  }
  ```

  ```go Go theme={null}
  package main

  import (
      "fmt"
      "io"
      "net/http"
      "os"
  )

  func deleteWebhook(webhookID int) error {
      secretKey := os.Getenv("MODULUS_SECRET_KEY")

      url := fmt.Sprintf("https://webhooks.sbx.moduluslabs.io/v1/webhooks/%d", webhookID)
      req, err := http.NewRequest("DELETE", url, nil)
      if err != nil {
          return err
      }

      req.SetBasicAuth(secretKey, "")

      client := &http.Client{}
      resp, err := client.Do(req)
      if err != nil {
          return err
      }
      defer resp.Body.Close()

      if resp.StatusCode == 204 {
          fmt.Printf(" Webhook %d deleted successfully\n", webhookID)
          return nil
      }

      body, _ := io.ReadAll(resp.Body)
      fmt.Printf(" Error: HTTP %d\n", resp.StatusCode)
      fmt.Printf("Details: %s\n", string(body))
      return fmt.Errorf("HTTP %d", resp.StatusCode)
  }

  func main() {
      err := deleteWebhook(123)
      if err != nil {
          fmt.Printf("Failed to delete webhook: %v\n", err)
      }
  }
  ```

  ```csharp C# / .NET theme={null}
  // Required NuGet package: jose-jwt (for decrypting response if needed)
  using System;
  using System.Net.Http;
  using System.Net.Http.Headers;
  using System.Text;
  using System.Text.Json;
  using System.Threading.Tasks;
  using Jose;

  class Program
  {
      static async Task Main(string[] args)
      {
          await DeleteWebhook("a78efd32-de3b-4854-b599-11ae9f98f97e");
      }

      static async Task<bool> DeleteWebhook(string webhookId)
      {
          // Get credentials from environment variables
          var secretKey = Environment.GetEnvironmentVariable("MODULUS_SECRET_KEY");
          var encryptionKey = Environment.GetEnvironmentVariable("MODULUS_ENCRYPTION_KEY");

          if (string.IsNullOrEmpty(secretKey))
          {
              Console.WriteLine("Error: MODULUS_SECRET_KEY environment variable not set");
              return false;
          }

          try
          {
              // Create HTTP client with Basic Authentication
              using var client = new HttpClient();
              var credentials = Convert.ToBase64String(
                  Encoding.ASCII.GetBytes($"{secretKey}:")
              );
              client.DefaultRequestHeaders.Authorization =
                  new AuthenticationHeaderValue("Basic", credentials);

              // Send DELETE request
              var response = await client.DeleteAsync(
                  $"https://webhooks.sbx.moduluslabs.io/v1/webhooks/{webhookId}"
              );

              var responseBody = await response.Content.ReadAsStringAsync();

              // API returns 200 OK with encrypted response (not 204 No Content)
              if (response.IsSuccessStatusCode)
              {
                  Console.WriteLine($"Webhook {webhookId} deleted successfully");

                  // Optionally decrypt the response for confirmation
                  if (!string.IsNullOrEmpty(encryptionKey) && responseBody.Contains("Token"))
                  {
                      var keyBytes = Base64UrlDecode(encryptionKey);
                      var encryptedResponse = JsonSerializer.Deserialize<JsonElement>(responseBody);
                      var responseToken = encryptedResponse
                          .GetProperty("response")
                          .GetProperty("Token")
                          .GetString();
                      var decryptedJson = JWT.Decode(responseToken, keyBytes);
                      Console.WriteLine($"Response: {decryptedJson}");
                  }

                  return true;
              }
              else
              {
                  Console.WriteLine($"Error: HTTP {(int)response.StatusCode}");
                  Console.WriteLine($"Details: {responseBody}");
                  return false;
              }
          }
          catch (Exception e)
          {
              Console.WriteLine($"Error: {e.Message}");
              return false;
          }
      }

      // Base64URL decoding helper
      static byte[] Base64UrlDecode(string input)
      {
          var base64 = input.Replace('-', '+').Replace('_', '/');
          switch (base64.Length % 4)
          {
              case 2: base64 += "=="; break;
              case 3: base64 += "="; break;
          }
          return Convert.FromBase64String(base64);
      }
  }
  ```
</RequestExample>

## Use Cases

<AccordionGroup>
  <Accordion title="Decommission Old Webhook" icon="trash">
    Remove webhooks for decommissioned services or endpoints:

    ```javascript theme={null}
    // Get all webhooks
    const webhooks = await getWebhooks();

    // Find webhooks for old domain
    const oldWebhooks = webhooks.filter(w =>
      w.webhookUrl.includes('old-domain.com')
    );

    // Delete them
    for (const webhook of oldWebhooks) {
      await deleteWebhook(webhook.id);
      console.log(`Deleted old webhook: ${webhook.webhookUrl}`);
    }
    ```
  </Accordion>

  <Accordion title="Clean Up Test Webhooks" icon="broom">
    Remove webhooks created during development and testing:

    ```javascript theme={null}
    const webhooks = await getWebhooks();

    // Find test webhooks
    const testWebhooks = webhooks.filter(w =>
      w.webhookUrl.includes('localhost') ||
      w.webhookUrl.includes('ngrok') ||
      w.webhookUrl.includes('test')
    );

    // Delete test webhooks
    for (const webhook of testWebhooks) {
      await deleteWebhook(webhook.id);
      console.log(`Deleted test webhook: ${webhook.id}`);
    }
    ```
  </Accordion>

  <Accordion title="Replace Webhook Configuration" icon="repeat">
    Delete old webhook before creating a new one with the same URL:

    ```javascript theme={null}
    const newUrl = 'https://api.example.com/webhooks/modulus';

    // Check if webhook with this URL already exists
    const webhooks = await getWebhooks();
    const existing = webhooks.find(w => w.webhookUrl === newUrl);

    if (existing) {
      // Delete old webhook
      await deleteWebhook(existing.id);
      console.log('Deleted existing webhook');
    }

    // Create new webhook
    const newWebhook = await createWebhook(
      newUrl,
      ['QRPH_SUCCESS', 'QRPH_DECLINED'],
      'ENABLED'
    );

    console.log('Created new webhook:', newWebhook.id);
    ```
  </Accordion>

  <Accordion title="Remove Duplicate Webhooks" icon="clone">
    Clean up accidentally created duplicate webhooks:

    ```javascript theme={null}
    const webhooks = await getWebhooks();

    // Group webhooks by URL
    const grouped = webhooks.reduce((acc, webhook) => {
      acc[webhook.webhookUrl] = acc[webhook.webhookUrl] || [];
      acc[webhook.webhookUrl].push(webhook);
      return acc;
    }, {});

    // Delete duplicates (keep the newest)
    for (const url in grouped) {
      const duplicates = grouped[url];

      if (duplicates.length > 1) {
        // Sort by creation date, keep newest
        duplicates.sort((a, b) =>
          new Date(b.createdAt) - new Date(a.createdAt)
        );

        // Delete older duplicates
        for (let i = 1; i < duplicates.length; i++) {
          await deleteWebhook(duplicates[i].id);
          console.log(`Deleted duplicate webhook: ${duplicates[i].id}`);
        }
      }
    }
    ```
  </Accordion>

  <Accordion title="Switch Environments" icon="code-branch">
    Remove sandbox webhooks when migrating to production:

    ```javascript theme={null}
    // Get sandbox webhooks
    const sandboxWebhooks = await getSandboxWebhooks();

    // Delete all sandbox webhooks
    for (const webhook of sandboxWebhooks) {
      await deleteWebhook(webhook.id);
      console.log(`Deleted sandbox webhook: ${webhook.id}`);
    }

    console.log('Sandbox webhooks cleaned up, ready for production');
    ```
  </Accordion>
</AccordionGroup>

## Best Practices

<CardGroup cols={2}>
  <Card title="Disable Before Deleting" icon="toggle-off">
    Disable webhook first, monitor for issues, then delete:

    ```javascript theme={null}
    // Step 1: Disable webhook
    await updateWebhook(webhookId, { status: 'DISABLED' });
    console.log('Webhook disabled');

    // Step 2: Monitor for 24 hours...
    await wait(86400000);

    // Step 3: Delete if no issues
    await deleteWebhook(webhookId);
    console.log('Webhook deleted');
    ```
  </Card>

  <Card title="Verify Before Deleting" icon="circle-check">
    Confirm webhook details before deletion:

    ```javascript theme={null}
    const webhooks = await getWebhooks();
    const webhook = webhooks.find(w => w.id === webhookId);

    console.log('About to delete:', webhook.webhookUrl);

    // Require confirmation in production
    if (process.env.NODE_ENV === 'production') {
      const confirmed = await promptConfirmation(
        `Delete webhook ${webhook.webhookUrl}?`
      );

      if (!confirmed) {
        console.log('Deletion cancelled');
        return;
      }
    }

    await deleteWebhook(webhookId);
    ```
  </Card>

  <Card title="Log Deletions" icon="file-lines">
    Track webhook deletions for audit purposes:

    ```javascript theme={null}
    async function deleteWebhookWithAudit(webhookId) {
      // Get webhook details before deleting
      const webhooks = await getWebhooks();
      const webhook = webhooks.find(w => w.id === webhookId);

      // Delete webhook
      await deleteWebhook(webhookId);

      // Log deletion
      await db.webhookAudit.insert({
        action: 'deleted',
        webhookId: webhookId,
        webhookUrl: webhook.webhookUrl,
        deletedAt: new Date(),
        deletedBy: getCurrentUser()
      });

      console.log(`Webhook ${webhookId} deleted and logged`);
    }
    ```
  </Card>

  <Card title="Consider Alternatives" icon="lightbulb">
    Ask yourself: Should I disable instead of delete?

    **Disable** if:

    * You might need the webhook again
    * Temporarily troubleshooting
    * Server maintenance

    **Delete** if:

    * Service permanently decommissioned
    * Webhook created by mistake
    * Cleaning up old configurations
  </Card>
</CardGroup>

## Safety Checklist

Before deleting a webhook in production:

<Steps>
  <Step title="Verify Webhook Details">
    * [ ] Confirm webhook ID is correct
    * [ ] Verify webhook URL matches expectations
    * [ ] Check webhook is not actively used
  </Step>

  <Step title="Assess Impact">
    * [ ] Determine which services use this webhook
    * [ ] Check if any orders are pending webhook notifications
    * [ ] Verify alternative webhooks exist (if needed)
  </Step>

  <Step title="Notify Stakeholders">
    * [ ] Alert team about webhook deletion
    * [ ] Update documentation
    * [ ] Inform monitoring systems
  </Step>

  <Step title="Create Backup">
    * [ ] Save webhook configuration
    * [ ] Document actions and status
    * [ ] Store for future reference
  </Step>

  <Step title="Execute Deletion">
    * [ ] Disable webhook first
    * [ ] Monitor for 24-48 hours
    * [ ] Delete if no issues detected
  </Step>

  <Step title="Verify Deletion">
    * [ ] Confirm webhook no longer appears in list
    * [ ] Check for any error logs or alerts
    * [ ] Update internal documentation
  </Step>
</Steps>

## What Happens After Deletion?

<AccordionGroup>
  <Accordion title="Immediate Effects" icon="bolt">
    * Webhook is immediately removed from your account
    * No more notifications will be sent to that URL
    * Webhook ID becomes invalid and cannot be reused
    * Cannot be undone - must create a new webhook if needed
  </Accordion>

  <Accordion title="Pending Notifications" icon="clock">
    * Webhooks scheduled for retry may still be delivered
    * New transactions will not trigger notifications
    * In-flight webhook deliveries may complete
  </Accordion>

  <Accordion title="Recovery" icon="rotate-left">
    If you deleted a webhook by mistake:

    1. Create a new webhook with the same URL
    2. Note: New webhook will have a different ID
    3. Update any stored references to use the new ID
    4. Test the new webhook with Simulate API
  </Accordion>
</AccordionGroup>

## Troubleshooting

<AccordionGroup>
  <Accordion title="404 Error When Deleting" icon="circle-xmark">
    **Symptom:** Receive 404 when trying to delete webhook

    **Possible Causes:**

    * Webhook was already deleted
    * Wrong webhook ID
    * Using wrong secret key (different merchant account)

    **Solutions:**

    * Call [Get Webhooks API](/api-reference/webhooks/list) to verify webhook exists
    * Check webhook ID is correct
    * Verify you're using the correct secret key
  </Accordion>

  <Accordion title="Deleted Webhook Still Receiving Notifications" icon="bell">
    **Symptom:** Webhook URL still receives POST requests after deletion

    **Possible Causes:**

    * Retries for transactions that occurred before deletion
    * Different webhook with same URL
    * Caching or replication delay (rare)

    **Solutions:**

    * Wait for pending retries to complete (max 45 minutes)
    * Check if another webhook is using the same URL
    * Verify deletion succeeded by listing webhooks
  </Accordion>

  <Accordion title="Cannot Delete Last Webhook" icon="triangle-exclamation">
    **Symptom:** Want to delete all webhooks but concerned about losing notifications

    **Consideration:**
    Webhooks are REQUIRED for QR Ph integration. Deleting all webhooks means you won't receive transaction notifications.

    **Solutions:**

    * Ensure you have at least one active webhook
    * Create a new webhook before deleting the last one
    * Consider disabling instead of deleting
  </Accordion>
</AccordionGroup>

## Comparison: Delete vs Disable

| Action      | Permanent | Reversible | Preserves Config | Use Case                          |
| ----------- | --------- | ---------- | ---------------- | --------------------------------- |
| **Delete**  | Yes       | No         | No               | Permanently remove unused webhook |
| **Disable** | No        | Yes        | Yes              | Temporarily pause notifications   |

<Tip>
  **Recommendation:** Use [Update Webhook](/api-reference/webhooks/update) to disable webhooks during maintenance instead of deleting them. This preserves your configuration and makes it easy to resume.
</Tip>

## Next Steps

<CardGroup cols={2}>
  <Card title="Create Webhook" icon="plus" href="/api-reference/webhooks/create">
    Register a new webhook to replace the deleted one
  </Card>

  <Card title="Update Webhook" icon="pen" href="/api-reference/webhooks/update">
    Disable webhook instead of deleting
  </Card>

  <Card title="Get Webhooks" icon="list" href="/api-reference/webhooks/list">
    View remaining registered webhooks
  </Card>

  <Card title="Webhooks Overview" icon="book" href="/docs/webhooks/overview">
    Learn about the Webhook API
  </Card>
</CardGroup>


## OpenAPI

````yaml DELETE /v1/webhooks/{id}
openapi: 3.1.0
info:
  title: Modulus Labs Webhooks API
  description: >-
    API for managing webhook endpoints to receive QR Ph transaction
    notifications
  version: 1.0.0
servers:
  - url: https://webhooks.sbx.moduluslabs.io
    description: Sandbox
security: []
paths:
  /v1/webhooks/{id}:
    delete:
      tags:
        - Webhooks
      summary: Delete Webhook
      description: >-
        Permanently remove a webhook endpoint. After deletion, you'll stop
        receiving notifications for transactions. This action cannot be undone.
      operationId: deleteWebhook
      parameters:
        - $ref: '#/components/parameters/WebhookIdParam'
      responses:
        '200':
          description: Webhook deleted successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DeleteWebhookResponse'
              example:
                message: Webhook deleted successfully
        '401':
          description: Unauthorized - Invalid or missing credentials
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '404':
          description: Not Found - Webhook with specified ID does not exist
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
      security:
        - basicAuth: []
components:
  parameters:
    WebhookIdParam:
      name: id
      in: path
      required: true
      description: The unique identifier of the webhook
      schema:
        type: string
      example: a78efd32-de3b-4854-b599-11ae9f98f97e
  schemas:
    DeleteWebhookResponse:
      type: object
      properties:
        message:
          type: string
          description: Confirmation message
    ErrorResponse:
      type: object
      properties:
        statusCode:
          type: integer
          description: HTTP status code
        message:
          type: string
          description: Error message
        error:
          type: string
          description: Error type
  securitySchemes:
    basicAuth:
      type: http
      scheme: basic
      description: >-
        HTTP Basic Authentication using your Secret Key as the username and an
        empty password

````