Skip to main content
GET
/
workspaces
/
usage
Workspace usage
curl --request GET \
  --url https://api.tess.im/workspaces/usage \
  --header 'Authorization: Bearer <token>' \
  --header 'x-workspace-id: <x-workspace-id>'
import requests

url = "https://api.tess.im/workspaces/usage"

headers = {
"x-workspace-id": "<x-workspace-id>",
"Authorization": "Bearer <token>"
}

response = requests.get(url, headers=headers)

print(response.text)
const options = {
method: 'GET',
headers: {'x-workspace-id': '<x-workspace-id>', Authorization: 'Bearer <token>'}
};

fetch('https://api.tess.im/workspaces/usage', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));
<?php

$curl = curl_init();

curl_setopt_array($curl, [
CURLOPT_URL => "https://api.tess.im/workspaces/usage",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"x-workspace-id: <x-workspace-id>"
],
]);

$response = curl_exec($curl);
$err = curl_error($curl);

curl_close($curl);

if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}
package main

import (
"fmt"
"net/http"
"io"
)

func main() {

url := "https://api.tess.im/workspaces/usage"

req, _ := http.NewRequest("GET", url, nil)

req.Header.Add("x-workspace-id", "<x-workspace-id>")
req.Header.Add("Authorization", "Bearer <token>")

res, _ := http.DefaultClient.Do(req)

defer res.Body.Close()
body, _ := io.ReadAll(res.Body)

fmt.Println(string(body))

}
HttpResponse<String> response = Unirest.get("https://api.tess.im/workspaces/usage")
.header("x-workspace-id", "<x-workspace-id>")
.header("Authorization", "Bearer <token>")
.asString();
require 'uri'
require 'net/http'

url = URI("https://api.tess.im/workspaces/usage")

http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true

request = Net::HTTP::Get.new(url)
request["x-workspace-id"] = '<x-workspace-id>'
request["Authorization"] = 'Bearer <token>'

response = http.request(request)
puts response.read_body
Your API token must be allowed to use agents (use_agents), and you must have access to the workspace—the same layers as other agent and file API routes (Sanctum authentication, workspace access check).

Code Examples

curl --request GET \
  --url 'https://api.tess.im/workspaces/usage?range=7d&page=1&per_page=20' \
  --header 'Authorization: Bearer YOUR_API_KEY' \
  --header 'Accept: application/json' \
  --header 'x-workspace-id: YOUR_WORKSPACE_ID'
const axios = require('axios');

const config = {
  method: 'get',
  url: 'https://api.tess.im/workspaces/usage',
  params: {
    range: '7d',
    page: 1,
    per_page: 20
  },
  headers: {
    'Authorization': 'Bearer YOUR_API_KEY',
    'Accept': 'application/json',
    'x-workspace-id': 'YOUR_WORKSPACE_ID'
  }
};

try {
  const response = await axios(config);
  console.log(response.data);
} catch (error) {
  console.error(error);
}
import requests

url = "https://api.tess.im/workspaces/usage"
params = {
    "range": "7d",
    "page": 1,
    "per_page": 20
}
headers = {
    "Authorization": "Bearer YOUR_API_KEY",
    "Accept": "application/json",
    "x-workspace-id": "YOUR_WORKSPACE_ID"
}

response = requests.get(url, params=params, headers=headers)
print(response.json())
<?php
$curl = curl_init();
$query = http_build_query([
  'range' => '7d',
  'page' => 1,
  'per_page' => 20
]);

curl_setopt_array($curl, [
  CURLOPT_URL => "https://api.tess.im/workspaces/usage?" . $query,
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_ENCODING => "",
  CURLOPT_MAXREDIRS => 10,
  CURLOPT_TIMEOUT => 30,
  CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
  CURLOPT_CUSTOMREQUEST => "GET",
  CURLOPT_HTTPHEADER => [
    "Authorization: Bearer YOUR_API_KEY",
    "Accept: application/json",
    "x-workspace-id: YOUR_WORKSPACE_ID"
  ]
]);

$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);

if ($err) {
  echo "Error: " . $err;
} else {
  echo $response;
}
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;

HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
    .uri(URI.create("https://api.tess.im/workspaces/usage?range=7d&page=1&per_page=20"))
    .header("Authorization", "Bearer YOUR_API_KEY")
    .header("Accept", "application/json")
    .header("x-workspace-id", "YOUR_WORKSPACE_ID")
    .GET()
    .build();

HttpResponse<String> response = client.send(request,
    HttpResponse.BodyHandlers.ofString());
System.out.println(response.body());
package main

import (
    "fmt"
    "io/ioutil"
    "net/http"
)

func main() {
    client := &http.Client{}
    req, err := http.NewRequest("GET", "https://api.tess.im/workspaces/usage?range=7d&page=1&per_page=20", nil)
    if err != nil {
        fmt.Println(err)
        return
    }

    req.Header.Add("Authorization", "Bearer YOUR_API_KEY")
    req.Header.Add("Accept", "application/json")
    req.Header.Add("x-workspace-id", "YOUR_WORKSPACE_ID")

    resp, err := client.Do(req)
    if err != nil {
        fmt.Println(err)
        return
    }
    defer resp.Body.Close()

    body, err := ioutil.ReadAll(resp.Body)
    if err != nil {
        fmt.Println(err)
        return
    }

    fmt.Println(string(body))
}
using System;
using System.Net.Http;
using System.Threading.Tasks;

