> ## Documentation Index
> Fetch the complete documentation index at: https://docs.gainable.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Agent action log

> The audit trail for everything a Autopilot agent does

## What is the action log?

Every time a Autopilot agent runs, the runtime records what happened. The result is a structured, queryable log that lives in your app as the `AgentAction` collection. If something is in doubt, the action log is the source of truth.

The log captures four things for every run:

1. **The trigger.** Which playbook fired and why.
2. **The observation.** What the agent looked at (records, signals, prior actions).
3. **The reasoning.** What the agent decided and why.
4. **The outcome.** Which tools ran, what they returned, what happened next.

This isn't optional. The log is always on. The runtime cannot run a tool without writing to it.

## Why every action is logged

* **Trust.** Users approve drafts faster when they can see the full reasoning chain. Skeptics become believers when they can audit every step.
* **Debugging.** When a playbook misbehaves, the log shows where the reasoning went sideways.
* **Compliance.** Regulated environments need provable audit trails. The log is structured for export.
* **Graduation.** [Risk tier graduation](/autopilot/risk-tiers) uses approval rates from the log. No log, no graduation.
* **Improvement.** Approval and skip patterns reveal which objectives are tuned correctly and which need editing.

## Schema

The `AgentAction` collection is a system-managed collection in every Gainable app. You can query it through the [Copilot](/copilots/overview), the dashboard, or the action log UI in [Autopilot](/autopilot/inbox).

| Field              | Type      | What it stores                                                             |
| ------------------ | --------- | -------------------------------------------------------------------------- |
| `id`               | string    | Unique action ID                                                           |
| `agent_id`         | string    | The agent that produced the action                                         |
| `playbook_id`      | string    | The playbook that ran                                                      |
| `objective_id`     | string    | The objective the playbook was working toward                              |
| `scope`            | enum      | `app_wide` or `personal`                                                   |
| `trigger_type`     | enum      | `schedule`, `data_change`, `webhook`, `user_triggered`                     |
| `trigger_payload`  | json      | Full payload of the trigger                                                |
| `observation`      | json      | Records and signals the agent considered                                   |
| `reasoning`        | text      | Human-readable reasoning chain                                             |
| `tool_calls`       | array     | Each tool the playbook called, with inputs and outputs                     |
| `outcome`          | enum      | `approved`, `skipped`, `auto_executed`, `rate_limited`, `error`, `pending` |
| `owner_user_id`    | string    | The user the action's draft was routed to                                  |
| `created_at`       | timestamp | When the run began                                                         |
| `resolved_at`      | timestamp | When the action reached a terminal outcome                                 |
| `parent_action_id` | string    | If this action was a follow-up to another                                  |

## Outcomes

| Outcome         | What it means                                              |
| --------------- | ---------------------------------------------------------- |
| `pending`       | Draft is waiting in Autopilot                              |
| `approved`      | Owner approved; underlying tool ran                        |
| `skipped`       | Owner skipped the draft                                    |
| `auto_executed` | Action ran without approval (auto tier or auto-with-undo)  |
| `rate_limited`  | Action was blocked by a rate limit                         |
| `error`         | Tool call failed; reason captured in `tool_calls[*].error` |

## Querying the log

The action log is just a collection, so you can query it the same way you query anything else.

### From Copilot

Ask the [Copilot](/copilots/overview) directly:

```text theme={null}
Show all SLA-enforcer actions from the last 7 days
where the outcome was skipped.
```

```text theme={null}
Which playbook produced the most rate_limited
outcomes this month?
```

### From a dashboard

Drop a chart or table view onto a dashboard scoped to the `AgentAction` collection:

```text theme={null}
Add a chart to the operations dashboard showing
SLA-enforcer outcomes (approved, skipped, error)
over the last 30 days, grouped by week.
```

### From a playbook

Yes, agents can read their own action log. This is how follow-up playbooks know what's already been tried:

```yaml theme={null}
- tool: query_collection
  collection: AgentAction
  filter: "playbook_id = 'stalled_deal_chaser' AND owner_user_id = {{ deal.owner }} AND created_at > now - 7_days"
  limit: 1
```

