> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.agentphone.ai/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.agentphone.ai/_mcp/server.

# API quickstart

> Create an agent and number, receive your first text, and understand what is needed to send a reply.

This walkthrough creates a US SMS/voice number and verifies an inbound text. Use your own mobile phone for the test. For iMessage, follow [Set up iMessage](/imessage) instead.

## Before you start

You need an [AgentPhone account](https://agentphone.ai), an API key from [Settings](https://agentphone.ai/settings), `curl`, and `jq`.

New accounts receive \$5 in credit. A standard US/Canadian number costs \$3 for its first month, charged when provisioned, and usage also draws from the balance. See [Billing](/usage) for other number types and account-specific rates.

Inbound SMS does not require 10DLC registration. Outbound SMS needs [registration and number activation](/sms-registration). Outbound phone calls require the account to satisfy the paid-access check; if you receive `402`, follow its message and add funds in Billing. Signup credit alone does not unlock outbound calling.

```bash
export AGENTPHONE_API_KEY="YOUR_API_KEY"
export AGENTPHONE_BASE_URL="https://api.agentphone.ai"
```

These commands target your main account. If using a sub-account, add `X-Sub-Account-Id` consistently to each command; see [Authentication](/authentication).

## 1. Create an agent

Use hosted mode for a voice assistant that can answer calls without a voice webhook. Text replies will still be handled by your application.

```bash
AGENT_ID=$(curl --fail-with-body --silent --show-error \
  "$AGENTPHONE_BASE_URL/v1/agents" \
  -H "Authorization: Bearer $AGENTPHONE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"name":"Support Bot","voiceMode":"hosted","systemPrompt":"You are a helpful support assistant.","beginMessage":"Hello! How can I help?"}' | jq -er '.id')
```

Save the returned ID. If the command fails, fix the reported error before continuing.

## 2. Buy and attach a number

This command charges the first month's number fee. `agentId` attaches the new number in the same request.

```bash
NUMBER_JSON=$(curl --fail-with-body --silent --show-error \
  "$AGENTPHONE_BASE_URL/v1/numbers" \
  -H "Authorization: Bearer $AGENTPHONE_API_KEY" \
  -H "Content-Type: application/json" \
  -d "$(jq -n --arg agentId "$AGENT_ID" '{country:"US",agentId:$agentId}')")
NUMBER_ID=$(printf '%s' "$NUMBER_JSON" | jq -er '.id')
printf '%s' "$NUMBER_JSON" | jq '{id, phoneNumber, agentId, outboundSms}'
```

Keep both `id` (used in API paths) and `phoneNumber` (the address people text or call). If you already own a number, attach it with `POST /v1/agents/{agent_id}/numbers` instead of buying another.

## 3. Receive a text

Text the returned `phoneNumber` from your mobile, then fetch its messages:

```bash
curl --fail-with-body "$AGENTPHONE_BASE_URL/v1/numbers/$NUMBER_ID/messages?limit=10" \
  -H "Authorization: Bearer $AGENTPHONE_API_KEY"
```

Look for your text in `data`, with `direction: "inbound"`. If it is missing, check that you used the right phone number and account scope. This proves inbound messaging before you add webhook processing.

## 4. Connect a webhook

Run a [signed webhook handler](/documentation/reference/code-examples#expressjs-webhook-handler) on a public HTTPS URL. For local development, follow [Testing](/documentation/reference/testing).

```bash
curl --fail-with-body "$AGENTPHONE_BASE_URL/v1/webhooks" \
  -H "Authorization: Bearer $AGENTPHONE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"url":"https://your-server.example/webhook","contextLimit":10}'
```

Save the response's `secret` as `AGENTPHONE_WEBHOOK_SECRET` on your server, then restart the handler. Each save can change this secret.

```bash
curl --fail-with-body -X POST "$AGENTPHONE_BASE_URL/v1/webhooks/test" \
  -H "Authorization: Bearer $AGENTPHONE_API_KEY"
```

Check for `success: true` and a `2xx` `httpStatus`. Your handler receives a signed synthetic `agent.message` with `test: true`; it should acknowledge the event without sending a text. This tests delivery to your server, not carrier delivery or voice responses. Send another real inbound text to verify the full messaging path.

## 5. Send a reply when SMS is enabled

Check `GET /v1/numbers/{number_id}`. Wait for `outboundSms: "enabled"`; `registration_required` needs registration, and `activating` needs carrier attachment to finish.

Set the recipient to your own mobile number in E.164 format:

```bash
export TEST_RECIPIENT="+14155550123"
curl --fail-with-body "$AGENTPHONE_BASE_URL/v1/messages" \
  -H "Authorization: Bearer $AGENTPHONE_API_KEY" \
  -H "Content-Type: application/json" \
  -d "$(jq -n --arg numberId "$NUMBER_ID" --arg to "$TEST_RECIPIENT" \
    '{number_id:$numberId,to_number:$to,body:"Hello from my AgentPhone integration."}')"
```

A successful send is acceptance, not proof of delivery. Read message history for `status` and `failureReason`. A timeout or `DELIVERY_UNCONFIRMED` can mean the send already happened; [check the outcome before resending](/documentation/reference/error-handling).

Returning `{"text":"…"}` to a text webhook does **not** send this reply. Your backend must call `POST /v1/messages`. Voice webhooks have a separate synchronous response contract.

## Next steps and cleanup

* [Messages](/documentation/guides/messages): media, reactions, groups, and conversation state.
* [Calls](/documentation/guides/calls): hosted voice, custom voice webhooks, and browser calls.
* [Troubleshooting](/troubleshooting): registration, delivery, account scope, and billing problems.

Numbers renew until released. If you no longer need the test number, release it from Numbers in the dashboard or with `DELETE /v1/numbers/{number_id}`. Release is irreversible; deleting the agent alone does not release its numbers or stop their fees.