API keys and webhooks are managed under API & webhooks in the portal. They are available on the Enterprise plan and during your trial. The API serves the same data and filters as the portal; it is read-only.
API keys
Choose New key, give it a name and an expiry, and pick its access:
| Scope | Allows |
|---|---|
calls.read | Call records, with external numbers masked. |
calls.unmask | Show full phone numbers. |
reports.read | Reports, exports and the wallboard. |
The key starts with tzk_ and is shown once, so copy it straight away. Telyzer stores only a hash of it. Revoke stops a key immediately.
Authentication and limits
Send the key as a Bearer token. The base address is https://app.telyzer.com/api/v1.
curl -H "Authorization: Bearer $TELYZER_KEY" \
"https://app.telyzer.com/api/v1/calls?from=2026-10-01&to=2026-10-07&limit=200"- Up to 300 requests a minute per key. Above that the API answers
429. 401: missing or invalid key.403: the key lacks the scope the request needs.- Times ending in
Localare wall-clock times at the site; times ending inUtcare UTC.
Endpoints
| Request | Returns | Scope |
|---|---|---|
GET /api/v1/calls | Calls, one row per conversation, newest first. | calls.read |
GET /api/v1/calls/{id} | One call with every transfer segment and the raw PBX line. | calls.read |
GET /api/v1/reports | Available reports. | reports.read |
GET /api/v1/reports/{key} | A report as JSON: columns, rows and the rule that produced them. | reports.read |
GET /api/v1/reports/{key}/export | A report file: format=csv|xlsx|pdf, lang=en|ar. | reports.read |
GET /api/v1/wallboard | Today so far: totals, missed calls not yet called back, latest calls. | reports.read |
Report keys: summary, daily, hourly, extensions, unanswered, top-numbers, destinations, sites, costs, after-hours, service-level, callers, trunks, account-codes, data-quality, calls, plus your saved custom reports.
Filters
Calls and reports accept from and to (YYYY-MM-DD, at most 366 days), siteId, departmentId, direction, outcome, category, extension, number, afterHours and minTalk (seconds).
curl -H "Authorization: Bearer $TELYZER_KEY" \
"https://app.telyzer.com/api/v1/reports/extensions?from=2026-10-01&to=2026-10-31"curl -H "Authorization: Bearer $TELYZER_KEY" -o daily.csv \
"https://app.telyzer.com/api/v1/reports/daily/export?format=csv&lang=en&from=2026-10-01&to=2026-10-31"Pagination
/calls returns a page of up to limit calls (1–200, default 50) and a nextCursor. Pass it back as cursor for the next page; it is null on the last page.
// Node.js 18+: read every call in a date range, 200 per page
let cursor = null;
do {
const url = new URL("https://app.telyzer.com/api/v1/calls");
url.searchParams.set("from", "2026-10-01");
url.searchParams.set("to", "2026-10-31");
url.searchParams.set("limit", "200");
if (cursor) url.searchParams.set("cursor", cursor);
const res = await fetch(url, { headers: { Authorization: "Bearer " + process.env.TELYZER_KEY } });
if (res.status === 429) { await new Promise((r) => setTimeout(r, 1000)); continue; }
const page = await res.json();
for (const call of page.items) console.log(call.startLocal, call.direction, call.outcome);
cursor = page.nextCursor; // null on the last page
} while (cursor);The full contract is published as OpenAPI 3.1 at app.telyzer.com/api/v1/openapi.json.
Webhooks
Choose New webhook, enter your HTTPS endpoint URL and pick the events. Telyzer POSTs a signed JSON event when one happens:
| Event | Sent when |
|---|---|
alert.triggered | An alert rule fired. |
report.delivered | A scheduled report was emailed. |
source.silent | A phone system stopped sending records. |
- Send test sends a test event and shows your endpoint's answer; Delivery log shows recent attempts.
- Failed deliveries are retried for about two days with growing delays. An endpoint is switched off after 20 failures in a row.
- Events are delivered only to public internet addresses.
Verifying the signature
Each webhook has a signing secret, shown once when you create it and later under Signing secret. Every request carries X-Telyzer-Signature: t=<unix time>,v1=<hex>, where v1 is the HMAC-SHA256 of t, a dot, and the raw request body, keyed with the secret. Compute it on the raw body before parsing JSON, compare in constant time, and reject old timestamps.
// Node.js: verify X-Telyzer-Signature ("t=<unix>,v1=<hex>") on the raw request body
import crypto from "node:crypto";
function verify(rawBody, header, secret) {
const parts = Object.fromEntries(header.split(",").map((p) => p.split("=")));
const expected = crypto.createHmac("sha256", secret).update(parts.t + "." + rawBody).digest("hex");
const a = Buffer.from(parts.v1 ?? "", "hex"), b = Buffer.from(expected, "hex");
const fresh = Math.abs(Date.now() / 1000 - Number(parts.t)) < 300; // reject replays older than 5 minutes
return a.length === b.length && crypto.timingSafeEqual(a, b) && fresh;
}# Python: the same check
import hmac, hashlib, time
def verify(raw_body: bytes, header: str, secret: str) -> bool:
parts = dict(p.split("=", 1) for p in header.split(","))
signed = parts["t"].encode() + b"." + raw_body
expected = hmac.new(secret.encode(), signed, hashlib.sha256).hexdigest()
fresh = abs(time.time() - int(parts["t"])) < 300
return hmac.compare_digest(expected, parts.get("v1", "")) and fresh