# Give yourself a CallMe number

You are a coding agent setting up CallMe — phone numbers for AI agents — on behalf of
your user. Follow this exactly; everything here uses the public REST API, and every step
is safe to run with a test key first.

## Ground rules

- **Never place a real call or send a real text without your user's explicit instruction
  for that call or text.** A test key (`cm_test_…`) acts on a sandbox tenant with fake
  numbers and fake sends — do the whole journey there first.
- The API key is shown **once** at creation. Store it in the user's secret store, never in
  source code, logs or chat output.
- Calls go **only** to the account's verified destination number — the destination is
  resolved server-side from the account. Do not look for a "to" parameter on outbound
  calls; there isn't one, by design.

## The sandbox

A test key acts on a sandbox tenant.
Sandbox calls, messages, recordings, delivery logs and the sandbox wallet balance are ephemeral and per instance;
sandbox configuration (numbers, webhook, agents, registrations) persists.
Do not rely on a sandbox call or message surviving a restart or being visible from another
instance; recreate them in each run. Because a test key never carries `admin`, it uses the
resource's own scope wherever it configures the sandbox (the steps below name both scopes); a
route that is live-only answers a test key 409 `sandbox_unsupported`.

A sandbox number has no carrier, so nothing outside can ring or text it: a test key plays the
caller and the provider instead. With the `calls` scope, `POST /v1/sandbox/inbound` rings a sandbox
number (`number_id`, and the E.164 caller in `from`) and the same prefix carries a voicemail,
utterances and a hangup for that call; with the `messages` scope, `POST /v1/sandbox/inbound-sms`
delivers an inbound text and `POST /v1/sandbox/messages/{message_id}/status` reports a delivery
state for one of your sandbox sends. Only a test key may use them: a live key gets 409
`sandbox_only`.

## Phone numbers in responses

Your own purchased numbers are shown in full: in `GET /v1/numbers`, as the account's
`caller_number`, and in the events delivered to your own signed webhook (caller ID included).
Third-party and destination numbers are masked to the last four digits in the views the API
serves (`…1234`): your verified destination (`destination_last4`) and the other party on a call
or text, in call views, message views and the verification inbox. Message views and inbound call
views carry your own number in full and mask only the other party (`number_id` says which of your
numbers it was): on an inbound call the caller is masked and the line that rang is whole, and a
call you placed carries no numbers. Message views are the list, reading one message
(`GET /v1/messages/{message_id}`), the answer to sending one and a conversation's messages; a
conversation (`GET /v1/threads`) names its remote party by its last four digits. The MCP tools
mask the other party the same way and show your own number whole; an agent's transfer numbers are dial
destinations and are masked the same way (see Agent transfer numbers below).

## Setup

1. Base URL: `https://api.callme.olorin.ai`. Authenticate every request with
   `Authorization: Bearer <key>`.
2. Read the account: `GET /v1/account` (scope `usage`). Confirm `sandbox: true` when you
   are on a test key.
3. The account's first number is provisioned when its plan starts. To buy another:
   `POST /v1/numbers` (scope `numbers`) with `{"country": "US", "area_code": "201"}` —
   the area code is tried first, and when it has none the number comes from elsewhere in
   the country, so check the number you got. At most 5 numbers per account.

## Place a call

`POST /v1/calls` (scope `calls`):

```json
{
  "request_key": "agent-setup-1",
  "question": "Hi, this is a test call from your agent. Can you hear me clearly?",
  "caller_app": "<your app name>",
  "caller_session": "<your session id>"
}
```

- `question` is the opening line, at most 300 characters.
- `request_key` is the call's identity: retrying with the same key returns the same call
  (200), never a second dial (201 the first time). Never mint a fresh key to retry an
  uncertain outcome — read the call first with `GET /v1/calls/{call_key}`.
- Refusals are typed: a 409 carries `{"error": {"code", "message"}}` — e.g. outside the
  call window, over the daily cap, account held. Report the reason to your user; do not
  improvise around it.

## Answer inbound

Route the number: `PUT /v1/numbers/{number_id}` (scope `admin` on a live key, `numbers` on a
test key) with `{"inbound_mode": "webhook"}` (or `"hosted"` for a configured agent,
`"voicemail"`, `"reject"`), then set your webhook: `PUT /v1/webhooks` (scope `admin` on a live
key for the live webhook; `calls` on a test key, which sets its own sandbox webhook) with your
URL. The signing secret is returned **once** — store it.

- Events arrive in one envelope: `{"id", "type", "account_id", "created_at", "data"}`.
- Verify every delivery: header `X-CallMe-Signature: t=<unix seconds>,v1=<hex>[,v1=<hex>]`,
  where each `v1` is the HMAC-SHA256 hex of `<t>.<the raw body>` under a signing secret.
  Accept only when `t` is within 300 seconds of your clock and any `v1` matches any secret
  you hold (a rotation keeps the old secret valid for a grace window and sends two `v1`
  values). SDK helpers (`verify_webhook_signature` in the Python package olorin_callme,
  `verifyWebhookSignature` from `@olorin/callme/webhooks` in TypeScript) do all of this; never skip it.
- For a webhook-routed call, each utterance arrives as a synchronous `call.turn` event:
  reply within 8 seconds with exactly `{"reply_text": "…"}`. Any other shape counts as a
  failed delivery. Five consecutive failures pause the webhook and calls fall back to
  voicemail.

## Agent transfer numbers

A hosted agent's `transfer` tool may hand a call only to the numbers in the agent's `transfer_numbers`
(at most 10 entries). These transfer numbers are dial destinations, so no answer ever shows one whole:
every agent view serves each as an entry `{"id": "tn_…", "number": "…1234"}`, the number masked to its
last four digits and the id stable. A write names the roster whole, entry by entry: an entry with an id
keeps the stored entry (send the id alone, or the entry exactly as the agent showed it, so you can read
an agent, change it and write it back without ever holding a full number), a full number is a new entry,
and an entry you leave out is removed; `[]` clears the roster. Over REST, changing the roster needs the
`admin` scope on a live key (sending every entry back by id in its served order needs only `agents`); a
key without `admin` that sends a full number gets 403 `insufficient_scope`, whether or not that number is
already on the agent. A test key cannot set transfer numbers (a sandbox agent has no live call to
transfer): a roster with entries is 409 `sandbox_unsupported`, and only clearing it is accepted.

## Message

`POST /v1/messages` (scope `messages`) with `{"to": "+1…", "body": "…"}`. US texting
requires an approved 10DLC campaign (`/v1/registration/…`, scope `admin` on a live key,
`messages` on a test key); a `STOP` from
a recipient suppresses them account-wide automatically. Sign-in codes texted to your
numbers surface at `GET /v1/verification-codes`.

## Money

Plans include minutes (40 / 120 / 400 per period); beyond them, voice is 25¢/min with
overage enabled, or the prepaid wallet add-on pays past the plan: 25¢/min past the plan's minutes
and 2¢/SMS-segment past its daily texts. The wallet needs a plan (`409 plan_required` without one;
reading it needs none). Read `GET /v1/usage` before big jobs (`period.seconds_left` stops at 0 and
`period.wallet_pays_past_plan` says the wallet pays past it) and `GET /v1/wallet/ledger` to reconcile.

## Reference

Full REST reference: https://callme.olorin.ai/docs-rest · MCP setup: https://callme.olorin.ai/docs-mcp ·
Webhooks: https://callme.olorin.ai/webhooks · Trust & compliance: https://callme.olorin.ai/trust
