Create Campaign
Create a new campaign. Campaigns always start as a draft with the email channel.
Endpointโ
POST /v1/campaigns
Authenticationโ
Requires an API key with the campaigns:write scope.
Request Bodyโ
| Field | Type | Required | Description |
|---|---|---|---|
name | string | Yes | Campaign name (max 255 chars) |
description | string | No | Internal description (max 500 chars) |
email_subject | string | No | Email subject line (max 255 chars, supports merge tags) |
email_body | string | No | Email body HTML (supports merge tags) |
audience_type | string | No | all_customers, selected_customers, or filter. Defaults to filter when audience_filter is sent, otherwise all_customers |
audience_filter | object | No | Filter criteria when audience_type is filter โ see Audience Filter |
template_key | string | No | plain, announcement, promo_blast, season_opener, or win_back |
cta_text | string | No | Button label (max 255 chars) |
cta_url | string | No | Button link (valid URL, max 2048 chars) |
Example Request - Season Opener with a Preset Audienceโ
curl -X POST "https://app.equipdash.com/api/v1/campaigns" \
-H "Authorization: Bearer eqd_live_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"name": "Summer Kayak Promo",
"description": "Promote summer kayak rentals",
"email_subject": "Summer is here, {customer_name}!",
"email_body": "<p>Hi {customer_name}, book your kayak now.</p>",
"audience_type": "filter",
"audience_filter": {
"v": 2,
"preset": "recent"
},
"template_key": "promo_blast",
"cta_text": "Book now",
"cta_url": "https://example.com/book"
}'
Example Request - Win-Back with a Custom Filterโ
curl -X POST "https://app.equipdash.com/api/v1/campaigns" \
-H "Authorization: Bearer eqd_live_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"name": "Win-Back - Last Season",
"email_subject": "We miss you, {customer_name}!",
"email_body": "<p>It has been a while โ here is 25% off your next trip: {promo_code}</p>",
"audience_type": "filter",
"audience_filter": {
"v": 2,
"experience_ids": [10, 15],
"not_booked_since": "2025-06-01"
},
"template_key": "win_back",
"cta_text": "Book again",
"cta_url": "https://example.com/book-again"
}'
Response (201 Created)โ
{
"data": {
"id": 42,
"name": "Summer Kayak Promo",
"description": "Promote summer kayak rentals",
"kind": "one_off",
"channel": "email",
"status": "draft",
"audience_type": "filter",
"audience_filter": {
"v": 2,
"preset": "recent"
},
"email_subject": "Summer is here, {customer_name}!",
"email_body": "<p>Hi {customer_name}, book your kayak now.</p>",
"template_key": "promo_blast",
"cta_text": "Book now",
"cta_url": "https://example.com/book",
"scheduled_at": null,
"sent_at": null,
"total_recipients": 0,
"total_sent": 0,
"promo_codes": [],
"created_at": "2026-07-20T09:00:00+00:00",
"updated_at": "2026-07-20T09:00:00+00:00"
},
"meta": {
"request_id": "req_abc123",
"timestamp": "2026-07-20T09:00:00+00:00"
}
}
Errorsโ
| Status | Code | Description |
|---|---|---|
| 401 | authentication_required | No API key provided |
| 401 | invalid_api_key | Invalid API key |
| 403 | insufficient_scope | API key lacks campaigns:write scope |
| 422 | validation_error | Invalid request data (e.g., missing name) |
Notesโ
email_subjectandemail_bodycan be set now or later โ but both are required before sending or scheduling- Attach promo codes and surveys in the EquipDash UI to populate the
{promo_code},{promo_discount}, and{survey_link}merge tags - Recipients are resolved when the campaign is sent or scheduled, not at creation time