If a stalled-deal playbook sees that it already drafted a nudge for this deal in the last 7 days, it can skip drafting another. Ackermann-class loops are bounded.

## What gets logged for each tool

| Tool                 | Logged inputs                 | Logged outputs                       |
| -------------------- | ----------------------------- | ------------------------------------ |
| `query_collection`   | Filter, sort, limit, fields   | Record IDs returned, count           |
| `update_records`     | Filter, set clause            | IDs updated, prior values for undo   |
| `send_email`         | Recipient, subject, body hash | Provider message ID, delivery status |
| `send_slack`         | Channel, message hash         | Slack message ID                     |
| `send_sms`           | Recipient hash, body hash     | Provider message ID                  |
| `notify_user`        | User ID, title, body          | Notification ID                      |
| `call_external`      | URL, method, body hash        | Status code, response hash           |
| `ask_human`          | User ID, question, options    | Answer, response time                |
| `create_task`        | Task fields                   | Created task ID                      |
| `draft_for_approval` | Underlying payload            | Draft ID, owner                      |

Bodies of outbound messages are hashed by default for compactness. The full content is retained on the draft itself in [Autopilot](/autopilot/inbox) until the configured retention window closes.

## Retention

| Data                       | Retention                                  |
| -------------------------- | ------------------------------------------ |
| Action records             | Indefinite (or per-org configured)         |
| Full draft bodies          | 90 days after `resolved_at` (configurable) |
| Tool input/output payloads | 30 days (configurable)                     |
| Hashes and metadata        | Same as the action record                  |

For regulated environments, retention can be raised to 7+ years. For consumer-facing apps with privacy obligations, retention can be tightened.

## Common queries

### Approval rate per playbook

```text theme={null}
Show approval rate per playbook over the last 30 days,
sorted highest to lowest.
```

This is the number you watch when deciding whether to graduate a playbook to a higher [risk tier](/autopilot/risk-tiers).

### Why was this draft created?

Open the draft in [Autopilot](/autopilot/inbox). The `reasoning` and `observation` fields render alongside the proposed action. No need to query the log directly.

### What did this agent do this morning?

```text theme={null}
Show all actions from the daily-briefing agent
in the last 24 hours.
```

### Where are skips concentrated?

```text theme={null}
Which objectives have the lowest approval rate this month?
```

A low approval rate means the objective is producing drafts that don't match what users actually want. Tighten the trigger filter or revise the template.

## Best practices

<AccordionGroup>
  <Accordion title="Add a log dashboard early">
    A dashboard with approval rate, skip reasons, and rate-limited counts is the single most useful artifact for tuning agents. Add it during the autonomy build phase.
  </Accordion>

  <Accordion title="Watch errors aggressively">
    Errors in `tool_calls` are usually a sign of upstream changes (renamed fields, missing API keys, deleted records). Spikes mean something broke.
  </Accordion>

  <Accordion title="Use parent_action_id for follow-ups">
    When playbook B runs because playbook A's draft was approved, link them. The thread of reasoning becomes traceable.
  </Accordion>

  <Accordion title="Don't write to AgentAction yourself">
    The runtime owns the schema. Custom apps that try to insert their own action records will fail validation. Use [`create_task`](/autopilot/tools) for human-visible TODOs instead.
  </Accordion>
</AccordionGroup>

## Learn more

<CardGroup cols={2}>
  <Card title="Tools" icon="screwdriver-wrench" href="/autopilot/tools">
    What gets logged per tool
  </Card>

  <Card title="Risk tiers" icon="shield-check" href="/autopilot/risk-tiers">
    How the log feeds graduation
  </Card>

  <Card title="Inbox" icon="inbox" href="/autopilot/inbox">
    Where the log surfaces in the UI
  </Card>

  <Card title="Scopes" icon="user-shield" href="/autopilot/scopes">
    How log slicing maps to scope
  </Card>
</CardGroup>
