curl --request POST \
--url https://api.reply.io/v3/ai-sdr/playbooks/{id}/duplicate \
--header 'Authorization: Bearer <token>'import requests
url = "https://api.reply.io/v3/ai-sdr/playbooks/{id}/duplicate"
headers = {"Authorization": "Bearer <token>"}
response = requests.post(url, headers=headers)
print(response.text)const options = {method: 'POST', headers: {Authorization: 'Bearer <token>'}};
fetch('https://api.reply.io/v3/ai-sdr/playbooks/{id}/duplicate', 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/ai-sdr/playbooks/{id}/duplicate",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://api.reply.io/v3/ai-sdr/playbooks/{id}/duplicate"
req, _ := http.NewRequest("POST", url, nil)
req.Header.Add("Authorization", "Bearer <token>")
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/ai-sdr/playbooks/{id}/duplicate")
.header("Authorization", "Bearer <token>")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.reply.io/v3/ai-sdr/playbooks/{id}/duplicate")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
response = http.request(request)
puts response.read_body{
"id": "t-7",
"name": "Outbound playbook v2",
"description": "Tone and pacing for cold outreach",
"body": "## Voice\nFriendly, concise, never desperate.\n\n## Pacing\nTwo touches per week max.",
"type": "team",
"lastUpdatedAt": "2026-05-10T14:32:11Z",
"authorUserId": 4821,
"styleFiles": [
{
"id": 12345,
"fileName": "tone-guide.pdf"
}
]
}{
"title": "Bad Request",
"status": 400,
"detail": "Playbook id 'foo-7' is not a valid composite identifier.",
"code": "playbook.invalidIdFormat"
}{
"title": "Unauthorized",
"status": 401,
"detail": "Authentication credentials are missing or invalid."
}{
"title": "Forbidden",
"status": 403,
"detail": "Playbook duplication is not enabled for your team",
"code": "playbook.forbidden"
}{
"title": "Not Found",
"status": 404,
"detail": "Playbook 't-99' not found",
"code": "playbook.notFound"
}{
"title": "Bad Gateway",
"status": 502,
"detail": "Upstream service returned an unexpected error",
"code": "playbook.upstreamFailure"
}Duplicate a playbook
ai-sdr:write scope (or a broader one that includes it).
Copy a playbook so you can adapt it to a new segment. Any playbook you can read works as the source, including a global one.
curl --request POST \
--url https://api.reply.io/v3/ai-sdr/playbooks/{id}/duplicate \
--header 'Authorization: Bearer <token>'import requests
url = "https://api.reply.io/v3/ai-sdr/playbooks/{id}/duplicate"
headers = {"Authorization": "Bearer <token>"}
response = requests.post(url, headers=headers)
print(response.text)const options = {method: 'POST', headers: {Authorization: 'Bearer <token>'}};
fetch('https://api.reply.io/v3/ai-sdr/playbooks/{id}/duplicate', 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/ai-sdr/playbooks/{id}/duplicate",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://api.reply.io/v3/ai-sdr/playbooks/{id}/duplicate"
req, _ := http.NewRequest("POST", url, nil)
req.Header.Add("Authorization", "Bearer <token>")
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/ai-sdr/playbooks/{id}/duplicate")
.header("Authorization", "Bearer <token>")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.reply.io/v3/ai-sdr/playbooks/{id}/duplicate")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
response = http.request(request)
puts response.read_body{
"id": "t-7",
"name": "Outbound playbook v2",
"description": "Tone and pacing for cold outreach",
"body": "## Voice\nFriendly, concise, never desperate.\n\n## Pacing\nTwo touches per week max.",
"type": "team",
"lastUpdatedAt": "2026-05-10T14:32:11Z",
"authorUserId": 4821,
"styleFiles": [
{
"id": 12345,
"fileName": "tone-guide.pdf"
}
]
}{
"title": "Bad Request",
"status": 400,
"detail": "Playbook id 'foo-7' is not a valid composite identifier.",
"code": "playbook.invalidIdFormat"
}{
"title": "Unauthorized",
"status": 401,
"detail": "Authentication credentials are missing or invalid."
}{
"title": "Forbidden",
"status": 403,
"detail": "Playbook duplication is not enabled for your team",
"code": "playbook.forbidden"
}{
"title": "Not Found",
"status": 404,
"detail": "Playbook 't-99' not found",
"code": "playbook.notFound"
}{
"title": "Bad Gateway",
"status": 502,
"detail": "Upstream service returned an unexpected error",
"code": "playbook.upstreamFailure"
}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.
Path Parameters
Composite id of the playbook to duplicate (g-N for global, o-N for organization, t-N for team)
Response
Playbook duplicated successfully
Detailed representation of a playbook, returned by get/create/update responses.
Composite identifier formatted as {prefix}-{numericId} where the prefix encodes the playbook scope:
g for global, o for organization, t for team (e.g. g-1, o-42, t-7).
Display name of the playbook
Short description of the playbook's purpose
Full playbook body — typically Markdown or plain text instructions the AI SDR follows when this playbook is applied.
Visibility scope of the playbook. Set on create and immutable thereafter.
global, organization, team Timestamp of the last modification
User ID of the playbook author. null for global playbooks and when the original author is no longer a team member.
Style files attached to the playbook. Managed via the /style-files sub-resource — uploads are immediate, deletes are immediate; the field is read-only on the playbook payload.
Show child attributes
Show child attributes