Licensing and usage
The licence decides which product modules are enabled on the installation and how much capacity it may use: token and transaction allowances per monthly or yearly period, and throughput limits. Licences are installed from the administration console. Every endpoint on this page stays available while the installation's licence is restricted.
Usage is recorded as events. Each event carries the tokens a request or job consumed (input, output, and embedding tokens, including those of failed requests) and the platform transactions billed for it. Event counts are usage records, not HTTP calls.
There are two kinds of reporting. The monthly statement aggregates a calendar month without any per-user detail. The usage reports break usage down by day, service, user, and event, and are available only on on-premises installations; elsewhere they return 403. All dates and times are in UTC.
Administrator access. Every endpoint on this page requires an administrator account.
- GET /license/usage/statement Retrieve a monthly usage statement
- GET /license/usage/export Export a monthly usage statement
- GET /license/usage/report Retrieve a usage report
- GET /license/usage/services List usage by service
- GET /license/usage/users List usage by user
- GET /license/usage/events List usage events
- GET /license/usage/report/export Export a usage report
Paths are relative to /api
Retrieve a monthly usage statement
GET /api/license/usage/statement
Returns aggregate usage for one calendar month.
Totals every usage event recorded in a calendar month, overall and per service, without any per-user detail. capacity, the throughput limits, and integrated_systems always describe the installation as it is now, in the current licence period, whichever month you request. To download the statement as a file, use Export a monthly usage statement.
- Authentication
- Bearer token How it works
- Permission
- Requires an administrator account.
Query parameters
-
monthstringMonth to report, as
YYYY-MM. Defaults to the current month.
Returns
Returns the statement in data: the month and its period_start and period_end; tokens (input, output, and embedding tokens), transactions (billed platform transactions), estimated_tokens (the part of tokens that was estimated rather than reported by the model), and events; by_source, one row per service, in which tokens is a decimal string; integrated_systems (connected systems now); installation_id; bundle; capacity; and the throughput limits in effect.
Errors
- 400
monthis not a validYYYY-MMmonth. - 403 The caller is not an administrator.
curl "$VDF_BASE_URL/api/license/usage/statement?month=2026-08" \
-H "Authorization: Bearer $VDF_ACCESS_TOKEN" const response = await fetch(`${process.env.VDF_BASE_URL}/api/license/usage/statement?month=2026-08`, {
headers: {
Authorization: `Bearer ${process.env.VDF_ACCESS_TOKEN}`,
},
});
if (!response.ok) throw new Error(`Request failed with status ${response.status}`);
const data = await response.json(); import os
import requests
response = requests.get(
f"{os.environ['VDF_BASE_URL']}/api/license/usage/statement",
headers={"Authorization": f"Bearer {os.environ['VDF_ACCESS_TOKEN']}"},
params={
"month": "2026-08",
},
timeout=30,
)
response.raise_for_status()
data = response.json() {
"success": true,
"data": {
"month": "2026-08",
"period_start": "2026-08-01T00:00:00+00:00",
"period_end": "2026-09-01T00:00:00+00:00",
"tokens": 18450210,
"transactions": 12877,
"estimated_tokens": 402115,
"events": 13240,
"by_source": [
{
"source": "agenthub",
"tokens": "11250400",
"transactions": 8120
},
{
"source": "chat",
"tokens": "7199810",
"transactions": 4757
}
],
"integrated_systems": 3,
"installation_id": "VDF-ONPREM-0A1B2C3D4E5F",
"bundle": "foundation",
"capacity": {
"monthly": {
"period_start": "2026-09-11T00:00:00+00:00",
"period_end": "2026-10-11T00:00:00+00:00",
"max_tokens": 25000000,
"max_transactions": 25000,
"tokens_used": 184220,
"tokens_remaining": 24815780,
"tokens_percent": 0.74,
"transactions_used": 131,
"transactions_remaining": 24869,
"transactions_percent": 0.52,
"surplus_band": "ok"
},
"binding_period": "monthly",
"binding_metric": "tokens",
"binding_percent": 0.74,
"surplus_band": "ok",
"enforcement": "none",
"bundle": "foundation"
},
"throughput_tokens_per_minute": 60000,
"throughput_requests_per_second": 25
}
} Export a monthly usage statement
GET /api/license/usage/export
Returns the same statement as Retrieve a monthly usage statement as an indented JSON file (application/json), sent as an attachment named vdf-usage-<YYYY-MM>.json. The file holds the statement object itself, without the success and data wrapper.
- Authentication
- Bearer token How it works
- Permission
- Requires an administrator account.
Query parameters
-
monthstringMonth to export, as
YYYY-MM. Defaults to the current month.
Returns
Returns the statement as a JSON file download.
Errors
- 400
monthis not a validYYYY-MMmonth. - 403 The caller is not an administrator.
curl "$VDF_BASE_URL/api/license/usage/export?month=2026-08" \
-H "Authorization: Bearer $VDF_ACCESS_TOKEN" \
-o export.bin const response = await fetch(`${process.env.VDF_BASE_URL}/api/license/usage/export?month=2026-08`, {
headers: {
Authorization: `Bearer ${process.env.VDF_ACCESS_TOKEN}`,
},
});
if (!response.ok) throw new Error(`Request failed with status ${response.status}`);
const file = await response.blob(); import os
import requests
response = requests.get(
f"{os.environ['VDF_BASE_URL']}/api/license/usage/export",
headers={"Authorization": f"Bearer {os.environ['VDF_ACCESS_TOKEN']}"},
params={
"month": "2026-08",
},
stream=True,
timeout=30,
)
response.raise_for_status()
with open("export.bin", "wb") as output:
for chunk in response.iter_content(chunk_size=65536):
output.write(chunk) Retrieve a usage report
GET /api/license/usage/report
Returns usage totals and a day-by-day breakdown for a date range.
Totals the usage events that match the filters and breaks them down by UTC day. Every day in the range is included, with zeros for days without usage. Available only on on-premises installations.
- Authentication
- Bearer token How it works
- Permission
- Requires an administrator account.
Query parameters
-
date_fromstringFirst day of the range, inclusive, as
YYYY-MM-DD(UTC). Defaults to 29 days before today. -
date_tostringLast day of the range, inclusive, as
YYYY-MM-DD(UTC). Defaults to today. The range must cover 1 to 366 days. -
sourcestringOnly usage recorded by this service:
chat(Chat),agenthub(Agent Hub),networks(Networks),data(Data), orworker(background jobs). Usage from services this reference does not cover is reported under their own identifiers; List usage by service returns every identifier with recorded usage.Possible values-
chat -
agenthub -
networks -
data -
worker
-
-
user_idstringOnly usage attributed to this user ID, a positive integer, or
nonefor usage without a user. -
searchstringOnly usage by users whose name or email address contains this text, matched case-insensitively. Up to 256 characters.
-
request_idstringOnly usage with exactly this request ID. Up to 256 characters.
-
event_idstringOnly the event with exactly this event ID. Up to 256 characters.
Returns
Returns totals, daily (one row per day with a day date and the same metrics), and generated_at in data. The metrics are tokens_in, tokens_out, tokens_embed, tokens (their sum), estimated_tokens, transactions, and events.
Errors
- 400 A filter is invalid: a date is not
YYYY-MM-DD, the range is outside 1 to 366 days,sourceis unknown,user_idis neither a positive integer nornone, or a text filter is longer than 256 characters. - 403 The caller is not an administrator, or the installation is not on-premises.
- 500 The report could not be produced.
curl "$VDF_BASE_URL/api/license/usage/report?date_from=2026-08-01&date_to=2026-08-31" \
-H "Authorization: Bearer $VDF_ACCESS_TOKEN" const response = await fetch(`${process.env.VDF_BASE_URL}/api/license/usage/report?date_from=2026-08-01&date_to=2026-08-31`, {
headers: {
Authorization: `Bearer ${process.env.VDF_ACCESS_TOKEN}`,
},
});
if (!response.ok) throw new Error(`Request failed with status ${response.status}`);
const data = await response.json(); import os
import requests
response = requests.get(
f"{os.environ['VDF_BASE_URL']}/api/license/usage/report",
headers={"Authorization": f"Bearer {os.environ['VDF_ACCESS_TOKEN']}"},
params={
"date_from": "2026-08-01",
"date_to": "2026-08-31",
},
timeout=30,
)
response.raise_for_status()
data = response.json() {
"success": true,
"data": {
"totals": {
"tokens_in": 12040112,
"tokens_out": 5620488,
"tokens_embed": 789610,
"tokens": 18450210,
"estimated_tokens": 402115,
"transactions": 12877,
"events": 13240
},
"daily": [
{
"day": "2026-08-01",
"tokens_in": 40210,
"tokens_out": 18840,
"tokens_embed": 0,
"tokens": 59050,
"estimated_tokens": 0,
"transactions": 36,
"events": 37
},
{
"day": "2026-08-02",
"tokens_in": 0,
"tokens_out": 0,
"tokens_embed": 0,
"tokens": 0,
"estimated_tokens": 0,
"transactions": 0,
"events": 0
}
],
"generated_at": "2026-09-11T08:30:00.731204+00:00"
}
} List usage by service
GET /api/license/usage/services
Returns usage totals per service for a date range.
Returns one row for each service that recorded usage matching the filters, in alphabetical order of source. Available only on on-premises installations.
- Authentication
- Bearer token How it works
- Permission
- Requires an administrator account.
Query parameters
-
date_fromstringFirst day of the range, inclusive, as
YYYY-MM-DD(UTC). Defaults to 29 days before today. -
date_tostringLast day of the range, inclusive, as
YYYY-MM-DD(UTC). Defaults to today. The range must cover 1 to 366 days. -
sourcestringOnly usage recorded by this service:
chat(Chat),agenthub(Agent Hub),networks(Networks),data(Data), orworker(background jobs). Usage from services this reference does not cover is reported under their own identifiers; List usage by service returns every identifier with recorded usage.Possible values-
chat -
agenthub -
networks -
data -
worker
-
-
user_idstringOnly usage attributed to this user ID, a positive integer, or
nonefor usage without a user. -
searchstringOnly usage by users whose name or email address contains this text, matched case-insensitively. Up to 256 characters.
-
request_idstringOnly usage with exactly this request ID. Up to 256 characters.
-
event_idstringOnly the event with exactly this event ID. Up to 256 characters.
Returns
Returns an array in data with one row per service: source and the metrics tokens_in, tokens_out, tokens_embed, tokens, estimated_tokens, transactions, and events.
Errors
- 400 A filter is invalid: a date is not
YYYY-MM-DD, the range is outside 1 to 366 days,sourceis unknown,user_idis neither a positive integer nornone, or a text filter is longer than 256 characters. - 403 The caller is not an administrator, or the installation is not on-premises.
- 500 The report could not be produced.
curl "$VDF_BASE_URL/api/license/usage/services?date_from=2026-08-01&date_to=2026-08-31" \
-H "Authorization: Bearer $VDF_ACCESS_TOKEN" const response = await fetch(`${process.env.VDF_BASE_URL}/api/license/usage/services?date_from=2026-08-01&date_to=2026-08-31`, {
headers: {
Authorization: `Bearer ${process.env.VDF_ACCESS_TOKEN}`,
},
});
if (!response.ok) throw new Error(`Request failed with status ${response.status}`);
const data = await response.json(); import os
import requests
response = requests.get(
f"{os.environ['VDF_BASE_URL']}/api/license/usage/services",
headers={"Authorization": f"Bearer {os.environ['VDF_ACCESS_TOKEN']}"},
params={
"date_from": "2026-08-01",
"date_to": "2026-08-31",
},
timeout=30,
)
response.raise_for_status()
data = response.json() {
"success": true,
"data": [
{
"source": "agenthub",
"tokens_in": 7600200,
"tokens_out": 3650200,
"tokens_embed": 0,
"tokens": 11250400,
"estimated_tokens": 402115,
"transactions": 8120,
"events": 8390
},
{
"source": "chat",
"tokens_in": 4439912,
"tokens_out": 1970288,
"tokens_embed": 789610,
"tokens": 7199810,
"estimated_tokens": 0,
"transactions": 4757,
"events": 4850
}
]
} List usage by user
GET /api/license/usage/users
Returns a page of usage totals per user for a date range.
Returns one row per user, ordered by tokens, highest first, and then by user ID. Usage without a user is grouped into a single row whose user_id is null. Available only on on-premises installations.
- Authentication
- Bearer token How it works
- Permission
- Requires an administrator account.
Query parameters
-
date_fromstringFirst day of the range, inclusive, as
YYYY-MM-DD(UTC). Defaults to 29 days before today. -
date_tostringLast day of the range, inclusive, as
YYYY-MM-DD(UTC). Defaults to today. The range must cover 1 to 366 days. -
sourcestringOnly usage recorded by this service:
chat(Chat),agenthub(Agent Hub),networks(Networks),data(Data), orworker(background jobs). Usage from services this reference does not cover is reported under their own identifiers; List usage by service returns every identifier with recorded usage.Possible values-
chat -
agenthub -
networks -
data -
worker
-
-
user_idstringOnly usage attributed to this user ID, a positive integer, or
nonefor usage without a user. -
searchstringOnly usage by users whose name or email address contains this text, matched case-insensitively. Up to 256 characters.
-
request_idstringOnly usage with exactly this request ID. Up to 256 characters.
-
event_idstringOnly the event with exactly this event ID. Up to 256 characters.
-
pageintegerPage number, starting at 1.
-
limitintegerRows per page, from 1 to 100.
Returns
Returns a page in data: items, total, page, limit, and pages. Each item has user_id, user_name, user_email, user_label (the user's name or email address, a label such as Deleted user (ID 57) for a user that no longer exists, or No user information for usage without a user), and the metrics tokens_in, tokens_out, tokens_embed, tokens, estimated_tokens, transactions, and events.
Errors
- 400 A filter is invalid (see Retrieve a usage report), or
pageorlimitis not a positive integer orlimitis above 100. - 403 The caller is not an administrator, or the installation is not on-premises.
- 500 The report could not be produced.
curl "$VDF_BASE_URL/api/license/usage/users?date_from=2026-08-01&date_to=2026-08-31&page=1&limit=50" \
-H "Authorization: Bearer $VDF_ACCESS_TOKEN" const response = await fetch(`${process.env.VDF_BASE_URL}/api/license/usage/users?date_from=2026-08-01&date_to=2026-08-31&page=1&limit=50`, {
headers: {
Authorization: `Bearer ${process.env.VDF_ACCESS_TOKEN}`,
},
});
if (!response.ok) throw new Error(`Request failed with status ${response.status}`);
const data = await response.json(); import os
import requests
response = requests.get(
f"{os.environ['VDF_BASE_URL']}/api/license/usage/users",
headers={"Authorization": f"Bearer {os.environ['VDF_ACCESS_TOKEN']}"},
params={
"date_from": "2026-08-01",
"date_to": "2026-08-31",
"page": 1,
"limit": 50,
},
timeout=30,
)
response.raise_for_status()
data = response.json() {
"success": true,
"data": {
"items": [
{
"user_id": 42,
"user_name": "Ada Lovelace",
"user_email": "ada@example.com",
"user_label": "Ada Lovelace",
"tokens_in": 2104400,
"tokens_out": 980100,
"tokens_embed": 120300,
"tokens": 3204800,
"estimated_tokens": 0,
"transactions": 1904,
"events": 1950
},
{
"user_id": 57,
"user_name": "Grace Hopper",
"user_email": "grace@example.com",
"user_label": "Grace Hopper",
"tokens_in": 1650300,
"tokens_out": 702200,
"tokens_embed": 0,
"tokens": 2352500,
"estimated_tokens": 12040,
"transactions": 1417,
"events": 1431
}
],
"total": 18,
"page": 1,
"limit": 50,
"pages": 1
}
} List usage events
GET /api/license/usage/events
Returns a page of individual usage events, newest first.
Returns the usage events that match the filters, newest first. Available only on on-premises installations.
- Authentication
- Bearer token How it works
- Permission
- Requires an administrator account.
Query parameters
-
date_fromstringFirst day of the range, inclusive, as
YYYY-MM-DD(UTC). Defaults to 29 days before today. -
date_tostringLast day of the range, inclusive, as
YYYY-MM-DD(UTC). Defaults to today. The range must cover 1 to 366 days. -
sourcestringOnly usage recorded by this service:
chat(Chat),agenthub(Agent Hub),networks(Networks),data(Data), orworker(background jobs). Usage from services this reference does not cover is reported under their own identifiers; List usage by service returns every identifier with recorded usage.Possible values-
chat -
agenthub -
networks -
data -
worker
-
-
user_idstringOnly usage attributed to this user ID, a positive integer, or
nonefor usage without a user. -
searchstringOnly usage by users whose name or email address contains this text, matched case-insensitively. Up to 256 characters.
-
request_idstringOnly usage with exactly this request ID. Up to 256 characters.
-
event_idstringOnly the event with exactly this event ID. Up to 256 characters.
-
pageintegerPage number, starting at 1.
-
limitintegerRows per page, from 1 to 100.
Returns
Returns a page in data: items, total, page, limit, and pages. Each item has id, occurred_at, source, user_id, user_name, user_email, user_label, event_id, request_id, tokens_in, tokens_out, tokens_embed, tokens, transactions (the transactions billed for the event), success, and estimated (whether the token counts were estimated rather than reported by the model).
Errors
- 400 A filter is invalid (see Retrieve a usage report), or
pageorlimitis not a positive integer orlimitis above 100. - 403 The caller is not an administrator, or the installation is not on-premises.
- 500 The report could not be produced.
curl "$VDF_BASE_URL/api/license/usage/events?date_from=2026-08-01&date_to=2026-08-31&source=agenthub" \
-H "Authorization: Bearer $VDF_ACCESS_TOKEN" const response = await fetch(`${process.env.VDF_BASE_URL}/api/license/usage/events?date_from=2026-08-01&date_to=2026-08-31&source=agenthub`, {
headers: {
Authorization: `Bearer ${process.env.VDF_ACCESS_TOKEN}`,
},
});
if (!response.ok) throw new Error(`Request failed with status ${response.status}`);
const data = await response.json(); import os
import requests
response = requests.get(
f"{os.environ['VDF_BASE_URL']}/api/license/usage/events",
headers={"Authorization": f"Bearer {os.environ['VDF_ACCESS_TOKEN']}"},
params={
"date_from": "2026-08-01",
"date_to": "2026-08-31",
"source": "agenthub",
},
timeout=30,
)
response.raise_for_status()
data = response.json() {
"success": true,
"data": {
"items": [
{
"id": 88412,
"occurred_at": "2026-08-31T17:42:10.118204+00:00",
"source": "agenthub",
"user_id": 42,
"user_name": "Ada Lovelace",
"user_email": "ada@example.com",
"user_label": "Ada Lovelace",
"event_id": "agenthub:6f1c2a9e-4b7d-4e2a-9c3f-2d8e5b1a7c40:execution",
"request_id": "6f1c2a9e-4b7d-4e2a-9c3f-2d8e5b1a7c40",
"tokens_in": 1820,
"tokens_out": 640,
"tokens_embed": 0,
"tokens": 2460,
"transactions": 1,
"success": true,
"estimated": false
}
],
"total": 8390,
"page": 1,
"limit": 50,
"pages": 168
}
} Export a usage report
GET /api/license/usage/report/export
Downloads usage by service, user, or event as a CSV file.
Exports every row that matches the filters, not just one page, as CSV (text/csv; charset=utf-8), sent as an attachment named vdf-usage-<kind>-<date_from>.csv. The file starts with a UTF-8 byte order mark and a header row, and timestamps are in UTC ISO 8601. Text cells that begin with =, +, -, or @ (after any leading spaces), or with a tab or line break, are prefixed with an apostrophe so that spreadsheet applications don't evaluate them. An export is limited to 100,000 rows; a larger result is refused with 422 rather than truncated.
The columns depend on kind:
services:source, then the metrics.users:user_id,user_label,user_email, then the metrics.events:id,occurred_at,source,user_id,user_label,user_email,event_id,request_id,tokens_in,tokens_out,tokens_embed,tokens,transactions,success, andestimated.
The metrics are tokens_in, tokens_out, tokens_embed, tokens, estimated_tokens, transactions, and events. Available only on on-premises installations.
- Authentication
- Bearer token How it works
- Permission
- Requires an administrator account.
Query parameters
-
kindstringWhat each row of the export represents.
Possible values-
services -
users -
events
-
-
date_fromstringFirst day of the range, inclusive, as
YYYY-MM-DD(UTC). Defaults to 29 days before today. -
date_tostringLast day of the range, inclusive, as
YYYY-MM-DD(UTC). Defaults to today. The range must cover 1 to 366 days. -
sourcestringOnly usage recorded by this service:
chat(Chat),agenthub(Agent Hub),networks(Networks),data(Data), orworker(background jobs). Usage from services this reference does not cover is reported under their own identifiers; List usage by service returns every identifier with recorded usage.Possible values-
chat -
agenthub -
networks -
data -
worker
-
-
user_idstringOnly usage attributed to this user ID, a positive integer, or
nonefor usage without a user. -
searchstringOnly usage by users whose name or email address contains this text, matched case-insensitively. Up to 256 characters.
-
request_idstringOnly usage with exactly this request ID. Up to 256 characters.
-
event_idstringOnly the event with exactly this event ID. Up to 256 characters.
Returns
Returns the matching rows as a CSV file download.
Errors
- 400
kindis notservices,users, orevents, or a filter is invalid (see Retrieve a usage report). - 403 The caller is not an administrator, or the installation is not on-premises.
- 422 More than 100,000 rows match. Narrow the date range or filters.
- 500 The export could not be produced.
curl "$VDF_BASE_URL/api/license/usage/report/export?kind=users&date_from=2026-08-01&date_to=2026-08-31" \
-H "Authorization: Bearer $VDF_ACCESS_TOKEN" \
-o export.bin const response = await fetch(`${process.env.VDF_BASE_URL}/api/license/usage/report/export?kind=users&date_from=2026-08-01&date_to=2026-08-31`, {
headers: {
Authorization: `Bearer ${process.env.VDF_ACCESS_TOKEN}`,
},
});
if (!response.ok) throw new Error(`Request failed with status ${response.status}`);
const file = await response.blob(); import os
import requests
response = requests.get(
f"{os.environ['VDF_BASE_URL']}/api/license/usage/report/export",
headers={"Authorization": f"Bearer {os.environ['VDF_ACCESS_TOKEN']}"},
params={
"kind": "users",
"date_from": "2026-08-01",
"date_to": "2026-08-31",
},
stream=True,
timeout=30,
)
response.raise_for_status()
with open("export.bin", "wb") as output:
for chunk in response.iter_content(chunk_size=65536):
output.write(chunk)