> ## 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.

# Update Webhook

> Modify an existing webhook endpoint configuration

## Overview

The Update Webhook endpoint allows you to modify an existing webhook's URL, actions, or status. Use this to change where notifications are sent, adjust which events you receive, or temporarily disable a webhook during maintenance.

## Endpoint

```
PUT 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="string" required>
  The unique identifier of the webhook to update. You can get this ID from the [Get Webhooks API](/api-reference/webhooks/list) or from the response when you created the webhook.

  **Example:** `"a78efd32-de3b-4854-b599-11ae9f98f97e"`
</ParamField>

### Headers

| Header          | Value                         | Required | Description                          |
| --------------- | ----------------------------- | -------- | ------------------------------------ |
| `Authorization` | `Basic {base64(secret_key:)}` | Yes      | HTTP Basic Auth with your secret key |
| `Content-Type`  | `application/json`            | Yes      | Request body format                  |

## Prerequisites

Before updating a webhook, ensure you have:

* Your **Secret Key** for HTTP Basic Authentication
* The **webhook ID** of the webhook you want to update

<Note>
  See the [Encryption Guide](/docs/qr/encryption) to understand how JWE tokens work — you'll need this to **decrypt incoming webhook notifications**, not for the Update Webhook API request itself.
</Note>

### Body Parameters (Encrypted Payload)

<Note>
  The following parameters describe the payload that must be JWE-encrypted into the `Token` field. See the [Encryption Guide](/docs/qr/encryption) for details on creating JWE tokens.
</Note>

<Note>
  All body parameters are optional. Include only the fields you want to update. Fields you don't include will remain unchanged.
</Note>

| Field           | Type   | Required | Description                                                  |
| --------------- | ------ | -------- | ------------------------------------------------------------ |
| `callbackUrl`   | string | No       | New HTTPS URL where Modulus Labs sends webhook notifications |
| `webhookAction` | string | No       | Updated transaction events: `QRPH_SUCCESS`, `QRPH_DECLINED`  |
| `webhookStatus` | string | No       | New status: `ENABLED` or `DISABLED`                          |

<AccordionGroup>
  <Accordion title="callbackUrl" icon="link">
    The new HTTPS URL where Modulus Labs sends webhook notifications. Must be publicly accessible and use HTTPS.

    * **Format:** Valid HTTPS URL
    * **Example:** `"https://api.yourcompany.com/webhooks/success"`
  </Accordion>

  <Accordion title="webhookStatus" icon="toggle-on">
    New webhook status:

    * `ENABLED` - Webhook receives notifications
    * `DISABLED` - Webhook stops receiving notifications (useful during maintenance)

    **Example:** `"DISABLED"`

    <Tip>
      Use `DISABLED` during server maintenance instead of deleting the webhook. This preserves your configuration and makes it easy to resume.
    </Tip>
  </Accordion>
</AccordionGroup>

### Example Payloads (Before Encryption)

<CodeGroup>
  ```json Update URL Only theme={null}
  {
    "callbackUrl": "https://api.yourcompany.com/webhooks/success",
    "webhookStatus": "DISABLED"
  }
  ```

  ```json Update Status Only theme={null}
  {
    "webhookStatus": "DISABLED"
  }
  ```

  ```json Update Actions Only theme={null}
  {
    "webhookAction": "QRPH_SUCCESS"
  }
  ```

  ```json Update Multiple Fields theme={null}
  {
    "callbackUrl": "https://new-domain.com/webhooks/modulus",
    "webhookAction": ["QRPH_SUCCESS", "QRPH_DECLINED"],
    "webhookStatus": "ENABLED"
  }
  ```
</CodeGroup>

## Response

### Success Response

**Status Code:** `200 OK`

Returns the updated webhook object with all current values.

<ResponseField name="id" type="string">
  Unique identifier for the webhook (unchanged).

  **Example:** `"a78efd32-de3b-4854-b599-11ae9f98f97e"`
</ResponseField>

<ResponseField name="webhookAction" type="string">
  String of webhook actions (updated or unchanged).

  **Example:** `"QRPH_SUCCESS"`
</ResponseField>

<ResponseField name="webhookStatus" type="string">
  Current webhook status (updated or unchanged).

  **Example:** `"DISABLED"`
</ResponseField>

<ResponseField name="callbackUrl" type="string">
  The webhook URL (updated or unchanged).

  **Example:** `"https://api.yourcompany.com/webhooks/success"`
</ResponseField>

### Response Example

```json theme={null}
{
  "id": "a78efd32-de3b-4854-b599-11ae9f98f97e",
  "webhookAction": "QRPH_SUCCESS",
  "webhookStatus": "DISABLED",
  "callbackUrl": "https://api.yourcompany.com/webhooks/success",
}
```

<ResponseExample>
  ```json 200 Success theme={null}
  {
    "id": "a78efd32-de3b-4854-b599-11ae9f98f97e",
    "webhookAction": "QRPH_SUCCESS",
    "webhookStatus": "DISABLED",
    "callbackUrl": "https://api.yourcompany.com/webhooks/success",
  }
  ```

  ```json 400 Bad Request theme={null}
  {
    "code": "20000002",
    "error": "Invalid callbackUrl: must be a valid HTTPS URL",
    "referenceNumber": "097bf60a-bdab-40bf-b615-3c0eae693b86"
  }
  ```

  ```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 409 Conflict theme={null}
  {
    "code": "20000003",
    "error": "Webhook URL already exists",
    "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="400 Bad Request" icon="circle-exclamation">
    **Status Code:** `400`

    **Causes:**

    * Invalid webhook URL format
    * Empty actions array
    * Invalid action values
    * Invalid status value

    **Response Example:**

    ```json theme={null}
    {
      "code": "20000002",
      "error": "Invalid callbackUrl: must be a valid HTTPS URL",
      "referenceNumber": "abc123-def456-ghi789"
    }
    ```

    **Solutions:**

    * Ensure callbackUrl uses HTTPS protocol
    * Include at least one valid action if updating actions
    * Verify status is either `ENABLED` or `DISABLED`
  </Accordion>

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

    **Cause:** Invalid or missing authentication credentials

    **Solution:**

    * Verify your secret key is correct
    * Ensure Authorization header format: `Basic {base64(secret_key:)}`
  </Accordion>

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

    **Cause:** Webhook ID does not exist

    **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 deleted
  </Accordion>

  <Accordion title="409 Conflict" icon="clone">
    **Status Code:** `409`

    **Cause:** New webhook URL already registered for this merchant

    **Response Example:**

    ```json theme={null}
    {
      "code": "20000003",
      "error": "Webhook URL already exists",
      "referenceNumber": "abc123-def456-ghi789"
    }
    ```

    **Solution:**

    * Use a different webhook URL, or
    * Delete the existing webhook using that URL first
  </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 (Update Status) theme={null}
  # Note: You need to encrypt the payload first using a script/tool
  # See the Encryption Guide for JWE token creation

  # 1. Create encrypted JWE token containing: {"webhookStatus": "DISABLED"}
  ENCRYPTED_TOKEN="eyJhbGciOiJBMjU2S1ciL..."

  # 2. Make API request
  curl -X PUT https://webhooks.sbx.moduluslabs.io/v1/webhooks/a78efd32-de3b-4854-b599-11ae9f98f97e \
    -u sk_your_secret_key: \
    -H "Content-Type: application/json" \
    -d "{\"request\": {\"Token\": \"$ENCRYPTED_TOKEN\"}}"
  ```

  ```bash cURL (Update URL) theme={null}
  # 1. Create encrypted JWE token containing: {"callbackUrl": "https://new-domain.com/webhooks/modulus"}
  ENCRYPTED_TOKEN="eyJhbGciOiJBMjU2S1ciL..."

  # 2. Make API request
  curl -X PUT https://webhooks.sbx.moduluslabs.io/v1/webhooks/a78efd32-de3b-4854-b599-11ae9f98f97e \
    -u sk_your_secret_key: \
    -H "Content-Type: application/json" \
    -d "{\"request\": {\"Token\": \"$ENCRYPTED_TOKEN\"}}"
  ```

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

  const SECRET_KEY = process.env.MODULUS_SECRET_KEY;
  const ENCRYPTION_KEY = process.env.MODULUS_ENCRYPTION_KEY;

  async function updateWebhook(webhookId, updates) {
    // 1. Encrypt payload into JWE token
    const key = Buffer.from(ENCRYPTION_KEY, 'base64');
    const jweToken = await new jose.CompactEncrypt(
      new TextEncoder().encode(JSON.stringify(updates))
    )
      .setProtectedHeader({ alg: 'A256KW', enc: 'A256CBC-HS512' })
      .encrypt(key);

    // 2. Make API request
    const response = await axios.put(
      `https://webhooks.sbx.moduluslabs.io/v1/webhooks/${webhookId}`,
      { request: { Token: jweToken } },
      {
        auth: { username: SECRET_KEY, password: '' },
        headers: { 'Content-Type': 'application/json' }
      }
    );

    const result = response.data;
    console.log('Webhook updated successfully');
    console.log('Webhook ID:', result.id);
    console.log('New URL:', result.callbackUrl);
    console.log('New Status:', result.webhookStatus);

    return result;
  }

  // Example: Disable webhook
  updateWebhook('a78efd32-de3b-4854-b599-11ae9f98f97e', { webhookStatus: 'DISABLED' })
    .catch(error => {
      console.error('Error:', error.response?.data || error.message);
    });

  // Example: Change URL
  updateWebhook('a78efd32-de3b-4854-b599-11ae9f98f97e', {
    callbackUrl: 'https://new-domain.com/webhooks/modulus'
  });

  // Example: Update multiple fields
  updateWebhook('a78efd32-de3b-4854-b599-11ae9f98f97e', {
    callbackUrl: 'https://api.example.com/webhooks/success',
    webhookAction: 'QRPH_SUCCESS',
    webhookStatus: 'ENABLED'
  });
  ```

  ```python Python theme={null}
  import os
  import json
  import requests
  from jose import jwe

  SECRET_KEY = os.getenv('MODULUS_SECRET_KEY')
  ENCRYPTION_KEY = os.getenv('MODULUS_ENCRYPTION_KEY')

  def update_webhook(webhook_id, updates):
      # 1. Encrypt payload into JWE token
      jwe_token = jwe.encrypt(
          json.dumps(updates),
          ENCRYPTION_KEY,
          algorithm='A256KW',
          encryption='A256CBC-HS512'
      )

      # 2. Make API request
      response = requests.put(
          f'https://webhooks.sbx.moduluslabs.io/v1/webhooks/{webhook_id}',
          json={'request': {'Token': jwe_token}},
          auth=(SECRET_KEY, ''),
          headers={'Content-Type': 'application/json'}
      )

      response.raise_for_status()

      result = response.json()
      print('Webhook updated successfully')
      print(f"Webhook ID: {result['id']}")
      print(f"New URL: {result['callbackUrl']}")
      print(f"New Status: {result['webhookStatus']}")

      return result

  # Example: Disable webhook
  update_webhook('a78efd32-de3b-4854-b599-11ae9f98f97e', {'webhookStatus': 'DISABLED'})

  # Example: Change URL
  update_webhook('a78efd32-de3b-4854-b599-11ae9f98f97e', {
      'callbackUrl': 'https://new-domain.com/webhooks/modulus'
  })

  # Example: Update multiple fields
  update_webhook('a78efd32-de3b-4854-b599-11ae9f98f97e', {
      'callbackUrl': 'https://api.example.com/webhooks/success',
      'webhookAction': 'QRPH_SUCCESS',
      'webhookStatus': 'ENABLED'
  })
  ```

  ```php PHP theme={null}
  <?php
  require 'vendor/autoload.php';

  use Jose\Component\Core\AlgorithmManager;
  use Jose\Component\Core\JWK;
  use Jose\Component\Encryption\Algorithm\KeyEncryption\A256KW;
  use Jose\Component\Encryption\Algorithm\ContentEncryption\A256CBCHS512;
  use Jose\Component\Encryption\JWEBuilder;
  use Jose\Component\Encryption\Serializer\CompactSerializer;

  $secretKey = getenv('MODULUS_SECRET_KEY');
  $encryptionKey = getenv('MODULUS_ENCRYPTION_KEY');

  function updateWebhook($webhookId, $updates) {
      global $secretKey, $encryptionKey;

      // 1. Encrypt payload (simplified - use proper JWE library)
      // See encryption documentation for full implementation
      $jweToken = encryptToJWE(json_encode($updates), $encryptionKey);

      // 2. Make API request
      $ch = curl_init("https://webhooks.sbx.moduluslabs.io/v1/webhooks/{$webhookId}");
      curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
      curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
      curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode(['request' => ['Token' => $jweToken]]));
      curl_setopt($ch, CURLOPT_USERPWD, $secretKey . ':');
      curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);

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

      if ($httpCode === 200) {
          $result = json_decode($response, true);
          echo "Webhook updated successfully\n";
          echo "Webhook ID: {$result['id']}\n";
          echo "New URL: {$result['callbackUrl']}\n";
          echo "New Status: {$result['webhookStatus']}\n";
          return $result;
      } else {
          echo "Error: HTTP $httpCode\n";
          echo "Details: $response\n";
          return null;
      }
  }

  // Example: Disable webhook
  updateWebhook('a78efd32-de3b-4854-b599-11ae9f98f97e', ['webhookStatus' => 'DISABLED']);

  // Example: Change URL
  updateWebhook('a78efd32-de3b-4854-b599-11ae9f98f97e', [
      'callbackUrl' => 'https://new-domain.com/webhooks/modulus'
  ]);
  ```

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

  import (
  	"bytes"
  	"encoding/base64"
  	"encoding/json"
  	"fmt"
  	"io"
  	"net/http"
  	"os"

  	"github.com/lestrrat-go/jwx/v2/jwa"
  	"github.com/lestrrat-go/jwx/v2/jwe"
  )

  type TokenWrapper struct {
  	Token string `json:"Token"`
  }

  type TokenRequest struct {
  	Request TokenWrapper `json:"request"`
  }

  type WebhookResponse struct {
  	ID            string `json:"id"`
  	WebhookAction string `json:"webhookAction"`
  	WebhookStatus string `json:"webhookStatus"`
  	CallbackUrl   string `json:"callbackUrl"`
  }

  func updateWebhook(webhookID string, updates map[string]interface{}) (*WebhookResponse, error) {
  	secretKey := os.Getenv("MODULUS_SECRET_KEY")
  	encryptionKey := os.Getenv("MODULUS_ENCRYPTION_KEY")

  	// 1. Encrypt payload into JWE token
  	payloadJSON, err := json.Marshal(updates)
  	if err != nil {
  		return nil, fmt.Errorf("failed to marshal payload: %w", err)
  	}

  	keyBytes, err := base64.RawURLEncoding.DecodeString(encryptionKey)
  	if err != nil {
  		return nil, fmt.Errorf("failed to decode encryption key: %w", err)
  	}

  	encrypted, err := jwe.Encrypt(payloadJSON,
  		jwe.WithKey(jwa.A256KW, keyBytes),
  		jwe.WithContentEncryption(jwa.A256CBC_HS512),
  	)
  	if err != nil {
  		return nil, fmt.Errorf("failed to encrypt payload: %w", err)
  	}
  	jweToken := string(encrypted)

  	// 2. Make API request with Basic Auth
  	reqBody := TokenRequest{Request: TokenWrapper{Token: jweToken}}
  	jsonData, _ := json.Marshal(reqBody)

  	url := fmt.Sprintf("https://webhooks.sbx.moduluslabs.io/v1/webhooks/%s", webhookID)
  	req, err := http.NewRequest("PUT", url, bytes.NewBuffer(jsonData))
  	if err != nil {
  		return nil, fmt.Errorf("failed to create request: %w", err)
  	}

  	req.SetBasicAuth(secretKey, "")
  	req.Header.Set("Content-Type", "application/json")

  	client := &http.Client{}
  	resp, err := client.Do(req)
  	if err != nil {
  		return nil, fmt.Errorf("request failed: %w", err)
  	}
  	defer resp.Body.Close()

  	body, _ := io.ReadAll(resp.Body)

  	if resp.StatusCode == 200 {
  		var result WebhookResponse
  		json.Unmarshal(body, &result)
  		fmt.Println("Webhook updated successfully")
  		fmt.Printf("Webhook ID: %s\n", result.ID)
  		fmt.Printf("New URL: %s\n", result.CallbackUrl)
  		fmt.Printf("New Status: %s\n", result.WebhookStatus)
  		return &result, nil
  	}

  	return nil, fmt.Errorf("HTTP %d: %s", resp.StatusCode, string(body))
  }

  func main() {
  	// Example: Disable webhook
  	_, err := updateWebhook("a78efd32-de3b-4854-b599-11ae9f98f97e", map[string]interface{}{
  		"webhookStatus": "DISABLED",
  	})
  	if err != nil {
  		fmt.Printf("Failed to update webhook: %v\n", err)
  	}
  }
  ```

  ```csharp C# / .NET theme={null}
  using System.Net.Http.Headers;
  using System.Text;
  using System.Text.Json;
  using Jose;

  namespace ModulusWebhooks;

  class Program
  {
      static async Task Main(string[] args)
      {
          await UpdateWebhook("a78efd32-de3b-4854-b599-11ae9f98f97e", new
          {
              callbackUrl = "https://api.yourcompany.com/webhooks/modulus",
              webhookAction = "QRPH_SUCCESS",
              webhookStatus = "DISABLED"
          });
      }

      static byte[] Base64UrlDecode(string base64Url)
      {
          var base64 = base64Url
              .Replace('-', '+')
              .Replace('_', '/');

          switch (base64.Length % 4)
          {
              case 2: base64 += "=="; break;
              case 3: base64 += "="; break;
          }

          return Convert.FromBase64String(base64);
      }

      static async Task UpdateWebhook(string webhookId, object updates)
      {
          var secretKey = Environment.GetEnvironmentVariable("MODULUS_SECRET_KEY");
          var encryptionKey = Environment.GetEnvironmentVariable("MODULUS_ENCRYPTION_KEY");

          if (string.IsNullOrEmpty(secretKey) || string.IsNullOrEmpty(encryptionKey))
          {
              Console.WriteLine("Error: MODULUS_SECRET_KEY and MODULUS_ENCRYPTION_KEY must be set");
              return;
          }

          try
          {
              // 1. Encrypt payload into JWE token
              var key = Base64UrlDecode(encryptionKey);
              var payloadJson = JsonSerializer.Serialize(updates);
              var jweToken = JWT.Encode(
                  payloadJson,
                  key,
                  JweAlgorithm.A256KW,
                  JweEncryption.A256CBC_HS512
              );

              // 2. Make API request with Basic Auth
              using var client = new HttpClient();
              var authBytes = Encoding.ASCII.GetBytes($"{secretKey}:");
              client.DefaultRequestHeaders.Authorization =
                  new AuthenticationHeaderValue("Basic", Convert.ToBase64String(authBytes));

              var requestBody = new { request = new { Token = jweToken } };
              var content = new StringContent(
                  JsonSerializer.Serialize(requestBody),
                  Encoding.UTF8,
                  "application/json"
              );

              var response = await client.PutAsync(
                  $"https://webhooks.sbx.moduluslabs.io/v1/webhooks/{webhookId}",
                  content
              );

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

              if (response.IsSuccessStatusCode)
              {
                  var result = JsonSerializer.Deserialize<WebhookResponse>(
                      responseBody,
                      new JsonSerializerOptions { PropertyNameCaseInsensitive = true }
                  );

                  Console.WriteLine("Webhook updated successfully");
                  Console.WriteLine($"Webhook ID: {result?.Id}");
                  Console.WriteLine($"Webhook URL: {result?.CallbackUrl}");
                  Console.WriteLine($"Action: {result?.WebhookAction}");
                  Console.WriteLine($"Status: {result?.WebhookStatus}");
              }
              else
              {
                  Console.WriteLine($"Error: HTTP {(int)response.StatusCode}");
                  Console.WriteLine($"Details: {responseBody}");
              }
          }
          catch (Exception e)
          {
              Console.WriteLine($"Error: {e.Message}");
          }
      }
  }

  public class WebhookResponse
  {
      public string? Id { get; set; }
      public string? CallbackUrl { get; set; }
      public string? WebhookAction { get; set; }
      public string? WebhookStatus { get; set; }
  }
  ```
