From 111fc881020e1bdbe65b5894a04565f97d887127 Mon Sep 17 00:00:00 2001 From: Farhad Hedayatifard Date: Tue, 20 Oct 2026 16:20:42 +0200 Subject: [PATCH] Docs: Campaign Builder guide checked against the product code Test sends (3 in the dashboard, placeholder text), the list check (sampled, only for new or changed lists, pause above 4%), review outcomes, dashboard button names, contact and list actions that exist only in the API, CSV import and export, custom field keys, template catalog actions, HTML upload, and the sending type through Update Domain. --- campaigns/api-and-automation.mdx | 4 ++-- campaigns/contacts-and-lists.mdx | 32 +++++++++++++++++--------------- campaigns/create.mdx | 16 ++++++++-------- campaigns/index.mdx | 11 ++++++----- campaigns/templates.mdx | 22 +++++++++++----------- campaigns/test-and-send.mdx | 28 ++++++++++++++++++---------- campaigns/unsubscribe.mdx | 2 +- deliverability/index.mdx | 2 +- domains.mdx | 4 ++-- facts.mdx | 4 ++-- index.mdx | 2 +- 11 files changed, 69 insertions(+), 58 deletions(-) diff --git a/campaigns/api-and-automation.mdx b/campaigns/api-and-automation.mdx index cfdd266..5404c98 100644 --- a/campaigns/api-and-automation.mdx +++ b/campaigns/api-and-automation.mdx @@ -5,11 +5,11 @@ description: "Automate Campaign Builder: push contacts from your app, manage lis icon: "code" --- -Everything in Campaign Builder is available outside the dashboard: contacts, lists, campaign templates and campaigns are resources of the Management API, with commands in the CLI and tools in the MCP server. The dashboard, the API, the CLI and the MCP server all work on the same data, so a contact pushed from your app shows up in the dashboard at once, and a campaign drafted by an AI tool is there for you to review. +Contacts, lists, campaign templates and campaigns are resources of the Management API, with commands in the CLI and tools in the MCP server. Two things stay in the dashboard: defining custom fields, under **Marketing > Contact fields**, and editing Simple Editor designs. The dashboard, the API, the CLI and the MCP server all work on the same data, so a contact pushed from your app shows up in the dashboard at once, and a campaign drafted by an AI tool is there for you to review. ## Typical Automations -- **Keep contacts in sync.** Push each new signup from your app or website as it happens with [Create Contact](/api-reference/contacts/create-contact), or import up to 1,000 at once with [Batch Upsert Contacts](/api-reference/contacts/batch-upsert-contacts). Add them to lists with [Batch Add List Contacts](/api-reference/lists/batch-add-list-contacts). Existing contacts are updated, not duplicated. +- **Keep contacts in sync.** Push each new signup from your app or website as it happens with [Create Contact](/api-reference/contacts/create-contact), or import up to 1,000 at once with [Batch Upsert Contacts](/api-reference/contacts/batch-upsert-contacts). Add them to lists with [Batch Add List Contacts](/api-reference/lists/batch-add-list-contacts). Batch Upsert Contacts updates contacts that already exist; Create Contact refuses an address that already exists with a `409`. - **Mirror unsubscribes.** When a user opts out in your app, set `unsubscribed: true` with [Update Contact](/api-reference/contacts/update-contact), or unsubscribe from one list with [Add or Update a List Contact](/api-reference/lists/upsert-list-contact). - **Generate a campaign.** Create the campaign from a template with [Create Campaign](/api-reference/campaigns/create-campaign), fill the content from your CMS, send a test with [Test Campaign](/api-reference/campaigns/test-campaign), and schedule it with [Send Campaign](/api-reference/campaigns/send-campaign). The campaign goes through the same [checks and review](/campaigns/test-and-send#review) as one made in the dashboard. - **Pull the results.** Read the counters and rates with [Get Campaign Stats](/api-reference/campaigns/get-campaign-stats), and the recipients behind a count with [List Campaign Recipients](/api-reference/campaigns/get-campaign-recipients). For events as they happen, use [webhooks](/integrations/webhooks). diff --git a/campaigns/contacts-and-lists.mdx b/campaigns/contacts-and-lists.mdx index 51784d6..275a3d7 100644 --- a/campaigns/contacts-and-lists.mdx +++ b/campaigns/contacts-and-lists.mdx @@ -14,20 +14,20 @@ A contact exists once in your account, whatever the number of lists it is on. Th Contacts come in three ways: - **CSV import** on the Contacts page, on a list, or while you create a new list. A file can be up to 100 MB. See [importing a CSV](#importing-a-csv). -- **By hand**, one contact at a time, on the Contacts page or on a list. +- **By hand**, one contact at a time, on the Contacts page. To put an existing contact on a list, use **Add to List** on the contact's page. - **From your own app or website**, through the [Management API](/api-reference/contacts/overview): one contact per request, or up to 1,000 per request with [Batch Upsert Contacts](/api-reference/contacts/batch-upsert-contacts). Every contact has its own page with its fields, the lists it is on, its opens and clicks, and every campaign it received. ### Importing a CSV -The import maps the columns of your file to the contact fields: email, first name, last name and your [custom fields](#custom-fields). Rules that apply to every import: +The import matches the columns of your file to the contact fields by their header: `email`, `first_name`, `last_name`, `status` and the name or key of each of your [custom fields](#custom-fields). Columns that match no field are ignored. There is no preview: the file is imported when you upload it. Rules that apply to every import: - A contact that already exists is updated, not duplicated. - An empty cell never wipes a value you already have. - A contact that unsubscribed from everything stays unsubscribed. A contact that unsubscribed from a list is not put back on that list by a new import. - The address column can be named `email` or `Email Address`, so a Mailchimp export imports as it is. From Mailchimp, import only the subscribed contacts file: the unsubscribed and cleaned files have no status column, so their contacts would come in as subscribed. -- Save the file as UTF-8. If names with accents or non-Latin letters look wrong in the preview, the file was saved in another encoding; export it again as CSV UTF-8. Check in the preview that the columns are separated correctly before you confirm the import. +- Save the file as UTF-8. A file saved in another encoding imports names with accents or non-Latin letters garbled; export it again as CSV UTF-8. Import into a list when the file is a list, for example your newsletter subscribers. Import on the Contacts page when you only want the contacts in your account, and add them to lists later. @@ -36,27 +36,29 @@ Import into a list when the file is a list, for example your newsletter subscrib | Status | Meaning | |---|---| | Enabled | A normal contact. Campaigns reach it, on the lists where it is subscribed. | -| Disabled | You switched the contact off. Campaigns skip it on every list, and its memberships stay as they are. | -| Blocked | The contact is blocked. Campaigns skip it, and the status reason on the contact page says why. | +| Disabled | Campaigns skip it on every list, and its memberships stay as they are. AhaSend disables a contact when a campaign to it hard-bounces; you can also set it. | +| Blocked | You blocked the contact. Campaigns skip it on every list. | + +You set the status when you add a contact, or with a `status` column in a CSV import of new contacts. To change the status of an existing contact, use [Update Contact](/api-reference/contacts/update-contact). Next to the status, a contact can be **unsubscribed from everything**. That is the account-wide opt-out: campaigns skip the contact on every list, and its memberships stay as they are. -Email validation adds a third signal: a contact whose address was found invalid is left out of campaigns. The contact page shows the result of the last validation. +Email validation adds a third signal: a contact whose address was found invalid is left out of campaigns. ## Custom Fields -Add the fields your campaigns need, such as company, plan, city, VIP or customer since. A field has a name and a type: text, number, yes/no, date or date/time. The type is fixed when you create the field, so a number stays a number. Renaming a field does not break the campaigns, searches or API calls that use it. +Add the fields your campaigns need, such as company, plan, city, VIP or customer since, under **Marketing > Contact fields**. A field has a name, a key and a type: text, number, yes/no, date or date/time. The type is fixed when you create the field, so a number stays a number. Renaming a field changes only its name, not its key, so it does not break the campaigns, searches or API calls that use it. Custom fields are used in two places: - **Filtering**: find the contacts that match, with [advanced search](#advanced-search-and-saved-lists). -- **Personalization**: write `{{ first_name }}` or any field of your own in the email, and each recipient sees their own value. Give a default for contacts where the field is empty. See [personalization](/campaigns/create#personalization). +- **Personalization**: write `{{ first_name }}` or the key of any field of your own in the email, and each recipient sees their own value. Give a default for contacts where the field is empty. See [personalization](/campaigns/create#personalization). Through the API, custom fields are the `attributes` of a contact. The API accepts only fields that you defined in the dashboard, with a value of the right type. See [attributes](/api-reference/contacts/overview#attributes). ## Lists -A list is a static group of contacts and the unit a campaign targets. A campaign can send to 1 to 50 lists at once. List names do not have to be unique, so give lists names that tell them apart. +A list is a static group of contacts and the unit a campaign targets. A campaign sends to one or more lists; through the API, up to 50. List names do not have to be unique, so give lists names that tell them apart. The Lists page shows two numbers for each list: how many contacts are on it, and how many a campaign would reach. The second number counts a contact when its membership is confirmed, the contact is enabled, it has not unsubscribed from everything, and email validation did not find its address invalid. The API returns the same number as `contact_count`. @@ -77,13 +79,13 @@ A list page can therefore show a contact as **Confirmed** on the list and **Bloc | Goal | What to do | Effect | |---|---|---| -| Stop email from one list | Set the contact's list status to unsubscribed | Can be undone; the record of the unsubscribe stays | -| Stop email from every list | Unsubscribe the contact from everything | Can be undone; the memberships stay | -| Take a contact off a list | Remove it from the list | The membership and its unsubscribe record are deleted, so a later import adds the contact again, silently | -| Delete a list | Delete it on the Lists page | The list is gone; the contacts and their unsubscribes and complaints are kept | +| Stop email from one list | Set the list status to unsubscribed with [Add or Update a List Contact](/api-reference/lists/upsert-list-contact) | Can be undone: **Add to List** on the contact's page subscribes the contact again. The record of the unsubscribe stays | +| Stop email from every list | Set `unsubscribed: true` with [Update Contact](/api-reference/contacts/update-contact) | Can be undone through the API; the memberships stay | +| Take a contact off a list | **Remove** on the list page or on the contact's page | The membership and its unsubscribe record are deleted, so a later import adds the contact again, silently. A complained membership cannot be removed | +| Delete a list | On the list's page, **Actions > Delete List** | The list is gone; the contacts and their unsubscribes and complaints are kept. A list that an unfinished campaign uses cannot be deleted | | Delete a contact | Delete it on the Contacts page | The contact and its history are gone | -To stop marketing email, unsubscribe the contact rather than removing it. +The dashboard has no button to unsubscribe a contact; recipients unsubscribe themselves with the link in a campaign. To stop marketing email to a contact yourself, unsubscribe it through the API rather than removing it. Contacts and [suppressions](/bounces) are separate records. A suppression you add by hand, or one that a bounce of your transactional mail creates, does not change any contact or list status. @@ -97,7 +99,7 @@ The **Send Campaign** button on a list page starts a campaign for the whole list ## Export -Export a list or the Contacts page as CSV. The export has the standard fields and your custom fields, in the order you set on the Contacts page. +**Export CSV** on the Contacts page exports all contacts of your account. The file has the standard fields, the status and your custom fields, in the order you set under **Marketing > Contact fields**. ## Related Guides diff --git a/campaigns/create.mdx b/campaigns/create.mdx index e9c71ad..e307067 100644 --- a/campaigns/create.mdx +++ b/campaigns/create.mdx @@ -9,7 +9,7 @@ A campaign is one email to one or more lists. This page walks through the parts ## Name and Start Point -Under **Marketing > Campaigns**, click **Create Campaign**. Give it a name that you will recognize in the list later, such as "October newsletter"; recipients never see it. Then choose where the design comes from: a blank design in the Simple or Advanced Editor, or a copy of one of your [campaign templates](/campaigns/templates). +Under **Marketing > Campaigns**, click **Create Campaign**. Give it a name that you will recognize in the list later, such as "October newsletter"; recipients never see it. Under **Start From**, start from scratch or from a copy of one of your [campaign templates](/campaigns/templates). To send a campaign again, for example next month's edition, **duplicate** an existing campaign instead. The copy is a new draft with the same sender, lists, subject and design. @@ -25,7 +25,7 @@ Use a subdomain for your campaigns, such as `news.yourdomain.com`, and keep your ## Recipients -Choose 1 to 50 [lists](/campaigns/contacts-and-lists). The campaign page shows how many contacts the campaign will reach: the subscribed, enabled contacts on those lists that have not unsubscribed from everything and whose address was not found invalid. +Choose one or more [lists](/campaigns/contacts-and-lists); through the API, up to 50. The campaign page shows how many contacts the campaign will reach: the subscribed, enabled contacts on those lists that have not unsubscribed from everything and whose address was not found invalid. A contact on more than one of the lists counts once per list in that number, but gets the email once. To send to part of a list, filter the list with advanced search and **Save as new list**, then choose that list here. @@ -35,7 +35,7 @@ The subject is what the inbox shows first. The preview text is the line most inb ## Design -Open the editor from the campaign page. The design is the campaign's own copy, so changes here never affect the template it came from. See [the two editors](/campaigns/templates) for what each one does, and for importing MJML or HTML. +A campaign started from scratch gets its design on the campaign page: choose the Simple Editor, the Advanced Editor, or **Custom HTML** to upload your own HTML. The design is the campaign's own copy, so changes here never affect the template it came from. See [the two editors](/campaigns/templates) for what each one does, and for importing MJML or HTML. ### Personalization @@ -46,25 +46,25 @@ Hi {{ first_name }}, Here is the October update for {{ company_name }}. ``` -The standard fields are `email`, `first_name` and `last_name`; your [custom fields](/campaigns/contacts-and-lists#custom-fields) are available under their own names. Give a default for contacts where the field is empty: +The standard fields are `email`, `first_name` and `last_name`; your [custom fields](/campaigns/contacts-and-lists#custom-fields) are available under their key, shown as the template variable under **Marketing > Contact fields**. Give a default for contacts where the field is empty: ```text Hi {{ first_name|default("there") }}, ``` -Personalization works in the design, the subject and the preview text. The test send shows the placeholders with sample values, and the editor's preview lets you step through real contacts of your lists. Template syntax that cannot be rendered, such as an unclosed brace, blocks the send with the issue `text_broken`. +Personalization works in the design, the subject and the preview text. To check the result, click **Preview** on the campaign page and choose a contact, or [send a test](/campaigns/test-and-send#test-sends) to one of your contacts. Template syntax that cannot be rendered, such as an unclosed brace, blocks the send with the issue `text_broken`. ### The Unsubscribe Link -Every campaign must have an unsubscribe link in its HTML. New designs start with one in the footer; if you remove it, put it back with `{{ unsubscribe_url }}`: +Every campaign must have an unsubscribe link in its HTML. New Simple Editor designs start with one in the footer. In the Advanced Editor, add it from the link dialog under **Automatic Links**, or write `{{ unsubscribe_url }}`: ```html Unsubscribe ``` -Each recipient gets their own signed link, and AhaSend adds the one-click `List-Unsubscribe` headers that Gmail and Yahoo expect from bulk senders. A recipient who unsubscribes is unsubscribed from the list the campaign went to. A campaign without the link has the issue `unsubscribe_link_missing` and cannot be sent; a test can still go out, so you can test a design before the footer is done. +Each recipient gets their own link, and AhaSend adds the one-click `List-Unsubscribe` headers that Gmail and Yahoo expect from bulk senders. A recipient who unsubscribes is unsubscribed from the list the campaign went to. A campaign without the link has the issue `unsubscribe_link_missing` and cannot be sent; a test can still go out, so you can test a design before the footer is done. -In a test send, the unsubscribe link leads to a contact that does not exist, so clicking it in a test unsubscribes nobody. +Clicking the unsubscribe link in a test send unsubscribes nobody. This section is about campaigns. For the unsubscribe link and headers of mail you send through the API or SMTP, see [unsubscribe links and one-click headers](/campaigns/unsubscribe). diff --git a/campaigns/index.mdx b/campaigns/index.mdx index 952287c..5ff738c 100644 --- a/campaigns/index.mdx +++ b/campaigns/index.mdx @@ -7,7 +7,7 @@ icon: "bullhorn" Campaign Builder sends newsletters, product updates and promotions from your AhaSend account, next to your transactional email. It is included in every plan, the Free plan too. Campaign emails count towards the monthly volume of your plan; there is no separate price. -Open **Marketing** in the dashboard to find it. Everything in Campaign Builder is also available through the [Management API](/campaigns/api-and-automation), the CLI and the [MCP server](/mcp-server). +Open **Marketing** in the dashboard to find it. Contacts, lists, campaign templates and campaigns are also available through the [Management API](/campaigns/api-and-automation), the CLI and the [MCP server](/mcp-server). ## How a Campaign Goes Out @@ -22,7 +22,7 @@ Open **Marketing** in the dashboard to find it. Everything in Campaign Builder i Set the sender, the lists, the subject and the preview text, and personalize the email with contact fields. See [creating a campaign](/campaigns/create). - Send yourself a test, fix the issues the campaign page lists, then send now or schedule it. Every campaign is checked before it goes out. See [testing, review and sending](/campaigns/test-and-send). + Send yourself a test, fix the issues the campaign page lists, then send now or schedule it. AhaSend checks new and changed lists before they are sent to. See [testing, review and sending](/campaigns/test-and-send). Delivered, opens, clicks, unsubscribes, complaints and bounces, per campaign and per list. See [reports](/campaigns/reports). @@ -31,14 +31,14 @@ Open **Marketing** in the dashboard to find it. Everything in Campaign Builder i ## Separate from Your Transactional Email -Campaigns go out over AhaSend's marketing infrastructure: their own IP pools and their own [daily marketing limit](/campaigns/marketing-limit). A large newsletter never delays your password resets or receipts, and the reputation of your transactional sending is not affected by your campaigns. +Campaigns go out over AhaSend's marketing infrastructure: their own IP pools and their own [daily marketing limit](/campaigns/marketing-limit). A large newsletter never delays your password resets or receipts. To keep the reputation of your domains apart too, send campaigns from their own subdomain. Bounces and complaints from campaigns still count towards the health of your account, so a poor list can pause all of your sending. You do not have to configure anything for this. Any verified sending domain of your account can send a campaign, whatever its [sending type](/domains#sending-type). The sending type only matters for marketing mail that you send yourself through the API or SMTP: that mail must come from a domain with the sending type marketing. Campaign Builder adds the unsubscribe link and processes opt-outs for you; for marketing mail from your own tool, you add the link and keep the opt-outs yourself. See [unsubscribe links](/campaigns/unsubscribe). ## Built for the Inbox - Every campaign carries an unsubscribe link and one-click unsubscribe headers, as Gmail and Yahoo expect from bulk senders. A campaign without the link cannot be sent. -- Before a campaign goes out, AhaSend checks its lists and its content. Addresses that cannot receive mail are left out, and a campaign that looks like spam, phishing or cold outreach is held for review. See [review](/campaigns/test-and-send#review). +- Before a campaign to a new or changed list goes out, AhaSend checks the list and the content. Addresses that cannot receive mail are left out, a list with too many bad addresses pauses the campaign, and a campaign that looks like spam, phishing or cold outreach is held for review. See [review](/campaigns/test-and-send#review). - Unsubscribes and spam complaints are processed for you: the contact gets no more campaigns from that list, and re-importing an old CSV never puts anyone back on a list they left. ## Where Things Are in the Dashboard @@ -47,8 +47,9 @@ You do not have to configure anything for this. Any verified sending domain of y |---|---| | Marketing > Campaigns | Your campaigns, their status and their reports | | Marketing > Lists | Lists, with the number of contacts a campaign would reach | -| Marketing > Contacts | All contacts of the account, with their fields, lists and history, and the custom fields you define | +| Marketing > Contacts | All contacts of the account, with their fields, lists and history | | Marketing > Templates | Your catalog of campaign templates | +| Marketing > Contact fields | The custom fields of your contacts | ## Related Guides diff --git a/campaigns/templates.mdx b/campaigns/templates.mdx index d31dae8..10b7d14 100644 --- a/campaigns/templates.mdx +++ b/campaigns/templates.mdx @@ -11,25 +11,25 @@ Campaign templates are not [transactional templates](/send-api/templates). Trans ## Your Template Catalog -Under **Marketing > Templates** you keep as many templates as you want: a newsletter, a product update, a promotion, one per brand or per language. From the catalog you can: +Under **Marketing > Templates** you keep as many templates as you want: a newsletter, a product update, a promotion, one per brand or per language. You can: -- **Start a campaign** from a template. The campaign gets its own copy of the design. Editing the template later changes nothing in a campaign you already made, and you can delete the template without an effect on its campaigns. -- **Start a new template** from an existing one, for example a Christmas version of your newsletter, or a second-brand version with another logo. -- **Save a campaign's design as a template**, so a design you made in a campaign becomes the base for the next ones. +- **Start a campaign** from a template: in **Create Campaign** on the Campaigns page, choose the template under **Start From**. The campaign gets its own copy of the design. Editing the template later changes nothing in a campaign you already made, and you can delete the template without an effect on its campaigns. +- **Start a new template** from an existing one with **Duplicate Template**, for example a Christmas version of your newsletter, or a second-brand version with another logo. +- **Save a campaign's design as a template** with **Save as Template** on the campaign page, so a design you made in a campaign becomes the base for the next ones. -Because each campaign copies the design, a template is a starting point, not a live dependency. That also means a fix in the template does not reach campaigns that already exist: duplicate the campaign from the fixed template, or make the fix in the campaign as well. +Because each campaign copies the design, a template is a starting point, not a live dependency. That also means a fix in the template does not reach campaigns that already exist: create a new campaign from the fixed template, or make the fix in the campaign as well. ## Simple Editor The Simple Editor works like a document. You type, and add an image, a button, a divider or columns where you need them. Use it for emails where the words matter more than the layout: an update from the founder, a short announcement, a plain newsletter. -A design made in the Simple Editor can only be changed in the Simple Editor. Through the API and the MCP server you can still change the name, subject, preview text and sender of a campaign made from it, but not the design itself. +A design made in the Simple Editor can only be changed in the Simple Editor. Through the API and the MCP server you can still change the other parts of a campaign made from it, such as the name, subject, preview text, sender and lists, but not the design itself. ## Advanced Editor -The Advanced Editor builds the email from [MJML](https://mjml.io) blocks that you drag in and style one by one: columns, images, buttons, text, dividers, social links and groups. MJML is the open-source markup made for responsive email, so the result works across email clients without hand-written table HTML. +The Advanced Editor builds the email from [MJML](https://mjml.io) blocks that you drag in and style one by one: columns, text, images, buttons, dividers, spacers, social links, navigation bars and hero sections. MJML is the open-source markup made for responsive email, so the result works across email clients without hand-written table HTML. -Use the Advanced Editor when the design matters, when you have an MJML or HTML design from another tool, or when a developer maintains the design in code. You can import and export MJML and HTML at any time. +Use the Advanced Editor when the design matters, when you have an MJML design from another tool, or when a developer maintains the design in code. Paste MJML in with **Import MJML**; **Export** shows the MJML and the HTML to copy. Both editors can add a **View in browser** link and write right to left, for Arabic, Hebrew and Persian. @@ -37,10 +37,10 @@ Both editors can add a **View in browser** link and write right to left, for Ara Bring a design from another tool as MJML or HTML: -- **MJML** from a tool that exports it, such as Klaviyo or Mailjet, opens in the Advanced Editor with its blocks intact. -- **HTML** opens as an HTML design. You edit the source, not blocks. +- **MJML** from a tool that exports it, such as Klaviyo or Mailjet: paste it into the Advanced Editor with **Import MJML**. It opens as blocks. A few MJML components, such as `mj-accordion`, `mj-carousel` and `mj-table`, are not supported. +- **HTML**: choose **Custom HTML** and upload an `.html` file, or a zip with the HTML, its images and a text version. AhaSend hosts the images from the zip. To change the design, upload a new version; there is no editor for it. -Images and stylesheets in an imported design must use full `https://` URLs that stay online; AhaSend does not copy them. Replace the unsubscribe link of the other tool with `{{ unsubscribe_url }}`, and remove placeholders that only the other tool understands, such as its delivery or view-in-browser tags; otherwise they appear in the email as plain text. See [the unsubscribe link](/campaigns/create#the-unsubscribe-link). +Images and stylesheets that the design links to must use full `https://` URLs that stay online; AhaSend does not copy them. Replace the unsubscribe link of the other tool with `{{ unsubscribe_url }}`, and remove placeholders that only the other tool understands, such as its delivery or view-in-browser tags; otherwise they appear in the email as plain text. See [the unsubscribe link](/campaigns/create#the-unsubscribe-link). Through the API, [Create Campaign Template](/api-reference/campaign-templates/create-campaign-template) takes `mjml` or `html`, and the [MCP server](/mcp-server) can import a file you hand to your AI tool. diff --git a/campaigns/test-and-send.mdx b/campaigns/test-and-send.mdx index 6a4ef6d..4f4e509 100644 --- a/campaigns/test-and-send.mdx +++ b/campaigns/test-and-send.mdx @@ -9,7 +9,7 @@ A campaign goes out in four steps: a test to yourself, the checks on the campaig ## Test Sends -Click **Send Test** on the campaign page and enter 1 to 5 addresses. Each address gets the email as a recipient would, with sample values in place of the contact fields. The links in a test lead to a contact that does not exist, so a click on the unsubscribe link in a test unsubscribes nobody. +Click **Send Test** on the campaign page and enter up to 3 addresses or pick up to 3 of your contacts; through the [API](/api-reference/campaigns/test-campaign), up to 5 addresses. A contact you pick gets the email with its own values. A typed address gets placeholder text in place of the contact fields, such as `--PLACEHOLDER for First Name--`. A click on the unsubscribe link in a test unsubscribes nobody. Rules for tests: @@ -37,28 +37,36 @@ The campaign page lists what is missing before the campaign can go out. The same Some sends are refused on the spot, with a message that says why: a send time less than a minute ahead, a sender domain that is not verified or is paused, and lists on which no contact can receive the campaign. -Changed your mind? **Unschedule** a scheduled campaign and it returns to draft, where you can edit it and send it again. Once a campaign is sending, it cannot go back to draft; to send the same email again, duplicate it. +Changed your mind? Click **Cancel** on a scheduled campaign, or **Cancel Validation** while its lists are checked, and it returns to draft, where you can edit it and send it again. Through the API, use [Unschedule Campaign](/api-reference/campaigns/unschedule-campaign). A paused, canceled or failed campaign that sent nothing has a **Return to Draft** button. Once a campaign has started sending, it cannot go back to draft; to send the same email again, duplicate it. ## Review -AhaSend's marketing infrastructure is shared by the campaigns of all customers, so every campaign is checked before it goes out. The check protects your delivery from other senders' bad lists, and theirs from yours. +AhaSend's marketing infrastructure is shared by the campaigns of all customers, so AhaSend checks campaigns before they go out. The check protects your delivery from other senders' bad lists, and theirs from yours. -**The list.** Every address is checked: is it well-formed, does its domain accept mail, is the domain on AhaSend's list of known bad domains, and, where possible, does the mailbox exist. Addresses that cannot receive mail are left out of the send; addresses the check could not reach keep their status and still get the campaign. Addresses on catch-all domains, which accept mail for any address, cannot be confirmed and are shown as Catch-all in the list check on the report, next to Risky; they still get the campaign. A list with a high bounce risk, or a large list that is mostly catch-all addresses, is held for review before anything is sent. +**When the list is checked.** AhaSend checks the lists of a campaign when a list was never checked, was last checked 90 or more days ago, or has more than 10% new contacts since its last check, and for every campaign of an account younger than 30 days. Otherwise the campaign goes straight to its send time. -**The content.** A language model that AhaSend fine-tuned and runs on its own servers in European datacenters helps catch campaigns that look like spam, phishing or cold outreach. AhaSend does not accept cold outreach: email to people who did not ask for it. See the [terms of service](https://ahasend.com/terms). +**The list.** AhaSend checks a sample of the contacts, or all of them on a small list: is the address well-formed, does its domain accept mail, is the domain on AhaSend's list of known bad domains, and, where possible, does the mailbox exist. Sampled addresses that cannot receive mail are left out of the send. Addresses on catch-all domains, which accept mail for any address, cannot be confirmed; they still get the campaign. -A campaign that passes continues to its send time. A campaign that is held shows the status **waiting for review** and the reason on its page. A person looks at it; when the review is done, the campaign continues, or it is canceled with the reason on its page. Contact [support](mailto:support@ahasend.com) if you want to discuss a held campaign. +**The content.** When the list is checked, a language model that runs on AhaSend's own servers also checks the content for spam, phishing or cold outreach. AhaSend does not accept cold outreach: email to people who did not ask for it. See the [terms of service](https://ahasend.com/terms). + +**The result.** + +- More than 4% of the sample cannot receive mail: the campaign is **paused** for poor list quality, and its page shows how many sampled contacts are invalid. Clean the list, then click **Return to Draft** and schedule the campaign again, or contact support. +- A borderline list, a large list that is mostly catch-all addresses, content that looks like spam, or an account that has sent fewer than 3 campaigns: the campaign shows **Awaiting Review**. A sender domain with a poor record, or an earlier rejected review, can also hold it. A person looks at it, usually within 1 to 2 business days. After the review, the campaign continues to its send time, or it returns to draft with the reviewer's notes on its page. +- Otherwise, the campaign continues to its send time. + +Contact [support](mailto:support@ahasend.com) if you want to discuss a paused or held campaign. ## Statuses | Status | Meaning | |---|---| | Draft | Being prepared. Only a draft can be edited, tested, sent or deleted. | -| Validating | Scheduled; the lists are being checked. Can be unscheduled. | -| Waiting for review | Held for a review of the list or the content. Cannot be unscheduled. | -| Scheduled | Accepted, waiting for its send time. Can be unscheduled. | +| Validating | Scheduled; the lists are being checked. Can be canceled back to draft. | +| Awaiting review | Held for a person to [review](#review). Cannot be canceled. The API calls this status `waiting_for_review`. | +| Scheduled | Accepted, waiting for its send time. Can be canceled back to draft. | | Sending | Going out now. | -| Paused | Sending stopped for now; the page says why. | +| Paused | Sending stopped; the page says why, for example poor list quality. | | Sent | Every message was sent. The report is complete. | | Canceled | Stopped before it finished; the page says why. | | Failed | Could not be sent; the page says why. | diff --git a/campaigns/unsubscribe.mdx b/campaigns/unsubscribe.mdx index d51af9f..b1d7dea 100644 --- a/campaigns/unsubscribe.mdx +++ b/campaigns/unsubscribe.mdx @@ -111,7 +111,7 @@ Implement an HTTPS endpoint that accepts the standard one-click POST and saves t With the [REST API](/api-reference/messages/create-message), set the two headers in the `headers` object; with SMTP, add them as message headers. Check that a received message's DKIM signature covers both headers, as [RFC 8058](https://www.rfc-editor.org/rfc/rfc8058.html) requires; AhaSend signs them together with the rest of the message. -Yahoo currently accepts a `mailto:` unsubscribe method too, while recommending POST; the HTTPS POST method meets Gmail's one-click mechanism as well. Process opt-outs promptly and stop future sends to that address. Keep durable preferences in your app, and add an AhaSend [suppression](/bounces) when the address must be blocked for the sending domain as a whole. +Yahoo currently accepts a `mailto:` unsubscribe method too, while recommending POST; the HTTPS POST method meets Gmail's one-click mechanism as well. On a marketing domain, AhaSend replaces a header that has only a `mailto:` link, as [shown above](#can-i-use-my-own-list-unsubscribe-header). Process opt-outs promptly and stop future sends to that address. Keep durable preferences in your app, and add an AhaSend [suppression](/bounces) when the address must be blocked for the sending domain as a whole. ## What Happens When a Recipient Unsubscribes? diff --git a/deliverability/index.mdx b/deliverability/index.mdx index fca532c..da7e393 100644 --- a/deliverability/index.mdx +++ b/deliverability/index.mdx @@ -71,7 +71,7 @@ A password reset requested by a user is different from a newsletter. Decide whet It depends on how the mail is sent: -- **Campaigns** always have one: Campaign Builder adds the signed link and the one-click headers, and will not send a campaign without the link. See [the unsubscribe link in a campaign](/campaigns/create#the-unsubscribe-link). +- **Campaigns** always have one: Campaign Builder adds a link for each recipient and the one-click headers, and will not send a campaign without the link. See [the unsubscribe link in a campaign](/campaigns/create#the-unsubscribe-link). - **API or SMTP mail from a marketing domain** gets the one-click headers from AhaSend. Add the visible link in the body yourself, with `{{unsubscribe_url}}` for a signed AhaSend link, or with your own unsubscribe endpoint. - **API or SMTP mail from a transactional domain** gets the link and the headers only when you put `{{unsubscribe_url}}` in the content. diff --git a/domains.mdx b/domains.mdx index 89f629c..4896210 100644 --- a/domains.mdx +++ b/domains.mdx @@ -51,7 +51,7 @@ Without proper domain setup, your emails will either be **rejected outright** or ## Sending Type -Each domain has a sending type: **transactional** or **marketing**. A new domain is transactional. Set it in the domain's settings in the dashboard, or with `sending_type` in the [API](/api-reference/domains/create-domain), to match the mail you send from the domain through the API or SMTP: +Each domain has a sending type: **transactional** or **marketing**. A new domain is transactional. Set it in the domain's settings in the dashboard, or with `sending_type` in [Create Domain](/api-reference/domains/create-domain) or [Update Domain](/api-reference/domains/update-domain), to match the mail you send from the domain through the API or SMTP: | You send from the domain | Sending type | What AhaSend does | |---|---|---| @@ -66,7 +66,7 @@ For that mail, the visible unsubscribe link and the handling of opt-outs are you Marketing mail from a transactional domain, a newsletter sent through the API or SMTP for example, can get your account paused. Give marketing mail its own subdomain, such as `news.yourcompany.com`, set that subdomain to marketing, and keep your transactional mail on its own domain. See [why the two should stay apart](/deliverability#which-sending-type-should-my-domain-use). -A change of the sending type takes effect for new messages. It does not lift a [pause](#why-is-sending-paused-on-my-domain). +A change of the sending type applies to new messages within five minutes. It does not lift a [pause](#why-is-sending-paused-on-my-domain). ## Prerequisites diff --git a/facts.mdx b/facts.mdx index e75fb63..0470294 100644 --- a/facts.mdx +++ b/facts.mdx @@ -49,8 +49,8 @@ New accounts keep message metadata and message data for 7 days. Zero data retent | Statistics, stream and trigger endpoints | 1 request per second | [API rate limits](/api-reference/rate-limits) | | Create Message recipients | Up to 100 per request | [Send email](/send-api/send-email) | | Create Template Message recipients | Up to 100 per request | [Transactional templates](/send-api/templates) | -| Campaign lists | 1 to 50 lists per campaign | [Creating a campaign](/campaigns/create) | -| Campaign test sends | 1 to 5 addresses per test, 50 test emails per account per day (UTC) | [Test, review and send](/campaigns/test-and-send) | +| Campaign lists | Up to 50 lists per campaign through the API | [Creating a campaign](/campaigns/create) | +| Campaign test sends | Up to 3 addresses per test in the dashboard and 5 through the API, 50 test emails per account per day (UTC) | [Test, review and send](/campaigns/test-and-send) | | Contact import | CSV up to 100 MB in the dashboard; up to 1,000 contacts per API request | [Contacts and lists](/campaigns/contacts-and-lists) | | Template content size | MJML, HTML and custom text each up to 200 KB | [Transactional templates](/send-api/templates) | | Scheduled sending | First attempt within 7 days; expiry within 8 days | [Create Message](/api-reference/messages/create-message) | diff --git a/index.mdx b/index.mdx index ec34009..3d0d97a 100644 --- a/index.mdx +++ b/index.mdx @@ -66,7 +66,7 @@ AhaSend is a European email service for developers: send transactional email thr Newsletters and campaigns to contact lists, in the dashboard, through the API or with your AI tool. - Marketing mail through the API or SMTP needs a marketing domain: separate infrastructure, its own limit, your transactional reputation untouched. + Marketing mail through the API or SMTP needs a marketing domain: separate infrastructure and its own limit, apart from your transactional mail.