OTEL Worklogs API
Exports AI Employee workflow-execution data as OpenTelemetry (OTLP) traces and logs. The response is standard OTLP TracesData and LogsData serialized to JSON, so it can be ingested by any OTLP-compatible observability backend -- Datadog, Grafana Tempo, Arize, New Relic, Honeycomb, Jaeger, and others.
- Fetch a single workflow run by ID, or query many runs for one AI Employee over a time range.
- Returns OTLP traces, plus correlated logs for callers with debug access.
- Cursor-based pagination for time-range queries.
- Permission-based filtering: debug-access callers receive full spans and logs; standard viewers receive action and step names, timing, and status only.
- Exports terminal workflow runs (
COMPLETED, FAILED). Within an exported run, individual action spans may carry a paused status when an action awaited human input.
- Pull-based -- for fetching data after a run reaches a terminal state. It does not stream in real time.
Endpoint
| Property | Value |
|---|
| URL | /api/v1/observability/worklogs |
| HTTP Method | GET |
| Protocol | HTTP/REST |
| Header | Type | Required | Description |
|---|
Authorization | string | Yes | Bearer token (JWT or API key). The token determines the caller's permission tier and which tenants and AI Employees are accessible. |
Request
The endpoint supports two query modes. If workflow_run_id is present, the request is treated as a single-run lookup. Otherwise it is treated as a time-range query, and persona_id, start_time, and end_time are all required.
| Parameter | Location | Type | Required | Description |
|---|
workflow_run_id | query | string (UUID) | Conditional | Single-run mode. Fetches one workflow run. Required when persona_id is not provided. |
persona_id | query | string (UUID) | Conditional | Time-range mode. AI Employee whose runs are exported. Required when workflow_run_id is not provided. |
start_time | query | string (RFC 3339) | Conditional | Start of the time range. Required in time-range mode. |
end_time | query | string (RFC 3339) | Conditional | End of the time range. Required in time-range mode. |
limit | query | integer | No | Runs per page (time-range mode). Default: 100. Min: 1. Max: 500. |
page_token | query | string (UUID) | No | Pagination cursor returned as next_page_token in a previous response. Omitted for the first page. |
Query Mode: Single Run
curl -H "Authorization: Bearer <access_token>" \
"https://your-instance.ema.co/api/v1/observability/worklogs?workflow_run_id=<RUN_ID>"
Query Mode: Time Range
curl -H "Authorization: Bearer <access_token>" \
"https://your-instance.ema.co/api/v1/observability/worklogs?persona_id=<PERSONA_ID>&start_time=2026-03-10T00:00:00Z&end_time=2026-03-11T00:00:00Z&limit=100"
Query Constraints
- The time-range window must not exceed 7 days per request, and
start_time must be before end_time. For longer ranges, issue multiple requests with consecutive windows.
limit must be between 1 and 500 (default 100). Use page_token to iterate larger result sets.
- Only runs in a terminal state (
COMPLETED, FAILED) can be exported. A single-run request for a run that is not yet terminal returns 409 Conflict.
Response
A JSON object. traces is always present; logs is present only for callers with debug access and only when log records exist; next_page_token is present only when more pages remain.
| Field | Type | Description |
|---|
traces | OTLP TracesData (JSON) | Workflow execution represented as OTEL traces and spans. |
logs | OTLP LogsData (JSON) | Step-level log records linked to trace and span IDs. Returned only for debug-access callers; omitted otherwise. |
next_page_token | string | Pagination cursor for the next page. Omitted when there are no more results. |
Note -- the response fields are named traces and logs (each an OTLP protobuf message serialized to JSON), not traces_data / logs_data. For callers without debug access, the logs field is omitted entirely rather than returned empty.
Example Response
{
"traces": {
"resourceSpans": [
{
"resource": {
"attributes": [
{ "key": "ema.tenant.id", "value": { "stringValue": "abc12345-0000-0000-0000-000000000000" } },
{ "key": "ema.persona.id", "value": { "stringValue": "2f6c12a8-7d2b-4f1a-9c0e-1234567890ab" } }
]
},
"scopeSpans": [
{
"scope": { "name": "ema.otel.export", "version": "1.0.0" },
"spans": [
{
"name": "Action: Search Knowledge Base",
"kind": "SPAN_KIND_INTERNAL",
"status": { "code": "STATUS_CODE_OK" },
"attributes": [
{ "key": "ema.action.title", "value": { "stringValue": "Search Knowledge Base" } },
{ "key": "ema.action.status", "value": { "stringValue": "ACTION_RUN_STATUS_SUCCESS" } },
{ "key": "ema.action.llm_call_count", "value": { "intValue": "1" } }
]
}
]
}
]
}
]
}
}
An action that paused for human input during the run is represented by an action span carrying a paused span event, with span status UNSET (see Span Status Mapping). Trace IDs are deterministic for a given run, so re-exporting the same workflow_run_id returns the same trace.
For time-range queries returning more results than the page limit, use cursor-based pagination. When more results remain, the response includes a next_page_token and a Link header with rel="next". Pass the token back as page_token and repeat until it is no longer returned.
# First page
curl -H "Authorization: Bearer <access_token>" \
"https://your-instance.ema.co/api/v1/observability/worklogs?persona_id=<PERSONA_ID>&start_time=2026-03-01T00:00:00Z&end_time=2026-03-08T00:00:00Z&limit=100"
# Response includes: "next_page_token": "<LAST_RUN_ID>"
# Next page
curl -H "Authorization: Bearer <access_token>" \
"https://your-instance.ema.co/api/v1/observability/worklogs?persona_id=<PERSONA_ID>&start_time=2026-03-01T00:00:00Z&end_time=2026-03-08T00:00:00Z&limit=100&page_token=<LAST_RUN_ID>"
Permission Tiers
The data returned depends on the permission tier of the authenticated caller:
| Permission Tier | Traces | Logs |
|---|
| Debug access | Full spans with all exported attributes (LLM metrics, token counts, model names, action statuses) | Yes -- step-level log records |
| Standard viewer | Action and step names, timing, and run status only | No |
- To receive useful observability data, use an API token with debug access. A standard-viewer token returns only structural information (action and step names, timing, and run status) without LLM metrics or logs.
- Contact your Ema administrator to configure the appropriate access for your integration account.
Data Exclusions
- LLM costs (per-call and aggregate) are not included in the export.
- Prompt content (system prompts, user prompts, model responses) is not included.
- The export does include metrics such as token counts, latency, and model names -- but not the actual text sent to or received from models.
Trace & Log Attributes
Root Span Attributes
| Attribute | Type | Description |
|---|
ema.tenant.id | string | Tenant ID. |
ema.persona.id | string | AI Employee ID. |
ema.workflow.run_id | string | Unique identifier for the workflow run. |
ema.conversation.id | string | Conversation ID, when the run is tied to one. |
Action Span Attributes
| Attribute | Type | Description |
|---|
ema.action.title | string | Name of the action. |
ema.action.status | string | Action outcome, e.g. ACTION_RUN_STATUS_SUCCESS, ACTION_RUN_STATUS_ERRORED, ACTION_RUN_STATUS_WARNING, ACTION_RUN_STATUS_NOT_RUN, ACTION_RUN_STATUS_PAUSED. |
ema.action.llm_latency_ms | float | Total LLM latency within the action, in milliseconds. |
ema.action.llm_call_count | integer | Number of LLM calls in the action. |
LLM Call Span Attributes
LLM call spans follow the OpenTelemetry GenAI semantic conventions where applicable:
| Attribute | Type | Description |
|---|
gen_ai.provider.name | string | LLM provider (e.g. openai, anthropic, google). |
gen_ai.request.model | string | Model requested. |
gen_ai.response.model | string | Model that responded (may differ if routed). |
gen_ai.usage.input_tokens | integer | Input token count. |
gen_ai.usage.output_tokens | integer | Output token count. |
ema.llm.latency_s | float | LLM call duration, in seconds. |
ema.llm.success | boolean | Whether the call succeeded. |
ema.llm.error_message | string | Error message, if the call failed. |
ema.llm.fusion_used | boolean | Whether EmaFusion™ model routing was used. |
ema.llm.optimization_criteria | string | Optimization strategy used for model selection. |
Log Record Attributes
Included in logs for debug-access callers only. Each record also carries standard OTLP traceId, spanId, severity, and body fields linking it to its parent span.
| Attribute | Type | Description |
|---|
ema.action.title | string | Parent action name. |
ema.step.name | string | Parent step name, for step-level messages. |
ema.workflow.run_id | string | Workflow run ID. |
ema.persona.id | string | AI Employee ID. |
Span Status Mapping
| Action Run Status | OTEL Span Status | Notes |
|---|
SUCCESS | OK | |
ERRORED | ERROR | Status message action errored. |
WARNING | UNSET | A WARNING span event is attached. |
NOT_RUN | UNSET | |
PAUSED | UNSET | A paused span event is attached. |
Error Responses
| HTTP Status | Description |
|---|
| 400 | Invalid or missing parameters: a malformed UUID (workflow_run_id, persona_id, or page_token); a missing persona_id, start_time, or end_time in time-range mode; non-RFC 3339 timestamps; start_time not before end_time; a time range over 7 days; or limit outside 1-500. |
| 401 | Missing or invalid authorization (no tenant context). |
| 403 | Caller lacks permission for the requested AI Employee or tenant. |
| 404 | Workflow run not found, or it belongs to a tenant the caller cannot see (single-run mode). |
| 409 | The workflow run is not yet in a terminal state (still executing). |
| 500 | Server error while retrieving data or building the OTLP export. |