class Program
{
    static async Task Main(string[] args)
    {
        using (var client = new HttpClient())
        {
            client.DefaultRequestHeaders.Add("Authorization", "Bearer YOUR_API_KEY");
            client.DefaultRequestHeaders.Add("Accept", "application/json");
            client.DefaultRequestHeaders.Add("x-workspace-id", "YOUR_WORKSPACE_ID");

            try
            {
                var response = await client.GetAsync("https://api.tess.im/workspaces/usage?range=7d&page=1&per_page=20");
                response.EnsureSuccessStatusCode();
                string responseBody = await response.Content.ReadAsStringAsync();
                Console.WriteLine(responseBody);
            }
            catch (HttpRequestException e)
            {
                Console.WriteLine("Exception: " + e.Message);
            }
        }
    }
}
require 'uri'
require 'net/http'

uri = URI('https://api.tess.im/workspaces/usage?range=7d&page=1&per_page=20')
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true

request = Net::HTTP::Get.new(uri)
request['Authorization'] = 'Bearer YOUR_API_KEY'
request['Accept'] = 'application/json'
request['x-workspace-id'] = 'YOUR_WORKSPACE_ID'

response = http.request(request)
puts response.read_body

Headers

x-workspace-id
integer
required
Numeric workspace ID. Invalid or missing values produce a 422 error. You must belong to this workspace or the API returns 403.

Query parameters

range
string
Relative window: 1d, 7d, or 30d. Ignored if both start_date and end_date are sent. If no date parameters are sent, the effective default is 30d.
start_date
date (YYYY-MM-DD)
Start of a custom range (inclusive). Required with end_date.
end_date
date (YYYY-MM-DD)
End of a custom range (inclusive). Must be greater than or equal to start_date. Required with start_date. The span cannot exceed 90 days; otherwise 422.
user_id
integer
Filter by the user who ran the agent. Without workspace permission to read other users’ activity, you only see your own executions; filtering by another user’s id returns 403.
type
string
Agent type filter: all, chat, image, text, voiceover, video, code. all or omitted means no type filter.
page
integer
Page number. Default 1, minimum 1.
per_page
integer
Page size. Default 20, between 1 and 100.
Date windows
  • With start_date + end_date: the inclusive range is capped at 90 days.
  • With range: the window is relative to the end of the current day (1d last 24 hours from that instant; 7d / 30d last seven or thirty calendar days from that end).
  • If the usage_history_min_date feature is enabled in app settings, the effective start of the range is not earlier than that date (silent clamp).
Caching
  • Listings are cached about 60 seconds per workspace, filters, and page. Identical requests within that window may return the same payload.

Response

{
  "items": [
    {
      "id": "183450",
      "created_at": "2026-04-07 16:17:09",
      "user_id": 16643,
      "type": "chat",
      "status": "succeeded",
      "email": "user@example.com",
      "credits": 1.5,
      "name": "My personal assistant",
      "slug": "9b4994e3-07e9-4163-b5c0-9c4f18945eda-my-personal-assistant",
      "output": "This content is only available on Tess",
      "root_id": null,
      "execution_origin": "Platform",
      "source": "current",
      "used_model": "gpt-4o-mini",
      "execution_mode": "chat",
      "tokens": {
        "input": 1240,
        "output": 320,
        "total": 1560
      },
      "link": "/dashboard/user/ai/chat/ai-chat/9b4994e3-07e9-4163-b5c0-9c4f18945eda-my-personal-assistant?_chat_id=183450"
    }
  ],
  "pagination": {
    "current_page": 1,
    "per_page": 20,
    "has_more": false
  }
}
Pagination uses has_more: the service requests per_page + 1 rows; if the extra row exists, has_more is true and only the first per_page items appear in items.

Item fields

FieldDescription
idExecution id, or a synthetic id such as chat-edited-{user_openai_id} for edited chats.
created_atTimestamp of the execution.
user_idUser who ran the agent.
typeAgent type from the agent definition (for example chat, image, text, voiceover, video, code).
statussucceeded or failed (non-success statuses map to failed).
emailEmail of the executing user.
creditsCredits charged; 0 when status is not succeeded.
nameAgent title.
slugAgent slug.
outputFor current rows: for image, video, and voiceover this is the real output; for other types it is the fixed text This content is only available on Tess. For archived: This item was deleted. For edited: This item was edited.
root_idConversation root for chats, when present.
execution_originTaken from execution metadata when available.
sourcecurrent, archived, or edited.
used_modelModel name when stored (user_openai.used_model, execution metadata, or usage-pricing breakdown for Agent Mode runs); null for edited rows or when no model can be resolved.
tokensObject { "input", "output", "total" } when token billing applies. For normal chat, values come from detailed_credits when metric == "token". For Agent Mode runs, values come from execution metadata even when detailed_credits is zeroed. null for image, video, and voiceover (billed per execution, not per token).
execution_modeagent for Agent Mode runs, chat for normal chat conversations, or null for non-chat execution types. Helps distinguish agent orchestration from standard chat when reviewing usage.
linkURL or app path to open the resource when applicable; null for archived and edited, and for types without a link. Chat (current): path /dashboard/user/ai/chat/ai-chat/{slug}?_chat_id={id} using root_id when set, else row id. Image / video / voiceover (current): signed or public URL from storage when output is usable; otherwise null.

Errors

StatusWhen
401Missing or invalid authentication (Sanctum).
403No access to the workspace, or user_id targets another user without permission to view their executions.
422Invalid query parameters (Laravel validation), invalid or missing workspace id, invalid dates, or a custom range longer than 90 days.
Validation errors use the standard Laravel error payload; some workspace errors return JSON { "message": "..." } with a translated message.