# 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.

SendPing 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 { SendPing } from 'sendping';

const mb = new SendPing('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 "sendping"

SendPing.api_key = "mb_xxxxxxxxx"

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

**PHP**

```php
<?php
require 'vendor/autoload.php';

use SendPing\SendPing;

$sendping = SendPing::client('mb_xxxxxxxxx');

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

**Python**

```python
import sendping

sendping.api_key = "mb_xxxxxxxxx"

sendping.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/sendping-sdks/sendping-go"

client := sendping.NewClient("mb_xxxxxxxxx")

sent, err := client.Emails.Send(&sendping.SendEmailRequest{
    From:    "Acme <receipts@yourdomain.com>",
    To:      []string{"customer@example.com"},
    Subject: "Your receipt",
    Html:    "<p>Thanks for your order!</p>",
})
```

**Rust**

```rust
use sendping::{SendEmailOptions, SendPing};

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

let params = SendEmailOptions::new(
    "Acme <receipts@yourdomain.com>",
    ["customer@example.com"],
    "Your receipt",
)
.with_html("<p>Thanks for your order!</p>");
let _sent = mb.emails.send(params).await?;
```

**Java**

```java
import co.sendping.SendPing;
import co.sendping.SendPingResponse;
import co.sendping.requests.SendEmailRequest;

SendPing sendping = new SendPing("mb_xxxxxxxxx");

SendEmailRequest request = SendEmailRequest.builder()
        .from("Acme <receipts@yourdomain.com>")
        .to("customer@example.com")
        .subject("Your receipt")
        .html("<p>Thanks for your order!</p>")
        .build();

SendPingResponse response = sendping.emails().send(request);
```

**.NET**

```csharp
using SendPing;

ISendPing sendping = SendPingClient.Create("mb_xxxxxxxxx");

var resp = await sendping.EmailSendAsync(new EmailMessage
{
    From = "Acme <receipts@yourdomain.com>",
    To = "customer@example.com",
    Subject = "Your receipt",
    HtmlBody = "<p>Thanks for your order!</p>",
});
```

**cURL**

```bash
curl -X POST 'https://www.sendping.co/api/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
sendping 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 SendPing to fan the message out across a sending domain’s [contacts](https://www.sendping.co/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 — SendPing delivers to every subscribed contact and appends a one-click unsubscribe footer.

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

**Node.js**

```js
import { SendPing } from 'sendping';

const mb = new SendPing('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 "sendping"

SendPing.api_key = "mb_xxxxxxxxx"

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

**PHP**

```php
<?php
require 'vendor/autoload.php';

use SendPing\SendPing;

$sendping = SendPing::client('mb_xxxxxxxxx');

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

**Python**

```python
import sendping

sendping.api_key = "mb_xxxxxxxxx"

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

**Go**

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

client := sendping.NewClient("mb_xxxxxxxxx")

campaign, err := client.Campaigns.Create(&sendping.CreateCampaignRequest{
    Domain:  "yourdomain.com",
    From:    "Acme <news@yourdomain.com>",
    Subject: "March newsletter",
    Html:    "<p>What we shipped this month…</p>",
})
```

**Rust**

```rust
use sendping::{CreateCampaignOptions, SendPing};

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

let params = CreateCampaignOptions::new(
    "yourdomain.com",
    "Acme <news@yourdomain.com>",
    "March newsletter",
)
.with_html("<p>What we shipped this month…</p>");
let _campaign = mb.campaigns.create(params).await?;
```

**Java**

```java
import co.sendping.SendPing;
import co.sendping.SendPingResponse;
import co.sendping.requests.CreateCampaignRequest;

SendPing sendping = new SendPing("mb_xxxxxxxxx");

CreateCampaignRequest request = CreateCampaignRequest.builder()
        .domain("yourdomain.com")
        .from("Acme <news@yourdomain.com>")
        .subject("March newsletter")
        .html("<p>What we shipped this month…</p>")
        .build();

SendPingResponse response = sendping.campaigns().create(request);
```

**.NET**

```csharp
using SendPing;

ISendPing sendping = SendPingClient.Create("mb_xxxxxxxxx");

var resp = await sendping.CampaignCreateAsync(new CampaignCreateOptions
{
    Domain = "yourdomain.com",
    From = "Acme <news@yourdomain.com>",
    Subject = "March newsletter",
    HtmlBody = "<p>What we shipped this month…</p>",
});
```

**cURL**

```bash
curl -X POST 'https://www.sendping.co/api/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
sendping 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 { SendPing } from 'sendping';

const mb = new SendPing('mb_xxxxxxxxx');

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

**Ruby**

```ruby
require "sendping"

SendPing.api_key = "mb_xxxxxxxxx"

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

**PHP**

```php
<?php
require 'vendor/autoload.php';

use SendPing\SendPing;

$sendping = SendPing::client('mb_xxxxxxxxx');

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

**Python**

```python
import sendping

sendping.api_key = "mb_xxxxxxxxx"

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

**Go**

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

client := sendping.NewClient("mb_xxxxxxxxx")

sent, err := client.Campaigns.Send("CAMPAIGN_ID", nil)
```

**Rust**

```rust
use sendping::SendPing;

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

let _sent = mb.campaigns.send("CAMPAIGN_ID", None).await?;
```

**Java**

```java
import co.sendping.SendPing;
import co.sendping.SendPingResponse;

SendPing sendping = new SendPing("mb_xxxxxxxxx");

SendPingResponse response = sendping.campaigns().send("CAMPAIGN_ID");
```

**.NET**

```csharp
using SendPing;

ISendPing sendping = SendPingClient.Create("mb_xxxxxxxxx");

var resp = await sendping.CampaignSendAsync("CAMPAIGN_ID");
```

**cURL**

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

**CLI**

```bash
sendping 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 unless you set a `topic_id`.

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 — and why a `POST /emails` send that sets a [`topic_id`](https://www.sendping.co/docs/topics/overview) does too: `topic_id` declares the message subscription mail, and the resulting opt-out is scoped to that topic.

| 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.sendping.co/docs/api/emails-send), [campaigns](https://www.sendping.co/docs/campaigns/managing), and [audiences](https://www.sendping.co/docs/audiences/overview) references.
