Timber
Timber is the project log trail for Protokol. It shows user-facing platform events emitted by services and accepts custom application logs.
Timber is available as the protokol-timber integration with three tiers:
| Tier | Retention | Custom Logs | Usage Metering |
|---|---|---|---|
| Free | 7 days | No | No |
| Standard | 30 days | Yes | Yes |
| Pro | 90 days | Yes | Yes |
Query Events
Query parameters:
| Parameter | Description |
|---|---|
limit |
Number of events to return (default 50, max 500). |
cursor |
Cursor from meta.next_cursor for pagination. |
source |
system or user. |
level |
debug, info, warn, or error. |
action |
Event action such as create, update, delete, execute, deploy. |
resource_type |
Resource family, for example workflow, media_file, runtime. |
resource_id |
Resource identifier. |
actor_id |
User, API key, or service actor id. |
actor_name |
Actor display name (partial match). |
actor_type |
user, api_key, service, or system. |
request_uuid |
Correlate events to a specific request. |
search |
Text search over message, resource, and actor fields. |
from / to |
RFC3339 time range. |
cURL
curl "https://timber.integration.protokol.cloud/logs?source=system&level=info&limit=50" \
-H "Authorization: Bearer <your-api-token>" \
-H "X-Project-Uuid: <your-project-uuid>"
Platform Functions
const { Timber } = $sdk.version('0.10')
const timber = new Timber()
const result = await timber.query({
source: 'system',
level: 'info',
limit: 50,
})
response.json(result)
SDK
import { Timber } from "@ptkl/sdk/beta"
const timber = new Timber()
const result = await timber.query({
source: 'system',
level: 'error',
from: new Date('2026-05-01'),
to: new Date('2026-05-27'),
limit: 100,
})
// Pagination
if (result.meta.has_more) {
const page2 = await timber.query({ cursor: result.meta.next_cursor })
}
Response
{
"data": [
{
"id": "01J...",
"timestamp": "2026-05-24T12:00:00Z",
"source": "system",
"level": "info",
"service": "workflow",
"project_uuid": "project-uuid",
"actor": { "type": "user", "id": "user-uuid", "name": "John" },
"action": "execute",
"resource_type": "workflow",
"resource_id": "workflow-uuid",
"message": "Workflow executed"
}
],
"meta": {
"limit": 50,
"has_more": false,
"next_cursor": ""
}
}
Write Custom Logs
Custom logs require Timber Standard or Pro. Payloads must be 64 KB or smaller.
| Field | Required | Description |
|---|---|---|
message |
yes | Log body. Maximum 10,000 characters. |
level |
no | debug, info, warn, or error. Defaults to info. |
service |
no | Service name. Defaults to user-app. |
resource_type |
no | Resource family. |
resource_id |
no | Resource identifier. |
resource_name |
no | Human-readable name. |
attributes |
no | Up to 64 custom key-value pairs. |
trace_id, span_id, request_id |
no | Correlation identifiers. |
cURL
curl "https://timber.integration.protokol.cloud/logs" \
-X POST \
-H "Authorization: Bearer <your-api-token>" \
-H "X-Project-Uuid: <your-project-uuid>" \
-H "Content-Type: application/json" \
-d '{
"level": "info",
"service": "checkout",
"message": "Order confirmation sent",
"resource_type": "order",
"resource_id": "ord_123",
"attributes": {
"channel": "email"
}
}'
Platform Functions
const { Timber } = $sdk.version('0.10')
const timber = new Timber()
// Convenience methods — debug(), info(), warn(), error()
await timber.info('Order confirmation sent', {
service: 'checkout',
resource_type: 'order',
resource_id: $input.body.order_id,
attributes: { channel: 'email' },
})
// Or use write() with an explicit level
await timber.write({
level: 'warn',
message: 'Payment retry scheduled',
service: 'billing',
attributes: { attempt: 3 },
})
response.json({ ok: true })
SDK
import { Timber } from "@ptkl/sdk/beta"
const timber = new Timber()
await timber.info('User exported report', {
service: 'reports',
resource_type: 'report',
resource_id: 'rpt_456',
attributes: { format: 'pdf', pages: 42 },
})
await timber.error('Payment failed', {
service: 'billing',
attributes: { reason: 'card_declined', provider: 'stripe' },
})
Usage
Returns current Timber tier and metered custom log bytes for the project.
Platform Functions
const { Timber } = $sdk.version('0.10')
const timber = new Timber()
const usage = await timber.usage()
response.json(usage)
Response
{
"tier": "standard",
"period": "2026-05",
"bytes_ingested": 32768,
"gb_ingested": 0.000030517578125
}
CLI
Timber logs are also available via the Protokol Toolkit CLI:
# Query recent logs
ptkl logs
# Filter by level and search
ptkl logs --level error --search "payment failed"
# Tail mode — poll for new events
ptkl logs -t
# Output as JSON for piping to jq
ptkl logs --json | jq '.message'
| Flag | Description |
|---|---|
--source <source> |
Filter by system or user. |
--level <level> |
Filter by debug, info, warn, or error. |
--action <action> |
Filter by action. |
--resource-type <type> |
Filter by resource type. |
-s, --search <text> |
Search messages, actors, or resources. |
-l, --limit <n> |
Number of logs to fetch (default 50). |
-t, --tail |
Poll and print new logs continuously. |
--interval <seconds> |
Tail polling interval (default 3). |
--json |
Output as newline-delimited JSON. |
OpenTelemetry (OTLP)
Timber accepts logs in the OTLP JSON format, so you can point any OpenTelemetry SDK or Collector at Timber for log ingestion.
Requires Timber Standard or Pro. Maximum payload size is 1 MB.
The endpoint accepts the standard OTLP Logs JSON structure. Timber maps the fields as follows:
| OTLP field | Timber field |
|---|---|
resource.attributes["service.name"] |
service |
body.stringValue |
message |
severityNumber / severityText |
level |
attributes |
attributes |
traceId |
trace_id |
spanId |
span_id |
timeUnixNano |
timestamp |
Example
curl "https://timber.integration.protokol.cloud/logs/otlp" \
-X POST \
-H "Authorization: Bearer <your-api-token>" \
-H "X-Project-Uuid: <your-project-uuid>" \
-H "Content-Type: application/json" \
-d '{
"resourceLogs": [{
"resource": {
"attributes": [
{ "key": "service.name", "value": { "stringValue": "checkout" } }
]
},
"scopeLogs": [{
"logRecords": [
{
"timeUnixNano": "1716825600000000000",
"severityNumber": 9,
"body": { "stringValue": "Order confirmed" },
"attributes": [
{ "key": "order_id", "value": { "stringValue": "ord_123" } }
],
"traceId": "a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4",
"spanId": "1a2b3c4d5e6f7a8b"
}
]
}]
}]
}'
OpenTelemetry SDK Configuration
To send logs from an OTEL-instrumented application, configure the OTLP exporter to point at Timber:
export OTEL_EXPORTER_OTLP_LOGS_ENDPOINT=https://timber.integration.protokol.cloud/logs/otlp
export OTEL_EXPORTER_OTLP_LOGS_HEADERS="Authorization=Bearer <your-api-token>,X-Project-Uuid=<your-project-uuid>"
export OTEL_EXPORTER_OTLP_LOGS_PROTOCOL=http/json
Only log collection is supported. Traces and metrics should be sent to your own observability backend.
Retention
Expired events are automatically cleaned up daily based on your project tier. No manual action is needed — events older than the retention window are permanently deleted.