Technical docs

Logs

Overview

Support and developer reference for execution-log surfaces backed by workflow runs.

Surfaces that use this system

  • Org-wide logs page: `/logs`
  • AI teammate logs tab: `/staff/ai/[employee_id]?tab=logs`
  • AI teammate API tab: `/staff/ai/[employee_id]?tab=api`

All three surfaces reuse the same `AiTeammateLogsTable` + `useWorkflowRuns()` stack and are backed by org-wide workflow-run APIs.

Org-wide `/logs` access

  • Frontend route: `/logs`
  • Frontend gate: `canAccessTool({ isOrgAdmin: true }, user)`
  • If access fails, the page renders `AccessDenied`.

Org-wide page behavior

  • Header: `Execution Logs`
  • Description: `All skill executions across the organization`
  • Scope: workflow runs across all skills and AI teammates in the current organization
  • Data source: `useWorkflowRuns()` backed by `GET /api/organizations/{orgId}/workflows/runs/`
  • Default page size: `25`
  • Allowed page sizes: `10`, `25`, `50`, `100`
  • Org-wide `/logs` and teammate `Logs` tab both persist page size in localStorage under `alloy:logs-page-size`

Shared logs-table behavior

  • Runs are ordered newest-first by `created_at`.
  • Refresh re-fetches the current page.
  • Rows can be deep-linked with `?run=<runId>`.
  • Pagination state is reflected as `?page=<n>`.
  • Filter and search state is synced into the URL and preserves unrelated params such as `run` and `tab`.
  • Expanding a row lazy-loads full run details from:
  • `GET /api/organizations/{orgId}/workflows/{workflowId}/runs/{runId}/`
  • Expanded details can show:
  • run metadata
  • runtime context
  • copied run links and IDs
  • conversation ID copy action
  • trigger URL copy action when `trigger_url` exists
  • flow/config snapshot
  • parsed steps
  • tool call args/results
  • voice transcripts and attachments when present
  • If an expanded run is no longer visible after filtering/pagination, the UI collapses it.

Search behavior

  • UUID search is server-backed exact match.
  • Plain-text search is not sent to the backend.
  • Search flow today:
  • UUID input searches `conversation_id` first
  • if that returns zero results once, the UI falls back to `run_id`
  • pasting a URL containing `run=<uuid>` switches immediately into run-ID search
  • non-UUID text search is client-side against the currently loaded rows only
  • Current client-side text search matches:
  • skill name
  • input message
  • output message
  • error message
  • model
  • run ID
  • AI teammate name
  • conversation channel label/key

Filters

  • Pagination and API filters are server-side.
  • UI chip filters are single-select because the server API accepts one value per field.
  • Org-wide `/logs` shows filter sections:
  • `Status`
  • `Channel`
  • `Model`
  • `Skill`
  • `AI Teammate`
  • `Date Range`
  • `Duration`
  • `Tokens`
  • AI teammate `Logs` tab reuses the same filters but fixes `ai_employee_id` from page context and hides the teammate column.
  • AI teammate `API` tab hides the channel filter and then client-filters the current page to `API`-labeled rows.
  • Date presets:
  • `Last hour`
  • `Last 24h`
  • `Last 7 days`
  • `Last 30 days`
  • Duration presets:
  • `< 1s`
  • `1–10s`
  • `10–60s`
  • `> 1 min`
  • Token presets:
  • `< 100`
  • `100–1K`
  • `1K–10K`
  • `> 10K`

Source labels

  • UI channel labels are derived as:
  • `employee_chat` -> `Internal Chat`
  • `ally_chat` -> `Ally Chat`
  • `web_chat` -> `Web Chat`
  • `skill_chat` -> `Skill Chat`
  • no conversation -> `API`
  • When the backend already populated `conversation_channel` or top-level `channel`, the UI uses that value first.
  • Otherwise the UI derives:
  • `API` when there is no `conversation_id`
  • `Web Chat` when `runtimeContext.isAsync === true`
  • `Internal Chat` as the fallback for conversation-backed runs without a more specific channel

Backend/API behavior

  • List endpoint:
  • `GET /api/organizations/{orgId}/workflows/runs/`
  • Detail endpoint:
  • `GET /api/organizations/{orgId}/workflows/{workflowId}/runs/{runId}/`
  • Both routes use `checkOrganizationAccess`.
  • List query validation currently supports:
  • pagination: `page`, `limit` (`limit` max `100`)
  • exact filters: `conversation_id`, `status`, `run_id`, `channel`, `ai_employee_id`, `workflow_id`, `model`, `trigger_id`
  • runtime-state filters: `state[{key}]={value}` exact matches against `state.runtimeContext.state`
  • range filters: `started`, `ended`, `duration`, `tokens`
  • `exclude` limited to `flow`, `state`, or `flow,state`
  • The frontend list surfaces send `exclude=flow,state`.
  • Current backend behavior for any truthy `exclude`:
  • switches serialization to `WorkflowRun.listData()`
  • excludes only `flow` at the DB query layer
  • still returns a compressed `state`
  • Compressed list `state` includes:
  • `currentStepId`
  • top-level `error`
  • `runtimeContext.userId`
  • `runtimeContext.isAsync`
  • trimmed step summaries with reduced input/output payloads
  • List responses are also enriched with aggregated `costs[]` entries when token-history rows exist for the run.
  • Each `costs[]` item is split by token `key_type` and currently includes:
  • `key_type`
  • `input_tokens`
  • `output_tokens`
  • `cached_input_tokens`
  • `usd_cost`
  • Detail endpoint behavior:
  • `404` if the run does not exist
  • `404` if the run exists but does not belong to the requested workflow
  • loads up to `1000` step logs
  • backfills temporary conversation titles when the run has a conversation ID but no stored title

Current limitations that matter for support

  • Plain-text search only searches the rows already loaded on the current page.
  • `started` filter maps to run `created_at`.
  • `ended` filter maps to `completed_at` only, not `failed_at` or `suspended_at`.
  • The `API` channel label is often derived client-side from the absence of `conversation_id`.
  • Backend `channel` is not reliably populated for direct API-triggered runs, so server-side `channel=api` filtering is not dependable even though the UI can label those rows as `API`.

Cross-reference

  • The org-wide page is `/logs`.
  • Teammate logs and API request history reuse the same run data model.
  • General workflow-run internals are documented in `workflow-engine.md`.

Start building your AI team