Make and troubleshoot an HTTP API request
Use this journey to connect your backend to Delphi's HTTP API and verify a read-only request before adding session or usage operations. The API origin is the deployment's TelAPI address, which can differ from TelWeb and the documentation address. Ask the deployment operator for that origin.
Availability: from 0.9.18, the operator must enable Browser SDK with FEATURE_BROWSER_SDK=true. Without it, the API serves no endpoint discovery, session, runtime-capability or usage routes, and no API-key access. The team also needs Browser SDK entitlement and the operation's allowed scopes. Browser voice additionally requires FEATURE_WEBRTC=true. A product login, documentation login and team API key are separate credentials. Use Manage team API keys to create, store, rotate and revoke keys.
The HTTP reference snapshot describes the 0.9.17-patch1 schema. For 0.9.18, use the API description that your deployment serves, as described in API description by capability profile.
API description by capability profile
The API builds its OpenAPI description from the capabilities your deployment enables, so two deployments of 0.9.18 can publish different descriptions. The interactive view is served at /documentation when the operator enables it; it is off by default in production.
| Enabled capability | What the description and API include |
|---|---|
| None of the three | Health checks only |
| Browser SDK | API-key and bearer authentication, endpoints, sessions, runtime capabilities, TTS usage and the session channel message schema |
| Browser SDK and WebRTC | Also the voice upgrade and downgrade operations and the gateway fields of voice sessions |
Feature availability lists the flags behind each capability. Generate client code from your own deployment's description, not from another installation.
Runtime capabilities and voice
GET /api/v1/runtime/capabilities requires a key with CREATE_CALL_TOKEN. It reports voice only when two conditions hold: the deployment enables WebRTC, and the calling key grants the WebRTC channel. A key without a channel restriction counts as granting it. When either condition is missing, the response omits web_voice entry points and reports voice_conversation and voiceInvocable as false.
When an application offers both browser chat and browser voice, runtime.migration lists text_to_voice, voice_to_text and api_to_media. From 0.9.18 the enum includes voice_to_text; update clients that validate this field against a fixed list.
1. Select the operation and credential
For the first check, use GET /api/v1/endpoints with a key containing READ_TEAM_APPS. This operation lists browser endpoints in the key's team, not all applications, telephone endpoints or another team's resources. You do not supply a team identifier to broaden the result.
Send the long-lived key in the X-API-Key header from your backend. Do not put it in browser code or a URL. A bearer/session token is not interchangeable with this key. If your following task is browser voice or chat, continue with the browser integration journey for scoped session credentials and readiness; listing an endpoint does not create or start a session.
2. Send a bounded read-only request
This Node.js backend example reads configuration from your server environment, applies a ten-second client timeout and logs only a count and pagination metadata. Set DELPHI_API_ORIGIN to the operator-supplied HTTPS origin and keep DELPHI_API_KEY in your secret storage. The timeout is this client's choice, not a server timeout guarantee.
const origin = process.env.DELPHI_API_ORIGIN;
const key = process.env.DELPHI_API_KEY;
if (!origin || !key) throw new Error('Configure the API origin and team API key');
const url = new URL('/api/v1/endpoints?page=1&limit=20', origin);
const response = await fetch(url, {
headers: {'X-API-Key': key},
signal: AbortSignal.timeout(10_000),
});
const contentType = response.headers.get('content-type') || '';
const body = contentType.includes('application/json') ? await response.json() : null;
if (!response.ok) {
// Keep full response bodies and credentials out of routine logs.
const code = body?.error?.code || 'HTTP_ERROR';
const requestId = body?.error?.requestId || 'unavailable';
throw new Error(
`Endpoint request failed: HTTP ${response.status}; ${code}; request ${requestId}`,
);
}
if (!Array.isArray(body?.endpoints) || !body?.pagination) {
throw new Error('Unexpected endpoint response; check the API origin and deployed contract');
}
console.log({endpointCount: body.endpoints.length, pagination: body.pagination});
A 200 response contains endpoints and pagination. Each endpoint has an id and name; the name comes from its application and is not a unique identifier. Use the endpoint ID when another operation requires it. An empty array can be a valid result for a team without browser endpoints. Confirm the selected team and application entry points before replacing the key.
A proxy or another server can return HTML or a different error body. Check the HTTP status and content type before assuming every response uses the TelAPI JSON envelope. A successful login page is not a successful API response.
3. Read further pages deliberately
page is one-based and defaults to 1; limit defaults to 20 and must be between 1 and 100. Use positive integer query values. Invalid values, including zero or an excessive limit, are rejected rather than silently clamped.
The response's pagination contains total, page, limit and totalPages. When the total is zero, totalPages is zero. Continue only while the returned page is below totalPages; a page beyond the available results can return an empty array.
This is offset pagination, not a stable snapshot or cursor. The query does not declare a stable ordering, and the count and page reads are separate. If endpoints change while you read, do not assume a complete, duplicate-free export. For application selection, prefer storing the chosen endpoint ID and reconciling it when configuration changes rather than continually rebuilding identity from display names.
4. Interpret errors before retrying
TelAPI's shared error handler returns an error object with code and message, and can include details and requestId. Details depend on the operation and error; do not require a fixed structure or display raw details to an end user. A request identifier helps an operator correlate logs; it is not a call ID or session credential.
| HTTP result | Meaning and what to do |
|---|---|
400 / VALIDATION_ERROR | Correct the query or body according to the operation. Retrying the same invalid input does not help. |
401 / AUTHENTICATION_ERROR | Check missing, inactive, expired or invalid key, team state and entitlement. The team daily request allowance can also be rejected through authentication, so read the safe message before assuming the secret is wrong. |
403 / AUTHORIZATION_ERROR | Check required scope and operation-specific authority. Repeated requests cannot add permission. |
404 / NOT_FOUND | A shared-handler resource error can use NOT_FOUND. Check origin, path, route availability and resource identifier; routing and proxy 404 responses can have a different body. |
409 / CONFLICT | Where the operation uses this error, reconcile its current state before attempting a conflicting change again. |
429 / RATE_LIMIT_EXCEEDED | Stop immediate retries. Honor Retry-After when supplied and inspect whether the cause is request rate or repeated credential failures. |
500 / INTERNAL_ERROR, network error or client timeout | Capture safe context and investigate. A failed response does not establish whether a state-changing operation took effect. |
503 / WEBRTC_DISABLED or TELPRO_UNCONFIGURED | A browser-voice operation can reject unavailable voice capability or gateway configuration. Ask the operator to correct availability; retrying cannot enable it. Other 503 responses can have different causes. |
An IP-tier rate-limit response can include limiter-specific details, for example:
{
"error": {
"code": "RATE_LIMIT_EXCEEDED",
"message": "Too many requests",
"details": {"max": 600, "after": "1 minute", "ttlMs": 42000},
"requestId": "example-request"
}
}
The shared builder forwards the limiter's max and after values and includes ttlMs when a TTL was supplied. This is an illustrative shape, not your deployment's configured limit. Other errors or limiters need not provide these fields; use Retry-After when present and investigate the actual limit rather than parsing the human-readable message.
Per-IP request controls, the team's daily allowance and the credential-retry jail are separate limits. A credential jail can temporarily reject a valid replacement key from the same client IP; changing keys repeatedly is not a recovery strategy. Follow API rate-limit operations when multiple clients share an egress address or failures persist.
For an idempotent read, bounded retries with delay can be appropriate after a transient failure. Set your own retry cap and stop on validation, authentication or authorization failures. Do not automatically replay a session-creation or other state-changing request after a timeout. Check that operation's state and contract first; this guide does not promise a universal idempotency key or exactly-once execution.
Read team TTS usage
Use GET /api/v1/usage/tts-characters with a backend API key containing READ_TEAM_USAGE to retrieve the calling team's recorded TTS character totals. The team comes from the key; a query parameter cannot select another team. Applications excluded from the dashboard are excluded from this aggregate too. These are character counts, not a currency invoice or proof that every provider has reported final usage.
| Query field | Meaning |
|---|---|
from | Optional ISO 8601 date-time, inclusive start of the usage-event window. Defaults to the Unix epoch. |
to | Optional ISO 8601 date-time, inclusive end of the window. Defaults to the request's current time. |
timeframe | day, week, month or total; defaults to total. This is an echoed label and does not select or shorten the time window. |
For a monthly report, send explicit from and to values; timeframe=month alone still uses the default all-time window. Either bound can be supplied alone. Invalid dates or labels and a start later than the end return validation errors. Both bounds are inclusive: avoid counting events on a shared boundary twice when combining adjacent windows.
GET /api/v1/usage/tts-characters?from=2026-06-01T00%3A00%3A00.000Z&to=2026-06-30T23%3A59%3A59.999Z&timeframe=month
X-API-Key: <server-side-team-key>
| Response field | Meaning |
|---|---|
teamId | Team associated with the calling key. |
timeframe | Effective label, independent of the date bounds. |
from, to | Resolved report bounds as ISO date-times. |
charactersTotal | Recorded total TTS characters in the window. |
charactersFromCache | Recorded characters served from the TTS cache. |
charactersGenerated | Recorded characters generated by an upstream provider. |
cacheHitRatio | Cached characters divided by total characters, rounded to four decimal places; zero when the total is zero. |
An empty result returns zero totals. If figures differ from an expected dashboard view, check the team, explicit date window, application exclusions and when usage was recorded before changing credentials. Use API key administration to grant reporting scope and dashboard reporting to interpret the UI view.
5. Verify the intended integration, then hand over
A successful endpoint list proves that this key, team, route and request worked at that time. It does not prove a browser session can start, a provider is configured, a SIP call can connect or a later write is permitted.
- For browser sessions, use session readiness, events and recovery.
- For key lifecycle and scope changes, return to API key administration.
- For missing endpoints, verify the app's channel and binding.
- For persistent failures, give the operator the deployment, operation, HTTP status/code, approximate time and safe request identifier. Do not include a key, authorization header, full response payload or customer conversation content. Use logs and traces to correlate the request without assuming its request ID is a call's correlation identifier.
Related
- Manage team API keys — to create or rotate the key that these requests use.
- Connect a browser application — to move from a backend request to a browser session.
- Feature availability — to check which capabilities your deployment enables.