</RequestExample>

## Use Cases

<AccordionGroup>
  <Accordion title="Temporarily Disable During Maintenance" icon="wrench">
    Disable webhooks before server maintenance, enable after completion:

    ```javascript theme={null}
    // Before maintenance
    await updateWebhook(webhookId, { webhookStatus: 'DISABLED' });
    console.log('Webhook disabled for maintenance');

    // Perform maintenance...
    await upgradeServer();

    // After maintenance
    await updateWebhook(webhookId, { webhookStatus: 'ENABLED' });
    console.log('Webhook re-enabled');
    ```
  </Accordion>

  <Accordion title="Migrate to New Domain" icon="arrow-right">
    Update webhook URL when changing domains or infrastructure:

    ```javascript theme={null}
    // Update webhook to point to new domain
    await updateWebhook(webhookId, {
      callbackUrl: 'https://new-domain.com/webhooks/modulus'
    });

    console.log('Webhook migrated to new domain');
    ```
  </Accordion>

  <Accordion title="Adjust Event Subscriptions" icon="sliders">
    Change which events you want to receive:

    ```javascript theme={null}
    // Only receive success events
    await updateWebhook(webhookId, {
      webhookAction: 'QRPH_SUCCESS'
    });

    // Later: re-enable declined events
    await updateWebhook(webhookId, {
      webhookAction: 'QRPH_SUCCESS', 'QRPH_DECLINED'
    });
    ```
  </Accordion>

  <Accordion title="Switch to Versioned Endpoint" icon="code-branch">
    Update webhook URL to use a new API version:

    ```javascript theme={null}
    await updateWebhook(webhookId, {
      callbackUrl: 'https://api.example.com/v2/webhooks/modulus'
    });

    console.log('Webhook updated to v2 endpoint');
    ```
  </Accordion>

  <Accordion title="Enable After Testing" icon="circle-check">
    Create disabled webhook for testing, enable when ready:

    ```javascript theme={null}
    // Create disabled webhook
    const webhook = await createWebhook(
      'https://api.example.com/webhooks/test',
      ['QRPH_SUCCESS', 'QRPH_DECLINED'],
      'DISABLED'
    );

    // Test with Simulate API
    await simulateWebhook('SUCCESS');

    // Enable after successful test
    await updateWebhook(webhook.id, { webhookStatus: 'ENABLED' });
    console.log('Webhook enabled after successful testing');
    ```
  </Accordion>
