Appearance
Campaigns
A campaign is one send: a design, an audience, and the printed result — the only object in the API that produces physical output.
One call
POST /api/v1/campaigns carries both the content and the recipients, so a whole integration can be one request. Nothing needs to exist beforehand but a template.
POST /api/v1/campaigns
├── content source (exactly one) templateId | pdf
└── recipient source (exactly one) listId | recipients | recipientsFileSending zero or two of either is a 400. The pairing is free: any content source works with any recipient source.
Content sources
templateId — recommended
A template you designed and previewed in the web app. The layout is settled; the campaign fills in each recipient's details.
json
{ "name": "Summer promo", "templateId": "550e8400-...", "listId": "550e8400-..." }The campaign inherits the template's back page, logo, return address and QR configuration. Per-campaign overrides:
| Field | Effect |
|---|---|
returnAddress | Replaces the template's sender text |
qrCode | Replaces the template's QR configuration entirely |
skipTemplateLogo | Do not inherit the logo |
skipTemplateReturnLogo | Do not inherit the return-address logo |
logo, returnAddressLogo (multipart) | Supply different images for this campaign |
pdf — your own print-ready file
Send a pdf part in a multipart request. Your pages become the background; only the postal address blocks are drawn on top.
bash
curl -X POST "$DRSE_BASE_URL/api/v1/campaigns" \
-H "x-api-key: $DRSE_API_KEY" \
-F "name=Autumn flyer" \
-F "pdf=@flyer.pdf" \
-F "hasBackPage=true" \
-F "listId=550e8400-e29b-41d4-a716-446655440001"PDF mode forbids templateId and qrCode — the PDF is the content. hasBackPage prints page 2 on the reverse. Sizing rules are yours to meet: PDF campaigns.
Raw editor content — not supported
The endpoint accepts a content field holding a rich-text document. Do not build on it.
Why raw content is excluded
Nothing has rendered it, so nobody has seen what would be printed — and mail cannot be proof-read after printing. A template was approved on screen; a PDF you can open and check.
content stays in the endpoint because the web app uses it. API keys will be rejected for it in a future release. Use templateId, or render a pdf.
Recipient sources
| Source | List created? | Limit |
|---|---|---|
listId | No — must already exist and be non-empty | — |
recipients | Yes, named after the campaign | 5000 rows per call |
recipientsFile | Yes, named after the campaign | 5 MB CSV / 10 MB Excel |
Use recipients when your system already holds the addresses, recipientsFile for a spreadsheet export, listId for an audience you send to repeatedly.
An implicit list is a real list — it appears in GET /api/v1/lists and is reusable.
All-or-nothing
Every recipient row is validated before any write — before storage uploads, before database inserts.
One bad row means 422 and nothing created: no list, no recipients, no campaign, no print job. Fix the data and retry the whole call.
json
{
"error": {
"code": "recipient_validation_failed",
"message": "Recipient validation failed",
"details": {
"issues": [
{ "row": 3, "field": "postTown", "issue": "missing_required", "message": "..." }
]
}
}
}So there is never a partial send to reconcile.
Lifecycle
pending ──► processing ──► completed
└─► failed| Status | Meaning |
|---|---|
pending | Accepted; work is being queued |
processing | Documents are being rendered and sent to print. Not cancellable |
completed | Every document rendered and dispatched |
failed | Processing could not finish. Some documents may already have printed |
progress runs 0–100. There are no webhooks — poll GET /api/v1/campaigns/:id. See Polling & failures.
failed does not mean nothing was printed
A campaign can fail partway through. Treat failed as "needs a human", not "safe to retry" — a fresh Idempotency-Key could duplicate mail already sent.
The templateId in the response is not yours
The templateId a campaign returns is an internal snapshot of the content, frozen at create time.
json
{ "templateId": "018f2c7a-0000-7000-8000-000000000000" }- It is not the id you passed in.
- Fetching it returns
Template not found— snapshots are invisible to the API. - It exists so that editing your template later cannot alter mail already sent.
Record which of your templates produced a campaign yourself.
Retries and duplicates
Sending is not idempotent by default — a timeout you retry can print twice. Always send an Idempotency-Key:
bash
-H "Idempotency-Key: $(uuidgen)"A retry with the same key returns the original campaign with 200 instead of creating a second. See Idempotency.
Reading campaigns
| Need | Endpoint |
|---|---|
| One campaign's status | GET /api/v1/campaigns/:id |
| Recent campaigns, paginated | GET /api/v1/campaigns |
| Totals per status | GET /api/v1/campaigns/status-counts |
Campaigns cannot be updated or deleted over the API — they are a permanent record of something that physically happened.