Skip to content
cleo.Developers
Get started/Make your first call
Cleo documentation

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.

Your first test calls your own phone

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
# 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"
What this test verifies

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.

self-test.json
{
  "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.json

A 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.

Example acceptance · 202
{
  "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:

JSON
{
  "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": []
  }
}
Results depend on the conversation

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.

Terminal
python test_live_call.py

# Preview the task without an API key or network request:
python test_live_call.py --dry-run --phone +15555550123

The 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:

Terminal
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 --cancel

The 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.

Documentation
Get an API key

Explore the docs

IntroductionSubmit a phone errand, follow the call, and inspect its outcome.Bot account onboardingRegister from Muse or another bot, verify your phone, receive an API key, and set your dashboard password by email.API overviewWhich endpoints are available, how they authenticate, and what is supported today.Make your first callCall your own phone, refresh its status, and inspect the returned call data.AuthenticationCreate a key in Cleo and authenticate requests with a bearer token.Create a callQueue one bounded phone task and receive durable task and call IDs.Get a callRead the most recently saved state of a call.Sync a callRefresh a call from the calling service and retrieve its latest saved state.Cancel a callCancel queued work or request a stop, then follow confirmation.The call objectThe shared response returned by create, get, sync, and cancel.Health checksCheck API liveness and readiness without an API key or placing a call.Results & schemasUnderstand result_schema, returned metadata, and what result_schema_valid does and does not establish.Task and call lifecycleFollow a call from acceptance to its final outcome.Idempotency & retriesRecover from an interrupted request without accidentally placing a second call.Safety & permissionsHow Cleo evaluates a call task before it can dial.Errors & troubleshootingUnderstand API errors and choose a safe next step.LimitsKeep call tasks bounded and leave room for API rate limits.Verification & limitationsReproduce our public health and schema checks and understand what has not been tested.Bot integrationsChoose a route for connecting your assistant to Cleo's public API.ChatGPTConfigure a private GPT Action with Cleo OpenAPI and bearer authentication.ClaudeConnect Claude client tools or a Claude Code shell workflow.Grok & Grok BotConnect xAI function tools and check Grok Bot execution requirements.OpenClawInstall a local skill for an approved Cleo calling workflow.GeminiMap Gemini function declarations to Cleo REST operations.Copilot StudioAdd Cleo through a REST API tool, custom connector, or flow.n8nBuild an authenticated HTTP workflow with durable call recovery.ZapierBuild a private Zapier integration for approved Cleo calls.