---
title: "Create and Manage WordPress Webhooks via REST API"
description: "Learn how to create and manage WordPress webhooks via REST API. Control endpoints, enable/disable integrations, and automate setups without code deployments."
url: "https://wpwebhooks.org/blog/create-manage-wordpress-webhooks-rest-api/"
date: "2026-03-26"
---

# Create and Manage WordPress Webhooks via REST API

TL;DR

-   The Webhook Actions REST API covers full webhook lifecycle management: create, list, update, enable/disable, and delete — no wp-admin needed
-   Create a webhook with `POST /wp-json/fswa/v1/webhooks`; required fields are `name` and `endpoint_url`
-   Designed for automation — CI pipelines, AI agents, and external systems can provision and manage webhooks programmatically

/ The Problem

## Why does WordPress need a Webhook **Control Layer**?

Most articles about WordPress webhooks focus on _sending_ them — how to fire an HTTP request when an order is placed, a form is submitted, or a user registers. The sending part is well-covered.

The missing piece is the control layer: there is no standard way to create webhooks remotely, update their endpoints, toggle integrations on and off, or manage them across multiple environments without logging into each site individually.

Without API control, webhooks are static. You write them once in PHP and they stay there until someone edits the code. When an endpoint changes, you deploy. When a staging environment should not fire to production, you comment out code. When a client's integration needs to be paused, you log in and click through the admin UI.

