# Which sending feature should I use?

> Transactional emails (POST /emails) are for one-to-one app email; campaigns are for one-to-many marketing to a sending domain’s contacts. How to choose.

MailBlastr has two ways to send mail, built for two different jobs. Use **transactional sends** (`POST /emails`) for messages you trigger for a single person from your application — and **campaigns** for one campaign sent to many contacts at once. Both send from a verified domain; they differ in how you address recipients and how unsubscribes are handled.

## At a glance

|  | Transactional | Campaign |
| --- | --- | --- |
| Endpoint | `POST /emails` | `POST /campaigns/:id/send` |
| Shape | One-to-one (or a few `to`/`cc`/`bcc`) | One-to-many to a domain’s contacts |
| Recipients | Addresses you pass in the request (`to`, 1–50) | Every subscribed contact on the linked `domain` |
| Typical use | Receipts, password resets, magic links, alerts, OTPs | Newsletters, product announcements, marketing campaigns |
| Triggered by | Your app, per event, in real time | You, once, when the campaign is ready |
| Unsubscribe | Not applicable (1:1 app mail) | Per-contact one-click unsubscribe, added automatically |
| Permission needed | `sending_access` or `full_access` | `full_access` to create/edit; send needs `sending_access` |
| Scheduling | `scheduled_at` on the send | `scheduled_at` on `/send` |
| Idempotency | `Idempotency-Key` header | Drafts are sent once (already-`sent` is rejected) |

## Use transactional (POST /emails) when…

- The email is **for one person** and triggered by something they did — a signup, a purchase, a password reset request.
- You already know the recipient address at send time and want to pass it directly.
- You need a programmatic, low-latency send straight from your backend.
- Examples: order receipts, password resets, email verification, magic links, security alerts, one-off notifications.

A transactional send takes the recipients inline and returns an email `id` immediately:

**Node.js**

```js
import { MailBlastr } from 'mailblastr';

const mb = new MailBlastr('mb_xxxxxxxxx');

const { data, error } = await mb.emails.send({
  "from": "Acme <receipts@yourdomain.com>",
  "to": ["customer@example.com"],
  "subject": "Your receipt",
  "html": "<p>Thanks for your order!</p>"
});
console.log({ data, error });
```

**Ruby**

```ruby
require "mailblastr"

Mailblastr.api_key = "mb_xxxxxxxxx"

Mailblastr::Emails.send({
  "from": "Acme <receipts@yourdomain.com>",
  "to": [
    "customer@example.com"
  ],
  "subject": "Your receipt",
  "html": "<p>Thanks for your order!</p>"
})
```

**PHP**

```php
$mailblastr = Mailblastr::client('mb_xxxxxxxxx');

$mailblastr->emails->send([
  'from' => "Acme <receipts@yourdomain.com>",
  'to' => [
    "customer@example.com"
  ],
  'subject' => "Your receipt",
  'html' => "<p>Thanks for your order!</p>"
]);
```

**Python**

```python
import mailblastr

mailblastr.api_key = "mb_xxxxxxxxx"

mailblastr.Emails.send({
  "from": "Acme <receipts@yourdomain.com>",
  "to": [
    "customer@example.com"
  ],
  "subject": "Your receipt",
  "html": "<p>Thanks for your order!</p>"
})
```

**Go**

```go
import "github.com/shekhu10/mailblastr-sdks/mailblastr-go"

client := mailblastr.NewClient("mb_xxxxxxxxx")

params := &mailblastr.SendEmailRequest{
    From:    "Acme <hello@yourdomain.com>",
    To:      []string{"delivered@example.com"},
    Subject: "hello world",
    Html:    "<p>it works!</p>",
}
sent, err := client.Emails.Send(params)
```

**Rust**

```rust
use mailblastr::{Mailblastr, CreateEmailBaseOptions};

let mb = Mailblastr::new("mb_xxxxxxxxx");

let email = CreateEmailBaseOptions::new(
    "Acme <hello@yourdomain.com>",
    ["delivered@example.com"],
    "hello world",
).with_html("<p>it works!</p>");
let _sent = mb.emails.send(email).await?;
```

**Java**

```java
import com.mailblastr.*;

Mailblastr mailblastr = new Mailblastr("mb_xxxxxxxxx");

SendEmailRequest request = SendEmailRequest.builder()
    .from("Acme <hello@yourdomain.com>")
    .to("delivered@example.com")
    .subject("hello world")
    .html("<p>it works!</p>")
    .build();
mailblastr.emails().send(request);
```

**.NET**

```csharp
using Mailblastr;

IMailblastr mailblastr = MailblastrClient.Create("mb_xxxxxxxxx");

var resp = await mailblastr.EmailSendAsync(new EmailMessage
{
    From = "Acme <hello@yourdomain.com>",
    To = "delivered@example.com",
    Subject = "hello world",
    HtmlBody = "<p>it works!</p>",
});
```

