Campaigns: Draft, Schedule & Send

Create and send email campaigns to audiences

A campaign is a one-time email broadcast to an audience. Create a campaign, optionally test it, then send it to hundreds or thousands of recipients.

Create a Campaign (Draft)

Create a campaign in draft status. name, subjectA and exactly one of templateId / html are required — omit subjectA and the request fails with 422.

cURL

curl -X POST https://api.tratto.email/v1/campaigns \
  -H "Authorization: Bearer tratto_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "name":"Q3 Summer Sale",
    "subjectA":"Our summer sale starts today",
    "templateId":"tmpl_abc123",
    "audienceId":"aud_abc123"
  }'

Response: the id, and nothing else.

{
  "data": {
    "id": "camp_xyz789"
  }
}

Fetch the campaign with GET /v1/campaigns/{id} to read its status, content and stats.

Body fields:

FieldRequiredNotes
nameyesmax 200 characters
subjectAyesthe subject line, max 998 characters
subjectBnosecond subject for an A/B test
templateIdone ofcopies the template's content into the campaign. Exactly one of templateId or html — both, or neither, is a 422
htmlone ofinline HTML, no template needed
audienceIdnoomitted = every contact in the workspace
fromNamenofalls back to the workspace default sender
fromEmailnofalls back to the workspace default sender

If neither the body nor the workspace supplies a sender, the request fails with 422 and a message telling you to set a default sender.

The campaign owns a copy of the template's content from creation onward: editing the template later does not change a campaign already created from it, and editing the campaign does not touch the template.

Test Send

Send a test email before launching to make sure everything looks correct.

cURL

curl -X POST https://api.tratto.email/v1/campaigns/camp_xyz789/test-send \
  -H "Authorization: Bearer tratto_live_..." \
  -H "Content-Type: application/json" \
  -d '{"to":"[email protected]"}'

A test email is sent to your address immediately.

Send Immediately

Send the campaign to all contacts in the audience right now.

cURL

curl -X POST https://api.tratto.email/v1/campaigns/camp_xyz789/send \
  -H "Authorization: Bearer tratto_live_..."

Response:

{
  "data": {
    "status": "sending"
  }
}

The campaign status becomes sending, then completed when the send is done.

Sending is refused with 409 unless the campaign is draft or paused, and it is checked against your remaining monthly quota before it starts.

Schedule for Later

Schedule the campaign with scheduledAt (ISO 8601). The field is not called sendAt.

cURL

curl -X POST https://api.tratto.email/v1/campaigns/camp_xyz789/send \
  -H "Authorization: Bearer tratto_live_..." \
  -H "Content-Type: application/json" \
  -d '{"scheduledAt":"2026-10-01T09:00:00Z"}'

The campaign status becomes scheduled and it sends automatically at that time. A scheduledAt in the past is treated as "send now".

To cancel a schedule and return the campaign to draft:

curl -X POST https://api.tratto.email/v1/campaigns/camp_xyz789/unschedule \
  -H "Authorization: Bearer tratto_live_..."

That returns 409 if the dispatcher has already started the send — pause it instead.

Campaign Status Lifecycle

The five statuses are draft, scheduled, sending, paused and completed. There is no sent.

draft
  ├─→ (test-send)
  ├─→ send             → sending → completed
  └─→ send + scheduledAt → scheduled → sending → completed
                              └─→ unschedule → draft

sending / scheduled  ─→ pause → paused ─→ send (resume) → sending

Pause a Campaign

Stop a scheduled or in-progress campaign. Only a sending or scheduled campaign can be paused; anything else is a 409.

cURL

curl -X POST https://api.tratto.email/v1/campaigns/camp_xyz789/pause \
  -H "Authorization: Bearer tratto_live_..."

Resuming is a fresh POST /:id/send on the paused campaign — there is no separate resume endpoint. It picks up only the recipients it never reached, so nobody is mailed twice.

A campaign can also be paused for you: when a send exhausts the monthly quota mid-flight, the dispatcher pauses it and sets pausedReason to "quota_exceeded". On a campaign you paused by hand, pausedReason is null — that field is the only way to tell the two apart.

A paused campaign can be resumed; only a draft one can be deleted.

Get Campaign Stats

View delivery statistics after sending.

cURL

curl https://api.tratto.email/v1/campaigns/camp_xyz789/stats \
  -H "Authorization: Bearer tratto_live_..."

Response: counts and rates are two separate objects.

{
  "data": {
    "campaignId": "camp_xyz789",
    "status": "completed",
    "stats": {
      "total": 5000,
      "sent": 5000,
      "delivered": 4950,
      "opened": 2475,
      "clicked": 742,
      "bounced": 40,
      "skipped": 12,
      "untracked": 130
    },
    "rates": {
      "deliveryRate": 99,
      "openRate": 50.82,
      "clickRate": 15.24,
      "bounceRate": 0.8,
      "untracked": 130
    }
  }
}

Rates are percentages, rounded to two decimals: 99 means 99%, not 99 in a hundred thousand and certainly not 0.99. An alert written as deliveryRate < 0.95 will never fire — you want < 95. Same scale as Analytics.

Field notes:

  • total is the number of recipients; sent counts the ones actually dispatched
  • skipped — sends cancelled because the contact unsubscribed after dispatch. They never happened, so they feed no rate
  • untracked — recipients who opted out of open/click tracking. They are excluded from the openRate/clickRate denominator, never estimated. The untracked key inside rates is that same count, not a rate
  • stats.variants and rates.variants appear only on a campaign that ran an A/B subject test
  • There is no complained field, and no totalSent

List Campaigns

Get all campaigns.

cURL

curl "https://api.tratto.email/v1/campaigns?status=completed&limit=50" \
  -H "Authorization: Bearer tratto_live_..."

Query parameters:

  • status: Filter by status — draft, scheduled, sending, paused, completed. sent is not a valid value and returns 422
  • q: Name prefix search, case-insensitive and accent-sensitive (sal matches Saldi estate, estate does not). While it is set, results are ordered by name instead of newest-first
  • limit: Results per page (default 50, max 100)
  • after: Cursor for pagination

Get Campaign Details

Retrieve a specific campaign.

cURL

curl https://api.tratto.email/v1/campaigns/camp_xyz789 \
  -H "Authorization: Bearer tratto_live_..."

Campaign Best Practices

1. Always Test First

Send a test email to yourself before launching to any audience.

2. Use Descriptive Names

Q3 Summer Sale - Newsletter Segment
Campaign 1, Test

3. Review Audience Size

Count the recipients before sending — GET /v1/contacts?status=subscribed&audienceId=… returns the same pagination.total the send gate counts. An audience with no subscribed contacts means no one receives it.

4. Schedule During Optimal Times

Consider your audience's timezone when scheduling. Early morning or lunch time usually see higher open rates.

5. Monitor Delivery Stats

After sending, review bounce, complaint, and open rates to understand engagement.

6. Create New Campaigns, Don't Re-send

A campaign reaches each recipient once. Re-sending a completed campaign is a 409; resuming a paused one only covers the recipients it never reached. To mail the same audience again, create a new campaign.


Next Steps


Edit this page on GitHub

Last updated on