curl --request PATCH \
--url https://api.reply.io/v3/ai-sdr/playbooks/{id} \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"name": "Outbound playbook v3",
"description": "Refined tone and pacing for cold outreach"
}
'import requests
url = "https://api.reply.io/v3/ai-sdr/playbooks/{id}"
payload = {
"name": "Outbound playbook v3",
"description": "Refined tone and pacing for cold outreach"
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.patch(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'PATCH',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
name: 'Outbound playbook v3',
description: 'Refined tone and pacing for cold outreach'
})
};
fetch('https://api.reply.io/v3/ai-sdr/playbooks/{id}', 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}",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "PATCH",
CURLOPT_POSTFIELDS => json_encode([
'name' => 'Outbound playbook v3',
'description' => 'Refined tone and pacing for cold outreach'
]),
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/ai-sdr/playbooks/{id}"
payload := strings.NewReader("{\n \"name\": \"Outbound playbook v3\",\n \"description\": \"Refined tone and pacing for cold outreach\"\n}")
req, _ := http.NewRequest("PATCH", 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.patch("https://api.reply.io/v3/ai-sdr/playbooks/{id}")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"name\": \"Outbound playbook v3\",\n \"description\": \"Refined tone and pacing for cold outreach\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.reply.io/v3/ai-sdr/playbooks/{id}")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Patch.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"name\": \"Outbound playbook v3\",\n \"description\": \"Refined tone and pacing for cold outreach\"\n}"
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,
"isAiGenerated": false,
"sourceOfferId": null,
"styleFiles": [
{
"id": 12345,
"fileName": "tone-guide.pdf"
}
]
}Update a playbook
ai-sdr:write scope (or a broader one that includes it).
Change part of a playbook without resending it all. Send at least one of name, description, and body; type cannot change.
curl --request PATCH \
--url https://api.reply.io/v3/ai-sdr/playbooks/{id} \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"name": "Outbound playbook v3",
"description": "Refined tone and pacing for cold outreach"
}
'import requests
url = "https://api.reply.io/v3/ai-sdr/playbooks/{id}"
payload = {
"name": "Outbound playbook v3",
"description": "Refined tone and pacing for cold outreach"
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.patch(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'PATCH',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
name: 'Outbound playbook v3',
description: 'Refined tone and pacing for cold outreach'
})
};
fetch('https://api.reply.io/v3/ai-sdr/playbooks/{id}', 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}",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "PATCH",
CURLOPT_POSTFIELDS => json_encode([
'name' => 'Outbound playbook v3',
'description' => 'Refined tone and pacing for cold outreach'
]),
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/ai-sdr/playbooks/{id}"
payload := strings.NewReader("{\n \"name\": \"Outbound playbook v3\",\n \"description\": \"Refined tone and pacing for cold outreach\"\n}")
req, _ := http.NewRequest("PATCH", 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.patch("https://api.reply.io/v3/ai-sdr/playbooks/{id}")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"name\": \"Outbound playbook v3\",\n \"description\": \"Refined tone and pacing for cold outreach\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.reply.io/v3/ai-sdr/playbooks/{id}")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Patch.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"name\": \"Outbound playbook v3\",\n \"description\": \"Refined tone and pacing for cold outreach\"\n}"
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,
"isAiGenerated": false,
"sourceOfferId": null,
"styleFiles": [
{
"id": 12345,
"fileName": "tone-guide.pdf"
}
]
}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 playbook id (g-N / o-N / t-N)
Body
Partial update for a playbook. Send only the fields you want to change; absent fields retain their current value. At least one of name, description, or body must be present.
type is immutable after creation and cannot be patched.
Response
Playbook updated 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.
Whether the playbook was generated by the AI SDR from an offer.
Id of the offer the playbook was generated from. null for playbooks that were not AI-generated.
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