Workspace and usage
Your workspace (tenant) is the boundary every other resource lives in. myTenant and apiUsageSummary take a dashboard JWT and describe the whole workspace: its plan, its allowed models and the month’s model spend against the plan’s cap. customerUsageSummary takes an API key and rolls up the usage of the sessions you started for one end customer (the clientId you passed to sdkStart), so a server-side integration can report or bill usage per customer or per ticket.
Every operation is a POST of { "query", "variables" } to https://api.wetel.dev/graphql (sandbox) with the x-huat-platform: customer header and the credential shown. New here? Start with the Quickstart and the Authentication cheat sheet. Errors are listed in Errors; Postman and OpenAPI files are on Downloads.
| Operation | Kind | Credential | What it does |
|---|---|---|---|
myTenant | query | Dashboard JWT | Read the workspace the signed-in user belongs to. |
apiUsageSummary | query | Dashboard JWT | Model spend for one calendar month, the plan’s monthly cap, and a breakdown by model and operation. |
customerUsageSummary | query | API key | Tokens, estimated cost and session count across every session of one end customer. |
myTenant
Section titled “myTenant”Read the workspace the signed-in user belongs to.
- Kind: query
- Credential: Dashboard JWT:
Authorization: Bearer <jwt>, fromlogin(a person or a one-off script) - Available in: sandbox and production
- Returns
nullfor a user who is not assigned to a workspace yet. allowedLlmModelsis the workspace’s model allowlist:nullmeans every model in the catalog, an empty list means none.
Request
query MyWorkspace { myTenant { id name plan isSuspended allowedLlmModels createdAt }}{}Allowed values
TenantPlanEnum:DEV,ENTERPRISE,PRO
Code samples
#!/usr/bin/env bash# myTenant: Read the workspace the signed-in user belongs to.# Credential: Dashboard JWT (WETEL_JWT).API_URL="${WETEL_API_URL:-https://api.wetel.dev/graphql}"
curl -sS "$API_URL" \ -H "Content-Type: application/json" \ -H "x-huat-platform: customer" \ -H "Authorization: Bearer $WETEL_JWT" \ --data-binary @- <<'JSON'{ "query": "query MyWorkspace{myTenant{id name plan isSuspended allowedLlmModels createdAt}}", "variables": {}}JSON// myTenant: Read the workspace the signed-in user belongs to.// Credential: Dashboard JWT (WETEL_JWT). Node 18+ (built-in fetch).const API_URL = process.env.WETEL_API_URL ?? "https://api.wetel.dev/graphql";
const query = `query MyWorkspace { myTenant { id name plan isSuspended allowedLlmModels createdAt }}`;
const variables = {};
type GraphQLResponse = { data?: Record<string, unknown>; errors?: { message: string; extensions?: { code?: string } }[];};
async function main(): Promise<void> { const res = await fetch(API_URL, { method: "POST", headers: { "Content-Type": "application/json", "x-huat-platform": "customer", Authorization: `Bearer ${process.env.WETEL_JWT ?? ""}`, }, body: JSON.stringify({ query, variables }), }); const body = (await res.json()) as GraphQLResponse; if (body.errors?.length) { const error = body.errors[0]; throw new Error(`${error.extensions?.code}: ${error.message}`); } console.log(JSON.stringify(body.data?.myTenant, null, 2));}
main().catch(err => { console.error(err); process.exit(1);});
export {};<?php// myTenant: Read the workspace the signed-in user belongs to.// Credential: Dashboard JWT (WETEL_JWT). PHP 7.3+ with the curl extension.$apiUrl = getenv('WETEL_API_URL') ?: 'https://api.wetel.dev/graphql';
$query = <<<'GRAPHQL'query MyWorkspace { myTenant { id name plan isSuspended allowedLlmModels createdAt }}GRAPHQL;
$variables = json_decode(<<<'JSON'{}JSON);
$ch = curl_init($apiUrl);curl_setopt_array($ch, [ CURLOPT_POST => true, CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ 'Content-Type: application/json', 'x-huat-platform: customer', 'Authorization: Bearer ' . getenv('WETEL_JWT'), ], CURLOPT_POSTFIELDS => json_encode(['query' => $query, 'variables' => $variables]),]);$body = json_decode(curl_exec($ch), true);curl_close($ch);
if (!empty($body['errors'])) { $error = $body['errors'][0]; fwrite(STDERR, ($error['extensions']['code'] ?? 'ERROR') . ': ' . $error['message'] . PHP_EOL); exit(1);}echo json_encode($body['data']['myTenant'], JSON_PRETTY_PRINT), PHP_EOL;// myTenant: Read the workspace the signed-in user belongs to.// Credential: Dashboard JWT (WETEL_JWT). .NET 8: put this in Program.cs of a console app and run "dotnet run".using System.Net.Http.Headers;using System.Net.Http.Json;using System.Text.Json;using System.Text.Json.Nodes;
var apiUrl = Environment.GetEnvironmentVariable("WETEL_API_URL") ?? "https://api.wetel.dev/graphql";
const string query = """ query MyWorkspace { myTenant { id name plan isSuspended allowedLlmModels createdAt } } """;
var variables = JsonNode.Parse(""" {} """);
using var http = new HttpClient();using var request = new HttpRequestMessage(HttpMethod.Post, apiUrl){ Content = JsonContent.Create(new { query, variables }),};request.Headers.Add("x-huat-platform", "customer");request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", Environment.GetEnvironmentVariable("WETEL_JWT"));
using var response = await http.SendAsync(request);var body = JsonNode.Parse(await response.Content.ReadAsStringAsync())!;if (body["errors"] is JsonArray errors && errors.Count > 0){ Console.Error.WriteLine($"{errors[0]?["extensions"]?["code"]}: {errors[0]?["message"]}"); return 1;}Console.WriteLine(body["data"]?["myTenant"]?.ToJsonString(new JsonSerializerOptions { WriteIndented = true }));return 0;# myTenant: Read the workspace the signed-in user belongs to.# Credential: Dashboard JWT (WETEL_JWT). Python 3.8+ with "requests".import jsonimport os
import requests
API_URL = os.environ.get("WETEL_API_URL", "https://api.wetel.dev/graphql")
QUERY = """query MyWorkspace { myTenant { id name plan isSuspended allowedLlmModels createdAt }}"""
VARIABLES = {}
response = requests.post( API_URL, json={"query": QUERY, "variables": VARIABLES}, headers={ "x-huat-platform": "customer", "Authorization": "Bearer " + os.environ["WETEL_JWT"], }, timeout=30,)body = response.json()if body.get("errors"): error = body["errors"][0] raise SystemExit(f"{error.get('extensions', {}).get('code')}: {error['message']}")print(json.dumps(body["data"]["myTenant"], indent=2))Responses
Success (HTTP 200)
{ "data": { "myTenant": { "id": 3, "name": "Example Support Team", "plan": "PRO", "isSuspended": false, "allowedLlmModels": null, "createdAt": "2026-08-04T02:10:00.000Z" } }}UNAUTHENTICATED: Called with an API key instead of a JWT (HTTP 200)
{ "errors": [ { "message": "Unauthorized", "locations": [ { "line": 2, "column": 3 } ], "path": [ "myTenant" ], "extensions": { "code": "UNAUTHENTICATED", "originalError": { "message": "Unauthorized", "statusCode": 401 } } } ], "data": { "myTenant": null }}apiUsageSummary
Section titled “apiUsageSummary”Model spend for one calendar month, the plan’s monthly cap, and a breakdown by model and operation.
- Kind: query
- Credential: Dashboard JWT:
Authorization: Bearer <jwt>, fromlogin(a person or a one-off script) - Available in: sandbox and production
- Covers all model usage of the workspace (sessions, workflow runs, evaluation and the LLM API). It cannot be split by API key; use
customerUsageSummaryfor one end customer. yearMonth(YYYY-MM, UTC) reads a past month; omit it for the current month. Every figure covers that month, whatever the field name says.usageByBucketalways has the four rowsAI_CREDITS,TTS,STTandAVATAR, even when a bucket is zero.
Request
query Usage($yearMonth: String) { apiUsageSummary(yearMonth: $yearMonth) { yearMonth costThisMonthUsd monthlySpendCapUsd creditsUsedThisMonthApprox usageByBucket { bucket callCount costUsd creditsUsed } breakdown { model operation count costUsd creditsUsed } }}{ "yearMonth": "2026-09"}Allowed values
UsageBucketEnum:AI_CREDITS,AVATAR,STT,TTS
Code samples
#!/usr/bin/env bash# apiUsageSummary: Model spend for one calendar month, the plan's monthly cap, and a breakdown by model and operation.# Credential: Dashboard JWT (WETEL_JWT).API_URL="${WETEL_API_URL:-https://api.wetel.dev/graphql}"
curl -sS "$API_URL" \ -H "Content-Type: application/json" \ -H "x-huat-platform: customer" \ -H "Authorization: Bearer $WETEL_JWT" \ --data-binary @- <<'JSON'{ "query": "query Usage($yearMonth:String){apiUsageSummary(yearMonth:$yearMonth){yearMonth costThisMonthUsd monthlySpendCapUsd creditsUsedThisMonthApprox usageByBucket{bucket callCount costUsd creditsUsed}breakdown{model operation count costUsd creditsUsed}}}", "variables": { "yearMonth": "2026-09" }}JSON// apiUsageSummary: Model spend for one calendar month, the plan's monthly cap, and a breakdown by model and operation.// Credential: Dashboard JWT (WETEL_JWT). Node 18+ (built-in fetch).const API_URL = process.env.WETEL_API_URL ?? "https://api.wetel.dev/graphql";
const query = `query Usage($yearMonth: String) { apiUsageSummary(yearMonth: $yearMonth) { yearMonth costThisMonthUsd monthlySpendCapUsd creditsUsedThisMonthApprox usageByBucket { bucket callCount costUsd creditsUsed } breakdown { model operation count costUsd creditsUsed } }}`;
const variables = { yearMonth: "2026-09",};
type GraphQLResponse = { data?: Record<string, unknown>; errors?: { message: string; extensions?: { code?: string } }[];};
async function main(): Promise<void> { const res = await fetch(API_URL, { method: "POST", headers: { "Content-Type": "application/json", "x-huat-platform": "customer", Authorization: `Bearer ${process.env.WETEL_JWT ?? ""}`, }, body: JSON.stringify({ query, variables }), }); const body = (await res.json()) as GraphQLResponse; if (body.errors?.length) { const error = body.errors[0]; throw new Error(`${error.extensions?.code}: ${error.message}`); } console.log(JSON.stringify(body.data?.apiUsageSummary, null, 2));}
main().catch(err => { console.error(err); process.exit(1);});
export {};<?php// apiUsageSummary: Model spend for one calendar month, the plan's monthly cap, and a breakdown by model and operation.// Credential: Dashboard JWT (WETEL_JWT). PHP 7.3+ with the curl extension.$apiUrl = getenv('WETEL_API_URL') ?: 'https://api.wetel.dev/graphql';
$query = <<<'GRAPHQL'query Usage($yearMonth: String) { apiUsageSummary(yearMonth: $yearMonth) { yearMonth costThisMonthUsd monthlySpendCapUsd creditsUsedThisMonthApprox usageByBucket { bucket callCount costUsd creditsUsed } breakdown { model operation count costUsd creditsUsed } }}GRAPHQL;
$variables = json_decode(<<<'JSON'{ "yearMonth": "2026-09"}JSON);
$ch = curl_init($apiUrl);curl_setopt_array($ch, [ CURLOPT_POST => true, CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ 'Content-Type: application/json', 'x-huat-platform: customer', 'Authorization: Bearer ' . getenv('WETEL_JWT'), ], CURLOPT_POSTFIELDS => json_encode(['query' => $query, 'variables' => $variables]),]);$body = json_decode(curl_exec($ch), true);curl_close($ch);
if (!empty($body['errors'])) { $error = $body['errors'][0]; fwrite(STDERR, ($error['extensions']['code'] ?? 'ERROR') . ': ' . $error['message'] . PHP_EOL); exit(1);}echo json_encode($body['data']['apiUsageSummary'], JSON_PRETTY_PRINT), PHP_EOL;// apiUsageSummary: Model spend for one calendar month, the plan's monthly cap, and a breakdown by model and operation.// Credential: Dashboard JWT (WETEL_JWT). .NET 8: put this in Program.cs of a console app and run "dotnet run".using System.Net.Http.Headers;using System.Net.Http.Json;using System.Text.Json;using System.Text.Json.Nodes;
var apiUrl = Environment.GetEnvironmentVariable("WETEL_API_URL") ?? "https://api.wetel.dev/graphql";
const string query = """ query Usage($yearMonth: String) { apiUsageSummary(yearMonth: $yearMonth) { yearMonth costThisMonthUsd monthlySpendCapUsd creditsUsedThisMonthApprox usageByBucket { bucket callCount costUsd creditsUsed } breakdown { model operation count costUsd creditsUsed } } } """;
var variables = JsonNode.Parse(""" { "yearMonth": "2026-09" } """);
using var http = new HttpClient();using var request = new HttpRequestMessage(HttpMethod.Post, apiUrl){ Content = JsonContent.Create(new { query, variables }),};request.Headers.Add("x-huat-platform", "customer");request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", Environment.GetEnvironmentVariable("WETEL_JWT"));
using var response = await http.SendAsync(request);var body = JsonNode.Parse(await response.Content.ReadAsStringAsync())!;if (body["errors"] is JsonArray errors && errors.Count > 0){ Console.Error.WriteLine($"{errors[0]?["extensions"]?["code"]}: {errors[0]?["message"]}"); return 1;}Console.WriteLine(body["data"]?["apiUsageSummary"]?.ToJsonString(new JsonSerializerOptions { WriteIndented = true }));return 0;# apiUsageSummary: Model spend for one calendar month, the plan's monthly cap, and a breakdown by model and operation.# Credential: Dashboard JWT (WETEL_JWT). Python 3.8+ with "requests".import jsonimport os
import requests
API_URL = os.environ.get("WETEL_API_URL", "https://api.wetel.dev/graphql")
QUERY = """query Usage($yearMonth: String) { apiUsageSummary(yearMonth: $yearMonth) { yearMonth costThisMonthUsd monthlySpendCapUsd creditsUsedThisMonthApprox usageByBucket { bucket callCount costUsd creditsUsed } breakdown { model operation count costUsd creditsUsed } }}"""
VARIABLES = { "yearMonth": "2026-09",}
response = requests.post( API_URL, json={"query": QUERY, "variables": VARIABLES}, headers={ "x-huat-platform": "customer", "Authorization": "Bearer " + os.environ["WETEL_JWT"], }, timeout=30,)body = response.json()if body.get("errors"): error = body["errors"][0] raise SystemExit(f"{error.get('extensions', {}).get('code')}: {error['message']}")print(json.dumps(body["data"]["apiUsageSummary"], indent=2))Responses
Success (HTTP 200)
{ "data": { "apiUsageSummary": { "yearMonth": "2026-09", "costThisMonthUsd": 3.4182, "monthlySpendCapUsd": 200, "creditsUsedThisMonthApprox": 97419, "usageByBucket": [ { "bucket": "AI_CREDITS", "callCount": 1840, "costUsd": 3.2011, "creditsUsed": 91231 }, { "bucket": "TTS", "callCount": 52, "costUsd": 0.2171, "creditsUsed": 6188 }, { "bucket": "STT", "callCount": 0, "costUsd": 0, "creditsUsed": 0 }, { "bucket": "AVATAR", "callCount": 0, "costUsd": 0, "creditsUsed": 0 } ], "breakdown": [ { "model": "gemini-2.5-flash", "operation": "generate", "count": 1712, "costUsd": 3.1904, "creditsUsed": 90926 }, { "model": "gemini-embedding-001", "operation": "embed", "count": 128, "costUsd": 0.0107, "creditsUsed": 305 } ] } }}BAD_REQUEST: Month in the future or badly formatted (HTTP 200)
{ "errors": [ { "message": "yearMonth must be a past or current month formatted 'YYYY-MM'", "locations": [ { "line": 2, "column": 3 } ], "path": [ "apiUsageSummary" ], "extensions": { "code": "BAD_REQUEST", "originalError": { "message": "yearMonth must be a past or current month formatted 'YYYY-MM'", "error": "Bad Request", "statusCode": 400 } } } ], "data": null}UNAUTHENTICATED: Called with an API key instead of a JWT (HTTP 200)
{ "errors": [ { "message": "Unauthorized", "locations": [ { "line": 2, "column": 3 } ], "path": [ "apiUsageSummary" ], "extensions": { "code": "UNAUTHENTICATED", "originalError": { "message": "Unauthorized", "statusCode": 401 } } } ], "data": null}customerUsageSummary
Section titled “customerUsageSummary”Tokens, estimated cost and session count across every session of one end customer.
- Kind: query
- Credential: API key:
X-Api-Key: sk_..., kept on your server only - Available in: sandbox and production
externalCustomerIdis theclientIdyou passed tosdkStart. Give each ticket its ownclientIdto read usage per ticket.costUsdis an estimate (about 4 characters per token), not a billing figure; the workspace’s billed spend is inapiUsageSummary.- An identifier with no sessions returns zeros, not an error.
Request
query CustomerUsage($externalCustomerId: String!) { customerUsageSummary(externalCustomerId: $externalCustomerId) { externalCustomerId sessionCount tokensIn tokensOut costUsd creditsUsedApprox }}{ "externalCustomerId": "ticket-1001"}Code samples
#!/usr/bin/env bash# customerUsageSummary: Tokens, estimated cost and session count across every session of one end customer.# Credential: API key (WETEL_API_KEY).API_URL="${WETEL_API_URL:-https://api.wetel.dev/graphql}"
curl -sS "$API_URL" \ -H "Content-Type: application/json" \ -H "x-huat-platform: customer" \ -H "X-Api-Key: $WETEL_API_KEY" \ --data-binary @- <<'JSON'{ "query": "query CustomerUsage($externalCustomerId:String!){customerUsageSummary(externalCustomerId:$externalCustomerId){externalCustomerId sessionCount tokensIn tokensOut costUsd creditsUsedApprox}}", "variables": { "externalCustomerId": "ticket-1001" }}JSON// customerUsageSummary: Tokens, estimated cost and session count across every session of one end customer.// Credential: API key (WETEL_API_KEY). Node 18+ (built-in fetch).const API_URL = process.env.WETEL_API_URL ?? "https://api.wetel.dev/graphql";
const query = `query CustomerUsage($externalCustomerId: String!) { customerUsageSummary(externalCustomerId: $externalCustomerId) { externalCustomerId sessionCount tokensIn tokensOut costUsd creditsUsedApprox }}`;
const variables = { externalCustomerId: "ticket-1001",};
type GraphQLResponse = { data?: Record<string, unknown>; errors?: { message: string; extensions?: { code?: string } }[];};
async function main(): Promise<void> { const res = await fetch(API_URL, { method: "POST", headers: { "Content-Type": "application/json", "x-huat-platform": "customer", "X-Api-Key": process.env.WETEL_API_KEY ?? "", }, body: JSON.stringify({ query, variables }), }); const body = (await res.json()) as GraphQLResponse; if (body.errors?.length) { const error = body.errors[0]; throw new Error(`${error.extensions?.code}: ${error.message}`); } console.log(JSON.stringify(body.data?.customerUsageSummary, null, 2));}
main().catch(err => { console.error(err); process.exit(1);});
export {};<?php// customerUsageSummary: Tokens, estimated cost and session count across every session of one end customer.// Credential: API key (WETEL_API_KEY). PHP 7.3+ with the curl extension.$apiUrl = getenv('WETEL_API_URL') ?: 'https://api.wetel.dev/graphql';
$query = <<<'GRAPHQL'query CustomerUsage($externalCustomerId: String!) { customerUsageSummary(externalCustomerId: $externalCustomerId) { externalCustomerId sessionCount tokensIn tokensOut costUsd creditsUsedApprox }}GRAPHQL;
$variables = json_decode(<<<'JSON'{ "externalCustomerId": "ticket-1001"}JSON);
$ch = curl_init($apiUrl);curl_setopt_array($ch, [ CURLOPT_POST => true, CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ 'Content-Type: application/json', 'x-huat-platform: customer', 'X-Api-Key: ' . getenv('WETEL_API_KEY'), ], CURLOPT_POSTFIELDS => json_encode(['query' => $query, 'variables' => $variables]),]);$body = json_decode(curl_exec($ch), true);curl_close($ch);
if (!empty($body['errors'])) { $error = $body['errors'][0]; fwrite(STDERR, ($error['extensions']['code'] ?? 'ERROR') . ': ' . $error['message'] . PHP_EOL); exit(1);}echo json_encode($body['data']['customerUsageSummary'], JSON_PRETTY_PRINT), PHP_EOL;// customerUsageSummary: Tokens, estimated cost and session count across every session of one end customer.// Credential: API key (WETEL_API_KEY). .NET 8: put this in Program.cs of a console app and run "dotnet run".using System.Net.Http.Headers;using System.Net.Http.Json;using System.Text.Json;using System.Text.Json.Nodes;
var apiUrl = Environment.GetEnvironmentVariable("WETEL_API_URL") ?? "https://api.wetel.dev/graphql";
const string query = """ query CustomerUsage($externalCustomerId: String!) { customerUsageSummary(externalCustomerId: $externalCustomerId) { externalCustomerId sessionCount tokensIn tokensOut costUsd creditsUsedApprox } } """;
var variables = JsonNode.Parse(""" { "externalCustomerId": "ticket-1001" } """);
using var http = new HttpClient();using var request = new HttpRequestMessage(HttpMethod.Post, apiUrl){ Content = JsonContent.Create(new { query, variables }),};request.Headers.Add("x-huat-platform", "customer");request.Headers.Add("X-Api-Key", Environment.GetEnvironmentVariable("WETEL_API_KEY"));
using var response = await http.SendAsync(request);var body = JsonNode.Parse(await response.Content.ReadAsStringAsync())!;if (body["errors"] is JsonArray errors && errors.Count > 0){ Console.Error.WriteLine($"{errors[0]?["extensions"]?["code"]}: {errors[0]?["message"]}"); return 1;}Console.WriteLine(body["data"]?["customerUsageSummary"]?.ToJsonString(new JsonSerializerOptions { WriteIndented = true }));return 0;# customerUsageSummary: Tokens, estimated cost and session count across every session of one end customer.# Credential: API key (WETEL_API_KEY). Python 3.8+ with "requests".import jsonimport os
import requests
API_URL = os.environ.get("WETEL_API_URL", "https://api.wetel.dev/graphql")
QUERY = """query CustomerUsage($externalCustomerId: String!) { customerUsageSummary(externalCustomerId: $externalCustomerId) { externalCustomerId sessionCount tokensIn tokensOut costUsd creditsUsedApprox }}"""
VARIABLES = { "externalCustomerId": "ticket-1001",}
response = requests.post( API_URL, json={"query": QUERY, "variables": VARIABLES}, headers={ "x-huat-platform": "customer", "X-Api-Key": os.environ["WETEL_API_KEY"], }, timeout=30,)body = response.json()if body.get("errors"): error = body["errors"][0] raise SystemExit(f"{error.get('extensions', {}).get('code')}: {error['message']}")print(json.dumps(body["data"]["customerUsageSummary"], indent=2))Responses
Success (HTTP 200)
{ "data": { "customerUsageSummary": { "externalCustomerId": "ticket-1001", "sessionCount": 2, "tokensIn": 1840, "tokensOut": 612, "costUsd": 0.0021, "creditsUsedApprox": 60 } }}UNAUTHENTICATED: Missing API key (HTTP 200)
{ "errors": [ { "message": "Missing X-Api-Key header", "locations": [ { "line": 2, "column": 3 } ], "path": [ "customerUsageSummary" ], "extensions": { "code": "UNAUTHENTICATED", "originalError": { "message": "Missing X-Api-Key header", "error": "Unauthorized", "statusCode": 401 } } } ], "data": null}BAD_USER_INPUT: Identifier sent as a number (HTTP 400)
Sent with variables:
{ "externalCustomerId": 1001}{ "errors": [ { "message": "Variable \"$externalCustomerId\" got invalid value 1001; String cannot represent a non string value: 1001", "locations": [ { "line": 1, "column": 21 } ], "extensions": { "code": "BAD_USER_INPUT" } } ]}