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.

Examples use the fictional domain api.example.test, the fictional key tq_test_EXEMPLE and numbers from a fictional range. None of them opens real access.

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.

Fictional example: authentication header. The key is made up.
Authorization: Bearer tq_test_EXEMPLE
Content-Type: application/json

Idempotency

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.

Fictional example of the end of a list.
{
  "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.

Fictional example of a 429 response.
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).

Fictional example of an error response.
{
  "error": {
    "code": "invalid_request",
    "message": "The \"to\" field must be a number in E.164 format.",
    "request_id": "req_EXEMPLE_002"
  }
}
HTTPcodeMeaningSuggested action
400invalid_requestMalformed request: missing or invalid field, missing Idempotency-Key on a POST.Fix the request.
401unauthorizedMissing, invalid or revoked key.Check the key and the Authorization header.
403forbiddenThe key is not allowed to access this resource or project.Use a key with the right permissions.
404not_foundResource does not exist or is outside your scope.Check the identifier.
409idempotency_conflictSame Idempotency-Key used with a different body.Generate a new key.
429rate_limitedRequest 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.
Fictional example response. Demo data.
{
  "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.
Fictional example response. The documents_required value is illustrative.
{
  "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).
Fictional example response. Simulation: no real number is reserved.
{
  "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).
Fictional example response. Simulation: no real communication was sent.
{
  "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.
Fictional example response. Demo data.
{
  "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).
Fictional example response. Simulation: no real SMS was sent.
{
  "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.
Fictional example response. Made-up values.
{
  "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

ObjectStatusesNote
Callringing, connected, completed, failedfailed does not give the cause. Do not guess it.
SMSqueued, sent, delivered, undelivered, unknowndelivered does not mean read. unknown may stay final.
Number orderpending_validationThe 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.

Fictional example of a delivery. The exact shape of the envelope may change.
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.