# Muse and similar bots: connect a user to Cleo

Muse, follow these onboarding instructions when your user asks to connect Cleo.
The same steps apply to other assistants with HTTP tools and secure credential
storage. Reading this file does not install a tool or give a bot those abilities.

API origin: https://api.cleolabs.com
Send `User-Agent: CleoBot/1.0` on every HTTP request. A descriptive client name
is supported; do not impersonate a browser. The default Python urllib user agent
can be rejected by the edge with HTTP 403 / error 1010 before reaching the API.
If that occurs, send this explicit User-Agent and retry the identical request
once, preserving the signup nonce or call idempotency key. Do not bypass a
challenge, weaken TLS verification, or repeatedly retry an edge block.
Reference: https://docs.cleopowered.com/onboarding/
Contract: https://docs.cleopowered.com/openapi.json
Dashboard: https://app.cleopowered.com

## 1. Check the bot's execution capabilities

Use your integration's HTTP executor and a credential store isolated per user.
Never put an API key, onboarding token, SMS code, or password in ordinary logs,
public messages, shared memory, or a URL. Use a private conversation and secure
code entry where your runtime provides it. Discard verification codes after use.
If you cannot execute HTTP or keep credentials private, stop and direct the user
to https://app.cleopowered.com/signup and the integration guides. Never claim a
native Muse partnership or a completed connection without a successful response.

## 2. Ask for the required account fields

Ask: "To connect Cleo, what is your name, email address, and US mobile phone
number? Organization is optional; you can skip it."

Collect `name`, `email`, and `phone_number`. Normalize the phone to E.164 with a
leading country code, for example +12015550123. Ask for clarification if the
country is unclear. Do not invent an email, name, phone, or organization.
`organization` can be omitted, null, or blank; the workspace name defaults to
the user's name. `client_name` is optional; use "Muse" for Muse or your bot's
actual name. Do not ask for or generate the user's dashboard password.

Explain: "Cleo will text you a verification code to connect this bot to your
account. You agree to Cleo's responsible-use terms: only authorized, lawful
calls; no fraud, impersonation, harassment, spam or emergency-service misuse.
You remain responsible for calls, and Cleo can block prohibited requests.
Do you agree and want Cleo to send the verification text?"
Terms: https://cleopowered.com/responsible-calling and
https://cleopowered.com/sms-terms.
Only send `acceptable_use_accepted: true` after the user's explicit agreement.

## 3. Start registration and send the SMS

Generate 32 cryptographically random bytes, encode as base64url without padding
(43 characters), and persist this secret as the signup `Idempotency-Key`.
For example, an executor can use Python `secrets.token_urlsafe(32)`.
Unlike a per-call idempotency identifier, this signup nonce is a credential.
Keep it private. Use the same nonce and identical signup body for a retry.

POST /v1/onboarding/start
Headers: Content-Type: application/json; Idempotency-Key: <SECRET_SIGNUP_NONCE>
No API key is needed for this endpoint.

```json
{
  "name": "Mina Patel",
  "email": "mina@example.com",
  "phone_number": "+12015550123",
  "client_name": "Muse",
  "acceptable_use_accepted": true
}
```

HTTP 200 returns `onboarding_token`, `expires_at`, and `phone` containing
`verified`, `challenge_id`, `expires_at`, and `resend_at`. Save the onboarding
token and challenge ID privately. The token can only complete onboarding and
expires in 30 minutes; it is not a dashboard session or a calling API key.
If `phone.verified` is already true on a retry, proceed to step 5.
Otherwise ask: "Enter the six-digit code Cleo texted to your phone."

An identical start retry does not send another SMS. If delivery failed, retry
start with the same nonce to recover the session, then use step 4's resend.
If a pending onboarding token expires, repeat start with its original nonce and
unchanged body to resume; the bot must still verify a valid SMS code. If the
secret was lost, direct the user to the login page's Forgot password flow.

## 4. Submit the code, or resend at the user's request

POST /v1/onboarding/verify
Headers: Content-Type: application/json; Authorization: Bearer <ONBOARDING_TOKEN>

```json
{"challenge_id": "<CHALLENGE_UUID>", "code": "<SIX_DIGIT_CODE>"}
```

Keep leading zeros. Submit the user's actual code, never guess it. Proceed only
when the response says `verified: true`. Codes expire in 10 minutes and allow
five incorrect attempts. `phone_code_incorrect` means ask the user to check the
code. `phone_code_expired` means offer a resend.

Only when the user requests a new code, POST /v1/onboarding/resend with
Authorization: Bearer <ONBOARDING_TOKEN> and no request body. Honor `resend_at`
and any Retry-After header. Replace the saved challenge ID with the returned
one; the old code no longer works. Never continuously send verification texts.

## 5. Get the API key and explain dashboard access

POST /v1/onboarding/complete
Header: Authorization: Bearer <ONBOARDING_TOKEN>
No request body is required.

HTTP 200 returns `api_key`, `project_id`, `workspace_id`, `login_url`,
`recharge_url`, and `password_setup_email: "requested"`.
Store the key in the runtime's secure credential store for this user and Cleo
project. Only send it to https://api.cleolabs.com using Authorization: Bearer
<API_KEY>. Do not display it in the conversation. Completion retries during the
onboarding token's lifetime return the same active key and do not create another.
Never retry to resurrect a revoked key. Delete the onboarding nonce/token after
the API key is safely stored.

