Make your first call
Call your own phone, refresh its status, and inspect the returned call data.
Content reviewed · Maintained by Cleo Powered · Report a documentation issue · Verification basis
Verify your US phone before using the API. Muse and other execution-capable bots can register your account and obtain a key with your consent. You can also sign up in Cleo and create a key in Settings. Every call still requires your authorization. Learn about API access.
You will play the receptionist at fictional Maple Bike Repair. Cleo asks for Saturday hours and a tune-up price, then confirms what it heard. No booking or purchase is requested.
For a guided test on Windows, macOS, or Linux, you can use the complete Python script. The steps below explain the individual requests.
1. Get an API key
Sign in to Cleo → Settings → API and create a key. If New API key is unavailable, complete assistant setup first. Choose a key name and expiration. The full key is shown once.
Choose either cURL or Python for submission; running both with different idempotency keys places two calls. cURL examples use Bash (Git Bash or WSL on Windows) and Python 3 for UUID generation; they are not PowerShell syntax. For Python, skip this Bash setup and use the Python tab in step 2. For cURL, enter your secret API key and either reuse an existing request key or generate one for a new call:
# Bash (for Windows, use Git Bash or WSL)
read -rsp "Cleo API key: " CLEO_API_KEY; echo
export CLEO_API_KEY
read -rp "Existing idempotency key for retry (Enter for a NEW call): " IDEMPOTENCY_KEY
if [ -z "$IDEMPOTENCY_KEY" ]; then
IDEMPOTENCY_KEY="$(python -c 'import uuid; print(uuid.uuid4())')"
fi
export IDEMPOTENCY_KEY
printf "Idempotency key: %s\n" "$IDEMPOTENCY_KEY"This tests a real conversation, durable call-status tracking, and extraction of the answers you provide. A completed call can still have missing answers or a pending result.
2. Send a task
Download self-test.json or copy the JSON below into a file with that name. Replace to with your own number, including the country code. Save the file in the folder where you run the example. Keep it and the idempotency key unchanged when recovering an interrupted submission.
{
"to": "+15555550123",
"objective": "Run a short test call with me. I will pretend to be the receptionist at fictional Maple Bike Repair. Ask for Saturday opening hours and the price of a basic tune-up. Read both answers back, thank me, and end the call.",
"context": {
"test_call": true,
"recipient": "The account owner is calling their own phone for a test."
},
"constraints": [
"Introduce yourself as Cleo, an AI assistant, and explain that this is a test.",
"Do not make a booking or purchase.",
"Use only answers heard during the call; return null for missing information."
],
"success_criteria": [
"Confirm Saturday opening hours and the basic tune-up price."
],
"result_schema": {
"type": "object",
"properties": {
"saturday_hours": {
"type": [
"string",
"null"
]
},
"basic_tune_up_price": {
"type": [
"string",
"null"
]
}
},
"required": [
"saturday_hours",
"basic_tune_up_price"
],
"additionalProperties": false
},
"limits": {
"max_duration_seconds": 120
}
}context supplies background information; constraints sets boundaries; success_criteria describes what the conversation should accomplish. The required result_schema describes desired output, subject to the limitation above.
Submitting this request queues a real call once admission checks pass; dispatch rechecks permission before dialing. Keep your phone nearby, then run:
curl --fail-with-body https://api.cleolabs.com/v1/calls \
-H "Authorization: Bearer $CLEO_API_KEY" \
-H "User-Agent: CleoDocs/1.0" \
-H "Idempotency-Key: $IDEMPOTENCY_KEY" \
-H "Content-Type: application/json" \
--data-binary @self-test.jsonA successful submission returns 202 Accepted with call id, owning task_id, and execution. This confirms durable admission, not that dialing has started. Follow execution stage and call state.
{
"id": "a26d4a62-53b7-4d82-a10c-67dacb3a9816",
"task_id": "41c7973f-d16b-40b7-935d-a3790f7cbb6e",
"execution": {
"stage": "queued",
"error_code": null
},
"state": "queued",
"to": "+15555550123",
"objective": "Run a short test call with me. I will pretend to be the receptionist at fictional Maple Bike Repair. Ask for Saturday opening hours and the price of a basic tune-up. Read both answers back, thank me, and end the call.",
"caller_identity": {
"name": "Jordan Lee"
},
"summary": null,
"transcript": [],
"result": null,
"result_schema_valid": null,
"result_extraction": {
"status": "pending",
"missing_fields": [],
"evidence": [],
"issues": []
},
"created_at": "2026-09-17T14:32:08Z",
"started_at": null,
"completed_at": null
}3. Follow the call
Set CALL_ID to the UUID from your response. Use POST /sync every few seconds to refresh status. Stop polling when the state is completed, failed, or cancelled.
For cURL, keep the API key in the same terminal and set the returned call ID. The Python version prompts for the API key and call ID again. Each example below refreshes once; repeat while the call is active.
export CALL_ID="PASTE_THE_RETURNED_CALL_ID"
curl --fail-with-body -X POST \
"https://api.cleolabs.com/v1/calls/$CALL_ID/sync" \
-H "Authorization: Bearer $CLEO_API_KEY" \
-H "User-Agent: CleoDocs/1.0"Answer your phone and try: “Saturday, 10 AM to 4 PM. A basic tune-up is 45 dollars.” You can correct one answer to test how Cleo handles a change.
4. Read the result
When the call finishes, look at summary, result, and result_schema_valid. Check result_extraction for pending processing, missing fields, and evidence for the extracted hours and price. An illustrative response is:
{
"state": "completed",
"summary": "The recipient said Saturday hours are 10 AM to 4 PM and a basic tune-up is $45.",
"result": {
"saturday_hours": "10 AM to 4 PM",
"basic_tune_up_price": "$45"
},
"result_schema_valid": true,
"result_extraction": {
"status": "complete",
"missing_fields": [],
"evidence": [
{
"path": "/saturday_hours",
"sequence": 2,
"quote": "Saturday, 10 AM to 4 PM."
},
{
"path": "/basic_tune_up_price",
"sequence": 2,
"quote": "A basic tune-up is 45 dollars."
}
],
"issues": []
}
}This sample shows a complete extracted result. Your call may produce nulls, missing fields, or no publishable result. Schema validity and supporting quotes do not prove the real-world errand succeeded.
Try the complete Python script
The interactive self-call script prompts for a hidden API key and your number, confirms before dialing, follows the call, and prints the result. It uses Python 3.12+ with no additional packages.
python test_live_call.py
# Preview the task without an API key or network request:
python test_live_call.py --dry-run --phone +15555550123The script submits POST /v1/calls once, polls POST /v1/calls/{id}/sync, and uses POST /v1/calls/{id}/cancel if you request cancellation. To resume from a known ID without submitting another call:
python test_live_call.py --call-id PASTE_CALL_ID
# Request cancellation of that call:
python test_live_call.py --call-id PASTE_CALL_ID --cancelThe repository script prints recovery commands with a scripts/ prefix. When using the downloaded file, run python test_live_call.py from its folder instead.
Press Ctrl+C while waiting to request cancellation. The script requests a 120-second call limit. If submission is uncertain, rerun with its printed --idempotency-key and the same inputs. The script has a slightly more detailed version of the same bike-shop task; use the unchanged script when recovering its call.