Tools reference
Every tool your assistant can call, and exactly what each returns.
Your assistant picks which tool to call, so you never name one directly. This page is here for when you want to know exactly what it can reach, what to pass, and what comes back.
Each tool needs one of three permissions: read, write, or dial. See Keys and permissions. A key only sees the tools its scopes allow, so a read-only key never even lists the write tools.
The input tables below are generated from the server's own tool definitions on every build, so they cannot drift from what the endpoint actually accepts. The return shapes further down are written by hand against each handler.
How a tool call is shaped
Every tool runs over the MCP endpoint as a JSON-RPC 2.0 tools/call. Your assistant builds this for you; the CLI wraps it; and you can send it by hand with curl. A call names a tool and passes arguments:
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": { "name": "list_leads", "arguments": { "stage": "new", "limit": 5 } }
}
The result comes back with the tool's own JSON as a string inside content[0].text:
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"content": [
{ "type": "text", "text": "{\n \"leads\": [ ... ]\n }" }
]
}
}
Safety rails on every write
Every write tool and every dial tool accepts two optional fields, because the caller is an AI and an AI retries on a timeout:
idempotency_keyis any unique string you generate for this write. Retrying with the same key returns the original result instead of writing a second time, withreplayed: trueadded so you can tell. Strongly recommended on every write, and it is the one thing standing between a flaky network moment and a lead getting called twice or three drip sequences getting built.dry_run, whentrue, validates and describes what the tool would write, and changes nothing. It comes back as{ "dry_run": true, "would_create": ... }or the equivalentwould_set/would_add/would_record/would_verify. This is what lets an assistant plan a whole account build, show you exactly what it will create, and only then run it for real.
Neither is repeated in the input tables below. Assume both are available on every write and dial tool.
Working across an organization
Most keys act on exactly one account. An organization key, which is any key created by an account that owns or manages an organization, can also act on the other seats in that same organization.
- Call
list_accountsfirst. An organization key gets back theorg_idand every seat, each with anaccount_id. A single-account key gets back just its own. - Pass that
account_idon any tool to run it against that seat. It is advertised on the go-to-market tools, where working across seats is the point, and the server honors it on every tool. - A single-account key that names another account is refused with
-32003before the tool runs. A team lead can only ever reach seats inside their own organization.
See Keys and permissions for the full rule.
28 tools, grouped by the permission each one needs.
Reading
14 tools, permission read. A read-only key can call every tool in this section and nothing else.
list_leads
List leads
List the account's leads, newest first. Optionally filter by pipeline stage. Each lead comes back with its address and map coordinates when it has them, so a lead you created with an address reads back the same way.
| Input | Type | Required | What it is |
|---|---|---|---|
stage |
string | no | Return only leads sitting in this pipeline stage. Omit to return every stage. One of: new, contacted, callback, hot, booked, sold, lost. |
limit |
number | no | Maximum rows to return. Defaults to 25, capped at 100. |
Behavior: changes nothing.
list_calls
List recent calls
List recent calls for the account with outcome, direction and duration. Use for 'what happened on the phone today'.
| Input | Type | Required | What it is |
|---|---|---|---|
limit |
number | no | Maximum rows to return. Defaults to 25, capped at 100. |
direction |
string | no | Return only calls in this direction. Omit to return both. One of: inbound, outbound. |
Behavior: changes nothing.
list_appointments
List upcoming appointments
List upcoming appointments for the account. Use for 'what is on my calendar'.
| Input | Type | Required | What it is |
|---|---|---|---|
limit |
number | no | Maximum appointments to return. Defaults to 25, capped at 100. |
Behavior: changes nothing.
get_call_metrics
Call performance metrics
Call performance over a period: total, connected, voicemail, and how many reached a real person. Reads the one canonical call vocabulary, so these numbers match the dashboard exactly.
| Input | Type | Required | What it is |
|---|---|---|---|
days |
number | no | How many days back to measure. Defaults to 30, capped at 730. |
Behavior: changes nothing.
get_next_actions
What to work on now
The agent's actual to-do list right now: leads that went cold, connected calls that never got a follow-up, and appointments coming up. Call this when asked 'what should I do today', 'what needs attention', or at the start of a work session. Returns work to be done, not a data dump. Each item includes the lead id and phone so you can act on it with other tools.
No inputs.
Behavior: changes nothing.
get_availability
Open booking slots
Open appointment slots for the account's booking calendar over the next two weeks. Call this BEFORE book_appointment so you offer a real time instead of guessing one.
| Input | Type | Required | What it is |
|---|---|---|---|
timezone |
string | no | IANA timezone the returned slots are expressed in, for example America/Chicago. Defaults to America/Chicago. |
Behavior: changes nothing.
list_lead_lists
List saved lead lists
List the account's saved lead lists with each list's member count and a per-stage rollup (new, contacted, booked, sold, and so on). Use this to find a listId to pass to run_list, and to watch results roll up as calls complete.
No inputs.
Behavior: changes nothing.
list_calendars
List booking calendars
List the account's named booking calendars and their settings.
No inputs.
Behavior: changes nothing.
list_drips
List drip sequences
List the account's drip sequences and their steps.
No inputs.
Behavior: changes nothing.
get_account_config
Read account configuration
Read the account's pipeline stages, custom lead field definitions, and automation settings. Use before writing config so you patch rather than clobber.
No inputs.
Behavior: changes nothing.
list_accounts
List reachable accounts
List the accounts this key can act on. A per-account key returns just its own account. An organization key (held by a team lead) returns every seat in the org, each with an account_id to pass as account_id on any GTM tool to act on that seat.
No inputs.
Behavior: changes nothing.
score_lead
Score a lead
Score a saved lead now and return the reasons. Gives the 1-99 priority score, the hot/warm/cold tier, the fit and intent dimensions, the A-D x 1-4 grid, the routing call, and the factor breakdown. This is a read: it computes and explains, and writes nothing. Fit is null when the account has no closed-deal history to judge source fit from, which is honest, not an error.
| Input | Type | Required | What it is |
|---|---|---|---|
leadId |
string | yes | Id of a saved lead (from list_leads). |
account_id |
string | no | Organization keys only: the seat to act on, from list_accounts. Omit to use the key's own account. A per-account key may only ever use its own account. |
Behavior: changes nothing.
get_closing_brief
Closing call brief
Build the closing-call brief for a saved lead: who they are, what they need, their pain points, the deal state, a price recommendation with its inputs shown, a talk track, and the next step. Every line cites the record it came from; a lead with no activity yet returns empty sections on purpose rather than guesses. Returns the structured brief and a ready-to-read text version.
| Input | Type | Required | What it is |
|---|---|---|---|
leadId |
string | yes | Id of a saved lead (from list_leads). |
account_id |
string | no | Organization keys only: the seat to act on, from list_accounts. Omit to use the key's own account. A per-account key may only ever use its own account. |
Behavior: changes nothing.
get_market_intel
Market intel
Real go-to-market intel distilled from this account's vertical: per-source win rates, price anchors by tier, winning hooks, and losing patterns, all counted from closed deals (never guessed or competitor data). Pass an asset_type to also get the current winning assets of that kind. Returns available:false when not enough deals have closed yet.
| Input | Type | Required | What it is |
|---|---|---|---|
asset_type |
string | no | Optional: also return the current best assets of this kind. One of: sms_step, email_step, sequence, form, funnel_headline, ad_copy, pre_nurture, closing_pitch. |
channel |
string | no | Optional: narrow the winning assets to one channel. One of: sms, email, linkedin, voice, web. |
account_id |
string | no | Organization keys only: the seat to act on, from list_accounts. Omit to use the key's own account. A per-account key may only ever use its own account. |
Behavior: changes nothing.
Writing
12 tools, permission write. These need a key with write. Every one of them also accepts idempotency_key and dry_run (see the safety rails above), which are not repeated in the tables.
book_appointment
Book an appointment
Book an appointment on the account's calendar. Requires a slot returned by get_availability. The customer must have agreed to be contacted; pass consent only when they actually have.
| Input | Type | Required | What it is |
|---|---|---|---|
startTime |
string | yes | ISO 8601 start time, taken from a slot get_availability returned. |
name |
string | yes | Full name of the person the appointment is for. |
email |
string | yes | Email address the confirmation is sent to. |
phone |
string | yes | Contact phone number for the person being booked, US format. |
consent |
boolean | yes | True only if the customer actually agreed to be contacted. |
timezone |
string | no | IANA timezone the start time is expressed in, for example America/Chicago. Defaults to America/Chicago. |
create_lead
Create a lead
Create a new lead on the account. Pass an address when you have one and the lead appears on the door-mode map, so a business your assistant found on a map becomes a stop a rep can walk to.
| Input | Type | Required | What it is |
|---|---|---|---|
name |
string | yes | Full name of the person, or the business name. |
phone |
string | yes | US phone number. Anything that is not a digit is ignored, and the last ten digits are stored. |
email |
string | no | Email address for the lead, if you have one. |
notes |
string | no | Free-text context to store on the lead, such as what they asked for. |
address |
string | no | Street address, for example 123 Main St. Give this and the lead can appear on the door-mode map. |
city |
string | no | City the address is in. |
state |
string | no | Two-letter US state code, for example TX. |
zip |
string | no | US postal code. |
update_lead
Update a lead
Update a saved lead: its contact details, its pipeline stage, its notes, or its address. Only the fields you send are changed; everything else is left alone. Get the leadId from list_leads.
| Input | Type | Required | What it is |
|---|---|---|---|
leadId |
string | yes | The lead to update, from list_leads. |
name |
string | no | Full name of the person, or the business name. |
phone |
string | no | US phone number. Anything that is not a digit is ignored, and the last ten digits are stored. |
email |
string | no | Email address for the lead. |
notes |
string | no | Replaces the notes on the lead. Read the lead first if you mean to append rather than replace. |
stage |
string | no | Move the lead to this pipeline stage. One of: new, contacted, callback, hot, booked, sold, lost. |
address |
string | no | Street address, for example 123 Main St. Give this and the lead can appear on the door-mode map. |
city |
string | no | City the address is in. |
state |
string | no | Two-letter US state code, for example TX. |
zip |
string | no | US postal code. |
find_businesses
Find businesses
Search Google Maps for businesses in a place and save them to the account as leads, with their street address so they appear on the door-mode map. This one costs credits, so it works in two steps: call it first to get the price and the balance, then call it again with confirm set to true to actually run it. Available on Right Hand and Front Desk Pro. Set enrich to also read each business's website for what they do and a contact email, priced per site on top.
| Input | Type | Required | What it is |
|---|---|---|---|
query |
string | no | The full search, for example: roofing companies in Katy TX. Give this, or give niche and city. |
niche |
string | no | Kind of business to look for, for example roofing contractor. |
city |
string | no | Where to look, for example Katy TX. |
limit |
number | no | How many businesses to look for. Defaults to 20, capped at 50. The price is per result. |
enrich |
boolean | no | When true, each result that has a website gets one page read, which adds a note about what they do and any contact email found. Priced per site read, on top of the search. |
confirm |
boolean | no | Must be true to actually run the search and charge for it. Without it you get the price, the balance and nothing else. |
create_calendar
Create a booking calendar
Create a named booking calendar. Slugs are unique per account, so re-running with an existing slug is refused rather than silently duplicating.
| Input | Type | Required | What it is |
|---|---|---|---|
slug |
string | yes | URL-safe identifier, unique within this account. Anything outside lowercase letters, digits and hyphens is replaced with a hyphen. |
name |
string | yes | Display name customers see on the booking page. |
description |
string | no | Short blurb shown under the calendar name on the booking page. |
timezone |
string | no | IANA timezone this calendar's hours are expressed in, for example America/Chicago. |
slot_minutes |
number | no | Length of one bookable slot, in minutes. |
buffer_minutes |
number | no | Gap held open after each appointment, in minutes. |
min_notice_minutes |
number | no | How far ahead a customer must book, in minutes. Blocks last-minute bookings. |
booking_window_days |
number | no | How many days into the future this calendar accepts bookings. |
auto_confirm |
boolean | no | True to confirm bookings automatically instead of leaving them pending review. |
set_availability
Set weekly availability
Set the account's default weekly availability and booking rules. weekly maps each weekday to its open windows.
| Input | Type | Required | What it is |
|---|---|---|---|
timezone |
string | no | IANA timezone the weekly hours are expressed in, for example America/Chicago. |
slot_minutes |
number | no | Length of one bookable slot, in minutes. |
buffer_minutes |
number | no | Gap held open after each appointment, in minutes. |
min_notice_minutes |
number | no | How far ahead a customer must book, in minutes. |
weekly |
object | no | Opening hours per weekday, in the account timezone. Each of the seven day keys maps to a list of open windows. Fields: sun, mon, tue, wed, thu, fri, sat. |
create_drip
Create a drip sequence
Create a drip sequence with its ordered steps. Steps send on a delay after enrollment. Creating a sequence does NOT enroll anyone.
| Input | Type | Required | What it is |
|---|---|---|---|
name |
string | yes | Internal name for the sequence, shown in the account's drip list. |
active |
boolean | no | True to start sending as soon as leads are enrolled. Defaults to false. |
auto_enroll_new |
boolean | no | Enroll newly created leads automatically. |
auto_enroll_cold |
boolean | no | Enroll existing leads automatically once they go cold (new or contacted stage, no contact in 4 or more days). |
steps |
array of object | yes | The steps to send, in the order they should go out. Each entry has: delay_hours (required), channel (required), subject, body (required). |
set_pipeline_config
Replace pipeline stages
Replace the account's pipeline stage configuration. Read get_account_config first, because this overwrites the whole object.
| Input | Type | Required | What it is |
|---|---|---|---|
pipeline_config |
object | yes | The complete pipeline configuration. This replaces the stored object, so read get_account_config first and send the whole thing back. Fields: stages (required). |
create_lead_field
Add a custom lead field
Add a custom lead field definition. Appends to the existing definitions rather than replacing them.
| Input | Type | Required | What it is |
|---|---|---|---|
key |
string | yes | Machine key, unique within the account. Anything outside lowercase letters, digits and underscores is replaced with an underscore. |
label |
string | yes | Display name shown on the lead record. |
type |
string | no | How the value is entered and stored. Defaults to text. One of: text, number, date, select, boolean. |
options |
array of string | no | The choices offered, for a select field only. |
request_payment
Request a card payment
Create a payment request the customer can pay by card, and return a pay-link URL to send them. Money goes to the account's own connected Stripe. Pass amountCents (e.g. 25000 for $250.00) and a short description; optionally a leadId to attach it to a saved lead. Requires the account's Stripe to be connected. If it is not, this returns needsConnect.
| Input | Type | Required | What it is |
|---|---|---|---|
amountCents |
number | yes | Amount to charge, in cents. 25000 = $250.00. Minimum 100. |
description |
string | yes | What the payment is for (shown to the customer on the pay page) |
leadId |
string | no | Optional saved lead to attach this payment to |
verify_lead
Verify a lead
Re-verify a saved lead's email deliverability and phone line-type (mobile, landline, or VoIP), optionally enrich it, and store the result on the lead. Returns the verification detail and skip_dial (true when the number is proven invalid or VoIP, so it should not be dialed). An unchecked leg reports "unknown", never a guess.
| Input | Type | Required | What it is |
|---|---|---|---|
leadId |
string | yes | Id of a saved lead (from list_leads). |
force |
boolean | no | Bypass the cache and re-check from scratch. |
enrich |
boolean | no | Also run the public-web enrichment leg. |
account_id |
string | no | Organization keys only: the seat to act on, from list_accounts. Omit to use the key's own account. A per-account key may only ever use its own account. |
record_close_outcome
Record a won or lost deal
Record a deal as won or lost so the GTM learning loop can learn from a real close. A won REQUIRES deal_id (a closed deal this account owns) and amount_cents. Optionally attach the lead, the reason it was won or lost, the price offered, and the asset/play ids that were in play, so the win is attributed back to what produced it. Nothing is counted from a timer; only this closer-confirmed outcome moves the loop.
| Input | Type | Required | What it is |
|---|---|---|---|
result |
string | yes | Whether the deal closed or was lost. One of: won, lost. |
deal_id |
string | no | Required on a won: the closed deal (agent_deals) this account owns. |
lead_id |
string | no | Optional saved lead the close came from (from list_leads). |
amount_cents |
number | no | Required on a won: what actually closed, in whole cents (25000 = $250.00). |
price_offered_cents |
number | no | Optional: the price offered, in whole cents. |
reason_code |
string | no | Why it was won or lost, as one of the standard codes. One of: price, timing, competitor, not_qualified, no_show, ghosted, other. |
context |
string | no | Optional free-text note on why it was won or lost (the closer's ground truth). |
variant_ids |
array of string | no | gtm_asset_variant ids that touched this lead. |
play_ids |
array of string | no | call_insights play ids that were in play. |
score_at_close |
number | no | Optional: the lead's score when it closed (calibrates scoring). |
account_id |
string | no | Organization keys only: the seat to act on, from list_accounts. Omit to use the key's own account. A per-account key may only ever use its own account. |
Calling
Two tools, permission dial. These need a key with dial, which is never implied by write. They place real phone calls to real people.
place_call
Place an AI phone call
Place an outbound AI phone call from the account's own number. The AI agent holds the conversation, books the appointment, and the call lands in list_calls when it completes. Dial a saved lead by leadId (uses the consent already on file), or an ad-hoc number with phone and name. Calls are DNC-scrubbed and consent-gated; a blocked number returns the reason instead of dialing.
| Input | Type | Required | What it is |
|---|---|---|---|
leadId |
string | no | Id of a saved lead to call (from list_leads). Preferred: uses the lead's consent on file. |
phone |
string | no | Ad-hoc US number to call when no leadId is given. |
name |
string | no | Name of the person being called, on an ad-hoc dial. |
Behavior: places a real phone call, so it reaches outside LeadBind.
run_list
Run AI calls against a list
Run outbound AI calls against a set of leads at once. Pass a listId (from list_lead_lists) to call every member of a saved list, or leadIds (from list_leads) to call a hand-picked set. Each call goes out from the account's own number, DNC-scrubbed, consent-gated, and only inside legal calling hours; leads that cannot be called are returned with the reason instead of being forced. Optionally cap this run with maxCalls. Returns a per-lead summary of what was placed and what was skipped. Outcomes (connected, qualified, booked) land as each call finishes; read them with list_calls, get_call_metrics, or list_leads.
| Input | Type | Required | What it is |
|---|---|---|---|
listId |
string | no | Saved list to run (from list_lead_lists). Calls every member. |
leadIds |
array of string | no | Hand-picked lead ids to run (from list_leads). Use instead of listId, not with it. |
maxCalls |
number | no | Optional cap on how many calls this run places. Defaults to the account's remaining daily-cap room. |
Behavior: places a real phone call, so it reaches outside LeadBind.
What each tool returns
Return shapes are not part of the tool schema, so these are read from each handler rather than generated. Every one of them arrives as JSON text inside result.content[0].text.
Reading
list_leads
{ "leads": [
{ "id": "ld_...", "name": "Maria Lopez", "phone": "+1...", "email": null, "stage": "contacted", "source": "web-form", "created_at": "..." }
] }
list_calls. caller is always the other party, whichever end that is, so you never have to reason about which of from_number / to_number was you. See Calls and outcomes for what each outcome means.
{ "calls": [
{ "created_at": "...", "direction": "inbound", "outcome": "booked", "duration_seconds": 142,
"from_number": "+1...", "to_number": "+1...", "caller": "+1..." }
] }
Note: This returns the outcome of a call, never the transcript or the recording. Those are not available to any assistant, at any scope. See What we send.
list_appointments
{ "appointments": [
{ "id": "ap_...", "contact_name": "Dan Reyes", "contact_phone": "+1...", "starts_at": "...",
"status": "confirmed", "source": "ai-call", "meeting_url": null }
] }
get_call_metrics returns the window you asked for plus the dashboard's own call breakdown, unmodified. It is the same database function the dashboard reads, so these numbers match the app exactly rather than being a second, drifting definition of "answered".
{
"days": 30,
"breakdown": {
"inbound": { "total": 120, "connected": 96, "voicemail": 18, "answered": 71 },
"outbound": { "total": 90, "connected": 42, "voicemail": 23, "answered": 26 },
"outcomes": [ { "outcome": "booked", "count": 31 } ],
"intents": [ { "intent": "quote", "count": 44 } ],
"hangups": [ { "reason": "user_hangup", "count": 52 } ],
"qa": { "rated": 40, "flagged": 3, "good": 31, "success": 28 },
"latency": { "p50": 780, "p90": 1420 }
}
}
The breakdown also carries a count of stored recordings inside the app. It is stripped on the way out here, along with anything else whose field name mentions a recording or a transcript, so it never reaches an assistant. breakdown is null when the account has no calls in the window.
get_next_actions returns work, not records. It is the one to start with.
{
"summary": "3 lead(s) contacted but untouched for 3+ days, 2 real conversation(s) in the last week with no booking, 1 appointment(s) in the next 48 hours.",
"stale_leads": [
{ "id": "ld_...", "name": "Maria Lopez", "phone": "+1...", "stage": "contacted", "updated_at": "...", "created_at": "..." }
],
"conversations_without_booking": [
{ "created_at": "...", "from_number": "+1...", "outcome": "answered", "duration_seconds": 96 }
],
"upcoming_appointments": [
{ "id": "ap_...", "contact_name": "Dan Reyes", "contact_phone": "+1...", "starts_at": "...", "status": "confirmed", "meeting_url": null }
]
}
Ask it: "What should I work on today?"
get_availability returns days, not a flat list of times, plus the horizon the business actually set. It already accounts for your hours, your buffers, your minimum notice, your daily cap, your blackout dates, and anything on your connected Google Calendar.
{
"days": [
{ "date": "2026-08-31", "slots": ["2026-08-31T14:00:00-05:00", "2026-08-31T14:30:00-05:00"] }
],
"agent": { "name": "Maria Lopez", "businessName": "Lopez Roofing" },
"bookingWindowDays": 14,
"calendar": { "slug": "roof-inspections", "name": "Roof Inspections", "description": null,
"slotMinutes": 30, "autoConfirm": false, "questions": [] }
}
calendar is null for an account booking on its default weekly availability rather than a named calendar. An account with no booking slug set up yet gets { "error": "..." } instead.
Ask it: "When am I free Thursday?"
list_calendars
{ "calendars": [
{ "id": "cal_...", "slug": "roof-inspections", "name": "Roof Inspections", "description": null,
"is_default": true, "auto_confirm": false, "timezone": "America/Chicago", "slot_minutes": 30,
"buffer_minutes": 15, "min_notice_minutes": 60, "booking_window_days": 14, "active": true }
] }
list_lead_lists gives you the listId to hand to run_list, and the per-stage rollup to watch results land.
{ "lists": [
{ "id": "ll_...", "name": "March storm leads", "dialing_enabled": true, "created_at": "...",
"stats": { "total": 214, "new": 90, "contacted": 96, "booked": 22, "sold": 6 } }
] }
list_drips
{ "sequences": [
{ "id": "seq_...", "name": "No-answer 3-touch", "active": true, "auto_enroll_new": false, "auto_enroll_cold": true,
"steps": [
{ "sequence_id": "seq_...", "step_order": 1, "delay_hours": 1, "channel": "sms", "subject": null,
"body": "Hi {{name}}, sorry we missed you..." }
] }
] }
get_account_config returns the row itself, with no wrapper.
{ "vertical": "roofing", "workspace_mode": "sales", "timezone": "America/Chicago",
"pipeline_config": { "...": "..." }, "lead_field_defs": [], "automation_settings": { "...": "..." } }
list_accounts. An organization key gets org_id too; a single-account key gets one row and no org_id.
{ "org_id": "org_...", "accounts": [
{ "account_id": "ag_...", "name": "Lopez Roofing", "role": "owner" },
{ "account_id": "ag_...", "name": "North crew", "role": "member" }
] }
score_lead computes and explains, and writes nothing. fit is null when the account has no closed-deal history to judge source fit from, which is honest rather than an error.
{ "ok": true, "leadId": "ld_...", "score": 78, "tier": "hot", "fit": "B", "intent": 2,
"grid": "B2", "routing": "call_now", "reason": "...", "factors": [ { "...": "..." } ] }
get_closing_brief returns the structured brief and a ready-to-read text version of the same thing. A lead with no activity yet returns empty sections on purpose rather than guesses.
{ "ok": true, "brief": { "...": "..." }, "text": "Closing brief for Maria Lopez\n..." }
get_market_intel fills in once enough deals have closed and says so plainly until then.
{ "ok": true, "available": true, "based_on_deals": 61, "confidence": 0.72,
"source_win_rates": { "web-form": 0.31 }, "price_anchors_cents": { "hot": 240000 },
"winning_hooks": ["..."], "losing_patterns": ["..."], "experiment": "...",
"winning_assets": [], "note": "Real intel distilled from this vertical's closed deals..." }
With nothing distilled yet it returns { "ok": true, "available": false, "note": "...", "winning_assets": ... } and never invents a competitor price or a rate.
Writing
create_lead. The phone is normalized to a 10-digit US number, so digits with spaces, dashes, or parentheses all land the same.
{ "ok": true, "leadId": "ld_..." }
Ask it: "Add Maria Lopez, her number, roof leak, called this morning."
book_appointment books onto your real calendar and triggers the normal confirmation.
{ "ok": true, "when": "Thursday, August 31 at 2:00 PM CDT", "status": "confirmed",
"manageUrl": "https://leadbind.org/b/..." }
when is the human-readable time in the viewer's timezone, not the ISO string you sent. On a calendar that takes a deposit, the response also carries checkout_url, a Stripe Checkout link the customer must complete, and status is pending_payment until they do.
Two things are enforced and neither is negotiable:
startTimemust come fromget_availability. A guessed time is rejected rather than double-booking you.consentmust betrue, and it must be real. It means the person actually agreed to this appointment and to being contacted about it. It is not a checkbox to satisfy. See Consent and opt-outs.
Ask it: "Book Maria for Thursday at 2."
create_calendar
{ "ok": true, "calendarId": "cal_...", "slug": "roof-inspections" }
set_availability tells you which fields it actually changed.
{ "ok": true, "set": ["timezone", "slot_minutes", "weekly"] }
create_drip. Creating a sequence does not enroll anyone by itself.
{ "ok": true, "sequenceId": "seq_...", "steps": 3 }
Ask it: "Set up a three-touch follow-up for leads who do not answer."
set_pipeline_config replaces the whole object, so read get_account_config first and pass the merged result. Do this one with dry_run: true the first time.
{ "ok": true }
create_lead_field appends to your existing definitions rather than replacing them, and refuses a key that already exists.
{ "ok": true, "field": { "key": "roof_type", "label": "Roof type", "type": "select",
"options": ["Shingle", "Metal", "Tile"] }, "total": 4 }
verify_lead stores the result on the lead, which is why it is a write. An unchecked leg reports "unknown", never a guess.
{ "ok": true, "leadId": "ld_...", "verified_at": "...",
"verification": { "email": { "...": "..." }, "phone": { "line_type": "mobile" } },
"skip_dial": false }
request_payment creates a payment request and hands back a pay-link URL you send the customer. They pay by card, the money lands in your own connected Stripe account, and LeadBind takes its platform fee. It never sees a card number.
{ "ok": true, "url": "https://leadbind.org/estimate/dl_...", "dealId": "dl_...",
"amountCents": 25000, "applicationFeeCents": 750 }
Until your Stripe is connected (Settings, then Payments) it returns { "error": "Connect your Stripe account first...", "needsConnect": true } instead of a half-made request.
Ask it: "Send Maria a $250 deposit link for the roof job."
record_close_outcome is the only place the learning loop takes a real won or lost. Nothing is counted from a timer.
{ "ok": true, "outcome_id": "go_...", "vertical": "roofing", "attributed_variants": 2 }
Calling
place_call places one real outbound call.
{ "ok": true, "called": true, "to": "+1...", "callId": "call_..." }
callId is the provider's own id for the call, and is null when the provider accepted the call without returning one. The dial permission is separate from write and is off by default. Reading your leads and dialing a real person are not the same risk, so place_call never rides in on write access. Every call it places runs through the exact same core as a call you place yourself in the app, so every guard rides along: rate limit, billing gate, your plan's calling entitlement, consent-only mode, per-lead consent, and a Do-Not-Call scrub. A blocked number returns the reason instead of dialing.
Ask it: "Call the lead I just added and book them for Tuesday."
run_list dials a whole list or a hand-picked set, and reports per lead what actually happened. It never reports a made-up outcome: calls are placed here, and connected, qualified and booked land later, read with list_calls, get_call_metrics, or list_leads.
{
"ok": true,
"target": { "type": "list", "listId": "ll_...", "requested": 40, "considered": 40 },
"placed": 12, "skipped": 28, "failed": 0,
"capRoomBefore": 12, "reachedCap": true,
"results": [
{ "leadId": "ld_...", "name": "Maria Lopez", "phone": "+1...", "status": "placed", "to": "+1..." },
{ "leadId": "ld_...", "name": "Dan Reyes", "phone": "+1...", "status": "skipped", "reason": "outside_calling_hours" },
{ "leadId": "ld_...", "name": null, "phone": null, "status": "skipped", "reason": "do_not_call" }
],
"note": "Calls are placed, not yet completed..."
}
Skip reasons are real guard results, never invented: do_not_call, outside_calling_hours, daily_cap_reached, rate_limited_retry_shortly, lead_not_found. Batch calling applies the lead's own local calling window on top of every guard place_call uses, because a batch runner is autonomous where a single manual dial is a person's judgment. Everything in Consent and opt-outs and Disclosing the AI applies to every call, whoever placed it.
When a tool fails
A tool that cannot do what you asked returns { "error": "..." } inside a normal result, often with a detail field. That is different from a protocol error such as a missing key or a missing scope, which comes back as a JSON-RPC error with a real HTTP status. See Errors.
What is deliberately missing
There is no tool that returns a call transcript, a recording, or an audio URL, and there will not be one. Recorded conversations carry consent rules that vary by state, and the person on that call never agreed to a third-party AI vendor holding their words. LeadBind blocks these fields on the way out, not just by leaving them out of the tool list. See What we send and Call recording.
No tool exposes a stored payment detail, your own LeadBind subscription billing, or another account's data. request_payment creates a pay-link and never sees a card number, and every tool is scoped to the account that owns the key, or for an organization key to a seat inside that same organization.