StatusAPI Dashboard ↗

Telroi API

Build voice, OTP, speech and CRM features on Telroi over one REST API.

Developer quickstart

Make your first API request in minutes. Send a voice OTP with a single call.

fetch("https://app.telroi.ai/v1/otp", {
  method: "POST",
  headers: {
    "Authorization": "Bearer tlr_live_xxx",
    "Content-Type": "application/json"
  },
  body: JSON.stringify({
    "to": "+15551234567"
  })
})
  .then((res) => res.json())
  .then((data) => console.log(data));
Base URLv1

Capabilities

Build voice verification, speech and calling into your product over one REST API.

Start building

Authentication

Authenticate every request with a secret API key in the Authorization header. Create keys in your dashboard under Developers.

cURL
curl https://app.telroi.ai/v1/numbers \
  -H "Authorization: Bearer tlr_live_xxx"

live tlr_live_ keys hit live infrastructure and bill your wallet. test tlr_test_ keys run in sandbox — calls are simulated, nothing is charged, and OTP code 000000 always verifies. Keep secret keys server-side.

Errors & rate limits

Telroi uses conventional HTTP status codes: 2xx success, 4xx a request problem, 5xx a Telroi-side error. Every error body has the same shape.

Error
{
  "error": {
    "code": "unauthorized",
    "message": "Invalid or revoked API key"
  }
}

HTTP status code summary

CodeDescription
200OK — the request succeeded.
201Created — a resource was created (e.g. an OTP was placed).
400Bad Request — a parameter is missing or invalid. See the error code in the body.
401Unauthorized — no valid API key was provided. Send it as a Bearer token.
403Forbidden — the key is valid but lacks the required scope (e.g. otp:write).
404Not Found — the resource or endpoint doesn't exist.
405Method Not Allowed — wrong HTTP method for this endpoint.
422Unprocessable Entity — the body is well-formed but failed validation.
429Too Many Requests — a rate limit was exceeded. Check the retry guidance.
5xxServer Error — something went wrong on Telroi's end. These are rare.

Error codes

When a request fails, the body contains an error.code you can branch on:

Error codeHTTPMeaning
invalid400A required field is missing or malformed (e.g. a non-E.164 destination number).
unauthorized401The API key is missing, revoked, or not recognized.
forbidden403The key lacks the scope this endpoint needs (otp:write, speech:write, etc.).
rate_limited429Too many OTP requests for this destination number. Respect the cooldown, hourly and daily caps; retry after the indicated delay.
otp_failed502The OTP call could not be placed by the configured voice vendor.
speech_failed502Text-to-speech or speech-to-text processing failed at the configured vendor.

Rate limits

The Voice OTP endpoint is rate-limited per destination number by the operator policy (cooldown, hourly and daily caps). Exceeding a limit returns 429 with a rate_limited code.

Live Call widget

A button on your site that lets a visitor talk to you without dialling. They click, you get a call, and it rings your team like any other — because it is one.

Installing it

One script tag, before the closing body tag. The key identifies which workspace the call reaches and which route answers it.

HTML
<script src="https://app.telroi.ai/widget/v1.js"
  data-telroi-key="wgt_your_key_here"></script>
AttributesDescription
data-telroi-keyRequired. Identifies your workspace and the route the call takes.
data-telroi-launcherOptional. Set to none to suppress the floating button when you raise the panel from your own control. Defaults to auto.

Opening it yourself

The script attaches window.TelroiLiveCall once it has initialised. Call open() from your own button — a link in your navigation, a call-to-action mid-page — and the panel appears without a floating launcher.

HTML
<!-- Your own control, anywhere on the page -->
<button id="talk-to-us">Talk to us</button>

<script>
  document.getElementById('talk-to-us').onclick = function () {
    // Bound to a click rather than to page load: the method exists only once
    // the widget script has initialised.
    if (window.TelroiLiveCall && window.TelroiLiveCall.open) {
      window.TelroiLiveCall.open();
    }
  };
</script>

The method exists only after the script has loaded. Calling it from code that runs earlier will find nothing there, so bind it to a user action rather than to page load, or check for it first.

Knowing when a call starts

Set onCall before the script loads and it will be invoked when a session begins, with the session id, where it routed, and whether an AI voice answered. Useful for your own analytics.

HTML
<script>
  // Set before the widget script loads, so it is there when a call begins.
  window.TelroiLiveCall = { onCall: function (c) {
    console.log(c.sessionId, c.routedTo, c.voice);
  } };
</script>
<script src="https://app.telroi.ai/widget/v1.js"
  data-telroi-key="wgt_your_key_here"
  data-telroi-launcher="none"></script>

How it appears

On a desktop the panel is a card of about 360px over a dimmed page. Below 640px it fills the screen instead, because a small rounded card on a handset leaves a strip of page around something somebody is trying to talk into. The size is decided each time the panel opens, so a rotated tablet gets the right shape.

The key is visible in your page source by design — it has to be, for the script to read it. It identifies a route rather than granting account access, but treat it as a public support line: anybody who copies it can place a call that rings your team and draws from your wallet.