**cURL**

```bash
curl -X POST 'https://api.mailblastr.com/emails' \
  -H 'Authorization: Bearer mb_xxxxxxxxx' \
  -H 'Content-Type: application/json' \
  -d '{
  "from": "Acme <receipts@yourdomain.com>",
  "to": ["customer@example.com"],
  "subject": "Your receipt",
  "html": "<p>Thanks for your order!</p>"
}'
```

**CLI**

```bash
mailblastr emails send \
  --from 'Acme <receipts@yourdomain.com>' \
  --to 'customer@example.com' \
  --subject 'Your receipt' \
  --html '<p>Thanks for your order!</p>'
```

## Use a campaign when…

- The email goes to **many people at once** — everyone on a list, not one triggered recipient.
- It is **marketing or newsletter** content where recipients must be able to unsubscribe.
- You want MailBlastr to fan the message out across a sending domain’s [contacts](https://www.mailblastr.com/docs/audiences/overview) and track per-campaign performance.
- Examples: monthly newsletters, launch announcements, promotions, re-engagement campaigns.

A campaign targets a sending `domain` — its contact pool — instead of inline recipients. You create the draft, then send it — MailBlastr delivers to every subscribed contact and appends a one-click unsubscribe footer.

**1. Create the campaign (draft):**

**Node.js**

```js
import { MailBlastr } from 'mailblastr';

const mb = new MailBlastr('mb_xxxxxxxxx');

const { data, error } = await mb.campaigns.create({
  "domain": "yourdomain.com",
  "from": "Acme <news@yourdomain.com>",
  "subject": "March newsletter",
  "html": "<p>What we shipped this month…</p>"
});
console.log({ data, error });
```

**Ruby**

```ruby
require "mailblastr"

Mailblastr.api_key = "mb_xxxxxxxxx"

Mailblastr::Campaigns.create({
  "domain": "yourdomain.com",
  "from": "Acme <news@yourdomain.com>",
  "subject": "March newsletter",
  "html": "<p>What we shipped this month…</p>"
})
```

**PHP**

```php
$mailblastr = Mailblastr::client('mb_xxxxxxxxx');

$mailblastr->campaigns->create([
  'domain' => "yourdomain.com",
  'from' => "Acme <news@yourdomain.com>",
  'subject' => "March newsletter",
  'html' => "<p>What we shipped this month…</p>"
]);
```

**Python**

```python
import mailblastr

mailblastr.api_key = "mb_xxxxxxxxx"

mailblastr.Campaigns.create({
  "domain": "yourdomain.com",
  "from": "Acme <news@yourdomain.com>",
  "subject": "March newsletter",
  "html": "<p>What we shipped this month…</p>"
})
```

**Go**

```go
client := mailblastr.NewClient("mb_xxxxxxxxx")

params := &mailblastr.CreateCampaignRequest{
    Domain:  "yourdomain.com",
    From:    "Acme <hello@yourdomain.com>",
    Subject: "Product update",
    Html:    "<p>Big news!</p>",
}
campaign, err := client.Campaigns.Create(params)
```

**Rust**

```rust
let mb = Mailblastr::new("mb_xxxxxxxxx");

let campaign = CreateCampaignOptions::new(
    "yourdomain.com",
    "Acme <hello@yourdomain.com>",
    "Product update",
).with_html("<p>Big news!</p>");
let _created = mb.campaigns.create(campaign).await?;
```

**Java**

```java
Mailblastr mailblastr = new Mailblastr("mb_xxxxxxxxx");

CreateCampaignRequest request = CreateCampaignRequest.builder()
    .domain("yourdomain.com")
    .from("Acme <hello@yourdomain.com>")
    .subject("Product update")
    .html("<p>Big news!</p>")
    .build();
mailblastr.campaigns().create(request);
```

**.NET**

```csharp
IMailblastr mailblastr = MailblastrClient.Create("mb_xxxxxxxxx");

var resp = await mailblastr.CampaignCreateAsync(new CampaignCreateOptions
{
    Domain = "yourdomain.com",
    From = "Acme <hello@yourdomain.com>",
    Subject = "Product update",
    HtmlBody = "<p>Big news!</p>",
});
```

**cURL**

```bash
curl -X POST 'https://api.mailblastr.com/campaigns' \
  -H 'Authorization: Bearer mb_xxxxxxxxx' \
  -H 'Content-Type: application/json' \
  -d '{
  "domain": "yourdomain.com",
  "from": "Acme <news@yourdomain.com>",
  "subject": "March newsletter",
  "html": "<p>What we shipped this month…</p>"
}'
```

**CLI**

```bash
mailblastr campaigns create \
  --domain 'yourdomain.com' \
  --from 'Acme <news@yourdomain.com>' \
  --subject 'March newsletter' \
  --html '<p>What we shipped this month…</p>'
```

**2. Send it** (optionally pass `scheduled_at`), using the `id` returned from the create call:

**Node.js**

```js
import { MailBlastr } from 'mailblastr';

const mb = new MailBlastr('mb_xxxxxxxxx');

const { data, error } = await mb.campaigns.send('CAMPAIGN_ID');
console.log({ data, error });
```

**Ruby**

```ruby
require "mailblastr"

Mailblastr.api_key = "mb_xxxxxxxxx"

Mailblastr::Campaigns.send("CAMPAIGN_ID")
```

**PHP**

```php
$mailblastr = Mailblastr::client('mb_xxxxxxxxx');

$mailblastr->campaigns->send('CAMPAIGN_ID');
```

**Python**

```python
import mailblastr

mailblastr.api_key = "mb_xxxxxxxxx"

mailblastr.Campaigns.send("CAMPAIGN_ID")
```

**Go**

```go
package main

import (
    "fmt"
    "io"
    "net/http"
)

func main() {
    req, _ := http.NewRequest("POST", "https://api.mailblastr.com/campaigns/CAMPAIGN_ID/send", nil)
    req.Header.Set("Authorization", "Bearer mb_xxxxxxxxx")
    res, _ := http.DefaultClient.Do(req)
    defer res.Body.Close()
    out, _ := io.ReadAll(res.Body)
    fmt.Println(string(out))
}
```

**Rust**

```rust
use reqwest::Client;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let res = Client::new()
        .post("https://api.mailblastr.com/campaigns/CAMPAIGN_ID/send")
        .header("Authorization", "Bearer mb_xxxxxxxxx")
        .send()
        .await?;
    println!("{}", res.text().await?);
    Ok(())
}
```

**Java**

```java
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;

var client = HttpClient.newHttpClient();
var request = HttpRequest.newBuilder()
    .uri(URI.create("https://api.mailblastr.com/campaigns/CAMPAIGN_ID/send"))
    .header("Authorization", "Bearer mb_xxxxxxxxx")
    .method("POST", HttpRequest.BodyPublishers.noBody())
    .build();
var response = client.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println(response.body());
```

**.NET**

```csharp
using System.Net.Http;
using System.Text;

var client = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Post, "https://api.mailblastr.com/campaigns/CAMPAIGN_ID/send");
request.Headers.Add("Authorization", "Bearer mb_xxxxxxxxx");
var response = await client.SendAsync(request);
Console.WriteLine(await response.Content.ReadAsStringAsync());
```

**cURL**

```bash
curl -X POST 'https://api.mailblastr.com/campaigns/CAMPAIGN_ID/send' \
  -H 'Authorization: Bearer mb_xxxxxxxxx'
```

**CLI**

```bash
mailblastr campaigns send CAMPAIGN_ID
```

## Transactional vs. marketing: the compliance line

The endpoint split mirrors a legal one. A **transactional** email is triggered by a user action or required for compliance — order confirmations, password resets, account notices — and recipients **cannot unsubscribe** from it; it’s an essential, expected message. That’s why `POST /emails` carries no unsubscribe footer.

A **marketing** email is anything that isn’t transactional: promotions, newsletters, product updates. These are regulated by laws such as **CAN-SPAM** (US) and **CASL** (Canada), and recipients **must** be able to unsubscribe. That’s why campaigns append a one-click unsubscribe automatically.

| Message | Recipient | Send as |
| --- | --- | --- |
| Order / signup confirmation | Single | Transactional |
| Password reset | Single | Transactional |
| Abandoned-cart reminder | Single | Campaign / marketing (unsubscribable) |
| Newsletter | Many | Campaign |
| Promotional offer | Many | Campaign |

> **Warning:** The deciding factor is the *nature* of the message, not the recipient count. A one-to-one **promotional** message (e.g. an abandoned-cart nudge) is still marketing and must be unsubscribable — don’t send it as a no-unsubscribe transactional email.

> **Note:** Rule of thumb: if you would send the same content to one person because of something they did, it is **transactional**. If you would send it to a list because you decided to, it is a **campaign**.

> **Warning:** Do not loop `POST /emails` over a contact list to send marketing — that bypasses audience unsubscribe handling and risks complaints. Use a campaign for one-to-many mail.

See the full [emails API](https://www.mailblastr.com/docs/api/emails-send), [campaigns](https://www.mailblastr.com/docs/campaigns/managing), and [audiences](https://www.mailblastr.com/docs/audiences/overview) references.