</AccordionGroup>

## Best Practices

<CardGroup cols={2}>
  <Card title="Verify Before Updating" icon="magnifying-glass">
    Check current webhook configuration before updating:

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

    console.log('Current config:', webhook);

    await updateWebhook(webhookId, { webhookStatus: 'DISABLED' });
    ```
  </Card>

  <Card title="Test New URL First" icon="flask">
    Verify new webhook URL is accessible before updating:

    ```javascript theme={null}
    // Test new endpoint
    try {
      await axios.post(newcallbackUrl, { test: true });
      console.log('New endpoint is accessible');

      // Update webhook
      await updateWebhook(webhookId, { callbackUrl: newcallbackUrl });
    } catch (error) {
      console.error('New endpoint unreachable, skipping update');
    }
    ```
  </Card>

  <Card title="Log All Changes" icon="file-lines">
    Track webhook configuration changes for audit purposes:

    ```javascript theme={null}
    const oldConfig = await getWebhook(webhookId);

    await updateWebhook(webhookId, updates);

    const newConfig = await getWebhook(webhookId);

    await db.webhookAudit.insert({
      webhookId,
      timestamp: new Date(),
      oldConfig,
      newConfig,
      updates
    });
    ```
  </Card>

  <Card title="Graceful Status Changes" icon="toggle-on">
    Notify your team before disabling production webhooks:

    ```javascript theme={null}
    async function disableWebhook(webhookId, reason) {
      await alertTeam('Disabling webhook', { webhookId, reason });

      await updateWebhook(webhookId, { webhookStatus: 'DISABLED' });

      console.log(`Webhook ${webhookId} disabled: ${reason}`);
    }

    disableWebhook(123, 'Server maintenance scheduled');
    ```
  </Card>
</CardGroup>

## Partial Updates

You only need to include fields you want to change:

```javascript theme={null}
// Update only the status (URL and actions remain unchanged)
await updateWebhook(123, {
  webhookStatus: 'DISABLED'
});

