curl --request POST \
--url https://api.reply.io/v3/sequences/{id}/steps/{step_id}/variants/bulk \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
[
{
"subject": "Follow up on our conversation",
"message": "Hi {{FirstName}}, I wanted to follow up...",
"isEnabled": true,
"attachmentIds": [
101,
102
]
}
]
'import requests
url = "https://api.reply.io/v3/sequences/{id}/steps/{step_id}/variants/bulk"
payload = [
{
"subject": "Follow up on our conversation",
"message": "Hi {{FirstName}}, I wanted to follow up...",
"isEnabled": True,
"attachmentIds": [101, 102]
}
]
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify([
{
subject: 'Follow up on our conversation',
message: 'Hi {{FirstName}}, I wanted to follow up...',
isEnabled: true,
attachmentIds: [101, 102]
}
])
};
fetch('https://api.reply.io/v3/sequences/{id}/steps/{step_id}/variants/bulk', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.reply.io/v3/sequences/{id}/steps/{step_id}/variants/bulk",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
[
'subject' => 'Follow up on our conversation',
'message' => 'Hi {{FirstName}}, I wanted to follow up...',
'isEnabled' => true,
'attachmentIds' => [
101,
102
]
]
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.reply.io/v3/sequences/{id}/steps/{step_id}/variants/bulk"
payload := strings.NewReader("[\n {\n \"subject\": \"Follow up on our conversation\",\n \"message\": \"Hi {{FirstName}}, I wanted to follow up...\",\n \"isEnabled\": true,\n \"attachmentIds\": [\n 101,\n 102\n ]\n }\n]")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.reply.io/v3/sequences/{id}/steps/{step_id}/variants/bulk")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("[\n {\n \"subject\": \"Follow up on our conversation\",\n \"message\": \"Hi {{FirstName}}, I wanted to follow up...\",\n \"isEnabled\": true,\n \"attachmentIds\": [\n 101,\n 102\n ]\n }\n]")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.reply.io/v3/sequences/{id}/steps/{step_id}/variants/bulk")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "[\n {\n \"subject\": \"Follow up on our conversation\",\n \"message\": \"Hi {{FirstName}}, I wanted to follow up...\",\n \"isEnabled\": true,\n \"attachmentIds\": [\n 101,\n 102\n ]\n }\n]"
response = http.request(request)
puts response.read_body[
{
"id": 123,
"error": 123,
"errorDetails": "<string>"
}
]Bulk create step variants
sequences:write scope (or a broader one that includes it).
Add several text variants to one step in a single call. They’re appended in the order you send them.
curl --request POST \
--url https://api.reply.io/v3/sequences/{id}/steps/{step_id}/variants/bulk \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
[
{
"subject": "Follow up on our conversation",
"message": "Hi {{FirstName}}, I wanted to follow up...",
"isEnabled": true,
"attachmentIds": [
101,
102
]
}
]
'import requests
url = "https://api.reply.io/v3/sequences/{id}/steps/{step_id}/variants/bulk"
payload = [
{
"subject": "Follow up on our conversation",
"message": "Hi {{FirstName}}, I wanted to follow up...",
"isEnabled": True,
"attachmentIds": [101, 102]
}
]
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify([
{
subject: 'Follow up on our conversation',
message: 'Hi {{FirstName}}, I wanted to follow up...',
isEnabled: true,
attachmentIds: [101, 102]
}
])
};
fetch('https://api.reply.io/v3/sequences/{id}/steps/{step_id}/variants/bulk', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.reply.io/v3/sequences/{id}/steps/{step_id}/variants/bulk",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
[
'subject' => 'Follow up on our conversation',
'message' => 'Hi {{FirstName}}, I wanted to follow up...',
'isEnabled' => true,
'attachmentIds' => [
101,
102
]
]
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.reply.io/v3/sequences/{id}/steps/{step_id}/variants/bulk"
payload := strings.NewReader("[\n {\n \"subject\": \"Follow up on our conversation\",\n \"message\": \"Hi {{FirstName}}, I wanted to follow up...\",\n \"isEnabled\": true,\n \"attachmentIds\": [\n 101,\n 102\n ]\n }\n]")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.reply.io/v3/sequences/{id}/steps/{step_id}/variants/bulk")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("[\n {\n \"subject\": \"Follow up on our conversation\",\n \"message\": \"Hi {{FirstName}}, I wanted to follow up...\",\n \"isEnabled\": true,\n \"attachmentIds\": [\n 101,\n 102\n ]\n }\n]")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.reply.io/v3/sequences/{id}/steps/{step_id}/variants/bulk")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "[\n {\n \"subject\": \"Follow up on our conversation\",\n \"message\": \"Hi {{FirstName}}, I wanted to follow up...\",\n \"isEnabled\": true,\n \"attachmentIds\": [\n 101,\n 102\n ]\n }\n]"
response = http.request(request)
puts response.read_body[
{
"id": 123,
"error": 123,
"errorDetails": "<string>"
}
]Authorizations
Authenticate every request with a Bearer token. Pass your Reply API key in the
Authorization header:
Authorization: Bearer <your-api-key>
Get your API key from the Reply dashboard: Settings → API Key.
Body
Message content of the variant. Variable names are case-sensitive, for example {{FirstName}}. See GET /v3/email-templates/variables for the full list. In email steps, unknown variables are rejected with 400.
Subject line of the variant. Variable names are case-sensitive, for example {{FirstName}}. See GET /v3/email-templates/variables for the full list. In email steps, unknown variables are rejected with 400.
Whether the variant is enabled
IDs of previously uploaded attachments to attach to this variant. Upload files via POST /v3/attachments first to obtain attachment IDs.
A variant may carry up to 3 attachments. Omit or pass null to leave attachments unchanged on update; pass an empty array to clear existing attachments.
3Response
Array of per-item results, one per input variant (same order as request).
Possible per-item error codes:
| Error code | Meaning |
|---|---|
invalidInput | Validation failed (e.g. more than 3 attachments, or a template variable that does not exist or is malformed in an email variant) |
invalidStep | Step type does not support this variant |
forbidden | One or more attachment IDs are not accessible to the caller |
internalError | Unexpected server error |