This section as Markdown: View Download

Agent Logs

Per-instance activity feed — tool calls, scheduled cron runs, message exchanges, and turn boundaries — for any deployed agent.

Overview

Every Wiro agent container runs a small wiro-commands plugin that appends one structured ActivityEvent JSON line per tool call, turn boundary, session boundary, message exchange, and synthesized cron run. The plugin redacts secrets-by-field-name, high-entropy assignment values, internal tool-call IDs, and the API URL/credentials before writing — so what shows up in Logs is safe to show end users.

A daily JSONL file is written for each calendar day; files older than 7 days are gzipped, files older than 180 days are deleted by the agent's daily maintenance cron.

The endpoints below are owner-or-team-admin scoped. Plain members of the agent's team receive useragent-team-admin-required (code 97); outside callers receive useragent-access-denied.

EndpointPurpose
POST /UserAgent/LogsLive tail (last N events for today, or a specific date). Cached server-side for 30s to absorb polling.
POST /UserAgent/LogsListList the date strings (YYYY-MM-DD) for which an activity file exists.
POST /UserAgent/LogsFileRead the full JSONL file for a specific date.
POST /UserAgent/LogsDeleteDelete one date's activity file (and its gzipped sibling). Idempotent.
Activity feed vs Message/History. Message/History is a chat-bubble-level read of the conversation. Logs is a runtime view: every tool call, every scheduled trigger, every session boundary the agent touched. They serve different audiences (Message/History for end users, Logs for operators / power users).

ActivityEvent Shape

Every event is a JSON object with this base shape:

