# Write, test and send email campaigns

> Write a campaign with the block designer, check it, send yourself a test, schedule it, and read its opens, clicks, bounces, orders and revenue.

Source: https://www.coritan.com/docs/organizations/commerce/email-campaigns/

In the dashboard:

- /dashboard/organizations/…/commerce/campaigns: https://www.coritan.com/dashboard/organizations

**Campaigns** is where you write the emails you send once to your audience. A campaign is one email, sent to lists and segments, now or at a time you choose. You write it with a block designer, check it, send yourself a test, and schedule it. Afterwards its report shows who opened and clicked, what bounced, and the orders and revenue it brought.

## Before you begin

- Set up the sending domain, its DNS records and your postal address ([Set up your sending domain and warm-up](/docs/organizations/commerce/email-sending/)). You can write campaigns before that, but none can start.
- You need Tier 1 support or higher to read campaigns and their reports. You need Tier 3 support or higher to write one and send a test. Scheduling, sending, pausing, resuming, cancelling and resending need an owner or admin.
- Have an audience: a list or a segment with subscribed contacts ([Manage your email contacts, lists and segments](/docs/organizations/commerce/email-contacts/)).
- For a coupon block, create the promotion first ([Create promotions and issue gift cards](/docs/organizations/commerce/discounts/)). For a product grid, publish the products.

## Start a campaign

