This page is the overview: base URL, response and error format, paging, a table of every resource and three complete requests. Route tables with parameters and examples are on the three resource pages linked in the table. Open the base address in a browser to see every route WordPress has registered.
Base URL and format #
https://example.com/wp-json/hivemail/v1
Without pretty permalinks use https://example.com/?rest_route=/hivemail/v1/.... Send and receive JSON; for a request body send Content-Type: application/json. To sign in, see authentication.
A successful answer is the JSON object or list itself, with no wrapper. A create answers 201; contacts, lists, custom fields and signup forms also send a Location header (tags do not). Routes that take PUT also accept POST and PATCH.
Errors #
Errors are standard WordPress REST errors. data.status is the HTTP status; some errors add keys such as data.errors (one message per field) or existing_id.
{
"code": "hivemail_contact_exists",
"message": "A contact with this email already exists.",
"data": { "status": 409, "existing_id": 42, "trashed": false }
}
| Status | Meaning |
|---|---|
| 400 | A value is not valid (data.errors names the field) or a rule was broken, for example hivemail_consent_required |
| 401 | Not signed in (hivemail_forbidden), or a wrong application password (incorrect_password) |
| 403 | Signed in, but the user lacks the capability (hivemail_forbidden) |
| 404 | No such route (rest_no_route) or item (hivemail_contact_not_found, hivemail_campaign_not_found, …) |
| 409 | A conflict: the item exists (hivemail_contact_exists, hivemail_tag_exists), the address is suppressed (hivemail_contact_suppressed), or the state forbids it (hivemail_campaign_state) |
Paging #
List routes take page (from 1) and per_page (default 20; the maximum is 500 for contacts and 100 for most other lists; a larger value is 400 rest_invalid_param) and answer:
{ "items": [ ], "total": 1405, "pages": 71, "page": 1, "per_page": 20 }
page and per_page come back on contacts, lists, tags, forms and the sending log; other lists (segments, campaigns, templates, flows, runs, suppressions) answer items, total and pages. The headers X-WP-Total and X-WP-TotalPages repeat total and pages. Most lists also take search, orderby and order (asc or desc).
All resources #
Capability letters are explained on each resource page. Every route in these pages was checked against register_rest_route in the plugin.
| Resource | Base route | Documented on |
|---|---|---|
| Ping, bootstrap, system status | /ping, /bootstrap, /system/status |
below, and Campaigns, segments, templates and reports |
| Contacts | /contacts |
Contacts, lists, tags and custom fields |
| Lists, tags | /lists, /tags |
same |
| Custom fields | /fields |
same |
| Import, export, background jobs | /import, /export, /jobs |
same |
| Migrate from another plugin | /migrate/{source} |
same |
| Segments | /segments |
Campaigns, segments, templates and reports |
| Campaigns | /campaigns |
same |
| Templates, designs, brand kit | /templates, /design, /brand-kit |
same |
| Reports | /campaigns/{id}/report, /reports/overview |
same |
| Sending log and test email | /sending |
same |
| Suppressions | /suppressions |
same |
| Settings, tools, scheduler | /settings/{group}, /tools, /system |
same |
| Flows and runs | /flows, /runs |
Flows, recipes, forms and shops |
| Recipes | /recipes |
same |
| Incoming webhook trigger | /flows/{id}/webhook |
same, and Webhooks and API keys |
| Bounce webhooks | /webhooks/{provider} |
Webhooks and API keys |
| Signup forms and connected forms | /forms, /integrations |
Flows, recipes, forms and shops |
| WooCommerce and Easy Digital Downloads | /commerce/{shop} |
same |
| Visitor reports and journeys | /intelligence, /contacts/{id}/journey |
same |
| API keys | /api-keys |
Webhooks and API keys |
| Roles and permissions | /roles |
Roles & Permissions |
| License | /license |
License |
| Email footer preview | /email-footer/preview |
Settings: Email footer |
| Check my list | /email-checks |
Check my list |
| AI assistant | /ai |
by name only, see The AI assistant |
| Migration from FluentCRM | /migration |
by name only, see Migrate from FluentCRM |
GET /ping (manage_hivemail) answers the plugin name and version. GET /bootstrap (same capability) answers what the admin app needs on load: the user's capabilities, detected shops, settings groups, the site time zone. GET /system/status (hivemail_manage_settings) answers database, queue and scheduler health.
Tip: The admin app uses this same API. Open your browser's network tab on any screen to see the exact request.
Request 1: list contacts #
Needs hivemail_view_audience.
curl -u "admin:abcd efgh ijkl mnop qrst uvwx" \
"https://example.com/wp-json/hivemail/v1/contacts?per_page=20&status=subscribed"
Answer 200 with X-WP-Total and X-WP-TotalPages headers (a trimmed real answer):
{
"items": [
{
"id": 1804, "email": "sales@harbourworks.example", "status": "subscribed",
"first_name": "Sales", "last_name": "", "full_name": "Sales",
"phone": "", "country": "", "company": "", "source": "import",
"wp_user_id": null, "score": 0, "ltv": 0,
"subscribed_at": "2026-10-28T04:25:54Z", "unsubscribed_at": null,
"consent_source": "admin", "consent_at": "2026-10-28T04:25:54Z",
"created_at": "2026-10-28T04:25:54Z", "updated_at": "2026-10-28T04:25:54Z", "deleted_at": null,
"allowed_statuses": [ "unsubscribed", "transactional" ],
"lists": [], "tags": [], "fields": {}
}
],
"total": 1405, "pages": 71, "page": 1, "per_page": 20
}
Request 2: create a contact #
Needs hivemail_manage_audience. Only email is required. Without status, the contact is subscribed if "consent": true and pending if not. status: "subscribed" without consent: true is 400 hivemail_consent_required. Only send consent when the person gave permission.
curl -u "admin:abcd efgh ijkl mnop qrst uvwx" \
-X POST -H "Content-Type: application/json" \
-d '{"email":"ada@example.com","first_name":"Ada","consent":true}' \
"https://example.com/wp-json/hivemail/v1/contacts"
Answer 201 with Location: https://example.com/wp-json/hivemail/v1/contacts/42 and the contact as the body.
| Status | Code | When |
|---|---|---|
| 400 | hivemail_invalid_contact |
Invalid email or country (data.errors) |
| 400 | hivemail_consent_required |
Subscribed without consent |
| 400 | hivemail_list_not_found, hivemail_tag_not_found |
An id in lists or tags does not exist |
| 409 | hivemail_contact_exists |
Already a contact (existing_id, trashed) |
| 409 | hivemail_contact_suppressed |
The address bounced, complained, or asked not to be emailed |
Request 3: add a tag to a contact #
Needs hivemail_manage_audience. Tags go on by id. With find_or_create the tag answer is 200 if the name exists and 201 if new. Without it, an existing name is 409 hivemail_tag_exists.
curl -u "admin:abcd efgh ijkl mnop qrst uvwx" -X POST -H "Content-Type: application/json" \
-d '{"name":"VIP","find_or_create":true}' \
"https://example.com/wp-json/hivemail/v1/tags"
# 201 {"id":11,"name":"VIP","slug":"vip","color":"","contact_count":0,"created_at":"2026-10-28T07:44:24Z"}
curl -u "admin:abcd efgh ijkl mnop qrst uvwx" -X POST -H "Content-Type: application/json" \
-d '{"add_tags":[11]}' \
"https://example.com/wp-json/hivemail/v1/contacts/42/memberships"
The second call answers 200 with the updated contact. An unknown tag is 404 hivemail_tag_not_found, an unknown contact 404 hivemail_contact_not_found. The same route takes add_lists, remove_lists and remove_tags.
API changes behave like admin changes: they fire the same action hooks, appear on the contact's timeline and can start flows.
Next: Contacts, lists, tags and custom fields, Flows, recipes, forms and shops.