{
  "ts": "2026-05-03T14:00:10.234Z",
  "kind": "tool_completed",
  "tool": "web_fetch",
  "title": "Fetched https://blog.example.com/weekly-roundup-12",
  "summary": {
    "url": "https://blog.example.com/weekly-roundup-12",
    "status": 200,
    "bytes": 18432
  },
  "durationMs": 1840,
  "ok": true,
  "cost": { "credits": 5, "skill": "int-wiro-aimodels", "action": "create" },
  "userUuid": "ada-uuid",
  "user": {
    "uuid": "ada-uuid",
    "firstname": "Ada",
    "lastname": "Lovelace",
    "email": "[email protected]",
    "username": "ada",
    "avatar": "https://cdn.wiro.ai/avatars/ada.webp",
    "avatarinitials": "AL"
  }
}
FieldTypeDescription
tsstringISO-8601 UTC datetime when the event was emitted inside the container (e.g. "2026-05-03T14:00:10.234Z"). Parse with Date.parse(ts) for arithmetic.
kindstringFine-grained event class. One of: "tool_started", "tool_completed" (one pair per tool invocation); "turn_started", "turn_ended" (LLM turn boundary); "cron_started", "cron_finished" (scheduled-cron tick); "session_start", "session_end" (chat-session boundary); "user_message" (user → agent message); "agent_reply" (agent → user reply); "token_usage" (a billed turn's token usage — chat / cron / voice post-call); "token_usage_idempotent" (a replayed usage callback, already billed — informational); "balance_gate_blocked" (a turn refused because the credit pool was exhausted).
toolstring?Present on tool_started / tool_completed. Tool family — one of: "read", "write", "edit", "exec", "web_fetch", "web_search", "sessions_spawn", "message".
titlestringHuman-readable one-line description of what happened (e.g. "Posted carousel: \"Brand voice teaser\"", "Fetched https://…", "Charged 5 credits · int-wiro-aimodels (create)"). Always present.
summaryobject?Structured event-specific payload. Opaque on the wire — render as JSON when expanding the row. Plugin pre-redacts apikey, apppassword, clientsecret, bearer, token, etc. before writing. Common keys: url, status, bytes, query, resultCount, path, lines, interval, trigger, messageguid, wordCount, elapsedTime.
durationMsnumber?Wall-clock duration in milliseconds. Populated on *_completed / *_finished / turn_ended events.
okboolean?true for a successful completion, false for a failure. Populated on *_completed / *_finished events. Pair with error for failure rows.
errorstring?One-line failure message (when ok: false).
costobject?Credit deduction attached to the event (exec tool calls that drove a per-action charge): { credits, skill, action }. Absent on no-charge events.
userUuidstring?UUID of the actor who triggered the event. "system" for cron / internal events. May be missing on legacy rows from the 180-day retention window pre-attribution rollout — the server falls back to the useragent owner.
userobject|nullResolved actor: { uuid, firstname, lastname, email, username, avatar, avatarinitials }. null when userUuid is "system" (automation / cron) or when the user record was deleted.
What is summary for? Tool calls include the input/output summary (URL, status, bytecount, search query); cron ticks include { interval, trigger }; message events include { messageguid, wordCount, elapsedTime }. Treat the object as opaque and render-on-expand — its shape is plugin-version-specific and is allowed to evolve without a docs revision.

Token-usage events

A token_usage event records the token spend and credit deduction for one billed turn — a chat reply, a cron run, or a voice post-call. A token_usage_idempotent event carries the same shape for a replayed usage callback that was already billed (informational; it does not deduct again). On top of the base ActivityEvent fields, these events add:

FieldTypeDescription
modelstringCanonical model slug that served the turn (e.g. "openai/gpt-5.4").
tokensobjectNested token counts in camelCase: { input, output, cacheRead, cacheWrite, total } (each an integer).
tokencostnumberCredits deducted for the turn (1 credit = $0.01).
durationMsnumberWall-clock duration of the turn in milliseconds.
callsnumberNumber of LLM calls made during the turn.
remainingcreditsnumberCredit pool remaining after the deduction.
{
  "ts": "2026-05-03T14:02:55.310Z",
  "kind": "token_usage",
  "title": "Token usage: 6670 tokens · 5 credits",
  "model": "openai/gpt-5.4",
  "tokens": { "input": 3450, "output": 820, "cacheRead": 2400, "cacheWrite": 0, "total": 6670 },
  "tokencost": 5,
  "durationMs": 4200,
  "calls": 2,
  "remainingcredits": 9995,
  "userUuid": "system",
  "user": null
}
Nested vs flat token fields. The activity-log token_usage event nests its counts in camelCase under tokens.{input, output, cacheRead, cacheWrite, total}. input is uncached input and total is the exact sum of all four components. This is distinct from the flat, lowercase columns on message rows (inputtokens, outputtokens, cachereadtokens, …). Same underlying data, different shape.

POST /UserAgent/Logs

Live tail of the agent's activity feed. Returns the last N events for the requested date (defaults to today).

ParameterTypeRequiredDescription
useragentguidstringYesYour UserAgent instance guid.
datestringNoYYYY-MM-DD to read a specific day's file. Default: today (UTC).
linesnumberNoMax events to return (max 5000). Default: 200.

Caching: the endpoint is cached server-side for 30 seconds per (useragentguid, date, lines) triple to absorb polling bursts. Plan for ≥30 s between repeated polls of the same key.

Request

curl -X POST "https://api.wiro.ai/v1/UserAgent/Logs" \
  -H "x-api-key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "useragentguid": "f8e7d6c5-b4a3-2190-fedc-ba0987654321", "lines": 200 }'

Response

{
  "result": true,
  "errors": [],
  "date": "2026-05-03",
  "totalLines": 1428,
  "events": [
    {
      "ts": "2026-05-03T14:00:12.481Z",
      "kind": "tool_completed",
      "tool": "exec",
      "title": "Charged 60 credits · int-wordpress-post (create)",
      "summary": { "skill": "int-wordpress-post", "action": "create", "balanceafter": 4940 },
      "durationMs": 92,
      "ok": true,
      "cost": { "credits": 60, "skill": "int-wordpress-post", "action": "create" },
      "userUuid": "system",
      "user": null
    },
    {
      "ts": "2026-05-03T14:00:10.305Z",
      "kind": "tool_completed",
      "tool": "exec",
      "title": "Posted: \"Weekly Roundup\"",
      "summary": {
        "title": "Weekly Roundup",
        "url": "https://blog.example.com/weekly-roundup-12",
        "categories": ["AI", "Tutorial"]
      },
      "durationMs": 1840,
      "ok": true,
      "userUuid": "system",
      "user": null
    },
    {
      "ts": "2026-05-03T13:30:00.000Z",
      "kind": "cron_finished",
      "title": "Cron tick: cs-cron-blog-scanner",
      "summary": { "skill": "cs-cron-blog-scanner", "interval": "0 9 * * *", "trigger": "scheduled" },
      "durationMs": 4210,
      "ok": true,
      "userUuid": "system",
      "user": null
    },
    {
      "ts": "2026-05-03T13:14:42.118Z",
      "kind": "agent_reply",
      "title": "Agent replied (412 words, 8.1s)",
      "summary": {
        "messageguid": "5c41dabf-f2be-4aa8-a5a4-8c9e3d2f3f11",
        "elapsedTime": "8.1s",
        "wordCount": 412
      },
      "durationMs": 8104,
      "ok": true,
      "userUuid": "ada-uuid",
      "user": {
        "uuid": "ada-uuid",
        "firstname": "Ada",
        "lastname": "Lovelace",
        "email": "[email protected]",
        "username": "ada",
        "avatar": "https://cdn.wiro.ai/avatars/ada.webp",
        "avatarinitials": "AL"
      }
    }
  ]
}
About the user field. Every event is decorated with the resolved actor: { uuid, firstname, lastname, email, username, avatar, avatarinitials }. It is null when:
  • The event was triggered by automation — userUuid: "system" for cron ticks, scheduled-skill runs, automated deductions, agent-initiated tool calls, and similar non-interactive writes (the three system / cron rows above).
  • The actor's user record was deleted or the lookup misses for any reason.
When the actor cannot be resolved (deleted account, automation), the server falls back to the useragent owner's UUID so the row never renders an empty placeholder.

POST /UserAgent/LogsList

Returns the list of dates for which an activity file exists for this agent. Useful for rendering a date-picker.

Response

{
  "result": true,
  "errors": [],
  "dates": [
    { "date": "2026-05-03", "sizeBytes": 184220, "compressed": false },
    { "date": "2026-05-02", "sizeBytes": 41280,  "compressed": true  },
    { "date": "2026-05-01", "sizeBytes": 38912,  "compressed": true  }
  ]
}

Each entry exposes date (YYYY-MM-DD, sorted newest-first), sizeBytes (raw byte size on disk; compressed size when compressed: true), and compressed (true once the worker's daily maintenance cron has gzipped the file to <date>.jsonl.gz; false for the active day's plain .jsonl). Reading either form via LogsFile returns the same decoded events. The host retains activity files for 180 days (rolling); older dates are pruned by a worker cron and won't appear here.

POST /UserAgent/LogsFile

Reads the full JSONL file for a specific date and returns every event in it. Use this for "Download today's activity" buttons or for off-band analysis.

ParameterTypeRequiredDescription
useragentguidstringYesYour UserAgent instance guid.
datestringYesYYYY-MM-DD of the file to read.

Response

{
  "result": true,
  "errors": [],
  "date": "2026-05-02",
  "truncated": false,
  "events": [
    {
      "ts": "2026-05-02T08:00:00.000Z",
      "kind": "session_start",
      "title": "Session started: user-42",
      "summary": { "sessionkey": "user-42" },
      "userUuid": "ada-uuid",
      "user": {
        "uuid": "ada-uuid",
        "firstname": "Ada",
        "lastname": "Lovelace",
        "email": "[email protected]",
        "username": "ada",
        "avatar": "https://cdn.wiro.ai/avatars/ada.webp",
        "avatarinitials": "AL"
      }
    },
    {
      "ts": "2026-05-02T08:00:20.142Z",
      "kind": "user_message",
      "title": "User: \"Hi, can you draft a roundup post?\"",
      "summary": { "messageguid": "5c41dabf-…", "wordCount": 8 },
      "userUuid": "ada-uuid",
      "user": {
        "uuid": "ada-uuid",
        "firstname": "Ada",
        "lastname": "Lovelace",
        "email": "[email protected]",
        "username": "ada",
        "avatar": "https://cdn.wiro.ai/avatars/ada.webp",
        "avatarinitials": "AL"
      }
    }
  ]
}

The user field follows the same resolution rules as the live tail above — see the Logs section's "About the user field" callout.

truncated is true when the file was capped at the worker's max-line ceiling (~50000 events per file). When true, paginate by date — older events for the same day are not retrievable through this endpoint.

POST /UserAgent/LogsDelete

Deletes one date's activity file and its gzipped sibling (if present). Idempotent — deleting an already-gone date is a success no-op.

ParameterTypeRequiredDescription
useragentguidstringYesYour UserAgent instance guid.
datestringYesYYYY-MM-DD of the file to remove.

Response

{
  "result": true,
  "errors": [],
  "date": "2026-05-01",
  "removed": { "plain": false, "gz": true }
}

removed.{plain,gz} reports which physical files were actually deleted. Both false is also a success (file was already gone).

Usage Patterns

Live polling for an in-app activity feed

Polling Logs every 30 seconds is the recommended cadence — the server-side cache TTL is calibrated for exactly this interval, so faster polling won't return fresher data:

async function pollActivity(useragentguid, onEvents) {
  let lastTs = 0;

  setInterval(async () => {
    const { data } = await axios.post(
      'https://api.wiro.ai/v1/UserAgent/Logs',
      { useragentguid, lines: 200 },
      { headers: { 'x-api-key': 'YOUR_API_KEY', 'Content-Type': 'application/json' } }
    );

    const fresh = data.events.filter(e => Date.parse(e.ts) > lastTs);
    if (fresh.length === 0) return;

    onEvents(fresh);
    lastTs = Date.parse(fresh[fresh.length - 1].ts);
  }, 30_000);
}

Daily download for off-band analysis

# 1) discover available dates
curl -X POST "https://api.wiro.ai/v1/UserAgent/LogsList" \
  -H "x-api-key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "useragentguid": "f8e7d6c5-b4a3-2190-fedc-ba0987654321" }'

# 2) download a specific date's full file
curl -X POST "https://api.wiro.ai/v1/UserAgent/LogsFile" \
  -H "x-api-key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "useragentguid": "f8e7d6c5-b4a3-2190-fedc-ba0987654321", "date": "2026-05-02" }' \
  > logs-2026-05-02.json

Retention & Rotation

PeriodWhat happens
Day 0 — 6File is plain JSONL (YYYY-MM-DD.jsonl). Live tail + LogsFile read it directly.
Day 7 — 180File is gzipped (YYYY-MM-DD.jsonl.gz). Reads are transparently decompressed; size in LogsList reports the on-disk compressed bytes.
Day 181+File is deleted by the agent's daily maintenance cron. LogsList no longer surfaces the date.
Persistence is per-agent. If Wiro re-allocates the agent's runtime (rare; happens during platform maintenance), the activity history starts fresh from that point. For long-term audit trails, schedule a daily LogsFile download to your own storage.

What's Next