API reference
Base URL /v1. Auth: Authorization: Bearer eppie_agent_sk_… or an OAuth access token. Every operation is also an MCP tool at /mcp (except sign-in and API-key management). Machine-readable: /openapi.json.
POST /v1/auth/email-code
Email a 6-digit sign-in code. Works for new and existing accounts. Read the code from the inbox (or ask your human), then call POST /v1/auth/verify.
Tool: (REST only) · no auth
POST /v1/auth/verify
Exchange the emailed code for an API key (eppie_agent_sk_…). Creates the account on first use. The key is shown once.
Tool: (REST only) · no auth
GET /v1/account
Get the signed-in account: email, trust tier and current limits.
Tool: get_account
GET /v1/api-keys
List active API keys (name and prefix only; full keys are shown once at creation).
Tool: (REST only)
DELETE /v1/api-keys/{id}
Revoke an API key. Requests with it fail with 401 from then on.
Tool: (REST only)
[
{
"name": "id",
"in": "path",
"required": true,
"schema": {
"type": "string"
}
}
]GET /v1/wallet
Get the prepaid balance (USD), trust tier and limits. Calls cost $0.12/min (per second); numbers $3.00/month.
Tool: get_wallet
GET /v1/wallet/ledger
List wallet movements (top-ups, call charges, number rentals, refunds), newest first.
Tool: list_ledger
[
{
"name": "cursor",
"in": "query",
"required": false,
"schema": {
"type": "string"
}
},
{
"name": "limit",
"in": "query",
"required": false,
"schema": {
"default": 20,
"type": "integer",
"minimum": 1,
"maximum": 100
}
}
]POST /v1/wallet/topups
Add $10–$500 of credit. With shared_payment_token (a Stripe shared payment token from your wallet, e.g. Link) the charge happens now; without it you get a checkout_url to pay, then poll GET /v1/wallet/topups/{id}.
Tool: top_up · Idempotency-Key
[
{
"name": "Idempotency-Key",
"in": "header",
"required": false,
"description": "Retry-safe key (≤ 200 chars). Same key + same body returns the original response.",
"schema": {
"type": "string",
"maxLength": 200
}
}
]GET /v1/wallet/topups/{id}
Get a top-up: pending (checkout not paid yet), succeeded, failed or expired.
Tool: get_top_up
[
{
"name": "id",
"in": "path",
"required": true,
"schema": {
"type": "string"
}
}
]GET /v1/voices
List the gpt-live-1 voices an agent can use, each with a short audio sample URL.
Tool: list_voices · no auth
POST /v1/agents
Create a voice agent: what it should do on calls (instructions), an optional first line (opener), a voice and a max call length. Callers always hear a fixed AI disclosure first. instructions and opener can use {{caller_number}} (the other party), {{local_time}}, {{agent_name}} and {{direction}} (inbound or outbound); batch targets can add their own {{variables}}. The opener is used on calls the agent answers; batches set their own.
Tool: create_agent
GET /v1/agents
List your voice agents.
Tool: list_agents
GET /v1/agents/{id}
Get one voice agent.
Tool: get_agent
[
{
"name": "id",
"in": "path",
"required": true,
"schema": {
"type": "string"
}
}
]PATCH /v1/agents/{id}
Change any of an agent's fields. Calls already in progress keep the old settings. instructions and opener can use {{caller_number}} (the other party), {{local_time}}, {{agent_name}} and {{direction}} (inbound or outbound); batch targets can add their own {{variables}}. The opener is used on calls the agent answers; batches set their own.
Tool: update_agent
[
{
"name": "id",
"in": "path",
"required": true,
"schema": {
"type": "string"
}
}
]DELETE /v1/agents/{id}
Delete a voice agent. Numbers assigned to it stop answering until you assign another agent, and its unfinished batches are canceled.
Tool: delete_agent
[
{
"name": "id",
"in": "path",
"required": true,
"schema": {
"type": "string"
}
}
]GET /v1/numbers/available
Search US local phone numbers you can buy, optionally by area code or digits they contain.
Tool: search_numbers
[
{
"name": "area_code",
"in": "query",
"required": false,
"schema": {
"type": "string",
"pattern": "^[2-9]\\d{2}$"
}
},
{
"name": "contains",
"in": "query",
"required": false,
"schema": {
"type": "string",
"pattern": "^[0-9*]{1,10}$"
}
},
{
"name": "limit",
"in": "query",
"required": false,
"schema": {
"default": 10,
"type": "integer",
"minimum": 1,
"maximum": 20
}
}
]POST /v1/numbers
Buy a US number for $3.00/month, charged now from the wallet and monthly after. Pass agent_id to have that agent answer its calls. Not refunded when released.
Tool: buy_number · Idempotency-Key
[
{
"name": "Idempotency-Key",
"in": "header",
"required": false,
"description": "Retry-safe key (≤ 200 chars). Same key + same body returns the original response.",
"schema": {
"type": "string",
"maxLength": 200
}
}
]GET /v1/numbers
List your numbers (active and in grace), with the agent that answers each.
Tool: list_numbers
GET /v1/numbers/{id}
Get one number, including its renewal date and grace status.
Tool: get_number
[
{
"name": "id",
"in": "path",
"required": true,
"schema": {
"type": "string"
}
}
]PATCH /v1/numbers/{id}
Assign the agent that answers this number (null = stop answering), or change its label.
Tool: update_number
[
{
"name": "id",
"in": "path",
"required": true,
"schema": {
"type": "string"
}
}
]DELETE /v1/numbers/{id}
Release a number back to the carrier. Immediate; the current month is not refunded.
Tool: release_number
[
{
"name": "id",
"in": "path",
"required": true,
"schema": {
"type": "string"
}
}
]GET /v1/calls
List calls, newest first: inbound calls to your numbers (including rejected ones; reject_reason says why) and outbound calls from batches. Filter by direction, batch, agent, number or status.
Tool: list_calls
[
{
"name": "direction",
"in": "query",
"required": false,
"schema": {
"type": "string",
"enum": [
"inbound",
"outbound"
]
}
},
{
"name": "batch_id",
"in": "query",
"required": false,
"schema": {
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
}
},
{
"name": "agent_id",
"in": "query",
"required": false,
"schema": {
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
}
},
{
"name": "number_id",
"in": "query",
"required": false,
"schema": {
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
}
},
{
"name": "status",
"in": "query",
"required": false,
"schema": {
"type": "string",
"enum": [
"queued",
"dialing",
"pending",
"in_progress",
"completed",
"failed",
"rejected",
"no_answer",
"busy",
"machine",
"canceled",
"skipped_dnc"
]
}
},
{
"name": "cursor",
"in": "query",
"required": false,
"schema": {
"type": "string"
}
},
{
"name": "limit",
"in": "query",
"required": false,
"schema": {
"default": 20,
"type": "integer",
"minimum": 1,
"maximum": 100
}
}
]GET /v1/calls/{id}
Get one call: status, how it ended, duration, cost, the transcript (null until it ends) and, for batch calls, the extracted outcome.
Tool: get_call
[
{
"name": "id",
"in": "path",
"required": true,
"schema": {
"type": "string"
}
}
]POST /v1/batches
Call up to 1,000 US numbers with one of your voice agents (agent_id). Each target can carry variables for {{name}}-style fields in the agent's instructions; context adds a note for this batch, opener is said right after the AI disclosure, outcome_schema (JSON Schema) gets a structured result per call. Every number must have agreed to be called: say how in consent.source. Calls go out 08:00–21:00 recipient time, skip your do-not-call list and hang up on voicemail. $0.12/min, charged per second, only for calls a person answers. Follow with GET /v1/batches/{id} and GET /v1/calls?batch_id=, or pass webhook_url.
Tool: create_batch · Idempotency-Key
[
{
"name": "Idempotency-Key",
"in": "header",
"required": false,
"description": "Retry-safe key (≤ 200 chars). Same key + same body returns the original response.",
"schema": {
"type": "string",
"maxLength": 200
}
}
]POST /v1/calls
Call one US number with one of your voice agents (agent_id); variables fill {{name}}-style fields in its instructions. Every number must have agreed to be called: say how in consent.source. Calls go out 08:00–21:00 recipient time, skip your do-not-call list and hang up on voicemail. $0.12/min, charged per second, only for calls a person answers. Follow with GET /v1/calls/{id}.
Tool: place_call · Idempotency-Key
[
{
"name": "Idempotency-Key",
"in": "header",
"required": false,
"description": "Retry-safe key (≤ 200 chars). Same key + same body returns the original response.",
"schema": {
"type": "string",
"maxLength": 200
}
}
]GET /v1/batches
List your batches, newest first, each with counts of its calls by status.
Tool: list_batches
[
{
"name": "status",
"in": "query",
"required": false,
"schema": {
"type": "string",
"enum": [
"running",
"paused",
"completed",
"canceled"
]
}
},
{
"name": "cursor",
"in": "query",
"required": false,
"schema": {
"type": "string"
}
},
{
"name": "limit",
"in": "query",
"required": false,
"schema": {
"default": 20,
"type": "integer",
"minimum": 1,
"maximum": 100
}
}
]GET /v1/batches/{id}
Get a batch: status (running, paused, completed, canceled), paused_reason and counts of its calls by status. Per-call results: GET /v1/calls?batch_id=.
Tool: get_batch
[
{
"name": "id",
"in": "path",
"required": true,
"schema": {
"type": "string"
}
}
]POST /v1/batches/{id}/pause
Pause a batch. Calls already ringing or connected finish; queued calls wait.
Tool: pause_batch
[
{
"name": "id",
"in": "path",
"required": true,
"schema": {
"type": "string"
}
}
]POST /v1/batches/{id}/resume
Resume a paused batch. A batch paused for a low balance resumes by itself after a top-up.
Tool: resume_batch
[
{
"name": "id",
"in": "path",
"required": true,
"schema": {
"type": "string"
}
}
]POST /v1/batches/{id}/cancel
Cancel a batch: its queued calls are canceled; calls already ringing or connected finish.
Tool: cancel_batch
[
{
"name": "id",
"in": "path",
"required": true,
"schema": {
"type": "string"
}
}
]GET /v1/do-not-call
List the numbers this account never calls. source is api (you added it) or recipient_opt_out (the person asked on a call; permanent).
Tool: list_do_not_call
[
{
"name": "cursor",
"in": "query",
"required": false,
"schema": {
"type": "string"
}
},
{
"name": "limit",
"in": "query",
"required": false,
"schema": {
"default": 50,
"type": "integer",
"minimum": 1,
"maximum": 100
}
}
]POST /v1/do-not-call
Add up to 1,000 US numbers this account must never call. Queued calls to them are skipped (status skipped_dnc).
Tool: add_do_not_call
DELETE /v1/do-not-call/{e164}
Remove a number you added to the do-not-call list. Numbers whose owner asked not to be called stay.
Tool: remove_do_not_call
[
{
"name": "e164",
"in": "path",
"required": true,
"schema": {
"type": "string",
"pattern": "^\\+1[2-9]\\d{9}$"
}
}
]GET /v1/webhook-secret
Get the secret that signs your webhooks. Each delivery has x-eppie-signature: t={unix},v1={hex HMAC-SHA256(secret, t + "." + raw body)}.
Tool: get_webhook_secret
POST /v1/webhook-secret/rotate
Replace the webhook signing secret. Deliveries are signed with the new one from now on.
Tool: rotate_webhook_secret