This is the difference between hardcoded integrations and API-driven infrastructure. The [delivery layer](https://wpwebhooks.org/blog/wordpress-webhooks-rest-api/) — retry, replay, monitoring — is one part of the picture. The control layer is the other: how you create and manage what gets sent, and where.

WordPress's REST API framework — introduced in WordPress 4.7 and documented in the [WordPress REST API Handbook](https://developer.wordpress.org/rest-api/) — provides the foundation for this kind of programmatic control. The same API that powers the Gutenberg block editor and the WordPress mobile apps makes remote webhook management technically possible across hosting providers, CI pipelines, and automation platforms.

/ Create

## **Create** a Webhook via REST API

Base URL: `https://your-site.com/wp-json/fswa/v1`  
Auth header: `X-FSWA-Token: <token>` — requires `full` scope

A webhook needs two things: a name and an endpoint URL. Everything else is optional — triggers (which WordPress actions fire this webhook), auth header (sent with each delivery), and whether it starts enabled.

curl — create a webhook

```
curl -X POST   "https://your-site.com/wp-json/fswa/v1/webhooks"   -H "X-FSWA-Token: your-full-scope-token"   -H "Content-Type: application/json"   -d '{
    "name": "CF7 → n8n",
    "endpoint_url": "https://n8n.yourdomain.com/webhook/abc123",
    "triggers": ["wpcf7_mail_sent"],
    "is_enabled": true
  }'
```

response — JSON

```
{
  "id": 7,
  "name": "CF7 → n8n",
  "endpoint_url": "https://n8n.yourdomain.com/webhook/abc123",
  "auth_header": null,
  "auth_credential_id": null,
  "is_enabled": true,
  "triggers": ["wpcf7_mail_sent"],
  "created_at": "2026-03-25 11:04:38",
  "updated_at": "2026-03-25 11:04:38"
}
```

`id` is the webhook identifier used in all subsequent operations. `triggers` maps to WordPress action hook names — use `GET /wp-json/fswa/v1/triggers` to browse all available hooks grouped by category (WooCommerce, Contact Form 7, WordPress core, etc.).

### With authorization — the Credentials Vault

If the receiving endpoint requires authentication, the preferred way is to store the secret once in the plugin's **Credentials Vault** and reference it by id via `auth_credential_id`. It takes precedence over the legacy plaintext `auth_header` field, and setting it to `null` clears it. Vault secrets are encrypted at rest and write-only over the API — the value is sent as the `Authorization` header (or a custom header) with every delivery, but can never be read back, not even by the `agent`\-scope tokens used by AI assistants.

curl — store the secret once in the Credentials Vault

```
curl -X POST   "https://your-site.com/wp-json/fswa/v1/credentials"   -H "X-FSWA-Token: your-full-scope-token"   -H "Content-Type: application/json"   -d '{
    "name": "HubSpot PAT",
    "type": "bearer",
    "secret": "hs_live_secret_abc123"
  }'

# → { "id": 3, "name": "HubSpot PAT", "hint": "…c123" } — the secret is never returned
```

curl — webhook referencing the vault credential

```
curl -X POST   "https://your-site.com/wp-json/fswa/v1/webhooks"   -H "X-FSWA-Token: your-full-scope-token"   -H "Content-Type: application/json"   -d '{
    "name": "WooCommerce → HubSpot",
    "endpoint_url": "https://hooks.example.com/crm/orders",
    "auth_credential_id": 3,
    "triggers": ["woocommerce_order_status_completed"],
    "is_enabled": true
  }'
```

### JavaScript — fetch

JavaScript — create webhook

```
const response = await fetch(
  'https://your-site.com/wp-json/fswa/v1/webhooks',
  {
    method: 'POST',
    headers: {
      'X-FSWA-Token': 'your-full-scope-token',
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      name: 'CF7 → n8n',
      endpoint_url: 'https://n8n.yourdomain.com/webhook/abc123',
      triggers: ['wpcf7_mail_sent'],
      is_enabled: true
    })
  }
);
const webhook = await response.json();
// webhook.id — use this for all subsequent operations
```

/ List & Inspect

## **List** and Inspect Webhooks

Listing webhooks requires only a `read` scope token — you don't need write access to audit what's configured. This is the right token scope for monitoring scripts, dashboards, and read-only tooling.

curl — list all webhooks

```
curl -X GET   "https://your-site.com/wp-json/fswa/v1/webhooks"   -H "X-FSWA-Token: your-read-scope-token"

# Add ?only_enabled=true to return only active webhooks
```

response — JSON (array)

```
[
  {
    "id": 5,
    "name": "Order sync — HubSpot",
    "endpoint_url": "https://hooks.example.com/crm/orders",
    "is_enabled": true,
    "triggers": ["woocommerce_order_status_completed"],
    "created_at": "2026-02-10 08:30:00",
    "updated_at": "2026-03-24 14:15:22"
  },
  {
    "id": 7,
    "name": "CF7 → n8n",
    "endpoint_url": "https://n8n.yourdomain.com/webhook/abc123",
    "is_enabled": true,
    "triggers": ["wpcf7_mail_sent"],
    "created_at": "2026-03-25 11:04:38",
    "updated_at": "2026-03-25 11:04:38"
  }
]
```

### Get a single webhook

curl — get webhook by ID

```
curl -X GET   "https://your-site.com/wp-json/fswa/v1/webhooks/7"   -H "X-FSWA-Token: your-read-scope-token"
```

/ Update & Control

## **Update** and Control Webhooks

### Update a webhook

`PATCH /webhooks/{id}` accepts any subset of fields — you only send what you want to change. Requires `full` scope.

curl — change endpoint URL

```
curl -X PATCH   "https://your-site.com/wp-json/fswa/v1/webhooks/7"   -H "X-FSWA-Token: your-full-scope-token"   -H "Content-Type: application/json"   -d '{ "endpoint_url": "https://n8n.yourdomain.com/webhook/production-xyz" }'
```

curl — add a trigger to an existing webhook

```
curl -X PATCH   "https://your-site.com/wp-json/fswa/v1/webhooks/5"   -H "X-FSWA-Token: your-full-scope-token"   -H "Content-Type: application/json"   -d '{
    "triggers": ["woocommerce_order_status_completed", "woocommerce_order_status_refunded"]
  }'
```

### Toggle a webhook on / off

`POST /webhooks/{id}/toggle` flips the `is_enabled` flag. No request body required. This endpoint needs only `operational` scope — not `full`.

Toggle requires only `operational` scope. This is intentional: CI pipelines and deployment scripts that need to pause integrations during a migration don't need full write access — they just need to flip a switch. Create a dedicated `operational` token for these workflows.

curl — toggle webhook

```
curl -X POST   "https://your-site.com/wp-json/fswa/v1/webhooks/7/toggle"   -H "X-FSWA-Token: your-operational-token"

# No request body needed. Flips current is_enabled state.
```

response — JSON

```
{
  "id": 7,
  "name": "CF7 → n8n",
  "endpoint_url": "https://n8n.yourdomain.com/webhook/abc123",
  "is_enabled": false,
  "triggers": ["wpcf7_mail_sent"],
  "updated_at": "2026-03-25 11:44:09"
}

// Always check is_enabled in the response — that is your confirmation.
```

### Delete a webhook

Remove a webhook entirely. Requires `full` scope. This cannot be undone — the webhook and its configuration are deleted permanently.

curl — delete webhook

```
curl -X DELETE   "https://your-site.com/wp-json/fswa/v1/webhooks/7"   -H "X-FSWA-Token: your-full-scope-token"
```

response — JSON

```
{ "deleted": true, "id": 7 }
```

/ Use Cases

## What are the Real-World **Use Cases** for webhook REST APIs?

### CI/CD and environment setup

During a deployment pipeline, create environment-specific webhooks automatically. Point staging to a test endpoint, production to the live one. Disable all webhooks before a database migration, re-enable after it completes. No one logs into wp-admin during a deployment window.

bash — CI/CD deployment script

```
SITE="https://your-site.com/wp-json/fswa/v1"
TOKEN="${FSWA_FULL_TOKEN}"

# Disable all webhooks before migration
WEBHOOK_IDS=$(curl -s "${SITE}/webhooks?only_enabled=true"   -H "X-FSWA-Token: ${TOKEN}"   | jq '[.[].id]')

for id in $(echo "${WEBHOOK_IDS}" | jq '.[]'); do
  curl -s -X POST "${SITE}/webhooks/${id}/toggle"     -H "X-FSWA-Token: ${TOKEN}" > /dev/null
done

# ... run migration ...

# Re-enable after migration completes
for id in $(echo "${WEBHOOK_IDS}" | jq '.[]'); do
  curl -s -X POST "${SITE}/webhooks/${id}/toggle"     -H "X-FSWA-Token: ${TOKEN}" > /dev/null
done
```

### SaaS integrations

If you're building a product that integrates with customer WordPress sites, you can register webhooks programmatically during onboarding. No manual configuration. No asking customers to copy-paste an endpoint URL into a form. The webhook is created, the trigger is registered, the integration is live — from a single API call in your onboarding flow.

### Automation platforms — n8n, Make, Zapier

When building flows in n8n or similar platforms, endpoint URLs change as you move between test and production workflows. Instead of updating WordPress manually every time, your automation can call the PATCH endpoint to update the webhook URL when a workflow is promoted to production. The endpoint stays in sync with the automation, not the other way around.

Both [n8n](https://docs.n8n.io/integrations/builtin/core-nodes/n8n-nodes-base.webhook/) and [Zapier](https://zapier.com/apps/webhook/integrations) rely on webhook ingestion as their primary real-time trigger mechanism. When endpoint URLs differ between test and production workflow environments, programmatic webhook management eliminates a category of manual error that typically only surfaces during a live deployment.

For a practical example of sending CF7 form data to n8n, see the [Contact Form 7 → webhook example](https://wpwebhooks.org/examples/cf7-to-webhook/).

### Agency / multi-site management

Managing webhooks across ten client sites means ten separate wp-admin logins to push a configuration change. With API control, one script handles all of them. Standardize webhook configuration across environments, audit what's enabled on each site, roll out endpoint changes without browser tabs.

/ Architecture

## Two Layers of a **Complete Webhook System**

Webhook infrastructure has two distinct concerns. Most systems handle only one of them.

| Layer | Operation | Method & endpoint |
| --- | --- | --- |
| Control | create | `POST /webhooks` |
| Control | list | `GET /webhooks` |
| Control | inspect | `GET /webhooks/{id}` |
| Control | update | `PATCH /webhooks/{id}` |
| Control | enable / disable | `POST /webhooks/{id}/toggle` |
| Control | remove | `DELETE /webhooks/{id}` |
| Delivery | retry | `POST /logs/{id}/retry` |
| Delivery | replay | `POST /logs/{id}/replay` |
| Delivery | monitor | `GET /health` |
| Delivery | queue depth | `GET /queue/stats` |
| Delivery | inspect errors | `GET /logs?status=error` |
| Delivery | bulk recover | `POST /logs/bulk-retry` |

The control layer (this article) handles what exists: which webhooks are registered, where they point, and whether they're active. The delivery layer handles what happened: did events reach their destination, what failed, how to recover.

Together they give you webhooks that are both reliable and controllable. The delivery layer is covered in the companion article: [WordPress Webhooks REST API: Retry, Replay and Monitor Events Programmatically](https://wpwebhooks.org/blog/wordpress-webhooks-rest-api/). The same scoped toolset is also published to AI agents through the [WordPress Abilities API](https://wpwebhooks.org/blog/wordpress-abilities-api-ai-agents/).

**Flow Systems Webhook Actions** exposes both layers via the same authenticated REST API. The full reference — all endpoints, request/response schemas, and scope requirements — is in the [API documentation](https://wpwebhooks.org/webhook-wordpress-plugin-api/).

/ Authentication

## **Authentication** and Token Scopes

Tokens are issued per-site from the plugin admin panel under _API Tokens_. Each token carries one scope. Use the minimum scope required for the operation — don't give a monitoring script write access.

| Scope | Permitted Operations | Typical Use |
| --- | --- | --- |
| read | GET /webhooks, GET /webhooks/{id}, GET /logs, GET /health, GET /queue/stats | Monitoring, dashboards, auditing |
| operational | All read operations, plus: POST /webhooks/{id}/toggle, POST /logs/{id}/retry, POST /logs/bulk-retry, POST /logs/{id}/replay | CI/CD pipelines, recovery scripts |
| full | All operational operations, plus: POST /webhooks (create), PATCH /webhooks/{id} (update), DELETE /webhooks/{id} | Onboarding scripts, provisioning |

The token is passed in the `X-FSWA-Token` header. Alternatively, use `Authorization: Bearer <token>` if your HTTP client expects a Bearer auth format, or append `?api_token=<token>` as a query parameter for tools that don't support custom headers.

Tokens are independently revocable. If a token is exposed in a log or a script, revoke it from the plugin settings without disrupting other integrations.

## Structured data

```json
{"@context":"https://schema.org","@type":"Article","headline":"Create and Manage WordPress Webhooks via REST API","description":"Learn how to create and manage WordPress webhooks via REST API. Control endpoints, enable/disable integrations, and automate setups without code deployments.","datePublished":"2026-03-26","dateModified":"2026-03-26","author":{"@type":"Person","name":"Mateusz Skorupa","url":"https://wpwebhooks.org/about/"},"publisher":{"@type":"Organization","name":"WP Webhooks","url":"https://wpwebhooks.org"},"url":"https://wpwebhooks.org/blog/create-manage-wordpress-webhooks-rest-api/","image":"https://wpwebhooks.org/blog/create-manage-wordpress-webhooks-rest-api/og_image.jpg","keywords":["create wordpress webhook","wordpress webhook api","manage wordpress webhooks","wordpress rest api webhook","wordpress automation","webhook control plane"]}

{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"name":"WP Webhooks","item":"https://wpwebhooks.org/"},{"@type":"ListItem","position":2,"name":"Blog","item":"https://wpwebhooks.org/blog/"},{"@type":"ListItem","position":3,"name":"Create and Manage WordPress Webhooks via REST API","item":"https://wpwebhooks.org/blog/create-manage-wordpress-webhooks-rest-api/"}]}

{"@context":"https://schema.org","@type":"FAQPage","mainEntity":[{"@type":"Question","name":"Can you create webhooks in WordPress via API?","acceptedAnswer":{"@type":"Answer","text":"Yes. The Flow Systems Webhook Actions plugin exposes POST /wp-json/fswa/v1/webhooks, which creates a new webhook from a JSON payload. Required fields are name and endpoint_url. Optional fields include triggers (array of WordPress action names), is_enabled (boolean), and auth_credential_id — the ID of an encrypted Credentials Vault entry used for authorization, preferred over (and taking precedence over) the legacy plaintext auth_header. Requires a token with full scope."}},{"@type":"Question","name":"How to manage WordPress webhooks programmatically?","acceptedAnswer":{"@type":"Answer","text":"Use PATCH /wp-json/fswa/v1/webhooks/{id} to update any field (endpoint URL, name, triggers) — all fields are optional so you can patch only what changed. Use POST /webhooks/{id}/toggle to flip the is_enabled flag without a full update (requires only operational scope). Use DELETE /webhooks/{id} to remove a webhook entirely. All operations are authenticated via the X-FSWA-Token header."}},{"@type":"Question","name":"What token scope is needed to create WordPress webhooks via API?","acceptedAnswer":{"@type":"Answer","text":"Creating, updating, and deleting webhooks requires a token with full scope. Toggling a webhook on or off (POST /webhooks/{id}/toggle) requires only operational scope — useful for CI pipelines that need to pause integrations without full write access. Listing and inspecting webhooks (GET /webhooks) requires only read scope. For AI assistants, the agent scope grants the same write access as full but can never reveal a webhook's Authorization header or any stored Credentials Vault secret."}},{"@type":"Question","name":"How do I list all webhooks via REST API?","acceptedAnswer":{"@type":"Answer","text":"Use GET /wp-json/fswa/v1/webhooks with an X-FSWA-Token header carrying a read scope token. The response is a JSON array where each item includes id, name, endpoint_url, is_enabled, triggers, created_at, and updated_at. Add ?only_enabled=true to filter to active webhooks only."}}]}
```
