# Set up email marketing for your store

> Build an audience, send campaigns, run automations and collect signups from your store, with consent recorded and an unsubscribe link in every email.

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

In the dashboard:

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

**Email**, in the **Commerce** tab, is where your store writes to the people who agreed to hear from it. It holds your audience (contacts, lists and segments), the campaigns you send once, the automations that send on their own, the signup forms that collect new contacts, and the sending setup. Marketing email leaves from a sending domain of your own, through Coritan's mail platform. It never uses the sender that carries your order emails, which keeps the reputation of your order emails apart from your marketing.

It is for a store that sends news, offers and follow-ups to its own audience, such as a board game shop announcing a restock or a coffee roaster sending a monthly letter. Every organization with a store can use it.

The store's own emails are separate. Order emails ([Write the emails your store sends](/docs/organizations/commerce/emails/)) and the three flows for abandoned carts, unpaid checkouts and restocks ([Win back shoppers with marketing emails](/docs/organizations/commerce/marketing/)) keep working as they are. The flows and email marketing share one suppression list, so an address that unsubscribes from either gets nothing from the other.

## Before you begin

- Your organization needs a store. Ask [support](https://www.coritan.com/dashboard/support) to turn commerce on, as [Run your store from the Commerce tab](/docs/organizations/commerce/#before-you-begin) explains.
- You need a role that opens the section you want, as [Who can do what](#who-can-do-what) shows. Tier 1 support and above can read every section except **Sending**, which needs Tier 3 support.
- To send, you need to change DNS records for the domain you send from, or have your DNS on Coritan DNS, which publishes them for you ([Set up your sending domain and warm-up](/docs/organizations/commerce/email-sending/)).
- You need a postal address to show in every email. A campaign cannot start without one.

## How the pieces fit

Contact
: A person you can email: an address, optional names, a status, tags, the lists they are on, custom fields, and the record of their consent. [Manage your email contacts, lists and segments](/docs/organizations/commerce/email-contacts/) covers contacts.

List
: A group of contacts who signed up for the same kind of email. A contact can be on many lists. One list is the default: a signup that names no list goes there.

Tag and custom field
: Labels and extra details you keep on contacts, such as a company or a birthday. Use them in segments, forms and emails.

Segment
: A saved set of rules that picks contacts, such as everyone who opened your last email. It stays up to date by itself.

Campaign
: One email sent to lists and segments, now or at a time you choose, with a report afterwards ([Write, test and send email campaigns](/docs/organizations/commerce/email-campaigns/)).

Automation
: Emails and actions that start when a contact does something, such as joining a list or placing an order ([Send emails automatically with automations](/docs/organizations/commerce/email-automations/)).

Signup form
: A form that collects an address and the consent that goes with it, as a page we host, a form inside your site or a popup ([Collect signups with forms](/docs/organizations/commerce/email-forms/)).

Sending domain
: The domain, usually `news.` in front of your own, that marketing email comes from, with its DNS records, warm-up and limits ([Set up your sending domain and warm-up](/docs/organizations/commerce/email-sending/)).

## Rules that hold everywhere

- Suppression wins. An address on the store's suppression list gets no campaign, no automation email and no test send. An address lands there when it unsubscribes, bounces or reports an email as spam. Only the contact's own consent brings back an address that unsubscribed, and nobody brings back one that bounced or reported spam.
- Consent is recorded. Each contact keeps the words it agreed to, when it agreed and how, with a hash of the visitor's IP address when the signup came from a visitor. A contact is subscribed only after it agreed.
- Test and live contacts stay apart. A store in test mode makes test contacts, and its campaigns mail test contacts only. Once the store is live, new contacts and new campaigns are live. A campaign's audience is the contacts of its own mode.
- Every email carries your postal address and an unsubscribe link. The footer shows both, and a campaign whose footer hides either fails its checks. Each email also carries the `List-Unsubscribe` and `List-Unsubscribe-Post` headers, so a mail app's own unsubscribe button works, and the `Precedence: bulk` header.
- Opening an unsubscribe link does not unsubscribe. The page asks the reader to press its button, so a mail scanner that follows links cannot remove anyone. A mail provider's one-click request does unsubscribe.
- Only people who agreed are mailed. A campaign, an automation and a list count only contacts with the status **Subscribed**. **Pending**, **Unsubscribed**, **Bounced** and **Marked as spam** contacts receive nothing.

## Who can do what

Each role can do everything the roles below it can. The table gives the lowest role for each section, and the scope an API key needs for the same work.

| Section | To open it | To change things |
| --- | --- | --- |
| **Overview** | Tier 1 support | — |
| **Contacts** (with Segments) | Tier 1 support | Tier 3 support. **Import** opens for Tier 3 support and above only. |
| **Lists** | Tier 1 support | Tier 3 support |
| **Signup forms** | Tier 1 support | Tier 3 support |
| **Campaigns** | Tier 1 support | Tier 3 support to write a campaign and send a test. Owners and admins to schedule, send, pause, resume, cancel and resend. |
| **Automations** | Tier 1 support | Tier 3 support |
| **Sending** | Tier 3 support | Owners and admins |

A section your role cannot open is left out of the **Email** row. A link to it opens the organization's **Overview** with a notice that names the role it needs. A section you can open but not change says its controls are read-only for you.

### API key scopes

An organization API key reaches the email marketing routes with a `marketing.*` scope. A `:write` scope includes the `:read` scope of its area. `marketing.campaigns:send` is not part of `marketing.campaigns:write`: a key needs it by name. `marketing:*` grants every marketing scope, and `commerce:*` grants them too.

| Scope | What it allows | Lowest role |
| --- | --- | --- |
| `marketing.contacts:read` | Reading contacts, lists, custom fields, tags and segments, and exporting contacts. | Tier 1 support |
| `marketing.contacts:write` | Adding, changing, deleting and bulk changing contacts, managing lists, fields, tags and segments, importing contacts and bringing in leads and customers. Reading imports takes this scope too. | Tier 3 support |
| `marketing.campaigns:read` | Reading campaigns, their reports, saved and starter designs and signup forms, checking a campaign, and rendering a design. | Tier 1 support |
| `marketing.campaigns:write` | Writing campaigns, designs and signup forms, and sending test emails. | Tier 3 support |
| `marketing.campaigns:send` | Scheduling and sending a campaign, pausing, resuming and cancelling it, and resending it to non-openers. | Admin |
| `marketing.automations:read` | Reading automations, their runs and stats, and the list of triggers and steps. | Tier 1 support |
| `marketing.automations:write` | Changing automations, sending a test of one, and reading the secret that signs its webhook steps. | Tier 3 support |
| `marketing.settings:read` | Reading the sending setup. | Tier 3 support |
| `marketing.settings:write` | Setting up the sending domain, checking its DNS records, changing the sender, the postal address and the limits, and pausing and resuming sending. | Admin |

The **New key…** dialog under **Settings**, then **API keys**, lists these scopes with the others, and **Every scope** grants them too. To create a key with only some of them through the API, send `permissions.scopes`, as [Create a key with scopes](/docs/organizations/api-keys/#create-a-key-with-scopes) shows.

## From nothing to a first campaign

1. In the [dashboard](https://www.coritan.com/dashboard/organizations), open the organization, then **Commerce**, then **Email**, then **Sending**. Set up the sending domain, add its DNS records and select **Check records**. The warm-up starts when the records are in place.
2. On the same page, under **Sender and limits**, fill in the **Postal address**, the **Sender name** and the address before the `@`, then select **Save changes**.
3. Bring in your audience. Add people under **Contacts** with **Add contact…**, bring in a file or an EmailOctopus account with **Import**, or choose **Bring in leads and customers…** to turn the store's existing leads and customers into contacts. Put a signup form on your site to keep the list growing.
4. Under **Campaigns**, select **New campaign…**, name it and choose a starter design. Write the subject and the design, and choose the lists or segments it goes to.
5. Select **Check before sending…** and fix what it lists. Select **Send test…** to read the email in a real inbox.
6. Select **Schedule…**. An owner or admin confirms it is them, then chooses **Send now** or **Send at a time**.
7. Open the campaign's report. It fills in as people open and click.

The first sends are small on purpose. A new domain starts at 50 emails a day and rises each day it stays clean, so a large first campaign continues over several days ([Set up your sending domain and warm-up](/docs/organizations/commerce/email-sending/#warm-up-and-the-daily-limit)).

## Read the Overview

**Overview** is the first section of the **Email** row. It shows how the audience and the emails are doing, for the store's own mode: test data while the store is in test mode, live data once it is live. Choose **7 days**, **30 days** or **90 days** on the **This period** card.

| Card | What it shows |
| --- | --- |
| **This period** | **Contacts** (and how many are subscribed), **Joined** (and how many left), **Emails sent** (and how many arrived), **Opens**, **Clicks**, **Orders** and **Revenue** for the emails sent in the span. |
| **Audience growth** | Contacts who joined above the line and contacts who left below it, for each day. |
| **Engagement** | Opens and clicks for each day, from every campaign and automation. |
| **Top campaigns** | The five campaigns that earned the most in the span, then by clicks and opens, with their status, open rate, click rate and revenue. |
| **Deliverability** | A badge, the **Bounce rate** and the **Spam reports** rate. |
| **Who is on the list** | Contacts by status. Each status links to the contacts list filtered to it. |
| **Where contacts came from** | Contacts by source, such as **Popup**, **Signup form**, **Checkout** or **Import**. Each source links to the contacts list filtered to it. |

The deliverability badge reads **Good** while bounces and spam reports are low. It reads **Watch** when more than 2% of emails bounced or more than 0.1% were reported as spam. It reads **Critical** when either is over the pause limits you set on **Sending**, or while sending is paused. Opens by mail privacy tools are left out of opens and clicks ([Write, test and send email campaigns](/docs/organizations/commerce/email-campaigns/#machine-opens)).

At the top of the page, an alert tells you when sending is not set up or is waiting for DNS, with **Open Sending**. When the store has no contacts and has sent nothing, another alert points to **Contacts**.

## Switch from EmailOctopus

You can bring your EmailOctopus audience over and keep working with it here. The import brings lists, custom fields, tags and each contact's status. It sends no email, starts no automation and sends no webhook.

Before you start:

- Set up the sending domain first, so the first campaign can go out as soon as you are ready. Use a new subdomain for your own domain, such as `news.example.com`.
- Create an API key in your EmailOctopus account under Integrations and API. Or use the key saved in the store's [EmailOctopus integration](/docs/organizations/commerce/integrations/#emailoctopus).

Run the import:

1. Open the organization, then **Commerce**, then **Email**, then **Contacts**, and choose **Import**.
2. Choose **EmailOctopus**. Enter the **EmailOctopus API key**, or tick **Use the key saved in the store's EmailOctopus integration**, then select **Fetch the lists**.
3. Tick the lists to bring over and select **Continue**. Each list shows how many contacts are subscribed, unsubscribed and pending.
4. Under **Tags for every contact**, keep `imported` or change it, then select **Start dry run**. The dry run reads the whole account and writes nothing. It shows how many contacts would be new, skipped, suppressed or not valid.
5. When the counts look right, choose **Add new contacts only** or **Add new and update existing**, tick **I hold these contacts' consent to email them**, and select **Run import**.

What comes over:

| In EmailOctopus | Here |
| --- | --- |
| A list | A list with the same name. A list with that name already here is reused. |
| A list field | A custom field. `FirstName` and `LastName` become the contact's names. |
| A tag | A tag. |
| `SUBSCRIBED` | **Subscribed**, with your confirmation of consent as the consent record. |
| `PENDING` | **Pending**. |
| `UNSUBSCRIBED` | **Unsubscribed**, and the address goes on the suppression list. |
| `BOUNCED` and `COMPLAINED` | **Bounced** and **Marked as spam**, and the address goes on the suppression list. |

An address that left any one list at EmailOctopus is suppressed for the whole store. An address that is already on the suppression list stays suppressed and counts as **Suppressed**. The API key is used for this import only and is removed when the import ends.

Keep EmailOctopus running until the first campaign's report looks right. A new sending domain starts at 50 emails a day, so you cannot send to the whole audience at once. Send the first campaign to a small segment, and compare its delivery and bounce rate with what you saw at EmailOctopus. Turn EmailOctopus off when you trust the numbers here.

## Webhook events

Your [organization webhooks](/docs/organizations/webhooks/) can listen for these events, or for every event with `*`:

| Event | When it is sent | Data |
| --- | --- | --- |
| `commerce.marketing.contact.subscribed` | A contact becomes subscribed. | `contact_id`, and `list_ids` and `source` when known |
| `commerce.marketing.contact.unsubscribed` | A contact unsubscribes or is unsubscribed. | `contact_id`, and `source` when known |
| `commerce.marketing.campaign.sent` | A campaign finishes sending. | `campaign_id`, `recipients` |
| `commerce.marketing.email.bounced` | An email bounces. | `campaign_id` when it belongs to one, `contact_id`, `reason` |
| `commerce.marketing.email.complained` | A reader reports an email as spam. | `campaign_id` when it belongs to one, `contact_id`, `reason` |

The events carry ids and small facts. The imports send none.

## Result

- **Overview** shows your audience and what your emails did in the span you choose.
- Every email you send carries your postal address and an unsubscribe link, and goes only to subscribed contacts that are not on the suppression list.

## Troubleshooting

An **Email** section is missing from the row
: Your role does not open it. Every section needs Tier 1 support or above, and **Sending** needs Tier 3 support. Ask an owner or admin to change your role.

A section says its controls are read-only
: Changing contacts, lists, forms, campaigns and automations needs Tier 3 support. Scheduling and sending a campaign, and changing **Sending**, need an owner or admin.

**Overview** shows no contacts, and your store is live
: The store shows the data of its own mode. Contacts made before the store went live are test contacts and appear only while the store is in test mode. [Manage your email contacts, lists and segments](/docs/organizations/commerce/email-contacts/) explains how to bring in your real audience.

A campaign will not start and says sending is not ready
: The sending domain, its records, the postal address or a pause stops it. [Set up your sending domain and warm-up](/docs/organizations/commerce/email-sending/#when-sending-is-not-ready) lists each reason and what to do.

An API call answers `403` with `scope_required`
: The key lacks the scope in the answer's `scope`. Scopes cannot change on an existing key, so create a key with the scopes it needs and revoke the old one.

## Related

- [Manage your email contacts, lists and segments](/docs/organizations/commerce/email-contacts/)
- [Collect signups with forms](/docs/organizations/commerce/email-forms/)
- [Write, test and send email campaigns](/docs/organizations/commerce/email-campaigns/)
- [Send emails automatically with automations](/docs/organizations/commerce/email-automations/)
- [Set up your sending domain and warm-up](/docs/organizations/commerce/email-sending/)
- [Create organization API keys](/docs/organizations/api-keys/)

## With the API

Every email marketing route is under `https://api.coritan.com/api/v1/orgs/{org_slug}/marketing/`, and takes a member's token in `Authorization: Bearer` or an organization API key in `X-API-Key`. The API reference lists them under [Marketing](/docs/api/reference/organizations/marketing/). Lists answer `{total, limit, offset, <name>}`, with 50 rows a page by default and at most 200 (`limit` and `offset`).

An error answers with a status and a `detail` that holds a stable `error` word and a `message`. The ones every page uses: `403` `scope_required` or `role_required`, `404` `not_found`, `409` for a clash such as `contact_exists`, and `422` `invalid` with a `field` that names the property.

`GET /marketing/reports/overview` answers the Overview. Send `since` (included) and `until` (not included) as ISO 8601 times, up to 366 days apart. Without them the span is the last 30 days. `livemode` picks live or test data, and the store's own mode when left out.

```bash
curl "https://api.coritan.com/api/v1/orgs/acme/marketing/reports/overview?since=2026-09-12&until=2026-10-12" \
  -H "X-API-Key: $ORG_API_KEY"
```

The answer holds:

- `audience`: `total`, the count for each status, and `growth`, a list of `{date, joined, left}` for each day.
- `sends`: `emails`, `delivered`, `opens`, `clicks`, `orders` and `revenue` in minor units.
- `engagement`: `{date, opens, clicks}` for each day.
- `deliverability`: `bounce_rate` and `complaint_rate` as numbers from 0 to 1, and `status`, one of `good`, `watch` or `critical`.
- `top_campaigns`: up to five campaigns with their `stats`.
- `by_source`: `{source, contacts}` for each source.

## API

- `GET /api/v1/orgs/{org_slug}/marketing/reports/overview`: The account's email marketing at a glance for a span of at most 366 days (https://www.coritan.com/docs/api/reference/organizations/marketing/marketing/#op-get-api-v1-orgs-org-slug-marketing-reports-overview)