// Update only the URL (status and actions remain unchanged)
await updateWebhook(123, {
  callbackUrl: 'https://new-domain.com/webhooks'
});

// Update everything
await updateWebhook(123, {
  callbackUrl: 'https://new-domain.com/webhooks',
  webhookAction: 'QRPH_SUCCESS',
  webhookStatus: 'ENABLED'
});
```

## Troubleshooting

<AccordionGroup>
  <Accordion title="404 Webhook Not Found" icon="circle-xmark">
    **Symptom:** Receive 404 error when updating

    **Possible Causes:**

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

    **Solutions:**

    * Call [Get Webhooks API](/api-reference/webhooks/list) to find valid webhook IDs
    * Verify you're using the correct secret key
    * Check if webhook was deleted
  </Accordion>

  <Accordion title="409 Conflict on URL Change" icon="clone">
    **Symptom:** Receive 409 error when changing webhook URL

    **Cause:** New URL is already registered for another webhook

    **Solutions:**

    * Use a different URL
    * Delete the other webhook using that URL first
    * Keep the current URL
  </Accordion>

  <Accordion title="Changes Not Taking Effect" icon="clock">
    **Symptom:** Webhook still receives old events or uses old URL

    **Possible Causes:**

    * Update request failed silently
    * Caching issue
    * Looking at wrong webhook

    **Solutions:**

    * Verify update succeeded by checking response
    * Call Get Webhooks API to confirm changes
    * Clear any application caches
  </Accordion>
</AccordionGroup>

## Next Steps

<CardGroup cols={2}>
  <Card title="Delete Webhook" icon="trash" href="/api-reference/webhooks/delete">
    Permanently remove a webhook endpoint
  </Card>

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

  <Card title="Test Webhook" icon="flask" href="/api-reference/webhooks/simulate">
    Simulate webhooks to test your integration
  </Card>

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


## OpenAPI

````yaml PUT /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}:
    put:
      tags:
        - Webhooks
      summary: Update Webhook
      description: >-
        Modify an existing webhook endpoint configuration. All body parameters
        are optional - include only the fields you want to update.
      operationId: updateWebhook
      parameters:
        - $ref: '#/components/parameters/WebhookIdParam'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/TokenRequest'
            example:
              request:
                Token: eyJhbGciOiJBMjU2S1ciLCJlbmMiOiJBMjU2Q0JDLUhTNTEyIn0...
      responses:
        '200':
          description: Webhook updated successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Webhook'
        '400':
          description: Bad Request - Invalid webhook URL or actions
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '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:
    TokenRequest:
      type: object
      required:
        - request
      properties:
        request:
          type: object
          required:
            - Token
          properties:
            Token:
              type: string
              description: >-
                JWE-encrypted token containing the request payload. Algorithm:
                A256KW | Encryption: A256CBC-HS512. See the Encryption Guide for
                details.
    Webhook:
      type: object
      properties:
        id:
          type: string
          description: Unique identifier for the webhook
        webhookAction:
          type: array
          items:
            type: string
            enum:
              - QRPH_SUCCESS
              - QRPH_DECLINED
          description: List of transaction events this webhook receives
        webhookStatus:
          type: string
          enum:
            - ENABLED
            - DISABLED
          description: Current status of the webhook
        webhookUrl:
          type: string
          format: uri
          description: The HTTPS URL where notifications are sent
    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

````