1. In the [dashboard](https://www.coritan.com/dashboard/organizations), open the organization, then **Commerce**, then **Email**, then **Campaigns**.
2. Select **New campaign…**.
3. Enter a **Name**. Only you see it. You write the subject next.
4. Under **Start from**, choose **A blank design**, one of the **Starter designs**, or one of your **Saved designs**.
5. Select **Create campaign**.

The starter designs are plain first drafts to edit: Newsletter, Product announcement, Sale with coupon, Welcome, Restock reminder, Plain letter, Order follow-up thank you and Back in stock. Product grids and coupons start empty, so you choose your own products and promotion.

The editor opens with two views, **Setup** and **Design**, under **What to edit**. **Save** keeps everything. **Campaign actions** has **Open the report**, **Duplicate campaign** and **Delete campaign…**. The list of campaigns has the status filters **All**, **Draft**, **Scheduled**, **Sending**, **Paused**, **Sent** and **Cancelled**, and **Search by name**.

## Set up the campaign

**Setup** has these cards:

**Content**
: The **Campaign name**, the **Subject** and the **Preview text**, the line many inboxes show after the subject. Merge tags work in both. The subject shows its length, and many inboxes cut a subject at 78 characters.

**Sender**
: The **Sender name**, the **Address before the @** and **Reply to**. Leave them empty to use the ones in your sending settings. The address ends with your sending domain.

**Audience**
: Who gets the campaign. Tick **Send to these lists** and **Send to these segments**. A contact gets it when it is on any ticked list or in any ticked segment. Under **Leave some contacts out**, tick **Not on these lists** and **Not in these segments** to remove people. With nothing ticked, every subscribed contact gets it. The estimate above says how many contacts will get the campaign, after a short pause. Only subscribed contacts that are not on the suppression list are counted or mailed.

**A/B test**
: See [Test two subjects against each other](#ab-test).

**Tracking**
: **Count opens** adds a one-pixel image to each email. **Count clicks** sends links through our click tracker. **UTM campaign** and **UTM content** set the values added to your store's links.

**Sending speed**
: **Emails each minute** is the most this campaign sends in a minute. Leave it empty to use the limit in your sending settings. Your daily limit during warm-up still applies.

Links to your store in the email gain `utm_source=coritan`, `utm_medium=email`, `utm_campaign` (your **UTM campaign**, or a short form of the campaign's name) and `utm_content` (your **UTM content**, or the version's letter). A value already on the link stays as written. A link to your store also gains `mkt`, a code for the one reader, which lets the storefront credit an order to this email.

A campaign changes only while it is a draft or scheduled. Saving a scheduled campaign returns it to a draft, so schedule it again. A campaign that has started cannot change. Duplicate it to send a changed version.

### Test two subjects against each other {#ab-test}

An *A/B test* sends two or three versions to a small, random part of your audience. After the wait you set, the version with the best result goes to everyone else.

1. On **Setup**, turn on **Run an A/B test**. Version A is the subject, preview text and sender above.
2. Fill in version B: its **Subject**, **Preview text** and **Sender name**. Select **Add version C** for a third. The remove button on the last version drops it.
3. Set **Share of the audience in the test**, from 10 to 50 percent, split evenly between the versions.
4. Under **The winner has**, choose **Most opens**, **Most clicks** or **Most revenue**.
5. Set **Send the winner after**, from 1 to 72 hours.

The rest of the audience gets nothing until the winner is picked. The result is compared per email sent, and opens by mail privacy tools are left out. **Most revenue** falls back to clicks while no order is credited to any version. The report's **A/B test** card lists each version and marks the **Winner**. A test needs at least a version B, or the campaign refuses to schedule.

## Design the email

Open the **Design** view. The designer has the block list on the left, the preview in the middle and the properties of the chosen block on the right. Under 1024 pixels wide they stack.

- **Add block** adds a block. Select a block in the list to edit it. Use the arrows to move it, the copy button to duplicate it and the bin to delete it. You can also drag a block.
- **Undo** and **Redo** step through your changes.
- **Start from…** replaces the design with a starter design or a saved design. You can undo it.
- **Save design…** keeps the design, with its subject and preview text, as a saved design. Campaigns made from it keep their own copy.
- The preview follows a moment after you stop typing. Choose **Desktop** or **Phone**, and **Light** or **Dark**. Issues the preview finds list under it, and each one with a block selects that block.

A design holds up to 80 blocks, and columns hold up to 10 blocks each. These are the blocks:

| Block | What it holds |
| --- | --- |
| **Heading** | Text of up to 300 characters, a size (**Large**, **Medium** or **Small**), alignment and colour. |
| **Text** | Rich text with paragraphs, line breaks, bold, italic, underline, links and lists. |
| **Image** | An `https` **Image address**, **Alt text**, an optional **Link** and a **Width** from 16 to 1200 pixels. |
| **Button** | A **Label** of up to 60 characters, a **Link** and a colour. It defaults to the design's accent colour. |
| **Divider** | A line across the email. |
| **Spacer** | A **Height** from 4 to 160 pixels. |
| **Columns** | Two or three columns, each holding other blocks except columns and the footer. Columns do not nest. |
| **Social links** | Up to eight links to Facebook, Instagram, X, TikTok, YouTube, LinkedIn, Pinterest, Threads, Bluesky, Reddit, Discord or a website. |
| **HTML** | Your own HTML. Scripts, event handlers and anything that embeds are removed when you save. Use tables and inline styles. |
| **Product grid** | Up to 12 products from your catalogue, in one, two or three columns, with an option to **Show the price** and a **Button label**. The price and the link come from the catalogue when the email is sent, and a product that is not published is left out. |
| **Coupon** | A **Promotion**, a **Label** such as `Your code`, an optional **Note** and **Valid for** in hours, from 1 to 8,760. Each reader gets a single-use code copied from the promotion when the email is sent. The preview and test sends show `SAMPLE-CODE`. |
| **Footer** | Optional **Text**, **Show the postal address** and **Show the unsubscribe link**. One footer closes every email. |
| **Disclaimer** | A fixed paragraph in your own words, up to 1,500 characters. |

**Design settings** shows when no block is chosen: **Width** (480 to 720 pixels), **Font**, **Header** (**Store name** or **None**), and the **Accent colour**, **Page colour**, **Message colour**, **Text colour** and **Link colour**. A designed email is table-based HTML that fits phones and has a dark mode.

Every email that leaves carries the footer with your postal address and the unsubscribe link, whatever the footer shows in the preview. A footer that hides either fails the checks below.

### Merge tags

A *merge tag* is a placeholder such as `{{ first_name }}` that the email fills in with each reader's own details when it is sent. Select **Insert merge tag** to add one. Choose the **Detail** and, where a fallback makes sense, a **Fallback** for readers who have none. The email then holds `{{ first_name|default("there") }}`.

| Tag | What it holds |
| --- | --- |
| `{{ first_name }}`, `{{ last_name }}`, `{{ email }}` | The contact's details. |
| `{{ fields.key }}` | A custom field, by its key. |
| `{{ store_name }}` | Your store's name. |
| `{{ shop_url }}` | Your storefront's address. |
| `{{ unsubscribe_url }}`, `{{ preferences_url }}` | The links that unsubscribe the reader and open their preference center. |
| `{{ coupon_code }}` | The reader's code, inside a coupon block. |

A tag that names nothing renders empty, or as its fallback. It never shows an error or the word `None`. A tag that cannot be read refuses the save and names the block.

## Check before sending

Select **Check before sending…**. The dialog opens as **Ready to send** or **Not ready to send**, shows the **Recipients** and the *spam score*, and groups the issues. The spam score runs from 0 (clean) to 10 and comes from the subject and the words of the email. Mail filters make their own call, so a low score is no promise. The checks run again each time you open the dialog, and nothing is sent or saved.

- **To fix before sending** holds errors. A campaign with an error cannot be scheduled.
- **To check** holds warnings. They advise and never stop a send.
- **Tips** holds ideas.

An issue that names a block has **Show block**. This is what the checks look for:

| Level | Check |
| --- | --- |
| Error | No subject (`missing_subject`), no content (`no_content`), no recipients (`no_recipients`), the sending domain not set up (`sender_not_set`), no postal address (`no_address`, also when the footer hides it), no unsubscribe link (`no_unsubscribe`), and a design the schema refuses (`invalid_design`). |
| Error | A block that links nowhere (`missing_link`), a link that is not a valid web, email or phone address (`malformed_link`), a link to a local address (`local_link`), a coupon with no promotion or one made from a single-use code (`coupon_unavailable`), and sending that is not ready (`sending_not_ready`). |
| Warning | A link that starts with `http` (`insecure_link`), an image with no alt text (`image_missing_alt`), an empty text block, button or image, an empty product grid, and products that are not published. |
| Warning | A message over 102 KB, which Gmail clips and hides the footer of (`message_too_large`), a subject over 78 characters, a subject in capital letters, words spam filters watch for (`spam_words`), and a spam score of 6 or more (`spam_risk`). |
| Warning | Today's daily limit is used up (`daily_limit`). A send that does not fit continues the next day. |
| Tip | A transparent PNG that can vanish on a dark background (`dark_mode_image`), and an email with no button (`no_call_to_action`). |

An A/B test is checked version by version, and each issue starts with the version.

## Send a test

Select **Send test…**. Enter up to five addresses in **Send to**, separated by commas or lines. For an A/B test, choose the **Version**. Then select **Send test**.

The subject starts with `[Test]`, the merge tags use a sample contact, links are not tracked and a coupon shows a sample code. An address on the suppression list is skipped, and the dialog says which. A person or key can send 20 test requests an hour. A test needs the sending domain set up, as a real send does.

## Schedule or send

Select **Schedule…**. The dialog shows the **Campaign**, **Subject**, **Sender**, **Audience** and **Recipients**.

1. Choose **Send now**, which queues every recipient at once and sends as fast as your limits allow, or **Send at a time**.
2. For **Send at a time**, pick the **Date and time**. The dialog names the time zone: your organization's, or your browser's when the organization has none set. The time must be in the future.
3. Select **Send now** or **Schedule campaign**.
4. Confirm it is you. The dashboard asks for your password, or a code when two-factor authentication is on ([Confirm it is you](/docs/organizations/roles-and-permissions/#confirm-it-is-you)). An API key with the send scope is never asked.

The campaign becomes **Sending** or **Scheduled**, and the dashboard opens its report. A scheduled campaign is queued when its time comes. An audience of more than 20,000 contacts is queued within a minute instead of at once.

If sending is not ready, the dialog says **Sending is not ready**, with the reason and a link to **Sending**. If the checks find an error, it says **The checks found a problem** and lists the errors.

### Pause, resume and cancel

An owner or admin can change a campaign that has started, from its report or from **Campaigns**:

- **Pause…** stops a sending campaign. Emails not sent yet wait and go out when you resume.
- **Resume** continues a paused campaign. It needs sending to be ready.
- **Cancel campaign…** ends a scheduled, sending or paused campaign for good. Emails not sent yet are skipped. Emails already sent stay in the figures.

A campaign also pauses by itself when, after at least 200 emails are delivered, its bounce rate or spam report rate goes over your limits, or when its scheduled time comes and sending is not ready. The report says why, and you resume it after you fix the cause ([Set up your sending domain and warm-up](/docs/organizations/commerce/email-sending/#automatic-pausing)).

## Read the report

A campaign that has started opens on its report. A campaign that is sending updates every ten seconds, with the number sent out of the recipients. Rates are shares of the emails delivered, or of those sent until delivery is known.

**Results**
: **Delivered** (out of the emails sent), **Open rate**, **Click rate**, **Unsubscribed**, **Bounced**, **Marked as spam**, **Orders** and **Revenue**. The open and click rates also show how many readers opened and clicked.

**Opens and clicks over time**
: A chart **By hour** or **By day**.

**Link clicks**
: Each link in the email with its clicks, readers and share of all clicks.

**Devices**, **Mail apps** and **Countries**
: Where opens and clicks came from.

**A/B test**
: Each version's emails sent, opens and clicks with their rates, and revenue.

**Bounces**
: Why emails did not arrive, with the number for each reason.

**Recipients**
: Everyone the campaign was queued for, with the **Version**, the **Result**, **Opens**, **Clicks**, **Revenue** and when it was **Sent**. Search by address, and filter the **Result**: **Any result**, **Delivered**, **Opened**, **Clicked**, **Unsubscribed**, **Bounced**, **Marked as spam**, **Failed**, **Delayed**, **Waiting to go** or **Held back**.

### Machine opens {#machine-opens}

Some mail apps, such as Apple Mail's privacy protection, and some security scanners open every message and load every image as it arrives. Those opens say nothing about the reader. We count them apart as *machine opens*, and leave them out of open rates, clicks, a contact's engagement and the A/B test. The report shows how many there were. A click by a link scanner is left out the same way. Without this split, a campaign's open rate would read far higher than the number of people who read it.

An open or click counts as machine made when it comes from a mail privacy proxy or a scanner, has no browser information, or arrives within seconds of the send.

### How orders and revenue are credited

An order is credited to one email, and only once. We use the first rule that finds an email:

1. The code your storefront kept. A link to your store carries `mkt`, the code for that reader's email. When the storefront keeps it with the shopper's visit, the order goes to that email ([Send where the shopper came from](/docs/organizations/storefront/store-api/#send-where-the-shopper-came-from)). The last visit is read first.
2. The reader's email address. Otherwise the order goes to the email sent to the order's address that the reader last clicked, as a person and not a scanner.

Either way, the click must fall in the 7 days before the order was placed. A cancelled or refunded order is never credited. **Orders** counts the credited orders, and **Revenue** adds up their totals as they are. A store that sells in several currencies sees them added together without conversion.

## Resend to people who did not open

Once a campaign is **Sent**, an owner or admin can open **Campaign actions** on its report and choose **Resend to non-openers…**. Enter what to **Start the subject with**, such as the default `Reminder:`, or leave it empty to keep the subject. Select **Create draft**.

The new draft has the same content and audience, aimed at the contacts who were sent the campaign and did not open it. A machine open does not count as an open. The draft is marked as a resend of the earlier campaign. Review it, then schedule it like any campaign.

**Duplicate campaign** makes a new draft named `Copy of` and the original's name, with the same content, audience, A/B settings and versions, and none of the results.

## Limits and errors

| Where | What happens |
| --- | --- |
| Sending is not ready | The dialog or the API names the reason: `not_set` (no sending domain), `pending_dns` (records missing), `no_address` (no postal address), `paused`, `staff_paused` or `daily_limit`. [Set up your sending domain and warm-up](/docs/organizations/commerce/email-sending/#when-sending-is-not-ready) says what to do for each. `daily_limit` stops a send now, but not a scheduled send or a resume. |
| The checks found an error | Scheduling answers `422` with `checks_failed` and the `issues`. Fix each error and schedule again. |
| The campaign has started | A change answers `409` with `campaign_locked`. Duplicate it. |
| Delete a campaign | Only a draft or a cancelled campaign can be deleted. A cancelled campaign that already sent some emails stays as the record of who got them (`campaign_sent`). |
| Test sends | 20 requests an hour for each person or key, and up to five addresses in each. |
| Audience | Only subscribed contacts that are not on the suppression list, in the campaign's own mode. A store in test mode can mail test contacts only. |

## Result

- A campaign you send is **Sending**, then **Sent**, and its report fills in as emails arrive and readers act.
- Orders that follow a click appear under **Orders** and **Revenue**, and in the campaigns table on [Overview](/docs/organizations/commerce/email-marketing/#read-the-overview).

## Troubleshooting

**Schedule…** is missing
: Scheduling and sending need an owner or admin. A member with Tier 3 support can write the campaign and send tests, and the page says so.

The dialog says **Sending is not ready**
: Open **Sending** and follow the reason. A campaign scheduled for later and not ready at its time pauses with the reason in the report.

The checks say there are no recipients
: No subscribed contact is on the lists and segments you chose, or every one is excluded. Check the estimate on **Setup**, and that the contacts are of the campaign's own mode.

The open rate looks lower than other tools show
: We leave machine opens out. The report shows how many there were.

A test never arrives
: The address may be on the suppression list, or sending is not ready. The dialog lists skipped addresses.

The campaign paused by itself
: Its bounce rate or spam report rate went over your limits, or its time came while sending was not ready. The report shows the reason on the campaign.

## Related

- [Set up email marketing for your store](/docs/organizations/commerce/email-marketing/)
- [Manage your email contacts, lists and segments](/docs/organizations/commerce/email-contacts/)
- [Set up your sending domain and warm-up](/docs/organizations/commerce/email-sending/)
- [Create promotions and issue gift cards](/docs/organizations/commerce/discounts/)

## With the API

The routes are under `https://api.coritan.com/api/v1/orgs/{org_slug}/marketing/`. Reading takes `marketing.campaigns:read`, writing and test sends take `marketing.campaigns:write`, and scheduling, sending, pausing, resuming, cancelling and resending take `marketing.campaigns:send` ([API key scopes](/docs/organizations/commerce/email-marketing/#api-key-scopes)).

### Write a campaign

`POST /campaigns` starts a draft and answers `201` with `{campaign}`. Only `name` is required:

```bash
curl -X POST "https://api.coritan.com/api/v1/orgs/acme/marketing/campaigns" \
  -H "X-API-Key: $ORG_API_KEY" -H "Content-Type: application/json" \
  -d '{"name": "October restock", "subject": "New games are in, {{ first_name|default(\"there\") }}",
       "preheader": "Fresh stock on the shelves.", "list_ids": [3], "exclude_segment_ids": [8],
       "design": {"settings": {"content_width": 600}, "blocks": [
         {"id": "title", "type": "heading", "text": "New in the shop", "level": 1},
         {"id": "cta", "type": "button", "label": "Shop now", "href": "{{ shop_url }}"},
         {"id": "footer", "type": "footer", "show_address": true, "show_unsubscribe": true}]}}'
```

| Field | What it takes |
| --- | --- |
| `name` | Up to 150 characters. |
| `subject`, `preheader` | Up to 200 characters. Merge tags work. |
| `from_name`, `from_local`, `reply_to` | The sender. Your sending settings' values when left out. |
| `template_id` | A saved design whose blocks are copied in. A `design` in the same request wins. |
| `design` | `{"settings": {...}, "blocks": [Block]}`. Every block has an `id` and a `type`. A refused design answers `422` with a `field` such as `design.blocks[3].href` and a `block_id`. |
| `list_ids`, `segment_ids` | Where to send. With neither, every subscribed contact. Every id must be yours. |
| `exclude_list_ids`, `exclude_segment_ids` | Contacts to leave out. |
| `ab` | `{enabled, test_percent (10 to 50), winner_metric (opens, clicks or revenue), winner_after_hours (1 to 72)}`. `null` turns the test off. |
| `throttle_per_minute` | Emails a minute for this campaign. |
| `track_opens`, `track_clicks` | `true` when left out. |
| `utm` | `{campaign, content}`. |
| `livemode` | The store's own mode when left out. A test-mode store can mail test contacts only. |

A campaign is `{id, name, status, livemode, subject, preheader, from_name, from_local, from_address, reply_to, template_id, design, list_ids, segment_ids, exclude_list_ids, exclude_segment_ids, ab, variants, schedule_at, throttle_per_minute, track_opens, track_clicks, utm, parent_id, recipients_count, started_at, completed_at, paused_reason, stats, created_at, updated_at}`. `status` is `draft`, `scheduled`, `sending`, `paused`, `sent` or `canceled`.

`stats` holds `queued`, `sent`, `delivered`, `bounced`, `complained`, `failed`, `unsubscribed`, `opens`, `unique_opens`, `machine_opens`, `clicks`, `unique_clicks`, the rates `open_rate`, `click_rate`, `click_to_open_rate`, `bounce_rate`, `complaint_rate` and `unsubscribe_rate` as numbers from 0 to 1, `orders`, `revenue` in minor units, and `currency`.

- `GET /campaigns` lists campaigns, newest first, as `{total, limit, offset, campaigns}`. `status` filters and `q` searches the name.
- `GET /campaigns/{campaign_id}` reads one. `PATCH` changes the fields you send. `DELETE` removes a draft or a cancelled campaign.
- `PUT /campaigns/{campaign_id}/variants` takes `[{key, subject, preheader, from_name, design}]` for versions `A`, `B` and `C`. Version A is the campaign's own. An empty list removes B and C. Turn the test on with `ab`.
- `POST /campaigns/{campaign_id}/duplicate` answers a new draft.
- `POST /campaigns/audience/estimate` takes `list_ids`, `segment_ids`, `exclude_list_ids`, `exclude_segment_ids` and `livemode`, and answers `{count}`.

### Check, test and send

`POST /campaigns/{campaign_id}/checks` answers `{issues, recipients, spam_score, ready}`. An issue is `{level, code, message, block_id}`, with `level` `error`, `warning` or `info`. `ready` is `false` while any issue is an error.

`POST /campaigns/{campaign_id}/test` takes `{"emails": ["owner@example.com"], "variant": "B"}` and answers `{sent, skipped}`.

`POST /campaigns/{campaign_id}/schedule` sends now, or at `schedule_at` as an ISO 8601 time. A time that has passed, or `null`, sends now. A person must have confirmed it is them in the last 10 minutes, and without it the answer is `403` with `reauth_required`. An API key with `marketing.campaigns:send` is not asked.

```bash
curl -X POST "https://api.coritan.com/api/v1/orgs/acme/marketing/campaigns/41/schedule" \
  -H "X-API-Key: $ORG_API_KEY" -H "Content-Type: application/json" \
  -d '{"schedule_at": "2026-10-20T09:00:00Z"}'
```

The answer is `{campaign}`. Asking again for a campaign that is already sending changes nothing. The refusals:

| Answer | Meaning |
| --- | --- |
| `409` with `sending_not_ready` and a `reason` | `not_set`, `pending_dns`, `no_address`, `paused`, `staff_paused` or `daily_limit`. |
| `422` with `checks_failed` and `issues` | The checks found an error. |
| `409` with `campaign_locked` | The campaign is paused, sent or cancelled. |
| `422` with `invalid` on `ab` | An A/B test needs a version B. |

`POST /campaigns/{campaign_id}/pause` takes an optional `reason` and answers `{campaign}`. `POST .../resume` continues a paused campaign. `POST .../cancel` answers `{campaign, skipped}`. `POST .../resend-unopened` takes an optional `subject_prefix` and answers `201` with a new draft that has `parent_id` set. Each answers `409` with `invalid_status` when the campaign is in another status.

### Reports

`GET /campaigns/{campaign_id}/report?granularity=hour` answers the campaign, its `stats`, a `timeline` of `{at, opens, clicks}`, the `links` with each one's `share`, the `devices`, `clients` and `countries` as `{key, count}`, the `bounces` by reason, and for an A/B test the `ab` versions with the `winner` marked. `granularity` is `hour` or `day`.

`GET /campaigns/{campaign_id}/recipients` lists who the campaign was queued for. `status` takes a send status (`queued`, `sending`, `sent`, `delivered`, `deferred`, `bounced`, `complained`, `failed`, `suppressed` or `skipped`) or `opened`, `clicked` or `unsubscribed`, and `q` searches addresses. A recipient is `{send_id, contact_id, email, variant, status, error, scheduled_at, sent_at, delivered_at, first_open_at, first_click_at, opens, machine_opens, clicks, orders, revenue, bounced_at, complained_at, unsubscribed_at}`.

`GET /reports/campaigns?since&until` is a table of every campaign that has started, newest first, with its stats. Without a span it covers all time.

### Designs

`GET /templates` lists saved designs, `?kind=campaign` or `automation`. `POST /templates` saves one from `name`, `kind`, `design`, `subject` and `preheader`. `GET`, `PATCH` and `DELETE /templates/{template_id}` read, change and delete one. `GET /templates/presets` answers the starter designs as `{presets: [{key, name, description, design, subject, preheader}]}`.

`POST /render` draws a design without saving it. Send `design`, and optionally `subject`, `preheader`, a sample `contact` and `dark: true`. The answer is `{html, text, issues}`, with `html_dark` when you asked for it.

## API

- `GET /api/v1/orgs/{org_slug}/marketing/campaigns`: Your campaigns, newest first: {total, limit, offset, campaigns} (https://www.coritan.com/docs/api/reference/organizations/marketing/marketing/#op-get-api-v1-orgs-org-slug-marketing-campaigns)
- `POST /api/v1/orgs/{org_slug}/marketing/campaigns`: Start a draft: {campaign} (https://www.coritan.com/docs/api/reference/organizations/marketing/marketing/#op-post-api-v1-orgs-org-slug-marketing-campaigns)
- `POST /api/v1/orgs/{org_slug}/marketing/campaigns/audience/estimate`: How many contacts a campaign with this audience would reach: {count} (https://www.coritan.com/docs/api/reference/organizations/marketing/marketing/#op-post-api-v1-orgs-org-slug-marketing-campaigns-audience-estimate)
- `GET /api/v1/orgs/{org_slug}/marketing/campaigns/{campaign_id}`: Get campaign (https://www.coritan.com/docs/api/reference/organizations/marketing/marketing/#op-get-api-v1-orgs-org-slug-marketing-campaigns-campaign-id)
- `PATCH /api/v1/orgs/{org_slug}/marketing/campaigns/{campaign_id}`: Change the fields you send (https://www.coritan.com/docs/api/reference/organizations/marketing/marketing/#op-patch-api-v1-orgs-org-slug-marketing-campaigns-campaign-id)
- `DELETE /api/v1/orgs/{org_slug}/marketing/campaigns/{campaign_id}`: Delete a draft or a canceled campaign (https://www.coritan.com/docs/api/reference/organizations/marketing/marketing/#op-delete-api-v1-orgs-org-slug-marketing-campaigns-campaign-id)
- `POST /api/v1/orgs/{org_slug}/marketing/campaigns/{campaign_id}/cancel`: Stop a scheduled, sending or paused campaign for good: {campaign, skipped} (https://www.coritan.com/docs/api/reference/organizations/marketing/marketing/#op-post-api-v1-orgs-org-slug-marketing-campaigns-campaign-id-cancel)
- `POST /api/v1/orgs/{org_slug}/marketing/campaigns/{campaign_id}/checks`: What to fix before sending: {issues, recipients, spamscore, ready} (https://www.coritan.com/docs/api/reference/organizations/marketing/marketing/#op-post-api-v1-orgs-org-slug-marketing-campaigns-campaign-id-checks)
- `POST /api/v1/orgs/{org_slug}/marketing/campaigns/{campaign_id}/duplicate`: Duplicate campaign (https://www.coritan.com/docs/api/reference/organizations/marketing/marketing/#op-post-api-v1-orgs-org-slug-marketing-campaigns-campaign-id-duplicate)
- `POST /api/v1/orgs/{org_slug}/marketing/campaigns/{campaign_id}/pause`: Stop sending a campaign that is sending: {campaign} (https://www.coritan.com/docs/api/reference/organizations/marketing/marketing/#op-post-api-v1-orgs-org-slug-marketing-campaigns-campaign-id-pause)
- `GET /api/v1/orgs/{org_slug}/marketing/campaigns/{campaign_id}/recipients`: List recipients (https://www.coritan.com/docs/api/reference/organizations/marketing/marketing/#op-get-api-v1-orgs-org-slug-marketing-campaigns-campaign-id-recipients)
- `GET /api/v1/orgs/{org_slug}/marketing/campaigns/{campaign_id}/report`: What one campaign did (https://www.coritan.com/docs/api/reference/organizations/marketing/marketing/#op-get-api-v1-orgs-org-slug-marketing-campaigns-campaign-id-report)
- `POST /api/v1/orgs/{org_slug}/marketing/campaigns/{campaign_id}/resend-unopened`: Resend unopened (https://www.coritan.com/docs/api/reference/organizations/marketing/marketing/#op-post-api-v1-orgs-org-slug-marketing-campaigns-campaign-id-resend-unopened)
- `POST /api/v1/orgs/{org_slug}/marketing/campaigns/{campaign_id}/resume`: Continue a paused campaign: {campaign} (https://www.coritan.com/docs/api/reference/organizations/marketing/marketing/#op-post-api-v1-orgs-org-slug-marketing-campaigns-campaign-id-resume)
- `POST /api/v1/orgs/{org_slug}/marketing/campaigns/{campaign_id}/schedule`: Send the campaign now, or at scheduleat: {campaign} (https://www.coritan.com/docs/api/reference/organizations/marketing/marketing/#op-post-api-v1-orgs-org-slug-marketing-campaigns-campaign-id-schedule)
- `POST /api/v1/orgs/{org_slug}/marketing/campaigns/{campaign_id}/test`: Send test (https://www.coritan.com/docs/api/reference/organizations/marketing/marketing/#op-post-api-v1-orgs-org-slug-marketing-campaigns-campaign-id-test)
- `PUT /api/v1/orgs/{org_slug}/marketing/campaigns/{campaign_id}/variants`: Put variants (https://www.coritan.com/docs/api/reference/organizations/marketing/marketing/#op-put-api-v1-orgs-org-slug-marketing-campaigns-campaign-id-variants)
- `GET /api/v1/orgs/{org_slug}/marketing/templates`: The saved designs, most recently changed first: {total, limit, offset, templates} (https://www.coritan.com/docs/api/reference/organizations/marketing/marketing/#op-get-api-v1-orgs-org-slug-marketing-templates)
- `POST /api/v1/orgs/{org_slug}/marketing/templates`: Save a design as a template (https://www.coritan.com/docs/api/reference/organizations/marketing/marketing/#op-post-api-v1-orgs-org-slug-marketing-templates)
- `GET /api/v1/orgs/{org_slug}/marketing/templates/presets`: The starter designs: {presets: [{key, name, description, design, subject, preheader}]} (https://www.coritan.com/docs/api/reference/organizations/marketing/marketing/#op-get-api-v1-orgs-org-slug-marketing-templates-presets)
- `GET /api/v1/orgs/{org_slug}/marketing/templates/{template_id}`: One template: {template} (https://www.coritan.com/docs/api/reference/organizations/marketing/marketing/#op-get-api-v1-orgs-org-slug-marketing-templates-template-id)
- `PATCH /api/v1/orgs/{org_slug}/marketing/templates/{template_id}`: Change the fields you send (https://www.coritan.com/docs/api/reference/organizations/marketing/marketing/#op-patch-api-v1-orgs-org-slug-marketing-templates-template-id)
- `DELETE /api/v1/orgs/{org_slug}/marketing/templates/{template_id}`: Delete a template (https://www.coritan.com/docs/api/reference/organizations/marketing/marketing/#op-delete-api-v1-orgs-org-slug-marketing-templates-template-id)
- `POST /api/v1/orgs/{org_slug}/marketing/render`: Render preview (https://www.coritan.com/docs/api/reference/organizations/marketing/marketing/#op-post-api-v1-orgs-org-slug-marketing-render)
- `GET /api/v1/orgs/{org_slug}/marketing/reports/campaigns`: Campaigns table (https://www.coritan.com/docs/api/reference/organizations/marketing/marketing/#op-get-api-v1-orgs-org-slug-marketing-reports-campaigns)
