Skip to main content
GET
/
orgs
/
{org_id}
/
projects
/
{project_id}
/
envs
/
{env_id}
/
logs
cURL
curl --request GET \
  --url https://api.timbal.ai/orgs/{org_id}/projects/{project_id}/envs/{env_id}/logs \
  --header 'Authorization: Bearer <token>'
import requests

url = "https://api.timbal.ai/orgs/{org_id}/projects/{project_id}/envs/{env_id}/logs"

headers = {"Authorization": "Bearer <token>"}

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

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

fetch('https://api.timbal.ai/orgs/{org_id}/projects/{project_id}/envs/{env_id}/logs', 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.timbal.ai/orgs/{org_id}/projects/{project_id}/envs/{env_id}/logs",
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>"
],
]);

$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.timbal.ai/orgs/{org_id}/projects/{project_id}/envs/{env_id}/logs"

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

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.timbal.ai/orgs/{org_id}/projects/{project_id}/envs/{env_id}/logs")
.header("Authorization", "Bearer <token>")
.asString();
require 'uri'
require 'net/http'

url = URI("https://api.timbal.ai/orgs/{org_id}/projects/{project_id}/envs/{env_id}/logs")

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

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

response = http.request(request)
puts response.read_body
{
  "end_time": 123,
  "events": [
    {
      "log_stream": "<string>",
      "message": "<string>",
      "timestamp": 123
    }
  ],
  "limit": 1,
  "start_time": 123,
  "next_page_token": "<string>"
}

Authorizations

Authorization
string
header
required

Timbal API key. Obtain your API key from the Timbal platform settings. See Authentication for more information.

Path Parameters

org_id
string
required
project_id
string
required
env_id
string
required

Query Parameters

component
enum<string>
required

Which component's logs to fetch. Shared vocabulary with GET /preview/logs (which 501s on workforce). workforce here covers agent and workflow components interchangeably. Routing selector for the deployable units a Timbal project owns: the project-level frontend (Ui), the project-level backend (Api), and the N workforce components (Workforce, keyed on manifest_id).

Used as a ?component= query parameter on every surface that acts on one component at a time (GET /preview/logs, GET /envs/{env}/logs, etc.). Wire values are lowercase (ui/api/workforce). Surfaces that don't apply to a given variant (e.g. previews don't run workforce components today) reject with 501 NotImplemented instead of forking the schema — clients rely on a single shared vocabulary.

Distinct from [AppType], which is a type discriminator on workforce rows (Agent vs Workflow) carried in response payloads. ProjectComponent collapses both into Workforce because every surface that selects a workforce component keys on manifest_id, not on the Agent-vs-Workflow distinction (the underlying log pipeline, deployment shape, etc. are identical for both). Use [From<AppType>] to bridge: any workforce type — including the forward-compat AppType::Unknown catch-all — maps to ProjectComponent::Workforce.

Available options:
ui,
api,
workforce
component_id
string | null

Numeric workforce app id (the value the SDK reads from TIMBAL_APP_ID, also returned as id on each deployment in the deployments-list response). XOR with component_uid when component=workforce; forbidden for ui / api.

component_uid
string | null

Workforce manifest uid (the _id field in timbal.yaml, also returned as uid on each deployment in the deployments-list response). XOR with component_id. Display name is deliberately not accepted as a selector — it's user-editable and not guaranteed unique within a project.

start_time
integer<int64> | null

ms since epoch. Defaults to end_time - 1h.

end_time
integer<int64> | null

ms since epoch. Defaults to now.

limit
integer | null

Defaults to 50.

Required range: x >= 0
page_token
string | null

Pagination cursor from a prior response. Returned only when cursor-based pagination is available for the component's runtime; ignored otherwise.

Response

Logs fetched

Resolved window echoed back so the client can paginate / refresh without re-deriving the defaults the server applied.

end_time
integer<int64>
required
events
object[]
required
limit
integer
required
Required range: x >= 0
start_time
integer<int64>
required
next_page_token
string | null