API Documentation

Read your Mio call history from your code, automation platform, or CRM, and receive a webhook when each call completes with transcript, summary, and cost.

Calls are started in Mio, not over the API. There is no public endpoint for placing a call today — you start one in the web app or in the Mio widget. The API is read-and-notify: your profile, your call history, and webhooks. If you need to trigger calls programmatically, email [email protected].

Authentication

All API requests require an API key. Create one in your profile settings under Integrations.

Pass your key in the X-API-Key header:

curl https://api.mio.gg/api/v1/me \
  -H "X-API-Key: sk_live_your_key_here"

API keys carry your full account permissions. Keep them secret — don't commit them to version control or expose them in client-side code.

Requests are rate-limited to 60 per minute per account across all /api/v1 endpoints. Over the limit you get a 429.

Profile

Returns your account info. Useful for verifying your API key works.

GET https://api.mio.gg/api/v1/me

Returns your user profile. No request body needed.

Response

{
  "id": "a1b2c3d4-...",
  "name": "Jane",
  "phoneNumber": "+15551234567",
  "balance": 450
}

balance is in cents (450 = $4.50). It is a leftover prepaid balance from an earlier version of Mio and is 0 for everyone else — calls are billed per call against a card hold, not against this balance.


Calls

Read the calls Mio has made for you. Calls themselves are started in the web app or the Mio widget.

GET https://api.mio.gg/api/v1/calls

Returns your 25 most recent completed calls, newest first. No parameters.

Example

curl https://api.mio.gg/api/v1/calls \
  -H "X-API-Key: sk_live_your_key_here"

Response 200

[
  {
    "id": "d4e5f6a7-...",
    "direction": "outbound",
    "status": "completed",
    "name": "IRS",
    "toNumber": "+18008291040",
    "fromNumber": "+15559876543",
    "instructions": "Get through to a live representative...",
    "summary": "Reached a representative after 41 minutes on hold...",
    "transcript": [
      { "role": "agent", "text": "Hi, I'm calling on behalf of..." },
      { "role": "user", "text": "Thanks for holding, how can I help?" }
    ],
    "cost": 180,
    "duration": 2540,
    "attempt": 1,
    "startedAt": "2026-07-27T18:30:05.000Z",
    "endedAt": "2026-07-27T19:12:25.000Z",
    "createdAt": "2026-07-27T18:30:00.000Z"
  }
]

cost is the metered charge in cents, duration is in seconds. In transcript, "agent" is Mio's voice agent and "user" is the other end of the line. The transcript covers only the part of the call Mio was on — when Mio bridges you in, the agent drops off and the transcript ends there.


Webhooks

Webhooks notify your server when a call completes. You get the transcript, summary, cost, and duration — everything you need to process the result.

Managing webhooks

GET https://api.mio.gg/api/v1/webhooks

List all your webhooks.

POST https://api.mio.gg/api/v1/webhooks

Create a webhook. Requires HTTPS URL. A signing secret is generated and returned once.

Parameter Description
url required HTTPS endpoint that will receive POST requests.
events optional Array of event types. Default: ["call.completed"]
GET https://api.mio.gg/api/v1/webhooks/:id

Fetch one webhook. The signing secret is masked — it is returned in full only on create.

PUT https://api.mio.gg/api/v1/webhooks/:id

Update a webhook's url, events, or active status. Setting active: true also resets the failure count.

DELETE https://api.mio.gg/api/v1/webhooks/:id

Delete a webhook. Returns 204.

Zapier REST hooks

Two extra endpoints exist for platforms that subscribe and unsubscribe automatically. Subscriptions created this way are unsigned — see webhook security below.

POST https://api.mio.gg/api/v1/webhooks/subscribe

Subscribe. Body: url (required), events (optional). Returns { "id": "..." }.

DELETE https://api.mio.gg/api/v1/webhooks/subscribe

Unsubscribe. Body: id (required). Returns 204.

Webhook payload

When a call completes, Mio sends a POST request to each active webhook. call is the same object GET /api/v1/calls returns:

{
  "event": "call.completed",
  "timestamp": "1753640000",
  "call": {
    "id": "d4e5f6a7-...",
    "direction": "outbound",
    "status": "completed",
    "name": "Riverside Dental",
    "toNumber": "+15551234567",
    "fromNumber": "+15559876543",
    "instructions": "Book a cleaning, any morning this week...",
    "summary": "Cleaning booked for Thursday at 9:40am.",
    "transcript": [
      { "role": "agent", "text": "Hi, I'm calling on behalf of..." },
      { "role": "user", "text": "Sure, what day works?" }
    ],
    "cost": 120,
    "duration": 185,
    "attempt": 1,
    "startedAt": "2026-07-27T18:30:05.000Z",
    "endedAt": "2026-07-27T18:33:10.000Z",
    "createdAt": "2026-07-27T18:30:00.000Z"
  }
}

cost is in cents, duration is in seconds.


Webhook security

A webhook created with POST /api/v1/webhooks gets a signing secret (prefixed whsec_), returned once at creation. Mio signs every delivery to that webhook so you can verify it came from us. Zapier-style subscriptions have no secret, so deliveries to them carry no X-Mio-Signature header.

Headers

Header Description
X-Mio-Signature HMAC-SHA256 signature of the payload. Present only when the webhook has a signing secret.
X-Mio-Timestamp Unix timestamp (seconds) of when the event was sent.
X-Mio-Event Event type, e.g. call.completed
X-Mio-Delivery Unique delivery ID (UUID).

Verifying signatures

The signature is computed as HMAC-SHA256(secret, "{timestamp}.{body}") where body is the raw JSON request body.

import crypto from 'crypto';

function verifyWebhook(secret, timestamp, body, signature) {
  const expected = crypto
    .createHmac('sha256', secret)
    .update(`${timestamp}.${body}`)
    .digest('hex');
  return crypto.timingSafeEqual(
    Buffer.from(signature),
    Buffer.from(expected)
  );
}

Always use constant-time comparison to prevent timing attacks. Reject requests where the timestamp is more than 5 minutes old to prevent replay attacks.

Failure handling

If your endpoint returns a non-2xx status or times out (10s), Mio retries with exponential backoff up to 10 times. After 10 consecutive failures, the webhook is automatically disabled. Re-enable it with PUT /api/v1/webhooks/:id and { "active": true }.


Errors

The API uses standard HTTP status codes. Error responses include a JSON body with an error field.

Status Meaning
400 Invalid request body or parameters.
401 Missing or invalid API key.
404 No such webhook, or it doesn't belong to your account.
429 Rate limited — more than 60 requests in a minute.
500 Server error. Try again or contact support.
// Example error response
{
  "error": "Webhook not found"
}

Ready to build?

Create your API key and wire up your first webhook in under a minute.

Get your API key

You're charged only when Mio reaches someone — and then for what the call actually cost.