Duplicate a campaign
Overview
Copy a campaign as a new draft with the same channels and templates but no recipients or schedule.
Use this to reuse a campaign that worked.
You'll need the campaign_id.
Returns the new draft, named with "(Copy)". It starts again at step 1, with the daily limit, frequency and delay set to 0; set them again with step 3 of Create or edit a campaign.
Prerequisites
- A bearer token. See Authentication.
- The campaign's id (
campaignId).
Base URL
| Environment | URL |
|---|---|
| Production | https://production-api.shoutaboutus.com |
| Development | https://development-api.shoutaboutus.com |
Endpoint
POST /api/v1/campaign/{campaignId}/duplicate
Authentication
Account token
- The campaign must belong to your own account. If it doesn't, the call returns
403(You are not authorized to duplicate this campaign).
Rate limit
This endpoint has no rate limit.
Request parameters
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
campaignId | path | integer | Yes | The id of the campaign to copy. Digits only. |
Request body
None.
Request payload
None.
Response body
The payload is inside data.
| Field | Type | Description |
|---|---|---|
status | string | success. |
message | string | Campaign duplicated successfully. |
data | object | The new campaign, in the same shape as Get one campaign. |
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 duplicated successfully",
"data": {
"id": 4826,
"name": "Summer Review Campaign (Copy)",
"description": null,
"status": "draft",
"current_step": 1,
"store_id": 3007,
"scheduled_at": null,
"started_at": null,
"completed_at": null,
"total_audience": 0,
"request_per_day": 0,
"request_frequency": 0,
"request_delay": 0,
"request_after_time": "21:00:00",
"next_execution_time": null,
"request_file_status": "pending",
"request_file_name": null,
"send_immediately": false,
"request_file_path": null,
"type": "kiosk",
"request_after_time_pst": "14:00:00",
"request_file_url": null,
"unique_code": "a1b2c3",
"channels": [
{
"id": 5396,
"rs_campaign_id": 4826,
"channel_type": "email",
"is_enabled": true,
"template_id": 9083,
"follow_up_template_id": null,
"follow_up_interval": null,
"follow_up_interval_count": null,
"unique_code": "d4e5f6",
"template": {
"id": 9083,
"company_id": 5994,
"store_id": 3007,
"name": "Default Email Template",
"subject": "How did we do?",
"html_content": "<p>Hi [customerName], tell us how we did.</p>",
"json_content": [],
"template_editor_mode": "custom",
"template_type": "regular",
"manager_email": null,
"other_manager_email": [],
"created_at": "2026-09-23T10:43:44.000000Z",
"updated_at": "2026-10-01T08:02:34.000000Z",
"deleted_at": null
},
"follow_up_email_template": null
}
],
"email_requests": [],
"sms_requests": [],
"file_header": null
}
}
}Examples
Success payload
See Response payload.
Error payload
403 Campaign outside your account
{
"message": "Forbidden",
"status": "error",
"errors": "You are not authorized to duplicate this campaign"
}422 Unknown campaign
{
"message": "The selected id is invalid.",
"errors": {
"id": [
"The selected id is invalid."
]
}
}| Status | Meaning |
|---|---|
200 | The new draft campaign. |
401 | The token is missing, expired or invalid. |
403 | The campaign belongs to another account. |
422 | No campaign has that id. |
500 | Unexpected server error. |
Example request
curl --request POST \
--url "https://production-api.shoutaboutus.com/api/v1/campaign/4823/duplicate" \
--header 'Authorization: Bearer <bearer-token>' \
--header 'Accept: application/json'Updated 17 days ago
Did this page help you?
