curl --request POST \
--url https://api.erdo.ai/v1/lead-playbook/read-back \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"body": "<string>",
"agent_id": "<string>"
}
'import requests
url = "https://api.erdo.ai/v1/lead-playbook/read-back"
payload = {
"body": "<string>",
"agent_id": "<string>"
}
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({body: JSON.stringify('<string>'), agent_id: '<string>'})
};
fetch('https://api.erdo.ai/v1/lead-playbook/read-back', 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/read-back",
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([
'body' => '<string>',
'agent_id' => '<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/read-back"
payload := strings.NewReader("{\n \"body\": \"<string>\",\n \"agent_id\": \"<string>\"\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.erdo.ai/v1/lead-playbook/read-back")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"body\": \"<string>\",\n \"agent_id\": \"<string>\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.erdo.ai/v1/lead-playbook/read-back")
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 \"body\": \"<string>\",\n \"agent_id\": \"<string>\"\n}"
response = http.request(request)
puts response.read_body{
"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>"
}
}{
"code": "not_found",
"details": {},
"message": "<string>"
}ReadLeadPlaybookAPI mirrors the erdo_read_lead_playbook MCP tool: the
read-back of a draft, without saving it.
curl --request POST \
--url https://api.erdo.ai/v1/lead-playbook/read-back \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"body": "<string>",
"agent_id": "<string>"
}
'import requests
url = "https://api.erdo.ai/v1/lead-playbook/read-back"
payload = {
"body": "<string>",
"agent_id": "<string>"
}
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({body: JSON.stringify('<string>'), agent_id: '<string>'})
};
fetch('https://api.erdo.ai/v1/lead-playbook/read-back', 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/read-back",
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([
'body' => '<string>',
'agent_id' => '<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/read-back"
payload := strings.NewReader("{\n \"body\": \"<string>\",\n \"agent_id\": \"<string>\"\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.erdo.ai/v1/lead-playbook/read-back")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"body\": \"<string>\",\n \"agent_id\": \"<string>\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.erdo.ai/v1/lead-playbook/read-back")
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 \"body\": \"<string>\",\n \"agent_id\": \"<string>\"\n}"
response = http.request(request)
puts response.read_body{
"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>"
}
}{
"code": "not_found",
"details": {},
"message": "<string>"
}Authorizations
An Erdo API key (erdo_api_...) or scoped token (erdo_token_...).
Response
Success response
goes out beside a gated text or call.
Mode is "automatic" (an email may go on its own beside the card) or "never". There is deliberately no "approval": an accompanying email that itself needed approving would be a second thing in front of a person about one lead, which is exactly the shape this engine does not have.
Show child attributes
Show child attributes
Show child attributes
Show child attributes
the per-channel gaps in each reachability state, and how soon after capture the engine may start. It is read out of the text like everything else in the read-back, and then clamped to what the platform allows.
Show child attributes
Show child attributes
a surface can draw it: each stage's steps in the order they happen, the rules that win from any stage, and the facts about a lead that change which steps happen.
It is a DRAWING, never a guard. The engine decides from the playbook's text; nothing here is enforced and no decision is checked against it. It exists because the rest of the read-back lists the parts of a playbook (stages, actions, a priority ladder) with no arrows between them, and the order is what a reader of the follow-up queue needs to understand why a card says what it says.
Every step, rule and condition carries the passage of the text it was read from, and SourceFound says whether that passage really is in the text. A surface draws an unfound one as unverified rather than as the playbook's.
Show child attributes
Show child attributes
Show child attributes
Show child attributes
the playbook's own rules put a lead at each level, and the factors the playbook forbids taking into account.
The three levels are fixed — high, medium, low. They are the platform's, not the playbook's: the evaluation's response schema constrains the model to exactly those three (see evaluationFormat in evaluate.go) and the queue's sort, its filters and its badges are built on them. A playbook cannot invent a tier; what it decides is which of its rules map onto each level.
NOTHING HERE IS ENFORCED, and this struct is not a guard. The model still chooses a decision's priority and writes its priority_reason, and no decision is rejected, downgraded or rewritten because its priority disagrees with this ladder. The read-back exists so an operator can SEE what Erdo understood — until now the ladder reached the model only as raw playbook text, so a misread rule was invisible until decisions came out wrong. Whether to enforce it is a product decision nobody has taken.
Show child attributes
Show child attributes
Show child attributes
Show child attributes
lowercase three-letter names; Start and End are "HH:MM" in Timezone.
Show child attributes
Show child attributes

