> ## Documentation Index
> Fetch the complete documentation index at: https://docs.paubox.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Overview

> Base URL, authentication, core concepts, and conventions for the Paubox Marketing API.

## Base URL

`https://api.paubox.com/v1/marketing`

## Authorization

Include an `Authorization` header with every request:

`Authorization: Token token=YOUR_API_KEY`

Replace `YOUR_API_KEY` with your API key. Generate your key on the [Paubox Marketing > Settings](https://next.paubox.com/marketing/settings) page (note: each API key is displayed only once upon creation):

<Frame>
  ![](https://docs.paubox.com/services/api/attachments/07715022-3347-4753-9125-2418540efb57)
</Frame>

## Example call

```bash theme={null}
curl -X POST \
  https://api.paubox.com/v1/marketing/subscribers \
  -H 'Authorization: Token token=YOUR_API_KEY' \
  -H 'Content-Type: application/json' \
  -d '{
    "subscriber": {
      "email": "recipient@example.com",
      "first_name": "Jane",
      "last_name": "Smith"
    }
  }'
```

## Core concepts

Four resources cover most of what the Marketing API does. Understanding how they relate makes the rest of the reference easier to navigate.

| Resource | What it is |
| - | - |
| **Campaign mailing** | The email itself — subject, HTML body, text body. Creating one only stores content; it sends nothing. |
| **Subscriber** | A person in your account, identified by email address. |
| **List** | Who receives a campaign. Either a *subscription list* (explicit membership) or a *dynamic list* (membership computed from saved filters). |
| **Subscription** | The link between one subscriber and one list. This is where a list-level opt-out is recorded. |

A **send** ties them together: it takes one campaign mailing, one list (or an explicit set of recipient addresses), and delivers the mailing to that audience. The same mailing can be sent more than once, and each send is tracked separately in analytics.

<Note>
  **Subscription lists vs. dynamic lists:**

  Membership in a subscription list is explicit — you add and remove subscribers yourself. Membership in a dynamic list is derived from filters saved on the list, so it changes as your subscriber data changes. Endpoints that act on a list in bulk come in two variants for this reason: `bulk_global_*` for subscription lists, `dynamic_bulk_*` for dynamic lists.
</Note>

## Send a campaign

<Steps>
  <Step title="Create the campaign mailing">
    [Create a campaign](/marketing/campaigns/create) with your subject and content. `subject` is required and must be unique within your account.

    ```bash theme={null}
    curl -X POST \
      https://api.paubox.com/v1/marketing/campaign_mailings \
      -H 'Authorization: Token token=YOUR_API_KEY' \
      -H 'Content-Type: application/json' \
      -d '{
        "campaign_mailing": {
          "subject": "March newsletter",
          "html_part": "<html><body><div>Hello</div></body></html>",
          "text_part": "Hello",
          "template_type": "html"
        }
      }'
    ```

    The response contains the new mailing's ID, which you will need for every step that follows:

    ```json theme={null}
    { "data": { "id": "123e4567-e89b-12d3-a456-426614174000" } }
    ```

    An unsubscribe footer is appended to `html_part` automatically, so you do not need to add one yourself.
  </Step>

  <Step title="Preview it">
    [Send a test email](/marketing/campaigns/send-test-email) to a single address to check rendering before you send to a list. The subject is prefixed with `[Test]`, and test sends are not recorded in analytics.

    ```bash theme={null}
    curl -X GET \
      'https://api.paubox.com/v1/marketing/campaign_mailings/123e4567-e89b-12d3-a456-426614174000/send_test_email?to_email=me@example.com' \
      -H 'Authorization: Token token=YOUR_API_KEY'
    ```

    A successful test send returns `204 No Content` with an empty body.
  </Step>

  <Step title="Revise if needed">
    [Update the campaign](/marketing/campaigns/update) to change any field. Only the fields you send are modified.
  </Step>

  <Step title="Send or schedule">
    [Send the campaign](/marketing/campaigns/send) to go out now, or [schedule it](/marketing/campaigns/schedule) for a future time. Both take the campaign mailing ID plus a target — a `subscription_list_id`, a `dynamic_list_id`, or an explicit list of `recipient_emails`.

    <Note>
      **Sending is asynchronous.**

      A successful response means the send was accepted and queued, not that delivery has finished. Track progress through the [campaign analytics](/marketing/analytics/campaigns) endpoints rather than the send response.
    </Note>
  </Step>

  <Step title="Measure">
    Use the analytics endpoints to see how the campaign performed: [send totals](/marketing/analytics/campaigns), [per-send results](/marketing/analytics/campaign-sends), [individual deliveries](/marketing/analytics/deliveries), and [tracking link engagement](/marketing/analytics/tracking-links).
  </Step>
</Steps>

<Tip>
  **Tip:**

  A brand-new Paubox Marketing account may be temporarily prevented from scheduling its first campaign as an anti-abuse measure. If you hit a `403` with a message about new accounts, contact [support@paubox.com](mailto:support@paubox.com) and they can clear it for you.
</Tip>

## Manage opt-ins and opt-outs

Paubox Marketing records two independent levels of opt-out, and it matters which one you use.

| Level | Where it lives | Effect |
| - | - | - |
| **List-level** | `unsubscribed_at` on the subscription | The subscriber stops receiving campaigns sent to that one list. They still receive campaigns sent to other lists. |
| **Global** | `opted_out_on` on the subscriber | The subscriber receives nothing, across every list. |

Which one an endpoint applies depends on whether you send `subscription_list_ids`:

* [`POST /subscriptions/unsubscribe`](/marketing/subscriptions/unsubscribe) **with** `subscription_list_ids` records a list-level opt-out.
* The same endpoint **without** `subscription_list_ids` records a global opt-out.
* [`POST /subscriptions/subscribe`](/marketing/subscriptions/subscribe) always clears the global opt-out, because a subscriber who is subscribed to any list is by definition not globally unsubscribed.

<Warning>
  Treat a global opt-out as permanent unless the recipient asks to be resubscribed. Re-subscribing someone who opted out, without a new request from them, is exactly the pattern that damages sending reputation and can put you out of compliance with anti-spam rules.
</Warning>

For a single subscriber and list you already have a subscription record for, [deleting the subscription](/marketing/subscriptions/delete) is the most direct route — it stamps `unsubscribed_at` and leaves everything else untouched.

### Acting on a whole list

When you want to opt out an entire list rather than a known set of subscribers, use the bulk endpoints. They accept a `from_subscription_list_id` and operate on everyone in that list, minus any `except_ids` you supply — useful for "select all except these" flows.

* Subscription lists: [bulk global subscribe](/marketing/subscriptions/bulk-global-subscribe) and [bulk global unsubscribe](/marketing/subscriptions/bulk-global-unsubscribe)
* Dynamic lists: [bulk subscribe](/marketing/subscriptions/dynamic-bulk-subscribe) and [bulk unsubscribe](/marketing/subscriptions/dynamic-bulk-unsubscribe)

These endpoints run in the background. See [Background jobs](#background-jobs) below.

## API conventions

### Identifiers

Records are identified by UUID. A `campaign_mailing_id`, `subscription_list_id`, or subscriber ID in a URL or request body is always the UUID form:

```
123e4567-e89b-12d3-a456-426614174000
```

See [Locating parameter values](/marketing/parameter-values) for where to find each one in the dashboard.

<Note>
  **One exception:**

  [`POST /subscriptions`](/marketing/subscriptions/create) takes the subscriber's internal numeric ID rather than the UUID. To add a subscriber to a list using the UUID you already have, [create the subscriber](/marketing/subscribers/create) with a `subscription_list_id` instead, or use [`POST /subscriptions/subscribe`](/marketing/subscriptions/subscribe).
</Note>

### Response shapes

Most read endpoints return resources in JSON:API form — an `id`, a `type`, and the fields nested under `attributes`:

```json theme={null}
{
  "data": {
    "id": "123e4567-e89b-12d3-a456-426614174000",
    "type": "campaign_mailing",
    "attributes": {
      "subject": "March newsletter",
      "created_at": "2026-03-01T12:00:00Z"
    }
  }
}
```

Write endpoints are terser. Creating or updating a campaign mailing returns only the ID, not the full record:

```json theme={null}
{ "data": { "id": "123e4567-e89b-12d3-a456-426614174000" } }
```

List and detail responses for the same resource do not always carry the same fields. Listing campaign mailings includes aggregate counts (`sent_count`, `delivered_count`, and so on) but omits the content; fetching a single one includes `html_part`, `text_part`, and `form_data` but omits the counts. Each reference page documents its own response.

### Errors

<Warning>
  **Check the response body, not just the status code.**

  Several Marketing API write endpoints return `200 OK` when a request fails validation, with the problem reported in an `errors` key instead of `data`. A client that branches only on HTTP status will treat these as successes.
</Warning>

A failed write looks like this:

```json theme={null}
{ "errors": ["Subject has already been taken"] }
```

Endpoints that behave this way include [creating](/marketing/campaigns/create) and [updating](/marketing/campaigns/update) a campaign mailing, [creating a subscription](/marketing/subscriptions/create), and the subscribe and unsubscribe endpoints. Treat the presence of `errors` as the failure signal, and fall back to the status code for authentication (`401`), missing records (`404`), and server errors (`500`).

### Pagination

List endpoints are paginated by default. Control it with:

| Parameter | Purpose |
| - | - |
| `items` | Records per page |
| `page` | Which page to return |
| `pagination` | Set to `false` to disable paging and return everything in one response |

Pagination metadata is returned in the response headers. Some endpoints additionally include a `page_info` object in the response body.

### Background jobs

Operations that can affect a large number of records do not run inline. Instead they queue a job and return its identifier immediately:

```json theme={null}
{ "data": { "jid": "a1b2c3d4e5f6a7b8c9d0e1f2" } }
```

A `jid` in the response means the work was accepted, not that it has finished. Bulk subscribe and unsubscribe across a whole list behave this way; supplying an explicit `subscriber_ids` array to those same endpoints processes the change inline and returns the affected subscribers instead.

### Dates

Dates are passed as ISO8601 strings with UTC timezone.

## Finding id parameters for subscription\_list\_id, campaign\_mailing\_id, etc.

<CardGroup>
  <Card title="Finding parameter values in the web dashboard" icon="file-text" href="/marketing/parameter-values" horizontal />
</CardGroup>

## Community & support

<CardGroup cols={2}>
  <Card title="Q&A" icon="comments" href="https://github.com/Paubox/community/discussions/categories/paubox-marketing">
    Ask usage questions in the Paubox Community.
  </Card>

  <Card title="Ideas" icon="lightbulb" href="https://github.com/Paubox/community/discussions/categories/ideas">
    Propose features and improvements.
  </Card>
</CardGroup>

<Warning>
  Never post PHI, recipient addresses, or message content in public threads. Account, billing, or anything sensitive goes to [support@paubox.com](mailto:support@paubox.com).
</Warning>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.