Demo API reference
This page describes the API as it is designed today: authentication, idempotency, pagination, errors, endpoints and webhooks. It is a demo version. All examples are fictional.
Updated on
Status of this reference
This is a design proposal and may change without notice. No production API is open today.
Until a provider is configured, calls, number orders and SMS are simulations. Responses then carry the simulated: true field.
Base URL and authentication
All requests go through the base URL https://api.example.test/v1, over HTTPS. Authenticate with an API key in the Authorization header, using the Bearer scheme.
A key is limited to one project and one environment. It must never appear in code that runs in the browser or in a URL.
Authorization: Bearer tq_test_EXEMPLE
Content-Type: application/jsonIdempotency
Every POST request requires an Idempotency-Key header holding a unique identifier, for example a UUID. Without it, the API answers 400 invalid_request.
If you send the same key with the same body, the API replays the original response and creates nothing more: a double click or a network retry has no effect. If you send the same key with a different body, the API answers 409 idempotency_conflict.
Generate a fresh key for each new operation. If a response never arrived, retry with the same key rather than a new one.
Cursor pagination
Lists accept limit and cursor. The response contains the data array and next_cursor. To read the next page, pass the value of next_cursor as cursor. When next_cursor is null, the list is finished.
A cursor is opaque: do not decode it and do not build one yourself.
{
"data": [],
"next_cursor": null
}Quotas and rate limits
The API limits request rates. Beyond the limit, it answers 429 rate_limited with a Retry-After header giving, in seconds, how long to wait before retrying.
The limits that apply to your project depend on your contract. No figure is published on this page.
HTTP/1.1 429 Too Many Requests
Retry-After: 2
Content-Type: application/json
{ "error": { "code": "rate_limited", "message": "Too many requests. Retry after the indicated delay.", "request_id": "req_EXEMPLE_001" } }Error format
Every error returns an error object with three fields: code (stable, to use in your logic), message (meant for a person, may change) and request_id (to give to support).
{
"error": {
"code": "invalid_request",
"message": "The \"to\" field must be a number in E.164 format.",
"request_id": "req_EXEMPLE_002"
}
}| HTTP | code | Meaning | Suggested action |
|---|---|---|---|
| 400 | invalid_request | Malformed request: missing or invalid field, missing Idempotency-Key on a POST. | Fix the request. |
| 401 | unauthorized | Missing, invalid or revoked key. | Check the key and the Authorization header. |
| 403 | forbidden | The key is not allowed to access this resource or project. | Use a key with the right permissions. |
| 404 | not_found | Resource does not exist or is outside your scope. | Check the identifier. |
| 409 | idempotency_conflict | Same Idempotency-Key used with a different body. | Generate a new key. |
| 429 | rate_limited | Request rate too high. | Wait for the Retry-After duration. |
GET /numbers
Lists the numbers on your account.
- limit: number of items per page (optional).
- cursor: cursor for the next page (optional).
- Each number carries id, e164, country, type, capabilities (voice, sms), project_id and status.
{
"data": [
{
"id": "num_EXEMPLE_001",
"e164": "+33199001234",
"country": "FR",
"type": "local",
"capabilities": ["voice", "sms"],
"project_id": "prj_EXEMPLE_A",
"status": "active"
}
],
"next_cursor": "cur_EXEMPLE_002"
}GET /numbers/search
Searches the stock for numbers. Actual availability depends on the country, the chosen provider and the contract.
- country: two-letter ISO country code, for example FR.
- type: number type, for example local.
- Each result carries e164, country, type, capabilities and documents_required, the list of documents to provide before activation.
{
"data": [
{
"e164": "+33199001234",
"country": "FR",
"type": "local",
"capabilities": ["voice", "sms"],
"documents_required": ["proof_of_address"]
}
]
}POST /number-orders
Places a number order. The Idempotency-Key header is required. The order waits for the documents and conditions to be validated.
- e164: number to order, taken from a search (required).
- project_id: project to attach it to (required).
{
"id": "ord_EXEMPLE_001",
"status": "pending_validation",
"simulated": true
}POST /calls
Starts an outbound call between two numbers. The Idempotency-Key header is required.
- from: calling number, in E.164 (required).
- to: called number, in E.164 (required).
- project_id: project concerned (required).
- flow_id: call flow to apply (optional).
{
"id": "call_EXEMPLE_001",
"status": "ringing",
"simulated": true
}GET /calls
Lists the call log, with cursor pagination. Durations are in seconds. Talk time and billable time are two separate fields: they may differ depending on the contract.
- limit and cursor: pagination (optional).
- Each call carries id, direction, status, duration_seconds, billable_seconds and project_id.
{
"data": [
{
"id": "call_EXEMPLE_001",
"direction": "outbound",
"status": "completed",
"duration_seconds": 42,
"billable_seconds": 42,
"project_id": "prj_EXEMPLE_A"
}
],
"next_cursor": null
}POST /messages
Sends or schedules an SMS. The Idempotency-Key header is required. A long text is split into segments, and each segment counts toward usage.
- from: sender (required).
- to: recipient, in E.164 (required).
- text: message content (required).
- nature: transactional or marketing (required).
- scheduled_at: date and time of a scheduled send (optional).
- timezone: time zone of scheduled_at (optional, provide it with scheduled_at).
- The response gives segments and encoding (GSM-7 or UCS-2).
{
"id": "msg_EXEMPLE_001",
"status": "queued",
"segments": 1,
"encoding": "GSM-7",
"simulated": true
}GET /usage
Returns the quantities used over a period. The response carries quantities and units, never amounts.
- from and to: bounds of the period, in YYYY-MM-DD format.
- group_by: day, number, project, country or service.
- provisional is true when events may still arrive late.
- One call can generate several usage lines depending on the contract.
{
"data": [
{ "key": "2026-10-01", "service": "voice", "direction": "outbound", "unit": "second", "quantity": 5400 },
{ "key": "2026-10-01", "service": "sms", "direction": "outbound", "unit": "segment", "quantity": 120 }
],
"provisional": false
}Statuses
| Object | Statuses | Note |
|---|---|---|
| Call | ringing, connected, completed, failed | failed does not give the cause. Do not guess it. |
| SMS | queued, sent, delivered, undelivered, unknown | delivered does not mean read. unknown may stay final. |
| Number order | pending_validation | The number is usable only after activation (number.activated event). |
Webhooks
You can receive events at an HTTPS address of your choice. Each delivery carries a Telqio-Signature header shaped like t=<unix timestamp>,v1=<hex signature>.
The signature is an HMAC-SHA256 of the text “t, dot, raw body” with the endpoint secret. Reject any delivery whose timestamp is more than 300 seconds off, and deduplicate on the event identifier id.
Events: call.ringing, call.connected, call.completed, call.failed, message.sent, message.delivered, message.undelivered and number.activated. Delivery retries follow a bounded policy, visible in the console. Arrival order is not guaranteed.
The guide “Verify a signed webhook” gives the full Node.js code.
POST /webhook HTTP/1.1
Telqio-Signature: t=1794096000,v1=<hex_signature>
Content-Type: application/json
{
"id": "evt_EXEMPLE_001",
"type": "call.completed",
"created": 1794096000,
"data": { "call_id": "call_EXEMPLE_001", "status": "completed" }
}What this reference is not
- It is not a service commitment: no availability, lead time or capability is promised.
- It is not an open API: no real key exists and no communication is sent.
- Actual capabilities will depend on the country, the chosen provider and the contract. For your scope, request a quote.
Frequently asked questions
Can I use this API in production?
No. This is a demo version. No production API is open and the interface contract may still change.
Why do responses contain simulated: true?
Because no provider is configured. Calls, SMS and number orders are simulated, and no real communication is sent.