CallableAI

Documentation

Endpoints

Four calls — ping, send, check, cancel — plus the read that tells you what happened to a code nobody received.

Everything lives under https://api.callable.com.au, authenticated with a bearer key, and anything with a body is application/json.

GET /v1/ping

Confirms a key works and reports what it can reach. Free, no side effects.

curl https://api.callable.com.au/v1/ping \
  -H "Authorization: Bearer $CALLABLE_API_KEY"

POST /v1/otp/send

Generates a code, delivers it, and returns the pending verification.

FieldTypeDefaultNotes
phonestringrequiredE.164, or a national number plus country
countrystringyour workspace's numberAU, US, CA, GB, NZ. Ignored when phone is E.164
channelvoice | smsvoice
brand_namestringyour workspace nameSpoken and written in the message. ≤ 60 chars
code_lengthinteger64–8
ttl_secondsinteger30060–900
max_attemptsinteger51–10
metadataobject{}Opaque JSON, echoed back on every read. Put your own user id here
curl -X POST https://api.callable.com.au/v1/otp/send \
  -H "Authorization: Bearer $CALLABLE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "phone": "+61412345678",
    "channel": "sms",
    "metadata": { "user_id": "u_123" }
  }'

201 Created:

{
  "id": "3f2a8c14-…",
  "status": "pending",
  "channel": "sms",
  "phone": "+61412345678",
  "attempts": 0,
  "max_attempts": 5,
  "expires_at": "2026-08-14T04:05:00+00:00",
  "verified_at": null,
  "created_at": "2026-08-14T04:00:00+00:00",
  "delivery": {
    "ok": true,
    "channel": "sms",
    "provider_id": "3d38c43b-…",
    "from_number": "+61400111222",
    "error_code": null,
    "error_message": null
  },
  "metadata": { "user_id": "u_123" }
}

The code itself is never in any response.

delivery carries diagnostics for when a user says the code never arrived — whether we handed it over, which number it came from, and the failure reason if there was one. Its inner fields are informational and may change; do not build logic on them.

Resending supersedes. Requesting a code for a number that already has a pending one kills the old code immediately. That is what makes "resend" unambiguous — but it means an old id is dead once you resend, so store the new one.

POST /v1/otp/check

Compares what your user typed against a pending verification.

FieldTypeNotes
codestringrequired. Non-digits are stripped, so a pasted 123 456 is not counted as a wrong guess
verification_idstringThe id from send. Preferred
phonestringAlternative to verification_id — checks the newest pending code for that number
countrystringCountry to assume for a national-format phone

Provide one of verification_id or phone.

curl -X POST https://api.callable.com.au/v1/otp/check \
  -H "Authorization: Bearer $CALLABLE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "verification_id": "3f2a8c14-…", "code": "123456" }'

Correct code — 200:

{ "id": "3f2a8c14-…", "status": "verified", "verified": true, "verified_at": "2026-08-14T04:01:12+00:00" }

Wrong code — also 200:

{ "id": "3f2a8c14-…", "status": "pending", "verified": false, "attempts": 1, "attempts_remaining": 4 }

A wrong code is not an error — branch on verified, not on the status code.

Checking again after a success is safe. It returns the same success without spending an attempt, so a double-submitted form or a retried webhook does not lock your user out.

GET /v1/otp/{id}

Current state of one verification. Useful when a user reports the code never arrived — delivery carries the reason.

curl https://api.callable.com.au/v1/otp/3f2a8c14-… \
  -H "Authorization: Bearer $CALLABLE_API_KEY"

POST /v1/otp/{id}/cancel

Abandons a pending verification — for instance when your user corrects their number. Idempotent: cancelling a finished verification returns it unchanged.

curl -X POST https://api.callable.com.au/v1/otp/3f2a8c14-…/cancel \
  -H "Authorization: Bearer $CALLABLE_API_KEY"

Verification statuses

StatusMeaning
pendingDelivered, waiting for a code. The only status that accepts one
verifiedThe correct code was submitted
expiredThe time limit elapsed
failedThe attempt budget was spent
undeliveredDelivery never succeeded
canceledSuperseded by a newer code, or cancelled

Everything except pending is terminal.