curl --request GET \
--url https://api.erdo.ai/v1/lead-playbook \
--header 'Authorization: Bearer <token>'import requests
url = "https://api.erdo.ai/v1/lead-playbook"
headers = {"Authorization": "Bearer <token>"}
response = requests.get(url, headers=headers)
print(response.text)const options = {method: 'GET', headers: {Authorization: 'Bearer <token>'}};
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 => "GET",
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.erdo.ai/v1/lead-playbook"
req, _ := http.NewRequest("GET", 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.get("https://api.erdo.ai/v1/lead-playbook")
.header("Authorization", "Bearer <token>")
.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::Get.new(url)
request["Authorization"] = 'Bearer <token>'
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,
"enabled_at": "2023-11-07T05:31:56Z",
"exists": true,
"handoff_alert_links": [
{
"label": "<string>",
"requires": [
"<string>"
],
"url": "<string>"
}
],
"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,
"whatsapp_minutes": 123
},
"new_lead_grace_minutes": 123,
"unanswered_attempts": [
"<string>"
],
"unconfirmed": {
"between_channels": [
{
"after": "<string>",
"minutes": 123,
"next": "<string>"
}
],
"call_minutes": 123,
"email_minutes": 123,
"sms_minutes": 123,
"whatsapp_minutes": 123
}
},
"flow": {
"any_time": [
{
"action": "<string>",
"exit_to": "<string>",
"if": "<string>",
"source": "<string>",
"source_found": true
}
],
"conditions": [
{
"builtin": true,
"key": "<string>",
"label": "<string>",
"options": [
{
"label": "<string>",
"value": "<string>"
}
],
"source": "<string>",
"source_found": true
}
],
"conflicts": [
{
"description": "<string>",
"sources": [
"<string>"
]
}
],
"stages": [
{
"name": "<string>",
"phases": [
{
"aim": "<string>",
"name": "<string>",
"steps": [
{
"action": "<string>",
"detail": "<string>",
"exit_to": "<string>",
"label": "<string>",
"only_if": [
{
"key": "<string>",
"values": [
"<string>"
]
}
],
"source": "<string>",
"source_found": true,
"when": "<string>"
}
]
}
]
}
]
},
"limits": [
{
"kind": "<string>",
"mode": "<string>",
"reason": "<string>"
}
],
"priority": {
"forbidden_factors": [
"<string>"
],
"high": [
"<string>"
],
"low": [
"<string>"
],
"medium": [
"<string>"
]
},
"stages": [
{
"actions": [
"<string>"
],
"label": "<string>",
"name": "<string>",
"summary": "<string>",
"tone": "<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>"
}GetLeadPlaybookAPI mirrors the erdo_get_lead_playbook MCP tool. An
organization that never saved a playbook gets the default template with exists=false.
curl --request GET \
--url https://api.erdo.ai/v1/lead-playbook \
--header 'Authorization: Bearer <token>'import requests
url = "https://api.erdo.ai/v1/lead-playbook"
headers = {"Authorization": "Bearer <token>"}
response = requests.get(url, headers=headers)
print(response.text)const options = {method: 'GET', headers: {Authorization: 'Bearer <token>'}};
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 => "GET",
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.erdo.ai/v1/lead-playbook"
req, _ := http.NewRequest("GET", 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.get("https://api.erdo.ai/v1/lead-playbook")
.header("Authorization", "Bearer <token>")
.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::Get.new(url)
request["Authorization"] = 'Bearer <token>'
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,
"enabled_at": "2023-11-07T05:31:56Z",
"exists": true,
"handoff_alert_links": [
{
"label": "<string>",
"requires": [
"<string>"
],
"url": "<string>"
}
],
"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,
"whatsapp_minutes": 123
},
"new_lead_grace_minutes": 123,
"unanswered_attempts": [
"<string>"
],
"unconfirmed": {
"between_channels": [
{
"after": "<string>",
"minutes": 123,
"next": "<string>"
}
],
"call_minutes": 123,
"email_minutes": 123,
"sms_minutes": 123,
"whatsapp_minutes": 123
}
},
"flow": {
"any_time": [
{
"action": "<string>",
"exit_to": "<string>",
"if": "<string>",
"source": "<string>",
"source_found": true
}
],
"conditions": [
{
"builtin": true,
"key": "<string>",
"label": "<string>",
"options": [
{
"label": "<string>",
"value": "<string>"
}
],
"source": "<string>",
"source_found": true
}
],
"conflicts": [
{
"description": "<string>",
"sources": [
"<string>"
]
}
],
"stages": [
{
"name": "<string>",
"phases": [
{
"aim": "<string>",
"name": "<string>",
"steps": [
{
"action": "<string>",
"detail": "<string>",
"exit_to": "<string>",
"label": "<string>",
"only_if": [
{
"key": "<string>",
"values": [
"<string>"
]
}
],
"source": "<string>",
"source_found": true,
"when": "<string>"
}
]
}
]
}
]
},
"limits": [
{
"kind": "<string>",
"mode": "<string>",
"reason": "<string>"
}
],
"priority": {
"forbidden_factors": [
"<string>"
],
"high": [
"<string>"
],
"low": [
"<string>"
],
"medium": [
"<string>"
]
},
"stages": [
{
"actions": [
"<string>"
],
"label": "<string>",
"name": "<string>",
"summary": "<string>",
"tone": "<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_...).
Response
Success response
Show child attributes
Show child attributes
the same organization. Null when none is linked.
It counts every contact the engine makes or puts in front of a person: emails, texts, WhatsApp messages and calls, each one place (an email sent beside a gated text or call is a second). Handoffs, waits and closes never count, and nor does a concierge answering a lead inside a WhatsApp, SMS, chat or phone conversation. 200 when never set.
before it is left alone until somebody starts the engine on it; absent for a playbook that was never on.
then carries the default template and ReadBack is empty.
caller declared them: URL templates filled from the lead (backend/utils/linktemplate). Empty means the alert carries no button. Erdo has no page of its own for a lead's follow-up, so it never invents a destination; the application the organization works its leads in declares where "open this lead" goes. Like the recipients, it is a delivery detail read live at send time and never frozen on a revision. Never nil.
Show child attributes
Show child attributes
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.

