Create or edit a campaign
Overview
Create a campaign, or update one in progress, one step at a time: details (step 1), channels (step 2), schedule (step 3) and launch (step 4).
Use this to build a campaign the way the dashboard wizard does. Pass the step you're on; the required fields change by step.
You'll need the store_id and the fields for the step. Later steps need the campaign_id returned by the first.
Returns the campaign after each step. It stays a draft until step 4, which schedules it (status becomes scheduled). current_step records the furthest step reached; going back to an earlier step doesn't lower it.
Prerequisites
- A bearer token. See Authentication.
- The location's id (
store_id). On steps 2–4, thecampaign_idreturned by step 1. - Before step 3, give the campaign a type: upload recipients (
bulk_upload), define a file format (automation), or sendtype: "kiosk"on step 3.
Base URL
| Environment | URL |
|---|---|
| Production | https://production-api.shoutaboutus.com |
| Development | https://development-api.shoutaboutus.com |
Endpoint
POST /api/v1/campaign/create
Authentication
Account token
store_idmust be a location inside your own account; otherwise422.campaign_idmust belong to thestore_idyou send; otherwise404Campaign not found.- A new campaign is created for the account of the user behind the token.
Rate limit
This endpoint has no rate limit.
Request parameters
None.
Request body
| Field | Type | Required | Description |
|---|---|---|---|
store_id | integer | Yes | The location the campaign belongs to. Must be inside your own account. |
step | integer | Yes | The wizard step: 1 details, 2 channels, 3 schedule, 4 launch. The other required fields depend on it. |
campaign_id | integer or null | Steps 2–4 | On step 1, leave it out or send null to create a campaign, or send an id to edit one. Required on steps 2–4. Must be an existing campaign (otherwise 422) that belongs to store_id (otherwise 404). |
name | string | Step 1 | The campaign's name, up to 255 characters. |
channel_sms | boolean | Step 2 | true adds the SMS channel (or restores a removed one), false removes it. Send this, channel_email, or both. Only applied when the location's plan includes SMS review requests. |
channel_email | boolean | Step 2 | true adds the email channel (or restores a removed one), false removes it. Send this, channel_sms, or both. Only applied when the location's plan includes email review requests. |
per_day_request | integer | Step 3 | Most requests sent a day, 1–50. Returned as request_per_day. |
request_frequency | integer | Step 3 | How often requests go out: 0 every day, 1 every other day, 2 once a week, 3 once every two weeks, 4 once a month. |
delay_request | integer | Step 3 | Days to wait before the first request, 0–60. Returned as request_delay. |
request_after_time_pst | string | No | Step 3. Time of day to send, in Pacific Time (H:i:s). Default 14:00:00. Returned as request_after_time_pst, and in UTC as request_after_time. |
send_immediately | boolean | No | Step 3. true makes step 4 schedule the first send for now instead of after the delay. Default false. |
type | string or null | Step 3 | Send it on step 3: bulk_upload, automation, kiosk or null, or leave it out. Only kiosk is applied (see below). |
Step 2 creates the channels without templates. Before you send or launch, attach one to each channel with
POST /api/v1/campaign/template/email/updateor/template/sms/update, using the channel's id fromdata.data.channels[].idin this response.
Only
kioskis applied at step 3. Step 3 writestypeonly when you sendtype: "kiosk", and then it sets or overwrites it, even on a campaign that wasbulk_uploadorautomation. Any other value is ignored silently: the call returns200and the campaign keeps the type it already had. Set the other types before step 3: uploading recipients setsbulk_upload(see Upload recipients), and defining the file format withPOST /api/v1/campaign/automate/file-headerssetsautomation. Step 3 returns400while the campaign has no type, or when it isautomationwithout a file format.
Request payload
Step 1, a new campaign:
{
"store_id": 3007,
"step": 1,
"campaign_id": null,
"name": "Summer Review Campaign"
}Step 2, channels:
{
"store_id": 3007,
"step": 2,
"campaign_id": 4824,
"channel_email": true,
"channel_sms": true
}Step 3, schedule:
{
"store_id": 3007,
"step": 3,
"campaign_id": 4824,
"per_day_request": 20,
"request_frequency": 0,
"delay_request": 0,
"request_after_time_pst": "14:00:00",
"send_immediately": false,
"type": "kiosk"
}Step 4, launch:
{
"store_id": 3007,
"step": 4,
"campaign_id": 4824
}Response body
The payload is inside data.
| Field | Type | Description |
|---|---|---|
status | string | success. |
message | string | Campaign has been saved successfully. |
campaign_id | integer | The campaign's id. Send it as campaign_id on the next step. |
data | object | The campaign after the step. |
data.id | integer | The campaign's id. Use it as campaign_id in later calls. |
data.name | string | The campaign's name. |
data.description | string or null | The campaign's description, or null. |
data.status | string | draft, scheduled, in_progress, paused, completed or cancelled. |
data.current_step | integer | The furthest wizard step reached, 1 to 4. Going back to an earlier step doesn't lower it. |
data.store_id | integer | The location the campaign belongs to. |
data.scheduled_at | string or null | When the campaign was scheduled (step 4), or null. |
data.started_at | string or null | When sending started, or null. |
data.completed_at | string or null | When the campaign finished, or null. |
data.total_audience | integer or null | Recipient count stored on the campaign. null until recipients are added. |
data.request_per_day | integer | Most requests sent a day. Set on step 3 as per_day_request. |
data.request_frequency | integer | How often requests go out: 0 every day, 1 every other day, 2 once a week, 3 once every two weeks, 4 once a month. |
data.request_delay | integer | Days to wait before the first request. Set on step 3 as delay_request. |
data.request_after_time | string | Time of day requests are sent, in UTC (H:i:s). |
data.next_execution_time | string or null | When the next batch is due, or null until step 4. |
data.request_file_status | string | Status of the uploaded recipient file: pending, processing, completed or failed. |
data.request_file_name | string or null | Name of the uploaded recipient file, or null. |
data.send_immediately | boolean | true when step 4 schedules the first send for now. |
data.request_file_path | string or null | Storage path of the uploaded recipient file, or null. |
data.type | string or null | bulk_upload, automation or kiosk; null until set. |
data.request_after_time_pst | string or null | The same send time in Pacific Time (H:i:s), as sent in request_after_time_pst. |
data.request_file_url | string or null | URL of the uploaded recipient file, or null. |
data.unique_code | string | The campaign's unique code. Use it as unique_code in Send a review request from a kiosk. |
data.channels | array of objects | The campaign's email and SMS channels. Empty until step 2. |
data.channels[].id | integer | The channel's id. Use it as channel_id when you attach a template. |
data.channels[].rs_campaign_id | integer | The campaign the channel belongs to. |
data.channels[].channel_type | string | email or sms. |
data.channels[].is_enabled | boolean | Whether the channel is on. |
data.channels[].template_id | integer or null | The attached email or SMS template, or null until you attach one. |
data.channels[].follow_up_template_id | integer or null | The follow-up email template, or null. |
data.channels[].follow_up_interval | integer or null | Days between the first email and its follow-up, or null. |
data.channels[].follow_up_interval_count | integer or null | Most follow-ups sent, 1 to 3, or null. |
data.channels[].unique_code | string | The channel's unique code. Use it with Remove a campaign's follow-up. |
data.channels[].template | object or null | The attached email or SMS template, or null. |
data.channels[].follow_up_email_template | object or null | The follow-up email template, or null. |
data.email_requests | array of objects | Email review requests created for the campaign. |
data.email_requests[].id | integer | The email request's id. |
data.email_requests[].email | string | The recipient's email address. |
data.email_requests[].status | string | pending, sent or failed. |
data.email_requests[].sent_at | string or null | When the email was sent. |
data.email_requests[].delivered_at | string or null | When delivery was confirmed. |
data.email_requests[].opened_at | string or null | When the email was first opened. |
data.email_requests[].clicked_at | string or null | When a link was first clicked. |
data.email_requests[].is_test | boolean | true for a test send. |
data.email_requests[].unique_code | string | The request's unique code, used in its tracking links. |
data.sms_requests | array of objects | Text review requests created for the campaign. |
data.file_header | object or null | The file format of an automation campaign (see Get a campaign's file format), or null. |
Response payload
{
"data": {
"status": "success",
"message": "Campaign has been saved successfully",
"campaign_id": 4824,
"data": {
"id": 4824,
"name": "Summer Review Campaign",
"description": null,
"status": "draft",
"current_step": 1,
"store_id": 3007,
"scheduled_at": null,
"started_at": null,
"completed_at": null,
"total_audience": null,
"request_per_day": 50,
"request_frequency": 0,
"request_delay": 0,
"request_after_time": "14:00",
"next_execution_time": null,
"request_file_status": "pending",
"request_file_name": null,
"send_immediately": false,
"request_file_path": null,
"type": null,
"request_after_time_pst": "07:00:00",
"request_file_url": null,
"unique_code": "3r2mto",
"channels": [],
"email_requests": [],
"sms_requests": [],
"file_header": null
}
}
}Examples
Success payload
Step 1, a new campaign: see Response payload. Later steps return the same shape, with channels filled after step 2 and status scheduled after step 4.
Error payload
400 Campaign already launched
Step 2 on a campaign that is no longer a draft.
{
"message": "Bad Request",
"status": "error",
"errors": "Campaign is finalized and cannot be updated."
}400 Review requests not on the plan
Step 2 when the location's plan has no enabled review-request feature.
{
"message": "Bad Request",
"status": "error",
"errors": "Review solicitation is not enabled for this store. Please enable it in your plan or contact support for assistance."
}400 No campaign type yet
Steps 3 and 4 while the campaign has no type, including step 3 with type left out.
{
"message": "Bad Request",
"status": "error",
"errors": "Please upload the audience file first or setup automation first."
}400 No file format
Steps 3 and 4 on an automation campaign without a file format.
{
"message": "Bad Request",
"status": "error",
"errors": "Please setup automation first."
}400 Recipient file not ready
Step 4 on a bulk_upload campaign. Other messages: Please upload the audience file first, File processing failed. Please upload the audience file again., No audience data found. Please upload the audience file first.
{
"message": "Bad Request",
"status": "error",
"errors": "Please wait for the file to be processed"
}404 Campaign not found
campaign_id belongs to a different location than store_id.
{
"message": "Not Found",
"status": "error",
"errors": "Campaign not found"
}422 Name missing
{
"message": "The name field is required.",
"errors": {
"name": [
"The name field is required."
]
}
}422 Location outside your account
{
"message": "The selected store id does not belong to your company hierarchy.",
"errors": {
"store_id": [
"The selected store id does not belong to your company hierarchy."
]
}
}422 Unknown campaign
{
"message": "The selected campaign id is invalid.",
"errors": {
"campaign_id": [
"The selected campaign id is invalid."
]
}
}422 Step 3 values out of range
{
"message": "The per day request field must not be greater than 50. (and 2 more errors)",
"errors": {
"per_day_request": [
"The per day request field must not be greater than 50."
],
"request_frequency": [
"The selected request frequency is invalid."
],
"type": [
"The selected type is invalid."
]
}
}| Status | Meaning |
|---|---|
200 | The step was saved; returns the campaign. |
400 | The step can't be applied yet: the campaign is already launched, review requests aren't on the plan, or the campaign has no type, file format or processed recipient file. |
401 | The token is missing, expired or invalid. |
404 | campaign_id belongs to a different location than store_id. |
422 | A field is invalid for the step, store_id is outside your account, or campaign_id doesn't exist. |
500 | Unexpected server error. |
Example request
curl --request POST \
--url "https://production-api.shoutaboutus.com/api/v1/campaign/create" \
--header 'Authorization: Bearer <bearer-token>' \
--header 'Accept: application/json' \
--header 'Content-Type: application/json' \
--data '{"store_id": 3007, "step": 1, "campaign_id": null, "name": "Summer Review Campaign"}'Updated 14 days ago