Say: "Cleo is connected. Cleo has requested a password-setup email for your
dashboard access; open its link to create your password. You can use Cleo here
without waiting for that step. What phone errand would you like handled?"
Do not claim email delivery is confirmed. The email link is single-use, tied to
the account and its email, and expires. If missing or expired, use Forgot
password at `login_url` to request a replacement. Never request that link or
password in chat. Email verification is not an onboarding gate.

## 6. Collect the facts for a call and get approval

Ask who to call and their destination phone number (`to`); what the user wants
done (`objective`); necessary facts (`context`); what Cleo may or must not do
(`constraints`); how to tell the task succeeded (`success_criteria`); and the
maximum duration (`limits.max_duration_seconds`). Distinguish the destination
from the user's verified account phone. If a number is missing, ask for it or
have the user find the business in Cleo; no public contact-search API is promised.
The executor supplies a valid JSON `result_schema`; the user need not write one.
Cleo attempts schema-driven extraction from saved recipient evidence. Explain
missing fields and incomplete or unavailable results honestly; schema validity
is not proof that the recipient completed an action. Inspect result_extraction.

Fundraising, investment discussions, and investor follow-up are supported.
Purpose screening blocks pranks/nuisance and concrete safety threats; it does
not require proof of a relationship just because a call involves fundraising.
The user remains responsible for lawful calling and any required consent.
If the API returns 422 `call_purpose_blocked`, explain its actual concern and
stop automatic retries. Never invent facts to get a request approved.

Show the recipient, objective, important facts, permissions and duration, then
ask the user to approve that specific call. Signup consent is not approval to
call a recipient. Default to at most 180 seconds for a Free/prepaid account;
Pro can allow up to 600 seconds. Never silently increase a duration or purchase.

Use optional `protected_context` for private answers that must be withheld from
the conversation. It accepts at most 20 string values, with lowercase field names
matching `[a-z][a-z0-9_]{0,63}`. Each value contains 1–2,000 characters; combined
UTF-8 value bytes must fit 16 KiB. Cleo stores them encrypted and redacts matching
values from ordinary task/context and execution snapshots. The current voice flow
does not automatically disclose them to recipients. Do not collect passwords,
card PINs, or one-time codes for this flow. Ordinary context can reach the calling
model. Protect the original request in your own storage and keep values out of logs.

Persist the complete approved payload, including protected_context, and a separate
per-call Idempotency-Key before POST /v1/calls. A retry must use the same key and payload; a different task needs
new approval and a new key. HTTP 202 confirms durable queued admission, not that
dialing has started or the errand succeeded. Save returned `id` and `task_id`;
use `id` in the existing call routes. Follow `execution.stage` and
`execution.error_code` alongside call state. Execution stages are queued,
submitting, reconciling, running, cancel_requested, cancelled, failed, completed.
Historical calls can have execution null. Reconciliation uncertainty must never
cause a fresh task, call, or idempotency key. Read saved progress with
GET /v1/calls/{call_id}, or refresh with POST /v1/calls/{call_id}/sync every few
seconds while active. Stop polling at completed, failed, or cancelled. A
completed call is not proof the requested errand succeeded; explain the returned
summary, unanswered questions and next action accurately. If result extraction
is pending after a terminal call, read or sync the same call later within a bounded
budget; never create another call just to obtain a result.

On user cancellation, POST /v1/calls/{call_id}/cancel with the saved ID. Queued
work is cancelled locally before dispatch. Once dispatch may have started,
`cancel_requested` remains pending until the gateway confirms a terminal outcome.
A 200 response or a cancellation timeout is not proof of hangup. Keep following
the same call. If execution.error_code is `reconciliation_required`, automatic
recovery has paused for operator review: retain the ID and contact the operator,
without repeatedly submitting a new cancel or replacement call.

## 7. Handle credits and errors without taking payment

HTTP 402 `insufficient_credits`: tell the user "You're out of Cleo calling
credits and have used your included calls. Log in to Cleo and recharge your
account to continue." Show the error's `action_url`, normally
https://app.cleopowered.com/billing.

HTTP 402 `insufficient_credits_for_call`: explain the remaining and required
prepaid minutes in `detail`. Offer the billing link or a shorter call limit
with the user's approval. Do not say the balance is zero when some remains.

These errors occur before dialing; the rejected request does not charge call
credits. `retryable: false` means stop automatic retries. Ask the user to log in
and recharge; do not collect card details, initiate checkout, or make purchases.
After the user says they recharged, retry the original approved request with
its original call idempotency key. The API will check the balance again.

`account_login_required` (409): this email already has an account. Ask the user
to log in to Cleo (Forgot password can create their password if needed), verify
their phone if requested, and create a bot key in Settings using the runtime's
secure credential input. Do not reset their password or issue a key based only
on knowing their email. `invalid_onboarding_token` (401): resume a pending
signup with its saved start nonce, or direct an existing user to login.
`onboarding_key_revoked` (409): use dashboard Settings to create a replacement.
429: wait for Retry-After; never hammer the endpoint. 503: explain temporary
unavailability and offer a later retry. If a call submission times out, recover
with the same call idempotency key; never submit a new call just to check status.
