# SendSend: full text for AI tools > SendSend is an email API and dashboard. It sends transactional email (receipts, password resets, notifications) and marketing email (newsletters and announcements to people who opted in), receives inbound email, and shows a timeline of what happened to every message. This file is generated from the same sources as the website, so it matches it. The short index is at https://sendsend.email/llms.txt and the API as OpenAPI 3.1 is at https://sendsend.email/openapi.json. ## Plans and prices - Free: $0. 3,000 emails a month, 100 emails a day, no extra emails (sending stops at the limit). Up to 3 domains, 1 live project and 2 team members. - Pro: $20 a month. 50,000 emails a month, no daily limit, extra emails at $0.90 per 1,000, with a spending cap you set. Up to 10 domains, 5 live projects and 5 team members. - Scale: $90 a month. 100,000 emails a month, no daily limit, extra emails at $0.90 per 1,000, with a spending cap you set. Up to 1,000 domains, 100 live projects and 25 team members. - Enterprise: priced by quote (contact us). Custom volume, rate limits, domain and project limits, and written support terms. ## What SendSend does - Send, schedule, batch (up to 100 per request), and cancel email through a REST API, with idempotency keys so a retry never sends twice. - A test mode that simulates delivery, bounces and complaints without sending real email. - Domain verification with per-record DNS checks, DKIM, SPF and DMARC guidance, deliverability insights per domain, optional open and click tracking, and optional TLS-only delivery. - Signed, retried webhooks for delivery, bounce, complaint, open, click and received-mail events. - Audiences, contacts, topics, segments, a visual broadcast editor, signup forms with double opt-in, one-click unsubscribe, and per-broadcast statistics. - Inbound email: an address for every project or your own domain, an inbox, an API, and a webhook. - Projects for each app or client, scoped API keys, team roles, read-only client seats, and an audit log. - Hard spending caps, so sending stops at the limit instead of producing a surprise bill. - Libraries for Python, PHP, Ruby, Go, .NET, Java and Rust, a Node SDK, a command-line tool, an MCP server (also hosted, so assistants connect with a URL), email address verification, DMARC monitoring, blocklist monitoring, broadcast split testing, a visual template builder with a library of sixty ready-made templates including WooCommerce, a WordPress plugin, and guides for 16 frameworks, Zapier, Make, n8n, Supabase and Clerk. ## What SendSend does not do yet - There is no SMTP relay yet: mail is sent through the API (or the WordPress plugin and integrations). - There are no dedicated IP addresses. - There is no single sign-on, and no SOC 2, ISO 27001 or HIPAA attestation. - There is no uptime SLA unless one is agreed in an Enterprise contract. - New accounts are reviewed before live sending is enabled. Test mode works immediately. ## Guides on email deliverability ### What is SPF, and how do you set it up? Source: https://sendsend.email/learn/what-is-spf SPF is a list, published in your DNS, of the servers allowed to send email as your domain. Receiving servers check it to decide whether a message that says it is from you really is. #### What an SPF record looks like An SPF record is a single TXT record at the root of your domain. It starts with `v=spf1`, lists who may send, and ends with a rule for everyone else: ``` v=spf1 include:_spf.google.com include:amazonses.com ~all ``` - `include:_spf.google.com` says “whatever Google Workspace says is allowed, is allowed.” Each service you send through gives you an include like this. - `ip4:203.0.113.5` allows one address of your own, if you run your own mail server. - `~all` at the end says “anyone else is probably not me” (a soft fail). `-all` says “definitely not me” (a hard fail). Never use `+all`: it allows the whole internet. #### What SPF actually checks SPF checks the envelope sender, also called the return-path or MAIL FROM, which is not the “From” address people see in their inbox. That is why SPF alone cannot stop someone forging your visible address. DMARC closes that gap by requiring the checked domain to match the visible one. SPF also breaks when mail is forwarded, because the forwarding server is not on your list. This is normal, and it is one reason you also need DKIM, which survives forwarding. #### The mistakes that break SPF - Two SPF records. A domain must have exactly one. With two, receivers treat SPF as broken. Merge them into a single record. - More than 10 DNS lookups. Every `include`, `a`, `mx`, `ptr`, `exists` and `redirect` counts, including the ones inside the records you include. Over 10, the whole record fails. Remove services you no longer use. - A service missing from the list. If you send through a newsletter tool or an email API and it is not in your SPF, its mail can fail. - Using `ptr`. It is slow and discouraged. - No `all` at the end. Without it, the record says nothing about mail from other servers. #### How to set it up - List every service that sends email as your domain: your mailbox provider, your email API, your newsletter tool, your help desk. - Find the SPF include each one tells you to use. - Create one TXT record at your domain that starts with `v=spf1`, has each include, and ends in `~all`. - Wait for DNS to update (often minutes, sometimes longer, depending on the record’s TTL), then check it. Run your domain through the free domain checker to see it. Common questions: - Do I need SPF if I already have DKIM? Yes, set up both. DMARC passes when either one passes and lines up with your From domain, but mailbox providers expect to see both, and having both keeps your mail working when one of them breaks, such as SPF when a message is forwarded. - Should I end my SPF record with ~all or -all? Start with ~all. It is safe while you make sure every sender is listed. Once you are certain the list is complete you can move to -all, though with DMARC in place the difference matters less. - Can I have two SPF records? No. A domain must have exactly one SPF record. Two records make SPF fail. Combine them into one by merging the includes. - How long does an SPF change take? As long as the record’s TTL: usually minutes to a few hours. Some resolvers cache longer, so allow up to a day before assuming something is wrong. ### What is DKIM, and how do you set it up? Source: https://sendsend.email/learn/what-is-dkim DKIM puts a digital signature on every message you send. Receiving servers check it against a public key in your DNS to confirm the message really came from your domain and was not changed on the way. #### How DKIM works Your email provider signs each message with a private key only it holds. The signature, and the name of the key to use (the selector), travel in the message’s headers. The receiving server looks up the matching public key in your DNS and checks the signature. If the message was changed after signing, the check fails. Because the signature travels with the message, DKIM usually survives forwarding, which SPF does not. #### What a DKIM record looks like The public key is published at `selector._domainkey.yourdomain.com`. Depending on the provider, you add either a TXT record holding the key: ``` selector1._domainkey.example.com TXT "v=DKIM1; k=rsa; p=MIIBIjANBgkqh..." ``` or a CNAME that points at a key the provider keeps (and can rotate for you): ``` abc123._domainkey.example.com CNAME abc123.dkim.amazonses.com ``` The selector is a name your provider chooses, such as `google`, `selector1` or a random string. Each service you send through has its own, so one domain can have several DKIM records. #### How to set it up - In your email provider’s settings, add your domain. It shows the DKIM record or records to create. - Add them at your DNS provider exactly as shown. Copy the whole value; keys are long. - Go back to the provider and press verify. Most show a green tick within minutes, and some take longer. - Check it from outside. Because the selector is provider-specific, enter it when you check. Run your domain through the free domain checker to see it. #### Common problems - The record was cut off. Some DNS tools split long values or add quotes. If verification fails, compare the stored value with the one your provider gave you. - The domain in the signature does not match. For DMARC, the signing domain has to line up with your From domain. Sending through a provider with its own domain signature is not enough; set up your own domain. - A key that is too short. Use 2048-bit keys where your provider offers them. - Old selectors left behind. A record for a service you stopped using is harmless, but rotate or remove keys you no longer control. Common questions: - What is a DKIM selector? A name that says which key to use. It is the part before ._domainkey in the DNS record. It lets one domain have many keys at once, for different services or for rotating keys. - Why can’t a checker find my DKIM record? DNS cannot list a domain’s records, so a checker has to guess selector names. If you know the selector your provider gave you, enter it and the check will look at exactly that name. - Is DKIM or SPF more important? Both matter. DKIM is generally the more robust of the two because it survives forwarding, and it is the one DMARC leans on most often. - Should I rotate DKIM keys? It is good practice to rotate periodically. With CNAME-style DKIM your provider can rotate the key without any change at your DNS. ### What is DMARC, and how do you roll it out safely? Source: https://sendsend.email/learn/what-is-dmarc DMARC ties SPF and DKIM to the address people see in the From line, tells receivers what to do when a message fails, and sends you reports about who is sending as your domain. #### What DMARC adds SPF and DKIM each prove something about a technical identity, not necessarily the visible From address. DMARC says: a message passes only if SPF or DKIM passes and the domain that passed matches the From domain (this is called alignment). Then it tells receivers what to do with messages that do not pass. It is published as a TXT record at `_dmarc.yourdomain.com`: ``` v=DMARC1; p=none; rua=mailto:dmarc@example.com ``` #### The three policies - `p=none`: monitor only. Receivers deliver as normal and send you reports. Start here. - `p=quarantine`: receivers treat failures as suspicious, usually sending them to spam. - `p=reject`: receivers refuse failures outright. This is the strongest protection against someone forging your domain. #### A safe rollout - Publish `p=none` with a reporting address (`rua=`). Nothing changes for your mail. - Read the reports for a few weeks. They list every server that sent mail as your domain and whether it passed. You will find services you forgot about. - Fix the legitimate senders that fail by setting up SPF or DKIM for them. - Move to `p=quarantine`, optionally with `pct=25` to apply it to a quarter of mail first, then raise it. - Move to `p=reject` once only your own mail passes. #### Why Gmail and Yahoo care Since February 2024, Google and Yahoo require senders who send in bulk (Google defines this as more than 5,000 messages a day to Gmail addresses) to authenticate with SPF and DKIM, publish a DMARC record, offer one-click unsubscribe on marketing mail, and keep complaint rates low. Smaller senders are not exempt from the spirit of the rules: mail from a domain with no DMARC is easier to distrust. You do not need `p=reject` to meet the requirement. A DMARC record with `p=none` is the minimum. #### How to check yours A DMARC record is easy to get slightly wrong: a missing `p=`, a policy that applies to only part of your mail, or no reporting address. Run your domain through the free domain checker to see it. Common questions: - What does p=none do? It tells receivers to take no action on failures but to send you reports. It is the right first step: you learn who sends as your domain before anything can be blocked. - Where do DMARC reports go, and what do they look like? To the address in rua=, as compressed XML files, usually once a day from each large receiver. They are hard to read raw, so many people use a DMARC reporting service to turn them into a table. - Will DMARC stop my own email being delivered? Not at p=none. At quarantine or reject it can, if a legitimate sender of yours fails. That is why you read the reports and fix senders before tightening the policy. - Do subdomains need their own record? No: a subdomain inherits the parent’s policy unless it has its own record, and you can set a different policy for subdomains with sp=. ### Why your emails go to spam, and how to fix it Source: https://sendsend.email/learn/why-emails-go-to-spam Mail goes to spam because the receiving server does not trust it. Trust comes from three things: proof you are who you say you are, a history of people wanting your mail, and mail that looks like what people expect. Here is how to check each. #### 1. Your email is not authenticated The most common and most fixable cause. Without SPF, DKIM and DMARC set up for the domain you send from, receivers have little reason to believe the message is yours. Start with the domain checker. #### 2. You are a new sender A new domain or a new sending service has no history, so mailbox providers are cautious. Begin with small volumes to people who expect your mail, such as your own team and recent customers, and increase over a couple of weeks. Jumping from nothing to thousands of messages in a day looks like abuse. #### 3. Your list is old, bought or full of typos - Messages to addresses that no longer exist bounce, and a high bounce rate damages your reputation. - Never buy or rent a list. People who did not ask for your email mark it as spam, and some addresses on such lists are traps set to catch senders. - Remove addresses that bounce for good, and stop mailing people who have not engaged in a long time. - Use confirmation (double opt-in) on signup forms, so typos and other people’s addresses never reach your list. #### 4. People mark your mail as spam Complaints are the strongest signal there is. Google’s guidance is to keep the spam rate reported in its Postmaster Tools below 0.1% and never reach 0.3%. The way to keep complaints low is to email only people who asked, say clearly what they signed up for, send what you promised, and make unsubscribing easy. #### 5. Unsubscribing is hard If people cannot find an unsubscribe link, they use the spam button instead. Put a visible link in every marketing message, and add the one-click unsubscribe header that Gmail and Yahoo now expect for bulk mail. Honour requests immediately. #### 6. The message itself looks wrong - A subject that misleads, or is in all capitals with a lot of punctuation. - A message that is one big image, or has no plain-text version. - Link shorteners, or links whose visible text points somewhere else. - A From name that suggests another company, or a Reply-To on an unrelated domain. - Sending from a free-mail address such as `@gmail.com` through a service. That fails DMARC; use your own domain. #### 7. Your domain or address is on a blocklist Check the major blocklists, such as Spamhaus. Listings usually explain why and how to be removed. Our domain checker looks at the Spamhaus domain list for you. #### 8. Sudden changes in how you send A spike in volume, a new kind of mail from an old domain, or a sudden surge of bounces all look suspicious. Change gradually, and send different kinds of mail (receipts and newsletters) from different subdomains so one cannot hurt the other. #### How to find out which one is yours - Send a test to a few addresses you control at different providers (Gmail, Outlook, Yahoo), and see where it lands. - In Gmail, open the message, choose “Show original”, and look at the SPF, DKIM and DMARC results. Any that say FAIL are your answer. - Register your domain with Google Postmaster Tools to see your domain’s reputation and spam rate, once you send enough volume for it to show data. - Fix one thing at a time, from the top of this list, and test again. Common questions: - Why does my email go to spam even though I have SPF and DKIM? Authentication is necessary but not enough. Reputation, your list, complaint rates and the content also decide where mail lands. If authentication passes, look at who you are mailing and how they react. - How long does it take to fix a spam problem? Authentication fixes work as soon as DNS updates. Reputation recovers slowly: expect weeks of sending only wanted mail to engaged recipients, in modest volumes. - Does using an email service guarantee inbox delivery? No service can promise inbox placement. A good one sets up authentication properly, suppresses bounces and complaints, and shows you what happened, but your list and your content are still yours. - Should I send marketing and transactional mail from the same domain? It is safer to separate them, for example receipts from mail.example.com and newsletters from news.example.com, so a complaint problem in one does not spill into the other. ### Gmail and Yahoo’s email sender requirements, explained Source: https://sendsend.email/learn/gmail-yahoo-sender-requirements Since February 2024, Gmail and Yahoo have required senders to authenticate their mail, make unsubscribing easy, and keep complaints low. The strictest rules apply to bulk senders, but the basics are worth meeting whatever your size. #### Who the rules apply to Google defines a bulk sender as anyone sending more than about 5,000 messages a day to personal Gmail addresses. Yahoo’s rules apply to bulk senders as well. If you cross that line once, Google treats you as a bulk sender from then on. Below that threshold the strictest rules are not enforced the same way, but every sender is expected to have SPF or DKIM, and mail with no authentication is far more likely to be rejected or sent to spam. In practice you should meet all of them. #### What is required - Authenticate your mail. SPF and DKIM for the domain you send from. See SPF and DKIM. - Publish a DMARC record. At minimum `p=none`, and the From domain must align with SPF or DKIM. See DMARC. - One-click unsubscribe on marketing and subscribed mail: the `List-Unsubscribe` and `List-Unsubscribe-Post` headers, plus a visible link in the body. Unsubscribes should be honoured within two days. - Keep spam complaints low. Google asks for a spam rate under 0.10% in Postmaster Tools and says never to reach 0.30%. - Send over TLS, and make sure the servers you send from have valid forward and reverse DNS. - Format messages properly and do not impersonate Gmail’s own From addresses. #### Transactional mail is treated differently, up to a point The one-click unsubscribe rule is for marketing and subscribed mail. A password reset or a receipt does not need an unsubscribe link, and should not have one. Authentication, DMARC and a low complaint rate apply to everything you send from the domain. #### How to check where you stand - Check your domain’s SPF, DKIM and DMARC. Run your domain through the free domain checker to see it. - Register the domain in Google Postmaster Tools. It shows your domain’s reputation and spam rate once you send enough mail for it to have data. - If you send newsletters, check that each one has the unsubscribe header and a working link. SendSend adds both to every broadcast automatically. Common questions: - Do the Gmail and Yahoo rules apply to small senders? The strictest requirements are for bulk senders, but authentication and low complaint rates matter for everyone. Unauthenticated mail from any size of sender is much more likely to be rejected or filtered. - What is one-click unsubscribe? Two email headers, List-Unsubscribe and List-Unsubscribe-Post, that let Gmail and Yahoo show an unsubscribe button that works with a single click, without opening a web page. You should also keep a visible link in the message. - Does p=none satisfy the DMARC requirement? Yes. The requirement is to publish a DMARC record, and p=none is a valid policy. It does not have to be quarantine or reject. - What spam rate is too high? Google recommends staying under 0.10% and says never to reach 0.30%. Both are measured in Google Postmaster Tools, over messages delivered to Gmail users. ### How to warm up a new email sending domain Source: https://sendsend.email/learn/warm-up-a-new-sending-domain Mailbox providers have never seen your new domain send anything, so they are cautious with it. Warming up means building a record of sending mail people want, in steadily increasing amounts. #### Why warm-up exists Spammers register new domains and blast mail the same day. Providers know this, so a brand-new domain that suddenly sends thousands of messages looks the same. Your reputation starts neutral and grows as providers see steady volumes, low bounces, low complaints and people opening or replying. #### Before you send anything - Set up SPF, DKIM and DMARC for the domain, and check them. - Use a subdomain for each kind of mail (for example `mail.example.com` for receipts and `news.example.com` for newsletters), so a problem in one does not affect the other. - Make sure the From address can receive replies. A mailbox that never answers looks like a bot. #### A cautious ramp There is no official schedule, and providers publish none. This is a careful one that works for most small senders; slow down if anything below goes wrong. - Week 1: a few dozen messages a day, to people who know you and will open or reply: your team, recent customers, friends. Ask some to reply. - Week 2: a few hundred a day, to your most engaged contacts. - Weeks 3 and 4: double every few days, adding less engaged contacts last. - After a month: your full list, but never a sudden jump of ten times your usual volume. #### What to watch - Bounces. Keep hard bounces well under 2% and remove bad addresses straight away. - Complaints. Keep them under 0.1%. If they rise, stop and look at who you are mailing. - Where it lands. Send tests to seed addresses at Gmail, Outlook and Yahoo throughout, and look in spam folders. - Google Postmaster Tools. Once you send enough to Gmail, your domain reputation appears there. #### Mistakes that undo a warm-up - Mailing a purchased or very old list on day one. - Sending a large first campaign because it is “just a test”. - Ignoring bounces and keeping them on the list. - Pausing for weeks, then restarting at full volume. - Fake opens and replies from your own accounts: providers can spot it, and it teaches you nothing about real recipients. Common questions: - How long does warming up take? Usually two to six weeks of steady, wanted mail. A larger list or a worse starting point takes longer. There is no fixed number. - Do I need to warm up if I use an email service? Yes, for a new domain. A good service gives you working authentication and handles bounces, but your domain’s reputation is still yours and still starts empty. - Can I warm up with a warm-up tool that sends fake engagement? We do not recommend it. Fake opens and replies exchanged between accounts are a known pattern, can be detected, and do not reflect how real recipients treat your mail. Send real mail to real people who want it. - Should I warm up a domain I have used before? If it has not sent for a long time, treat it as new. If it sends regularly, keep volume steady and increase gradually. ### Transactional email vs marketing email Source: https://sendsend.email/learn/transactional-vs-marketing-email A transactional email is a message someone expects because of something they just did. A marketing email is a message you choose to send to a list. They look similar but follow different rules and need different care. #### The difference in one line If a person did something and this email is the direct result (signed up, bought, asked to reset a password), it is transactional. If you decided to send it to a group because you have something to say, it is marketing. #### Examples - Transactional: password resets, sign-in links, order confirmations and receipts, shipping updates, invoices, security alerts, a reply to a support request. - Marketing: newsletters, product announcements, promotions, sales, event invitations, re-engagement campaigns. #### How the rules differ - Consent. Marketing email needs people to have agreed to receive it, with the exact rules depending on where your recipients live (CAN-SPAM in the US, CASL in Canada, GDPR in the EU and UK). Transactional email generally does not need that consent, but it must stay about the transaction. - Unsubscribe. Marketing email must have an easy unsubscribe. Transactional email does not, and a password reset should not have one. - Content. Adding promotions to a receipt can turn it into a marketing message under some laws. Keep transactional mail to the transaction. - Mailbox rules. Gmail and Yahoo require one-click unsubscribe for marketing and subscribed mail. See their requirements. #### Why to keep them apart Marketing mail gets more complaints and more unsubscribes than receipts and resets. If both go out from one domain and a campaign draws complaints, your password-reset emails can start landing in spam. Send them from different subdomains (for example `mail.example.com` and `news.example.com`) so each builds its own reputation. This is general information, not legal advice. Check the rules for the countries you send to. #### How to send each - Transactional: call an email API from your app when the event happens, with an idempotency key so a retry never sends twice. - Marketing: keep an audience of people who opted in, use a signup form with confirmation, send broadcasts with an unsubscribe link, and watch your complaint rate. Common questions: - Is a welcome email transactional or marketing? A short welcome that confirms the signup and says what happens next is generally transactional. If it is mostly promotion, treat it as marketing. - Do I need an unsubscribe link in a receipt? No. Receipts and similar transactional messages do not need one. Marketing messages do. - Can I put a promotion in an order confirmation? A small related note is usually tolerated, but a message that is mainly promotional can count as marketing, and then the marketing rules apply. When in doubt, keep them separate. - Do I need different domains for each? Different subdomains of the same domain are enough and usually the best choice: they share your brand but build separate sending reputations. ### WordPress contact form emails going to spam: why, and how to fix it Source: https://sendsend.email/learn/wordpress-contact-form-emails-going-to-spam By default WordPress sends email through your web host’s mail system, which is not set up to prove the mail is yours. That is why contact form messages and order emails so often land in spam or vanish. #### Why it happens - WordPress uses the host’s basic mail function. Many hosts share an address with thousands of other sites, and some block or throttle outgoing mail. - The mail is not authenticated. Without SPF and DKIM for your domain covering the server that sent it, receivers cannot tell it is genuine. - The From address is wrong. Many form plugins set the visitor’s email as From. That says “this message came from gmail.com”, sent by your server, which fails DMARC and looks forged. The visitor’s address belongs in Reply-To. - The default sender looks generic. Mail from `wordpress@yourdomain.com` with no mailbox behind it looks like a bot. #### The fix, in order - Send through a real email service instead of the host’s mail function. Install a plugin that sends WordPress mail through an API or authenticated SMTP. SendSend has a WordPress plugin for this, and other services have their own. - Authenticate your domain. Add the service’s DKIM and SPF records and publish a DMARC record. Run your domain through the free domain checker to see it. - Send from an address on your own domain, such as `noreply@yourdomain.com`, and put the visitor’s address in Reply-To so you can answer them. - Send a test from the form and from WooCommerce to a Gmail and an Outlook address, and check the spam folder. #### Checking why a message failed - In Gmail, open the message, choose “Show original”, and read the SPF, DKIM and DMARC lines. FAIL tells you what to fix. - If mail never arrives at all, check your service’s logs for a bounce or a rejection, rather than the inbox. - Turn on your form plugin’s own delivery log if it has one, to confirm WordPress handed the message to the mail service. #### What not to do - Do not use a free-mail address (Gmail, Yahoo) as the From address for mail sent by your site. - Do not keep sending through the host’s mail function “just in case” once you have set up a service: mail can double-send or bypass the authenticated path. - Do not ignore bounces: remove addresses that fail for good. Common questions: - Why do my WooCommerce emails go to spam? For the same reasons as form mail: sent through the host’s basic mail function, not authenticated for your domain, and sometimes with a mismatched From address. Sending through an authenticated email service fixes most of it. - Which plugin should I use? Any plugin that sends WordPress mail through a real email service by API or authenticated SMTP. Check that it lets you set the From address and keeps the visitor’s address in Reply-To. - Should the From address be the person who filled in the form? No. Send from your own domain and use Reply-To for the visitor. Using the visitor’s address as From usually fails DMARC. - Do I need DMARC for a small site? You should publish one. A p=none record is enough to start, and Gmail and Yahoo expect one from anyone who sends in volume. ### An email deliverability checklist for small businesses Source: https://sendsend.email/learn/email-deliverability-checklist Work through this in order. The early items fix most problems, and each one links to a guide with the detail. #### Set up - Send from your own domain, not a free-mail address, and use a subdomain for each kind of mail. - Publish SPF. One record, every sending service listed, under 10 lookups. How. - Turn on DKIM for each service that sends as your domain. How. - Publish DMARC, starting with `p=none` and a reporting address. How. - Check it all. Run your domain through the free domain checker to see it. #### Before you send - Warm up a new domain with small volumes to people who want your mail. How. - Collect addresses properly: a clear signup form, and confirmation by email so typos and strangers never reach your list. - Never buy or scrape a list. - Make sure replies reach someone. #### Every message - Include a plain-text version and keep images in proportion to text. - Make unsubscribing easy on marketing mail: a visible link and the one-click header. Why. - Avoid misleading subjects, link shorteners and mismatched link text. - Keep receipts and resets plain and about the transaction. More. #### Keep it healthy - Remove bounces straight away, and stop mailing people who never engage. - Watch complaints. Keep them under 0.1%. - Change volume gradually and never jump to ten times your usual. - Test regularly to seed addresses at Gmail, Outlook and Yahoo, and read the headers if something lands in spam. Why mail goes to spam. Common questions: - What is the single most important thing? Authenticate the domain you send from with SPF, DKIM and DMARC, then email only people who asked for it. Most deliverability problems trace back to one of those two. - How often should I check my DNS records? After any change to your email services, and every few months otherwise. Services get added and removed over time, and records drift. - Can an email service guarantee delivery? No. A good one sets up authentication, suppresses bounces and complaints, and shows you what happened, but where mail lands also depends on your reputation and your content. ### How to set up an SPF record for Google Workspace Source: https://sendsend.email/learn/spf-record-for-google-workspace If you use Google Workspace for mail, your SPF record needs to include Google. If you also send from a website or another service, it needs to include them too, in the same single record. #### The record Add one TXT record at the root of your domain (the name is `@`, or blank, depending on your DNS host): ``` v=spf1 include:_spf.google.com ~all ``` That says Google's servers may send mail for your domain, and anything else should be treated with suspicion (`~all`, a soft fail). #### Adding another service A domain must have one SPF record. Two records make SPF fail. To allow another service, add its `include:` to the same record: ``` v=spf1 include:_spf.google.com include:spf.your-sender.example ~all ``` Each service tells you its exact include value. Check the total: SPF allows at most 10 DNS lookups, and every `include:` counts, along with whatever it includes in turn. #### Do not stop at SPF SPF alone is not enough. In the Google Admin console, turn on DKIM signing for your domain (under Gmail's email authentication settings), then publish a DMARC record. What DKIM is and what DMARC is. Changes can take from a few minutes to a day to appear. #### Check it Run your domain through the free domain checker to see it. It tells you if you have more than one SPF record, too many lookups, or a missing include. Common questions: - Can I have two SPF records? No. Publish a single record that lists every service. Two records cause a permanent error and SPF fails. - Should I use ~all or -all? Most domains start with ~all (soft fail) and move to -all (hard fail) only once they are sure every sender is listed. DMARC does more of the work than either. - Does my website contact form need to be in SPF? Yes, if it sends mail as your domain from its own server or a mail service. Add that sender, or better, send it through a service that signs with DKIM. ### SPF error: too many DNS lookups (the 10 lookup limit) Source: https://sendsend.email/learn/spf-too-many-dns-lookups SPF lets a receiving server make at most ten DNS lookups while it checks a record. Go past ten and SPF returns a permanent error, so your mail can fail authentication even though every sender is listed correctly. #### What counts as a lookup These mechanisms each cost one lookup: `include`, `a`, `mx`, `ptr`, `exists` and `redirect`. An `include:` also costs whatever the included record uses, so one include for a large service can use several. `ip4`, `ip6` and `all` cost nothing. #### How to find out Run your domain through the free domain checker to see it. It counts the lookups for you. Otherwise, add up every include and expand each one by hand. #### How to get under ten - Remove services you no longer use. Old newsletters and trial tools are the usual cause. - Drop `a` and `mx` unless you really send from those servers. - Replace an include with fixed addresses (`ip4:`) only for a sender with stable addresses, and know you must keep them current. - Move different mail to subdomains. Send marketing from `news.example.com` with its own SPF record, so each record has its own ten. #### Why it matters A permanent error counts as an SPF failure, which can push a message toward spam when DMARC depends on it. More on why mail goes to spam. Common questions: - Is the limit ten includes or ten lookups? Ten DNS lookups in total, counting everything the included records use too. Ten includes can easily be more than ten lookups. - Does flattening SPF help? It can, by replacing includes with address lists, but those lists go stale when a provider changes its servers. Subdomains are the safer fix. - Will mail definitely fail if I am over ten? SPF returns a permanent error. Whether the message is rejected depends on the receiver and on your DKIM and DMARC setup, but you should not rely on it. ### WooCommerce emails not sending or going to spam: how to fix it Source: https://sendsend.email/learn/woocommerce-emails-not-sending WooCommerce hands every email to WordPress, and WordPress by default sends through your web host. That is the usual weak point. Work through these in order. #### First, find out which problem you have - Nothing arrives, not even in spam. The server is not sending, or the host blocks it. - It arrives in spam. The email is sent but not authenticated. Go to step 3. - Only some emails are missing. Check that the email is enabled under WooCommerce, Settings, Emails. #### Fix it - Check the basics. Under WooCommerce, Settings, Emails, make sure each email is turned on and the "From" address uses your own domain, not a free-mail address. - Stop sending through the web host. Many hosts limit or block mail sent directly from the server, and mail from a shared server has no signature of your own. Send through an email service instead, using a plugin that connects to it. SendSend has a WordPress plugin for this. - Authenticate your domain. Publish SPF, turn on DKIM for the service you send through, and add DMARC. SPF, DKIM, DMARC. - Send a test and read the result. Check the message headers for SPF, DKIM and DMARC "pass". #### Check your domain Run your domain through the free domain checker to see it. It shows missing SPF, DKIM and DMARC records, which are behind most spam problems. Common questions: - Why do WooCommerce emails go to spam? Usually because they are sent from the web server with no DKIM signature and an SPF record that does not list the server. Receivers cannot tell they are really from you. - Do I need an SMTP plugin? You need a way to send through a proper email service. That can be SMTP or an API plugin. The goal is authenticated mail from your own domain. - Will this fix password reset emails too? Yes. Password resets, order emails and form notifications all use the same WordPress mail function. ## API reference Base URL: https://api.sendsend.email. Authenticate with `Authorization: Bearer `. Errors are JSON with `statusCode`, `name`, `message` and an `error` object holding `code`, `message` and `request_id`. ### Emails #### POST /v1/emails: Send an email Required scope: emails:send. Success status: 202. Queues one message. A `202` means the message was **durably queued**, not delivered: follow its outcome with webhooks or by fetching it. Send either `subject` with `html` and/or `text`, or a `template_id` with `variables`, never both. Up to 50 recipients in total across `to`, `cc` and `bcc`. Live messages must come from a verified domain in the same project. Recipients on your suppression list are skipped. Add up to 10 `attachments` (base64 `content` or an https `path` we download, with a `content_id` to show an image inline; up to 10 MB in total; executables and scripts are refused). Attachments are not available in a batch. Add up to 20 of your own `headers` (for example `List-Unsubscribe`); headers SendSend sets itself cannot be overridden. Send an `Idempotency-Key` header so a retry can never deliver twice. #### GET /v1/emails/{id}: Retrieve an email Required scope: emails:read. Success status: 200. Returns the message, its overall status and the status of each recipient. Delivery means the recipient’s mail server accepted the message, not that it reached the inbox. #### GET /v1/emails: List emails Required scope: emails:read. Success status: 200. Newest first, with cursor pagination. Pass the `next_cursor` from one page as `cursor` for the next. #### PATCH /v1/emails/{id}: Reschedule an email Required scope: emails:send. Success status: 200. Changes when a scheduled email goes out, from now up to 30 days ahead. Only possible while the email is still waiting: once it is on its way it returns `409 not_schedulable`. #### POST /v1/emails/{id}/cancel: Cancel a scheduled email Required scope: emails:send. Success status: 200. Stops a scheduled email before it is sent and gives its quota back. A message that is already on its way cannot be cancelled. #### POST /v1/emails/batch: Send a batch of emails Required scope: emails:send. Success status: 202. Queues up to 100 messages in one request, each with the same fields as Send an email. Every message is validated first, so one invalid message rejects the whole request before anything is queued. After that each message is queued on its own: one that fails (a suppressed recipient, a quota) is reported in its own slot and never blocks the others. Send an `Idempotency-Key` to make the whole batch safe to retry. Message N uses `:N`. ### Domains #### POST /v1/domains: Add a domain Required scope: domains:manage. Success status: 201. Registers a sending domain and returns the DNS records to add at your DNS provider. The three DKIM CNAME records are required; MAIL FROM and DMARC records are recommended. Each record carries a `dns` field showing whether we can see it in public DNS yet. #### GET /v1/domains: List domains Required scope: domains:manage. Success status: 200. All domains in the project. #### GET /v1/domains/{id}: Retrieve a domain Required scope: domains:manage. Success status: 200. Returns the domain, its records and the last check results. #### POST /v1/domains/{id}/verify: Check a domain now Required scope: domains:manage. Success status: 200. Re-reads public DNS and the sending provider’s verification immediately. We also re-check automatically; a domain that was verified and loses its records stops sending until it is fixed. #### GET /v1/domains/{id}/insights: Get domain insights Required scope: domains:manage. Success status: 200. How a domain is doing over the last 1 to 90 days (default 30): delivery, bounce and complaint rates against the limits that pause sending, open and click rates when tracking is on, the state of its DNS setup, and a short list of what to do next. Only live mail counts. This reports what receiving servers told us; it cannot show inbox versus spam placement. #### DELETE /v1/domains/{id}: Remove a domain Required scope: domains:manage. Success status: 200. Removes the domain from the project. Live sends from it stop immediately. #### PATCH /v1/domains/{id}: Update a domain Required scope: domains:manage. Success status: 200. Turn open and click tracking on or off for the domain. Both are off by default. Opens are approximate, because some mail apps load images for the reader. Click tracking rewrites the links in your emails, so leave it off for emails whose links are secrets, such as sign-in and password-reset links. The `tracking.available` field says whether tracking is switched on for the platform; until it is, the settings are saved but have no effect. ### Templates #### POST /v1/templates: Create or publish a template Required scope: templates:manage. Success status: 201. Publishes an immutable version. Posting an existing `id` publishes the next version; versions are never edited. Write the content as `html` and/or `text`, or build it from a visual `design` (a list of blocks, the same builder as the dashboard) and we generate the HTML and plain text. If you send only HTML, a plain-text version is made for you. Use `{{name}}` placeholders and declare each one under `variables` (types: string, number, boolean, array, object); values are escaped in HTML when sending. The template language also has `{{name | fallback}}`, filters such as `upper`, `currency:'USD'` and `date`, conditions (`{{#if plan == 'pro'}}…{{else}}…{{/if}}`) and loops (`{{#each items as item}}{{item.name}}{{/each}}`). Add `translations` (`{ fr: { subject, html, text } }`) and send with `language` to use them; anything a translation leaves out comes from the main version. A mistake in a template, or a variable it uses without declaring, is refused with a message that says where. #### PATCH /v1/templates/{id}: Update a template’s details Required scope: templates:manage. Success status: 200. Renames a template, files it under a category, or shares traffic across its versions (a split test). `split` lists 2 to 5 versions with percentages that add up to 100. Sends that name no version then use them: a recipient is always given the same version, so the same person does not see both. Sends that name a version always get that version. Use `null` to turn it off. #### GET /v1/templates/{id}/versions/{version}: Retrieve a template version Required scope: templates:manage. Success status: 200. One stored version with its subject, HTML, text, visual design, translations and variables. #### POST /v1/templates/{id}/restore: Restore an earlier version Required scope: templates:manage. Success status: 201. Publishes a copy of an earlier version as the newest. Nothing is lost: the versions in between stay, and sends that name them keep working. #### POST /v1/templates/{id}/test: Send a test email from a template Required scope: templates:manage. Success status: 202. Sends the template to one address so you can see it in a real inbox. It goes through the normal sending path, so it needs a verified `from` domain, counts toward your plan, and the key also needs `emails:send`. Test sends are left out of the template’s stats. #### GET /v1/templates/{id}/stats: See how each version did Required scope: templates:manage. Success status: 200. For each version sent in the last `days` days (1 to 90, default 30): sent, delivered, bounced, complained, and unique opens and clicks when tracking is on for the sending domain. Use it to compare the versions of a split test. #### GET /v1/template-library: List ready-made templates Required scope: templates:manage. Success status: 200. A library of sixty transactional templates, grouped into categories: onboarding, security, e-commerce, WooCommerce (every store email), billing and notifications. Each declares its variables. Add one to your project with “Create a template from the library”. #### GET /v1/template-library/{id}: Retrieve a library template Required scope: templates:manage. Success status: 200. The full design, sample data, and a preview rendered with the sample data in your project’s brand. #### POST /v1/templates/from-library: Create a template from the library Required scope: templates:manage. Success status: 201. Adds a ready-made template to your project as version 1, in your brand kit’s colors, font, logo and footer unless you pass `apply_brand: false`. You can then edit it by publishing new versions. #### POST /v1/saved-blocks: Save a block for reuse Required scope: templates:manage. Success status: 201. Stores one block of a design (a signature, a discount box, a footer) so you can drop it into other templates. A project can keep 100. #### GET /v1/saved-blocks: List saved blocks Required scope: templates:manage. Success status: 200. The blocks saved for reuse in this project, newest first. #### DELETE /v1/saved-blocks/{id}: Delete a saved block Required scope: templates:manage. Success status: 200. Removes it from the saved list. Designs that already use it are not changed. #### GET /v1/brand: Retrieve the brand kit Required scope: templates:manage. Success status: 200. The project’s look: logo, accent and background colors, font and footer text. It is applied when you add a template from the library and in the dashboard’s builder. #### PUT /v1/brand: Update the brand kit Required scope: templates:manage. Success status: 200. Changes only the fields you send. Use `null` to clear one. #### GET /v1/templates: List templates Required scope: templates:manage. Success status: 200. Templates with their latest version. #### GET /v1/templates/{id}: Retrieve a template Required scope: templates:manage. Success status: 200. Returns every version of the template, newest first. #### POST /v1/templates/{id}/preview: Preview a template Required scope: templates:manage. Success status: 200. Renders a template version with sample variables and sends nothing. It renders whatever it can and lists `problems` (missing or mistyped variables, or a mistake in the template), so a preview never fails just because a sample value is not filled in yet. Omit `version` for the latest, and pass `language` to preview a translation. #### POST /v1/templates/{id}/duplicate: Duplicate a template Required scope: templates:manage. Success status: 201. Copies a version (the latest unless you name one) into a new template, at version 1. #### DELETE /v1/templates/{id}: Delete a template Required scope: templates:manage. Success status: 200. Removes the template and all its versions. Emails you already sent are not affected: each keeps the subject and body it was sent with. ### Webhooks #### POST /v1/webhooks: Create a webhook endpoint Required scope: webhooks:manage. Success status: 201. Registers an HTTPS endpoint for delivery events. The signing `secret` is shown **once**, in this response. Requests to private network addresses are refused. #### GET /v1/webhooks: List webhook endpoints Required scope: webhooks:manage. Success status: 200. Active endpoints in the project. #### DELETE /v1/webhooks/{id}: Delete a webhook endpoint Required scope: webhooks:manage. Success status: 200. Stops deliveries to the endpoint. #### POST /v1/webhooks/{id}/rotate-secret: Rotate the signing secret Required scope: webhooks:manage. Success status: 200. Issues a new secret, shown once. The old secret keeps signing alongside the new one for 24 hours so you can switch over without dropping events. #### GET /v1/webhooks/{id}/deliveries: List deliveries Required scope: webhooks:manage. Success status: 200. The most recent 100 deliveries to the endpoint, with their status and last response. #### GET /v1/webhooks/{id}/deliveries/{did}: Retrieve a delivery Required scope: webhooks:manage. Success status: 200. One delivery with each attempt: status code, error and duration. #### POST /v1/webhooks/{id}/deliveries/{did}/replay: Replay a delivery Required scope: webhooks:manage. Success status: 202. Sends the same event again (same event id and payload) as a new attempt. It never creates a new event, so your idempotency on `SendSend-Event-Id` keeps working. #### GET /v1/webhooks/{id}: Retrieve a webhook endpoint Required scope: webhooks:manage. Success status: 200. Returns the endpoint. The signing secret is never shown again after creation. #### PATCH /v1/webhooks/{id}: Update a webhook endpoint Required scope: webhooks:manage. Success status: 200. Change where the endpoint points or which events it receives. Its secret and delivery history are kept. The new URL goes through the same safety checks as when you create one. ### Suppressions #### POST /v1/suppressions: Add a suppression Required scope: suppressions:manage. Success status: 201. Stops all mail to an address. The suppression list belongs to your organization, so every project shares it. Addresses are stored in lower case. Adding an address that is already listed re-activates it. #### POST /v1/suppressions/batch: Add suppressions in bulk Required scope: suppressions:manage. Success status: 201. Adds up to 1,000 addresses at once. Duplicates are ignored. #### GET /v1/suppressions: List suppressions Required scope: suppressions:manage. Success status: 200. Active suppressions, newest first, with cursor pagination. #### GET /v1/suppressions/{id}: Retrieve a suppression Required scope: suppressions:manage. Success status: 200. Look up by suppression id or by email address (URL-encoded). #### DELETE /v1/suppressions/{id}: Remove a suppression Required scope: suppressions:manage. Success status: 200. Lets mail to the address resume. The removal is recorded in your audit log. If the address bounces permanently or complains again, it is suppressed again. Only remove an address you are sure wants your mail. #### POST /v1/suppressions/remove-batch: Remove suppressions in bulk Required scope: suppressions:manage. Success status: 200. Removes up to 1,000 addresses. Addresses that are not listed are ignored. ### Verification #### POST /v1/verify: Verify an email address Required scope: emails:verify. Success status: 200. Checks one address before you send to it: that it is well formed, that its domain exists and accepts mail, whether it is a throwaway inbox or a role address such as info@, whether the domain looks like a typo of a popular one (with a suggestion), and whether it is on your suppression list. It does not contact the recipient’s mail server, so `valid` means worth sending to, not guaranteed to arrive. Results: `valid`, `invalid` (will bounce), `risky` (see `reasons`) and `unknown` (DNS did not answer; try again). Each address uses one of your plan’s monthly verifications. #### POST /v1/verify/batch: Verify up to 100 email addresses Required scope: emails:verify. Success status: 200. The same checks as verifying one address, for a list of up to 100. Results come back in the order you sent them, with a count of each outcome. Every address uses one verification, and the whole request is refused if your remaining verifications would not cover it. ### DMARC #### POST /v1/dmarc/domains: Monitor a domain’s DMARC Required scope: domains:manage. Success status: 201. Starts DMARC monitoring for a domain. We give you a report address; add it to your domain’s DMARC record as `rua=mailto:…` (the `record` field is a ready-made record for a domain with none). Mailbox providers such as Google, Microsoft and Yahoo then send a daily summary of the mail they saw using your domain, and we turn it into who is sending, whether it passes, and what to fix. The first reports usually arrive within a day or two. Reports are kept for 90 days. How many domains you can monitor depends on your plan. #### GET /v1/dmarc/domains: List monitored domains Required scope: domains:manage. Success status: 200. The domains monitored in this project, with their report address and whether reports are arriving. #### GET /v1/dmarc/domains/{id}: Get a domain’s DMARC summary Required scope: domains:manage. Success status: 200. How the domain is doing over the last `days` days (1 to 90, default 30): messages seen, how many passed DMARC (an aligned SPF or DKIM pass), how many were quarantined or rejected, daily totals, the policy your domain publishes, whether your DNS record points at us (`record_check`: `ok`, `missing`, `not_pointing_here` or `unknown`), and plain advice on whether it is safe to move to a stricter policy. #### GET /v1/dmarc/domains/{id}/sources: List who sends as your domain Required scope: domains:manage. Success status: 200. Every server that sent mail using the domain in the period, busiest first: its address and reverse-DNS name, the service it belongs to when we recognise it (Amazon SES, Google Workspace, SendGrid and so on), how many messages passed and failed, and what to do about it. #### DELETE /v1/dmarc/domains/{id}: Stop monitoring a domain Required scope: domains:manage. Success status: 200. Removes the domain and every report we stored for it. Reports sent to its address afterwards are discarded. ### Blocklists #### POST /v1/blocklist/monitors: Monitor a domain or IP for blocklists Required scope: domains:manage. Success status: 201. Watches a domain name or a public IPv4 address against well-known public spam blocklists (Spamhaus, SURBL, URIBL, SpamCop and others). It is checked straight away, then again every six hours, and the account’s owners and admins are emailed when it becomes listed and when it is cleared. Domains are checked against domain blocklists and IPs against IP blocklists. A list that refuses to answer is reported as not checked and never counted as clean. #### GET /v1/blocklist/monitors: List blocklist monitors Required scope: domains:manage. Success status: 200. Every monitored domain and IP in this project with its current status: `clean`, `listed` (see `listed_on` for the lists and the reasons), `unknown` (no list could be checked) or `pending`. #### GET /v1/blocklist/monitors/{id}: Get a blocklist monitor Required scope: domains:manage. Success status: 200. One monitor with its recent history of being listed and cleared. #### POST /v1/blocklist/monitors/{id}/check: Check a monitor now Required scope: domains:manage. Success status: 200. Checks the lists right away, for example after you asked a list to remove you. A monitor can be checked at most once a minute. #### DELETE /v1/blocklist/monitors/{id}: Stop monitoring Required scope: domains:manage. Success status: 200. Removes the monitor and its history. ### Audiences #### POST /v1/audiences: Create an audience Required scope: contacts:manage. Success status: 201. An audience is a list of contacts you send broadcasts to. A project can have 20. #### GET /v1/audiences: List audiences Required scope: contacts:manage. Success status: 200. All audiences in the project. #### GET /v1/audiences/{id}: Retrieve an audience Required scope: contacts:manage. Success status: 200. Includes how many contacts it holds and how many have unsubscribed. #### DELETE /v1/audiences/{id}: Delete an audience Required scope: contacts:manage. Success status: 200. Deletes the audience and its contacts. Refused while a broadcast to it is scheduled or sending, and for audiences that have already sent a broadcast, which are kept for your records. ### Contacts #### POST /v1/audiences/{id}/contacts: Create a contact Required scope: contacts:manage. Success status: 201. Adds one person to an audience. Properties must be created first with the contact properties endpoints. An audience holds up to 50,000 contacts. #### POST /v1/audiences/{id}/contacts/import: Import contacts Required scope: contacts:manage. Success status: 200. Adds or updates up to 1,000 contacts in one request. Emails that exist are updated, and an import never resubscribes someone who unsubscribed. #### GET /v1/audiences/{id}/contacts: List contacts Required scope: contacts:manage. Success status: 200. Contacts in an audience, with cursor pagination. #### GET /v1/audiences/{id}/contacts/{contact}: Retrieve a contact Required scope: contacts:manage. Success status: 200. Look a contact up by id or by email address. #### PATCH /v1/audiences/{id}/contacts/{contact}: Update a contact Required scope: contacts:manage. Success status: 200. Change names, email, properties or the unsubscribed flag. Properties you send are merged with the ones already set. #### DELETE /v1/audiences/{id}/contacts/{contact}: Delete a contact Required scope: contacts:manage. Success status: 200. Removes the contact from the audience. #### GET /v1/audiences/{id}/contacts/{contact}/topics: List a contact’s topics Required scope: contacts:manage. Success status: 200. Every topic in the project and whether this contact is subscribed. A topic they have not chosen uses its default. #### PATCH /v1/audiences/{id}/contacts/{contact}/topics: Update a contact’s topics Required scope: contacts:manage. Success status: 200. Subscribe or unsubscribe a contact from topics. ### Contact properties #### POST /v1/contact-properties: Create a contact property Required scope: contacts:manage. Success status: 201. A custom field you can store on contacts and use in broadcasts as `{{{key}}}`. The fallback is used when a contact has no value. #### GET /v1/contact-properties: List contact properties Required scope: contacts:manage. Success status: 200. All custom fields in the project. #### GET /v1/contact-properties/{id}: Retrieve a contact property Required scope: contacts:manage. Success status: 200. By id or by key. #### PATCH /v1/contact-properties/{id}: Update a contact property Required scope: contacts:manage. Success status: 200. Change the fallback value. The key and type cannot change. #### DELETE /v1/contact-properties/{id}: Delete a contact property Required scope: contacts:manage. Success status: 200. Removes the field and its value from every contact. ### Topics #### POST /v1/topics: Create a topic Required scope: contacts:manage. Success status: 201. A kind of email people can opt in or out of, such as product news. A broadcast with a topic goes only to contacts subscribed to it, and its unsubscribe link removes them from that topic alone. #### GET /v1/topics: List topics Required scope: contacts:manage. Success status: 200. All topics in the project. #### GET /v1/topics/{id}: Retrieve a topic Required scope: contacts:manage. Success status: 200. One topic. #### PATCH /v1/topics/{id}: Update a topic Required scope: contacts:manage. Success status: 200. Rename it, change its description or its default. #### DELETE /v1/topics/{id}: Delete a topic Required scope: contacts:manage. Success status: 200. Removes the topic and every contact’s choice about it. ### Segments #### POST /v1/segments: Create a segment Required scope: contacts:manage. Success status: 201. A saved filter over one audience's contacts. A broadcast can be sent to a segment instead of the whole audience, and the people it matches are worked out when each batch is sent, so late changes to a contact count. Each rule has a `field` (`email`, `first_name`, `last_name`, `status`, `created_at`, `property` with a `key`, or `topic` with a `topic_id`), an `op` and usually a `value`. Text operators: `equals`, `not_equals`, `contains`, `not_contains`, `starts_with`, `ends_with`, `is_set`, `is_not_set` (case-insensitive). Number properties also take `gt`, `gte`, `lt`, `lte`. `status` is `subscribed` or `unsubscribed`. `created_at` takes `before` or `after` with a date, or `within_days` or `older_than_days` with a number. `topic` takes `subscribed` or `not_subscribed`. `match` is `all` (every rule) or `any` (at least one). Up to 10 rules. #### POST /v1/segments/preview: Preview a segment Required scope: contacts:manage. Success status: 200. Try rules out before you save them: how many contacts match, and up to five of their addresses. Nothing is stored. #### GET /v1/segments: List segments Required scope: contacts:manage. Success status: 200. All segments in the project, or those of one audience. #### GET /v1/segments/{id}: Retrieve a segment Required scope: contacts:manage. Success status: 200. Includes how many contacts match right now. #### PATCH /v1/segments/{id}: Update a segment Required scope: contacts:manage. Success status: 200. Rename it, or change its rules or `match`. Rules replace the old ones. Each rule has a `field` (`email`, `first_name`, `last_name`, `status`, `created_at`, `property` with a `key`, or `topic` with a `topic_id`), an `op` and usually a `value`. Text operators: `equals`, `not_equals`, `contains`, `not_contains`, `starts_with`, `ends_with`, `is_set`, `is_not_set` (case-insensitive). Number properties also take `gt`, `gte`, `lt`, `lte`. `status` is `subscribed` or `unsubscribed`. `created_at` takes `before` or `after` with a date, or `within_days` or `older_than_days` with a number. `topic` takes `subscribed` or `not_subscribed`. `match` is `all` (every rule) or `any` (at least one). Up to 10 rules. #### DELETE /v1/segments/{id}: Delete a segment Required scope: contacts:manage. Success status: 200. Refused while a scheduled, sending or paused broadcast uses it. Finished broadcasts keep their history without the segment. #### GET /v1/segments/{id}/contacts: List a segment’s contacts Required scope: contacts:manage. Success status: 200. The contacts that match now, with a total and cursor pagination. ### Forms #### POST /v1/forms: Create a signup form Required scope: contacts:manage. Success status: 201. A form that adds people to an audience. Each form has a hosted page at its `url`, which you can link to or embed in a frame, and the same address accepts a plain HTML form `POST` from your own site. For JavaScript, post JSON to `https://api.sendsend.email/public/forms/{id}/subscribe` with `{ "email": "…", "first_name": "…" }`; it allows requests from any site. With `double_opt_in` (the default) a new person is held as pending, we email them a confirmation link from `from`, and they are only subscribed when they press the button in it. Pending contacts are never sent a broadcast. Anyone who unsubscribed before must confirm again, even on a form without double opt-in, so nobody can be put back on a list by someone else typing their address. The response never says whether an address was already on the list. Signups are rate limited, and we send at most one confirmation to an address every ten minutes. #### GET /v1/forms: List signup forms Required scope: contacts:manage. Success status: 200. All forms in the project, or those of one audience. #### GET /v1/forms/{id}: Retrieve a signup form Required scope: contacts:manage. Success status: 200. One form, with the address of its hosted page. #### PATCH /v1/forms/{id}: Update a signup form Required scope: contacts:manage. Success status: 200. Change any setting. Set `active` to false to stop accepting signups without deleting the form. Send `null` to clear the title, confirmation subject or a redirect. #### DELETE /v1/forms/{id}: Delete a signup form Required scope: contacts:manage. Success status: 200. The form stops working at once. Contacts it already added stay in the audience. ### Broadcasts #### POST /v1/broadcasts: Create a broadcast Required scope: broadcasts:manage. Success status: 201. Saves a draft email to send to everyone in an audience. Use `{{{FIRST_NAME|there}}}`, `{{{LAST_NAME}}}`, `{{{EMAIL}}}` or any contact property; text after `|` is used when a contact has no value. Every message gets an unsubscribe link and the one-click unsubscribe headers, and a footer link is added if you leave out `{{{UNSUBSCRIBE_URL}}}`. Instead of `html` you can send a `design`: a list of blocks (heading, text, button, image, divider, spacer, footer) with an accent color, background and font. We turn it into email-safe HTML and a plain-text version, and the dashboard can open it in the visual editor. Pass `segment_id` to send to a segment of the audience instead of everyone in it. Pass `ab_test` to split test it: your subject and content are version A, each entry in `ab_test.variants` is another version (B, C, D) that replaces whatever it sets, `test_percent` of the audience (5 to 50) is divided between the versions, and after `wait_hours` (1 to 72) the version with the best `winner_by` (`opens` or `clicks`) is sent to everyone else. A split test needs open (or click) tracking on the sending domain. Nothing is sent until you call send. #### GET /v1/broadcasts: List broadcasts Required scope: broadcasts:manage. Success status: 200. Newest first, without the message bodies. #### GET /v1/broadcasts/{id}: Retrieve a broadcast Required scope: broadcasts:manage. Success status: 200. Includes how many contacts it was sent to, skipped (unsubscribed or suppressed) and failed. #### GET /v1/broadcasts/{id}/stats: Get broadcast stats Required scope: broadcasts:manage. Success status: 200. How a broadcast did: delivered, bounced and complained counts, unique opens and clicks, the links people clicked, and the unsubscribes it caused. Opens and clicks are counted once per recipient and only exist when tracking is on for the sending domain (`tracking` says whether it is). Opens are approximate: some mail apps load images for the reader. For a split test, `ab_test` shows each version’s results from the test slice, the phase (`test`, `waiting` or `final`), and the winner once chosen. #### GET /v1/broadcasts/{id}/links: List clicked links Required scope: broadcasts:manage. Success status: 200. Every link recipients clicked in a broadcast, most clicked first. Each link is counted once per recipient, the same as in the stats (which show only the top ten). Clicks only exist when click tracking is on for the sending domain. #### POST /v1/broadcasts/{id}/duplicate: Duplicate a broadcast Required scope: broadcasts:manage. Success status: 201. Creates a new draft with the same audience, segment, topic, sender, subject and content, named “Copy of …”. Works on a broadcast in any state, so you can resend last month’s update with changes. Nothing is sent until you call send on the copy. #### PATCH /v1/broadcasts/{id}: Update a broadcast Required scope: broadcasts:manage. Success status: 200. Only while it is a draft. #### DELETE /v1/broadcasts/{id}: Delete a broadcast Required scope: broadcasts:manage. Success status: 200. Only a draft or cancelled broadcast. #### POST /v1/broadcasts/{id}/send: Send a broadcast Required scope: broadcasts:manage. Success status: 202. Starts sending now, or at `scheduled_at`. Contacts who unsubscribed (or are not subscribed to the broadcast’s topic) are skipped, and every message goes through the same checks as any other email: your plan’s allowance, daily limit, verified domain and account review. If a limit is reached the broadcast pauses with the reason; call send again to continue where it stopped, without resending to anyone. #### POST /v1/broadcasts/{id}/cancel: Cancel a broadcast Required scope: broadcasts:manage. Success status: 200. Stops a scheduled, sending or paused broadcast. Messages already sent are not recalled. ### Inbound #### GET /v1/inbound: Get the receiving address Required scope: inbound:manage. Success status: 200. Every project has its own address. Mail to any name at that domain (support@, orders@, anything) is received, scanned for viruses and spam, and stored for your retention period (30 days by default). The first call creates the address; it stays the same after that. #### GET /v1/received-emails: List received emails Required scope: inbound:manage. Success status: 200. Newest first, without bodies. Spam-flagged mail is included with `spam: true`, and does not trigger an `email.received` webhook. #### GET /v1/received-emails/{id}: Retrieve a received email Required scope: inbound:manage. Success status: 200. Includes the text and HTML bodies, the headers and the attachment list. The content comes from the sender, so treat the HTML as untrusted: never render it in your own page without sandboxing. #### GET /v1/received-emails/{id}/raw: Download the original message Required scope: inbound:manage. Success status: 200. The message exactly as it arrived, as an `.eml` file. Kept for messages up to 15 MB. #### GET /v1/received-emails/{id}/attachments: List attachments Required scope: inbound:manage. Success status: 200. Attachments on a received email. `available` is false for files over 10 MB, which are listed but not kept. #### GET /v1/received-emails/{id}/attachments/{attachment}: Download an attachment Required scope: inbound:manage. Success status: 200. The file itself, always as a download. Scan files before opening them: anyone can send them. #### DELETE /v1/received-emails/{id}: Delete a received email Required scope: inbound:manage. Success status: 200. Removes the message and its attachments now, rather than waiting for the retention period. #### POST /v1/inbound/domains: Receive on your own domain Required scope: inbound:manage. Success status: 201. Start receiving mail at a domain you control, such as `support.acme.com`, instead of the project address. Publish the two records in the response: an MX record pointing at us, and a TXT record that proves you own the domain. We check them every minute, and for up to 72 hours, and the domain turns `active` once both are right. The MX must be the only MX at that name: a second one would send some of your mail elsewhere. **Use a subdomain.** Pointing a root domain such as `acme.com` here sends all of its mail to SendSend and none to the mailboxes it has now. The response includes a `warning` for root domains. A project can receive on up to 5 domains. #### GET /v1/inbound/domains: List inbound domains Required scope: inbound:manage. Success status: 200. The domains this project receives on, and whether each one’s DNS is in place. #### GET /v1/inbound/domains/{id}: Retrieve an inbound domain Required scope: inbound:manage. Success status: 200. One domain, with the state of each record. #### POST /v1/inbound/domains/{id}/verify: Check an inbound domain now Required scope: inbound:manage. Success status: 200. Looks the records up immediately instead of waiting for the next automatic check. A domain that gave up after 72 hours starts waiting again. #### DELETE /v1/inbound/domains/{id}: Stop receiving on a domain Required scope: inbound:manage. Success status: 200. Closes receiving for the domain straight away. Remove the MX record too, or mail sent to it will bounce. Mail already received is kept until its retention period ends. ### API keys #### POST /v1/api-keys: Create an API key Required scope: api_keys:manage. Success status: 201. Creates a key for the same project as the key making the call. The new key can only have permissions that the calling key has itself, so this can never be used to gain more access. The secret is shown **once**. No key has the `api_keys:manage` scope by default; grant it deliberately. #### GET /v1/api-keys: List API keys Required scope: api_keys:manage. Success status: 200. The project’s keys, newest first. Secrets are never shown. #### PATCH /v1/api-keys/{id}: Update an API key Required scope: api_keys:manage. Success status: 200. Rename a key or change its permissions. You can only give a key permissions you have, and you cannot change a key that is more powerful than yours. #### DELETE /v1/api-keys/{id}: Revoke an API key Required scope: api_keys:manage. Success status: 200. The key stops working immediately. You cannot revoke a key that is more powerful than yours. ### Logs #### GET /v1/logs: List request logs Required scope: emails:read. Success status: 200. The last 30 days of calls made with this project’s API keys, newest first: what was called, the status, the time it took and the request id. Message content, recipients and request bodies are never stored. Reading the log does not add to it. #### GET /v1/logs/{id}: Retrieve a request log Required scope: emails:read. Success status: 200. One logged call. ### Usage #### GET /v1/usage: Retrieve usage Required scope: emails:read. Success status: 200. This month’s sending against your allowance, the project’s daily limit, spending settings and sending health. Test-environment sends are not counted. #### GET /v1/metrics: Retrieve metrics Required scope: emails:read. Success status: 200. Recipient outcomes per day for the project: sent, delivered, bounced, complained, failed and suppressed, with totals and rates, plus unique opens and clicks when tracking is on (measured against delivered). `sent` counts recipients the provider accepted. Days are in UTC. The default range is the last 30 days, and the longest is 92.