Sometimes you want to block a customer without opening the WordPress admin. Maybe a fraud-scoring service flags an order, or a helpdesk tool marks someone as abusive. Checkout Guard ships a REST API so your own scripts and tools can read and manage your blocked entries, and check whether a customer would be blocked.
All routes live under one namespace, cgfw/v1. This article lists every route for blocked entries and settings, what it accepts, what it returns, and the errors you can hit. The namespace also has two license routes that the plugin’s own Settings screen uses. This article does not cover them.
Prerequisites
| Requirement | Details |
|---|---|
| Plugin | Checkout Guard 1.0.2 or later. The /check and /entries/batch routes, and wildcard matching, are part of 1.0.2. |
| WordPress | 5.6 or later to use Application Passwords. Checkout Guard itself supports WordPress 5.2 and up, but sites on 5.2 to 5.5 can’t use the authentication method below. |
| User role | A user with the manage_woocommerce capability, such as an Administrator or Shop Manager. |
| Connection | HTTPS. WordPress only enables Application Passwords on HTTPS sites, or on sites where WP_ENVIRONMENT_TYPE is set to local. |
Base URL and request format
Every route starts with your site’s REST base URL followed by the namespace:
https://your-store.example.com/wp-json/cgfw/v1
In every example below, replace your-store.example.com with your own domain.
If your site uses plain permalinks, /wp-json/ won’t work. Use the rest_route form instead:
https://your-store.example.com/?rest_route=/cgfw/v1/entries
Send request bodies as JSON and set the Content-Type: application/json header. Responses are JSON too. If a URL returns a 404, check your site’s REST base first.
Authentication
Every route needs the manage_woocommerce capability, so only Administrators and Shop Managers can call the API. For scripts and other automated callers, use a WordPress Application Password. It signs each request in as a specific user with HTTP Basic authentication. You don’t need a cookie or a nonce.
To create an Application Password:
- Log in as an Administrator or Shop Manager and go to Users → Profile.
- Scroll down to the Application Passwords section.
- Enter a name, such as
Checkout Guard automation, in the New Application Password Name field. - Click Add Application Password.
- Copy the password now, because WordPress shows it only once. It looks like
xxxx xxxx xxxx xxxx xxxx xxxx. You can send it with or without the spaces.
Use that user’s login name as the username and the Application Password as the password:
curl https://your-store.example.com/wp-json/cgfw/v1/entries \
--user "shopmanager:xxxx xxxx xxxx xxxx xxxx xxxx"
The API answers authentication problems in two ways:
- No credentials: you get
401 Unauthorized. - Valid credentials, but the user lacks
manage_woocommerce: you get403 Forbidden. An Application Password for a Customer or Editor account signs in fine but is still refused.
Route overview
Paths are relative to /wp-json/cgfw/v1.
| Method | Route | Purpose |
|---|---|---|
| GET | /entries | List blocked entries, optionally filtered. |
| POST | /entries | Create an entry. Safe to repeat. |
| POST | /entries/batch | Create and delete many entries in one request. |
| PUT | /entries/{id} | Update an entry. |
| DELETE | /entries/{id} | Delete an entry. |
| POST | /check | Find out whether a customer would be blocked. |
| GET | /settings | Read the plugin settings. |
| POST | /settings | Update the plugin settings. |
How a blocked entry works
Each blocked entry has these fields:
| Field | Type | Details |
|---|---|---|
id | string | A UUID that Checkout Guard assigns when you create the entry. |
first_name | string | Optional. Supports a * wildcard, such as jo*. |
last_name | string | Optional. Supports a * wildcard, such as sm*th. |
email | string | Optional. Supports a * wildcard, such as *@example.com. |
ip_address | string | Optional. A single IPv4 or IPv6 address, or an IPv4 wildcard such as 192.168.1.*. |
notes | string | Optional. Cut to 140 bytes, which is 140 characters for plain Latin text and fewer for other scripts. |
created_by_user_id | integer | The user who created the entry. |
source | string | api when the entry was created with an Application Password, otherwise admin. |
You must send at least one of first_name, last_name, email or ip_address. A name only blocks when the entry has both a first and a last name.
An entry blocks a customer when any one of these is true:
- Both its first and last name match the customer’s name.
- Its email matches the customer’s email.
- Its IP address matches the customer’s IP address at checkout.
Name and email matching ignores upper and lower case. IP address matching is exact.
Checkout Guard reads the customer’s IP address the way WooCommerce does, so it follows common proxy headers.
Wildcard rules
Name and email values can use * to match zero or more characters. Matching is anchored to the whole value, so john* matches johnny but not ajohn. Every other character, including ., matches literally.
IP addresses accept wildcards only in IPv4 form. Use exactly four segments, where each segment is either all digits or exactly *, and at least one segment is a digit.
Each digit segment must be 0 to 255 with no leading zeros, so 192.168.01.* and 256.1.1.* are rejected with 400.
| Value | Result |
|---|---|
192.168.1.*, 192.168.*.1, 192.*.*.1 | Accepted |
*.*.*.* | Rejected with 400. No digit segment. |
19*.168.1.1 | Rejected with 400. Mixed segment. |
1.2.3.*.5 | Rejected with 400. Five segments. |
2001:db8::* | Rejected with 400. IPv6 wildcards aren’t supported. |
A few more limits apply to every field:
- No bare wildcards. A value that matches everything, such as
*,**,*@*or*.*, is rejected with400.*@example.comis fine because it has literal text left over. - At most 5 wildcards. A value with 6 or more
*characters is rejected with400. - Batch creates skip bad values. In a batch request, a rejected item isn’t saved. It’s counted in the
skippedtotal, and the request itself still returns200. - Lookups stay literal. When you filter
GET /entriesor send a repeat create,*is treated as a plain character. A wildcard rule and a literal value with the same characters are always separate entries.
Endpoints
List entries
GET /entries returns an array of entries. Add any of email, first_name, last_name or ip_address as query parameters to filter the list. Name and email filters are exact, case-insensitive matches. The ip_address filter is an exact match. Several filters combine with AND. An invalid email or ip_address returns 400.
Each entry in the list also includes created_by_display_name, and created_by_edit_url when the creating user still exists.
curl https://your-store.example.com/wp-json/cgfw/v1/entries \
--user "shopmanager:xxxx xxxx xxxx xxxx xxxx xxxx"
# Look up one email
curl "https://your-store.example.com/wp-json/cgfw/v1/[email protected]" \
--user "shopmanager:xxxx xxxx xxxx xxxx xxxx xxxx"
Response:
[
{
"id": "11112222-3333-4444-5555-666677778888",
"first_name": "",
"last_name": "",
"email": "[email protected]",
"ip_address": "",
"notes": "chargeback fraud",
"created_by_user_id": 2,
"source": "api",
"created_by_display_name": "Shop Manager",
"created_by_edit_url": "https://your-store.example.com/wp-admin/user-edit.php?user_id=2"
}
]
Create an entry
POST /entries creates an entry and returns it. Repeating the same request doesn’t create a duplicate. If an entry already exists with the same email, Checkout Guard updates it with any non-empty fields you send and returns it. When you send no email, it matches on the same first and last name. When you send neither, it matches on the same ip_address.
curl -X POST https://your-store.example.com/wp-json/cgfw/v1/entries \
--user "shopmanager:xxxx xxxx xxxx xxxx xxxx xxxx" \
-H "Content-Type: application/json" \
-d '{"email":"[email protected]","notes":"chargeback fraud"}'
Response:
{
"id": "11112222-3333-4444-5555-666677778888",
"first_name": "",
"last_name": "",
"email": "[email protected]",
"ip_address": "",
"notes": "chargeback fraud",
"created_by_user_id": 2,
"source": "api"
}
To block by IP address only, send {"ip_address":"203.0.113.42","notes":"repeat spammer"}. A non-empty ip_address that isn’t valid returns 400.
Create and delete in bulk
POST /entries/batch lets you sync a whole list in one request. Send a create array, a delete array of entry IDs, or both. Checkout Guard processes the creates first, each one through the same repeat-safe path as a single create. Then it processes the deletes. At least one of the two arrays must have something in it, or you get 400.
curl -X POST https://your-store.example.com/wp-json/cgfw/v1/entries/batch \
--user "shopmanager:xxxx xxxx xxxx xxxx xxxx xxxx" \
-H "Content-Type: application/json" \
-d '{
"create": [
{ "email": "[email protected]", "notes": "ring leader" },
{ "first_name": "Jane", "last_name": "Doe" }
],
"delete": [ "99998888-7777-6666-5555-444433332222" ]
}'
Response:
{
"created": [
{
"id": "aaaa1111-2222-3333-4444-555566667777",
"first_name": "",
"last_name": "",
"email": "[email protected]",
"ip_address": "",
"notes": "ring leader",
"created_by_user_id": 2,
"source": "api"
}
],
"deleted": [ "99998888-7777-6666-5555-444433332222" ],
"not_found": [],
"skipped": 0
}
| Response field | Meaning |
|---|---|
created | The entries that were created or updated. |
deleted | The IDs that were removed. |
not_found | Delete IDs that didn’t match any entry. |
skipped | How many create items were ignored. An item is skipped when it has no name, email or IP address, or when it has an invalid IP address or wildcard. |
Update or delete one entry
PUT /entries/{id} changes an entry. Send only the fields you want to change. The response is the full updated entry. You can’t clear every identifying field. The entry must keep at least one of the name, email or IP fields, or you get 400.
DELETE /entries/{id} removes an entry and returns { "success": true }. If the ID doesn’t match an entry, both routes return 404.
curl -X PUT https://your-store.example.com/wp-json/cgfw/v1/entries/11112222-3333-4444-5555-666677778888 \
--user "shopmanager:xxxx xxxx xxxx xxxx xxxx xxxx" \
-H "Content-Type: application/json" \
-d '{"notes":"confirmed fraud"}'
curl -X DELETE https://your-store.example.com/wp-json/cgfw/v1/entries/11112222-3333-4444-5555-666677778888 \
--user "shopmanager:xxxx xxxx xxxx xxxx xxxx xxxx"
Check a customer
POST /check tells you whether a customer would be blocked at checkout. It runs the same matching logic as the live checkout, so the answer matches what a real order would get. Send at least one of email, first_name, last_name or ip_address, or you get 400.
This route is strict about wildcards. The values you send are a real customer’s details, not a pattern. A * in any field returns 400.
curl -X POST https://your-store.example.com/wp-json/cgfw/v1/check \
--user "shopmanager:xxxx xxxx xxxx xxxx xxxx xxxx" \
-H "Content-Type: application/json" \
-d '{"email":"[email protected]"}'
Response:
{
"blocked": true,
"matched_entry": {
"id": "11112222-3333-4444-5555-666677778888",
"first_name": "",
"last_name": "",
"email": "[email protected]",
"ip_address": "",
"notes": "chargeback fraud",
"created_by_user_id": 2,
"source": "api"
}
}
When no entry matches, blocked is false and matched_entry is null.
Read and update settings
GET /settings returns the plugin settings. The only setting is checkout_denial_message, which is the message a blocked customer sees at checkout. POST /settings changes it and returns the saved settings. The message can’t be empty, or you get 400.
curl https://your-store.example.com/wp-json/cgfw/v1/settings \
--user "shopmanager:xxxx xxxx xxxx xxxx xxxx xxxx"
curl -X POST https://your-store.example.com/wp-json/cgfw/v1/settings \
--user "shopmanager:xxxx xxxx xxxx xxxx xxxx xxxx" \
-H "Content-Type: application/json" \
-d '{"checkout_denial_message":"Sorry, you cannot place an order right now."}'
Response:
{ "checkout_denial_message": "Sorry, you cannot place an order right now." }
Error responses
Errors use the standard WordPress format, with a code, a message and an HTTP status.
| Status | Code | When it happens |
|---|---|---|
400 | invalid_entry | A create or update has no name, email or IP address. |
400 | invalid_check | A /check request has none of email, first_name, last_name or ip_address. |
400 | invalid_batch | A batch request has empty create and delete arrays. |
400 | invalid_ip_address | An IP address or IP wildcard isn’t valid. |
400 | invalid_wildcard_value | A wildcard value is a bare wildcard or has more than 5 * characters. |
400 | invalid_settings | The settings message is empty. |
400 | rest_invalid_param | A query or /check parameter isn’t valid, such as an email that isn’t valid or a * in a /check field. |
401 | rest_forbidden | The request has no credentials. |
403 | rest_forbidden | The user doesn’t have the manage_woocommerce capability. |
404 | entry_not_found | No entry matches the ID in the URL. |
A 404 without a code means your site’s REST base URL is different from the one you used.
Privacy
Blocked entries are personal data. If you store customer emails, names or IP addresses, you’re responsible for handling them in line with your privacy obligations, such as GDPR.
Troubleshooting
manage_woocommerce capability. Create the Application Password under an Administrator or Shop Manager account instead.WP_ENVIRONMENT_TYPE is set to local. Check your WordPress version and that your store URL starts with https://. If your site is on 5.2 to 5.5, update WordPress first.Authorization header through to WordPress.?rest_route=/cgfw/v1/entries form, or ask your host what your REST base is.Frequently asked questions
/check takes a real customer’s details, so a * in any field returns 400. Use wildcards only when you create or update entries.POST /entries is built to be safe to repeat. If an entry with the same email already exists, Checkout Guard updates it instead of adding a duplicate. That also applies to the same name or IP address when no email is sent.source field. Entries created with an Application Password have source set to api. Entries added in the admin screen have admin.Need help?
Checkout Guard is a licensed plugin. If you’re still stuck, open a support ticket and include the route, the request you sent and the response you got back. That helps our team check your setup directly.

