Skip to main content
The base URL is https://api.ephone.ai and every request carries Authorization: Bearer sk-admin-.... Apart from the two export endpoints, responses use the {"success": bool, "message": string, "data": ...} envelope.

Account

Get account profile

GET /api/user/self · scope account:read Returns the full account profile. balance is the current balance.
The response has many more fields; only the common ones are shown. Amounts are decimal strings — parse them as strings to avoid floating-point error. Passwords, session credentials and identity documents never appear in the response. The profile does not include lifetime spend or request counts. To get those for a time range, use “Call statistics” and “List log details” below.

Call logs

How to query cheaply. The log table is partitioned by creation time, and a time range is what lets the database prune partitions instead of scanning the whole table. So the recommended pattern is a narrow rolling window: pull the last few minutes to an hour on a fixed interval.Pulling everything without a time range is the most expensive shape — there is nothing to prune. The endpoints will not reject it, but every query has a time limit (thirty seconds for the log list) and simply fails when it runs out. For one-off reconciliation, fetch day by day or week by week rather than a whole year at once.
Timestamps can be seconds or milliseconds. The endpoints in this group detect the unit of start_timestamp and end_timestamp automatically: values below 100000000000 are Unix seconds, anything larger is milliseconds. Both ends are inclusive and filter on the request’s creation time. Timestamps in responses are always milliseconds.
These endpoints only see billed requests: a request that failed before it was charged does not appear here.

List log details

GET /api/log/self · scope logs:read Returns a page of call log details, newest first. Same data as the export endpoint below, in JSON.
Each item has more fields than shown; the common ones are:
total may come back as -1. The count is a separate query limited to three seconds; over a wide time range it may not finish, in which case total is -1 for “unknown” while items is still complete. Page with has_more, not with total. When you only need the data, pass skip_count=1 to skip that count on every page; when you need the total, use “Count log entries” below.

Count log entries

GET /api/log/self/count · scope logs:read Counts the log entries that match your filters — in other words, the number of requests over a period. It takes the same filters as the log list and may count for up to ten seconds, so it finishes in cases where the three-second count inside the list gives up.
A total of -1 means the count did not finish within ten seconds. Split the range into pieces that do not overlap (both ends are inclusive, so adjacent pieces must not share an instant), count each and add them up. For a table of counts per key or per hour, the requests column of “Export usage analytics” below is easier.

Call statistics

GET /api/log/self/stat · scope logs:read Returns the amount spent over a time range, plus throughput over the last 60 seconds.
Check unavailable before using the numbers. The three parts are computed under separate time limits. When one of them does not finish in time, the response is still "success": true but carries an unavailable array naming the missing parts: quota (the amount), rate (rpm, tpm, mpm) and unfinished (unfinished tasks). The 0 in the matching fields is then not a real value. Reconciliation scripts must check this field and retry with a narrower range when they see it.

Export log details

GET /api/log/self/stream-export · scope logs:read Streams log details.
This endpoint returns the CSV body directly; it does not return a download link. The response is Content-Type: text/csv; charset=utf-8, written row by row as a stream, with a UTF-8 BOM at the start so Excel reads it correctly. Redirect the response body to a file — no second request is needed.
Available columns include created_at, model_name, token_name, prompt_tokens, completion_tokens, cached_tokens, reasoning_tokens and quota. Column order is fixed and does not follow the order in columns, so read columns by header. If you use caching, reasoning or multimodal models, export the detailed columns explicitly — the aggregate counts alone will not reconcile against what you were charged.

Usage analytics

Export usage analytics

GET /api/data/self/pivot/stream-export · scope usage:read Aggregates usage by dimension and streams it as CSV, same as above. Each row is one group, with columns including requests (request count), errors (failed requests), spend (amount), token counts by type and avg_latency_ms, up to 5000 rows. With group_by=token the key column is the key ID; with group_by=time rows are hourly and key is the hour’s start in Unix seconds.
This endpoint takes milliseconds only. The log endpoints accept either unit, but this one does not: seconds are read as a date in 1970 and the file comes back with only a header.Data is aggregated by the hour, so both bounds work on whole hours: the hour containing the start is included, and if the end falls exactly on the hour, that hour is excluded. When exporting in pieces, start each piece where the previous one ended — they will meet without overlapping. A single query spans at most about six months (186 days).Aggregation runs on a schedule, so the most recent requests may not be included yet; use the log endpoints for up-to-the-minute numbers.

API key management

These endpoints manage API keys used to call models, not Admin Keys. An Admin Key can never create, edit or revoke any Admin Key, including itself.

List keys

GET /api/token/ · scope keys:read Returns a page of API keys. Key values are masked; plaintext is not retrievable through any endpoint.
An expired_time of -1 means the key never expires. used_quota is the total amount the key has spent.

Get one key

GET /api/token/{id} · scope keys:read Returns the full configuration of one key, with the value masked. Fetch it before updating a key — see “Update a key” below.

Create a key

POST /api/token/ · scope keys:write Creates a new API key. The plaintext appears only in this response.
Save key right away. Only its hash is stored, so no endpoint can return the plaintext afterwards — that applies to Admin Keys and signed-in users alike.

Update a key

PUT /api/token/ · scope keys:write Updates an existing key. The body must include id.
This replaces the whole configuration; it does not patch the fields you send. Anything missing from the body is reset to its default: send only the name and quota and expired_time becomes 0 and unlimited_count becomes false, so the key stops working at once — and the group, model limits, IP allowlist and rate limits are cleared too.Fetch the full configuration with “Get one key” first, change only what you need, and send the whole object back.
The fetched key and key_mask are masked; send them back unchanged — the key itself is not affected.

Regenerate a key

POST /api/token/{id}/rotate · scope keys:write Replaces a key’s value. The old value stops working immediately. Name, quota, group, model limits, IP allowlist and all other settings are kept, and the id does not change. The new value appears only in this response. Use it for scheduled rotation or when you suspect a key has leaked. Make sure everything using the key can switch right away: requests still sending the old value get 401.
Save the new key right away. As with creation, only a hash is stored, so no endpoint can return the plaintext again.

Delete a key

DELETE /api/token/{id} · scope keys:write Deletes the key immediately.
Bulk deletion is not available to Admin Keys. Delete keys one at a time, or use the console.