curl --request PUT \
--url https://api.erdo.ai/v1/lead-playbook \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"body": "<string>",
"agent_id": "<string>",
"daily_contact_cap": 123,
"enabled": true,
"handoff_alert_recipients": [
"<string>"
],
"signature_name": "<string>"
}
'import requests
url = "https://api.erdo.ai/v1/lead-playbook"
payload = {
"body": "<string>",
"agent_id": "<string>",
"daily_contact_cap": 123,
"enabled": True,
"handoff_alert_recipients": ["<string>"],
"signature_name": "<string>"
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.put(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'PUT',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
body: JSON.stringify('<string>'),
agent_id: '<string>',
daily_contact_cap: 123,
enabled: true,
handoff_alert_recipients: ['<string>'],
signature_name: '<string>'
})
};
fetch('https://api.erdo.ai/v1/lead-playbook', 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.erdo.ai/v1/lead-playbook",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "PUT",
CURLOPT_POSTFIELDS => json_encode([
'body' => '<string>',
'agent_id' => '<string>',
'daily_contact_cap' => 123,
'enabled' => true,
'handoff_alert_recipients' => [
'<string>'
],
'signature_name' => '<string>'
]),
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.erdo.ai/v1/lead-playbook"
payload := strings.NewReader("{\n \"body\": \"<string>\",\n \"agent_id\": \"<string>\",\n \"daily_contact_cap\": 123,\n \"enabled\": true,\n \"handoff_alert_recipients\": [\n \"<string>\"\n ],\n \"signature_name\": \"<string>\"\n}")
req, _ := http.NewRequest("PUT", 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.put("https://api.erdo.ai/v1/lead-playbook")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"body\": \"<string>\",\n \"agent_id\": \"<string>\",\n \"daily_contact_cap\": 123,\n \"enabled\": true,\n \"handoff_alert_recipients\": [\n \"<string>\"\n ],\n \"signature_name\": \"<string>\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.erdo.ai/v1/lead-playbook")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Put.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"body\": \"<string>\",\n \"agent_id\": \"<string>\",\n \"daily_contact_cap\": 123,\n \"enabled\": true,\n \"handoff_alert_recipients\": [\n \"<string>\"\n ],\n \"signature_name\": \"<string>\"\n}"
response = http.request(request)
puts response.read_body{
"agent": {
"id": "<string>",
"name": "<string>",
"phone_number": "<string>",
"slug": "<string>"
},
"agent_id": "<string>",
"body": "<string>",
"daily_contact_cap": 123,
"enabled": true,
"exists": true,
"handoff_alert_recipients": [
"<string>"
],
"policy_digest": "<string>",
"read_back": {
"accompanying_email": {
"mode": "<string>",
"when": "<string>"
},
"actions": [
{
"kind": "<string>",
"mode": "<string>",
"when": "<string>"
}
],
"cadence": {
"confirmed": {
"between_channels": [
{
"after": "<string>",
"minutes": 123,
"next": "<string>"
}
],
"call_minutes": 123,
"email_minutes": 123,
"sms_minutes": 123
},
"new_lead_grace_minutes": 123,
"unconfirmed": {
"between_channels": [
{
"after": "<string>",
"minutes": 123,
"next": "<string>"
}
],
"call_minutes": 123,
"email_minutes": 123,
"sms_minutes": 123
}
},
"limits": [
{
"kind": "<string>",
"mode": "<string>",
"reason": "<string>"
}
],
"priority": {
"forbidden_factors": [
"<string>"
],
"high": [
"<string>"
],
"low": [
"<string>"
],
"medium": [
"<string>"
]
},
"stages": [
{
"actions": [
"<string>"
],
"name": "<string>",
"summary": "<string>"
}
],
"working_hours": {
"days": [
"<string>"
],
"end": "<string>",
"start": "<string>",
"timezone": "<string>"
}
},
"revision": 123,
"signature_name": "<string>",
"updated_at": "2023-11-07T05:31:56Z",
"updated_by": "<string>"
}{
"code": "not_found",
"details": {},
"message": "<string>"
}PutLeadPlaybookAPI saves the playbook text, reads it into the typed
read-back, and stores both. Organization admins only.
There is deliberately no MCP tool for this: enabling a playbook that marks email automatic lets Erdo email real leads without a person, and that is a person’s decision. A scoped API key is refused.
curl --request PUT \
--url https://api.erdo.ai/v1/lead-playbook \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"body": "<string>",
"agent_id": "<string>",
"daily_contact_cap": 123,
"enabled": true,
"handoff_alert_recipients": [
"<string>"
],
"signature_name": "<string>"
}
'import requests
url = "https://api.erdo.ai/v1/lead-playbook"
payload = {
"body": "<string>",
"agent_id": "<string>",
"daily_contact_cap": 123,
"enabled": True,
"handoff_alert_recipients": ["<string>"],
"signature_name": "<string>"
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.put(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'PUT',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
body: JSON.stringify('<string>'),
agent_id: '<string>',
daily_contact_cap: 123,
enabled: true,
handoff_alert_recipients: ['<string>'],
signature_name: '<string>'
})
};
fetch('https://api.erdo.ai/v1/lead-playbook', 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.erdo.ai/v1/lead-playbook",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "PUT",
CURLOPT_POSTFIELDS => json_encode([
'body' => '<string>',
'agent_id' => '<string>',
'daily_contact_cap' => 123,
'enabled' => true,
'handoff_alert_recipients' => [
'<string>'
],
'signature_name' => '<string>'
]),
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.erdo.ai/v1/lead-playbook"
payload := strings.NewReader("{\n \"body\": \"<string>\",\n \"agent_id\": \"<string>\",\n \"daily_contact_cap\": 123,\n \"enabled\": true,\n \"handoff_alert_recipients\": [\n \"<string>\"\n ],\n \"signature_name\": \"<string>\"\n}")
req, _ := http.NewRequest("PUT", 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.put("https://api.erdo.ai/v1/lead-playbook")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"body\": \"<string>\",\n \"agent_id\": \"<string>\",\n \"daily_contact_cap\": 123,\n \"enabled\": true,\n \"handoff_alert_recipients\": [\n \"<string>\"\n ],\n \"signature_name\": \"<string>\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.erdo.ai/v1/lead-playbook")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Put.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"body\": \"<string>\",\n \"agent_id\": \"<string>\",\n \"daily_contact_cap\": 123,\n \"enabled\": true,\n \"handoff_alert_recipients\": [\n \"<string>\"\n ],\n \"signature_name\": \"<string>\"\n}"
response = http.request(request)
puts response.read_body{
"agent": {
"id": "<string>",
"name": "<string>",
"phone_number": "<string>",
"slug": "<string>"
},
"agent_id": "<string>",
"body": "<string>",
"daily_contact_cap": 123,
"enabled": true,
"exists": true,
"handoff_alert_recipients": [
"<string>"
],
"policy_digest": "<string>",
"read_back": {
"accompanying_email": {
"mode": "<string>",
"when": "<string>"
},
"actions": [
{
"kind": "<string>",
"mode": "<string>",
"when": "<string>"
}
],
"cadence": {
"confirmed": {
"between_channels": [
{
"after": "<string>",
"minutes": 123,
"next": "<string>"
}
],
"call_minutes": 123,
"email_minutes": 123,
"sms_minutes": 123
},
"new_lead_grace_minutes": 123,
"unconfirmed": {
"between_channels": [
{
"after": "<string>",
"minutes": 123,
"next": "<string>"
}
],
"call_minutes": 123,
"email_minutes": 123,
"sms_minutes": 123
}
},
"limits": [
{
"kind": "<string>",
"mode": "<string>",
"reason": "<string>"
}
],
"priority": {
"forbidden_factors": [
"<string>"
],
"high": [
"<string>"
],
"low": [
"<string>"
],
"medium": [
"<string>"
]
},
"stages": [
{
"actions": [
"<string>"
],
"name": "<string>",
"summary": "<string>"
}
],
"working_hours": {
"days": [
"<string>"
],
"end": "<string>",
"start": "<string>",
"timezone": "<string>"
}
},
"revision": 123,
"signature_name": "<string>",
"updated_at": "2023-11-07T05:31:56Z",
"updated_by": "<string>"
}{
"code": "not_found",
"details": {},
"message": "<string>"
}Authorizations
An Erdo API key (erdo_api_...) or scoped token (erdo_token_...).
Body
Response
Success response
Show child attributes
Show child attributes
the same organization. Null when none is linked.
then carries the default template and ReadBack is empty.
handoff — a decision it cannot carry out, which needs a person at the sales desk. Empty means the alert is off: there is no default audience and no fallback to the organization's members, so a development that has not filled this in is told nothing and no email is sent.
It is deliberately not on a playbook REVISION, unlike SignatureName: it is who gets told, not what a decision says, so it is read live at send time and changing it takes effect at once rather than at the next save. See migration 012.
a decision that is genuinely out of date from one whose playbook has merely been re-saved. A decision whose policy_digest equals this one was decided under the playbook as it stands; one that differs is due to be decided again, and the sweep will. See policy.go.
Only Actions[].Mode and WorkingHours are enforced; the stages and the priority ladder are a picture of the text drawn for the person who wrote it, and every evaluation still reads the whole text.
Show child attributes
Show child attributes
signed and sent as, and what a call brief opens with. Empty falls back to the linked concierge's display name, which is what every playbook written before this setting existed does. See writeIdentitySection.

