---
title: "REST API: the main endpoints"
description: "The Hive Mail REST API at a glance: base URL, every resource and the page that documents it, error format, paging and three tested requests."
url: "https://hiveplugins.com/docs/hive-mail-rest-api-reference/"
updated: "2026-10-28"
category: Hive Mail
---

# REST API: the main endpoints

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](/docs/hive-mail-rest-api-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](/docs/hive-mail-rest-api-campaigns-segments-reports/) |
| Contacts | `/contacts` | [Contacts, lists, tags and custom fields](/docs/hive-mail-rest-api-contacts-lists-tags-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](/docs/hive-mail-rest-api-campaigns-segments-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](/docs/hive-mail-rest-api-automation-and-commerce/) |
| Recipes | `/recipes` | same |
| Incoming webhook trigger | `/flows/{id}/webhook` | same, and [Webhooks and API keys](/docs/hive-mail-webhooks-and-api-keys/) |
| Bounce webhooks | `/webhooks/{provider}` | [Webhooks and API keys](/docs/hive-mail-webhooks-and-api-keys/) |
| Signup forms and connected forms | `/forms`, `/integrations` | [Flows, recipes, forms and shops](/docs/hive-mail-rest-api-automation-and-commerce/) |
| 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](/docs/hive-mail-webhooks-and-api-keys/) |
| Roles and permissions | `/roles` | [Roles & Permissions](/docs/hive-mail-settings-roles-and-permissions/) |
| License | `/license` | [License](/docs/hive-mail-settings-license/) |
| Email footer preview | `/email-footer/preview` | [Settings: Email footer](/docs/hive-mail-settings-email-footer/) |
| Check my list | `/email-checks` | [Check my list](/docs/hive-mail-check-my-list/) |
| AI assistant | `/ai` | by name only, see [The AI assistant](/docs/hive-mail-ai-assistant/) |
| Migration from FluentCRM | `/migration` | by name only, see [Migrate from FluentCRM](/docs/hive-mail-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](/docs/hive-mail-hooks-actions/), appear on the contact's timeline and can start flows.

Next: [Contacts, lists, tags and custom fields](/docs/hive-mail-rest-api-contacts-lists-tags-fields/), [Flows, recipes, forms and shops](/docs/hive-mail-rest-api-automation-and-commerce/).
