# Analytics events

> **This file is generated.** Run `make events` to regenerate it, and commit the result.
> CI fails when it is out of date. Edit the event declarations in each service, not this file.

Every product analytics event Arcade emits, and what each of its properties
carries. This is the bill of materials for a self-hosted deployment: it states,
property by property, what leaves a customer's cluster.

## On-premises dispositions

| Disposition | Meaning                                    |
| ----------- | ------------------------------------------ |
| `allow`     | The value is sent verbatim.                |
| `hash`      | The value is replaced with a keyed digest. |
| `deny`      | The value is not sent.                     |

A hashed value is a deployment-keyed HMAC, so it groups a customer's own
events together without being reversible to the original and without matching
the same value from another deployment.

Arcade Cloud emits every property verbatim; dispositions apply only where the
event crosses out of a customer's network.

Coverage: **55** declared events, **418** properties (300 allow, 78 hash, 40 deny).

## Always-attached properties

These are not part of any one event. Middleware attaches each bundle to
every event emitted while serving the surface it describes, so they reach
the wire alongside whatever the event itself declares.

### APIRequestContext

Is attached to every event emitted while serving an authenticated HTTP API request.

Attached by `engine` — declared in `apps/engine/internal/usage/events/ambient.go`.

| Property        | Type   | Always present | On-premises |
| --------------- | ------ | -------------- | ----------- |
| `arcade_client` | string | yes            | `allow`     |

### GatewayContext

Is attached to every event emitted while serving an MCP gateway request, so gateway-scoped reporting works without each call site having to know which gateway it is inside.

Attached by `engine` — declared in `apps/engine/internal/usage/events/ambient.go`.

| Property                 | Type   | Always present | On-premises |
| ------------------------ | ------ | -------------- | ----------- |
| `gateway_auth_type`      | string | yes            | `allow`     |
| `gateway_id`             | string | yes            | `hash`      |
| `gateway_name`           | string | yes            | `deny`      |
| `gateway_slug`           | string | yes            | `hash`      |
| `gateway_user_source_id` | string | no             | `hash`      |

### MCPProtocolContext

Is attached to every event emitted while serving a stateless MCP request, carrying the protocol version the client declared in its headers.

Attached by `engine` — declared in `apps/engine/internal/usage/events/ambient.go`.

| Property           | Type   | Always present | On-premises |
| ------------------ | ------ | -------------- | ----------- |
| `protocol_version` | string | yes            | `allow`     |

## Events

### Account created

A person signed up. `$set` is a PostHog convention, not an ordinary property: its contents are written to the person record rather than the event.

Emitted by `coordinator` — declared in `apps/coordinator/arcade/app/usage/events/_events.py`.

| Property                 | Type   | Always present | On-premises |
| ------------------------ | ------ | -------------- | ----------- |
| `$set`                   | object | yes            | `deny`      |
| `$set.company`           | string | no             | `deny`      |
| `$set.created_timestamp` | string | no             | `deny`      |
| `$set.email`             | string | yes            | `deny`      |
| `$set.name`              | string | no             | `deny`      |
| `$set.preferred_name`    | string | no             | `deny`      |

### API called

Reports every authenticated request to the Engine's HTTP API. It is the platform's highest-volume event by an order of magnitude. The client properties come from the SDK's own headers and describe the integration, not the person using it, so they are disclosed. The caller's IP address identifies a customer's own network and is not.

Emitted by `engine` — declared in `apps/engine/internal/usage/events/api.go`.

| Property                 | Type   | Always present | On-premises |
| ------------------------ | ------ | -------------- | ----------- |
| `arcade_client`          | string | yes            | `allow`     |
| `client_architecture`    | string | yes            | `allow`     |
| `client_ip`              | string | yes            | `deny`      |
| `client_language`        | string | yes            | `allow`     |
| `client_os`              | string | yes            | `allow`     |
| `client_runtime`         | string | yes            | `allow`     |
| `client_runtime_version` | string | yes            | `allow`     |
| `client_sdk_version`     | string | yes            | `allow`     |
| `duration_ms`            | number | yes            | `allow`     |
| `end_timestamp`          | string | yes            | `allow`     |
| `engine_endpoint`        | string | yes            | `allow`     |
| `engine_host`            | string | yes            | `deny`      |
| `http_method`            | string | yes            | `allow`     |
| `retry_count`            | string | yes            | `allow`     |
| `start_timestamp`        | string | yes            | `allow`     |
| `status_code`            | number | yes            | `allow`     |

### API key created

An API key was minted. `api_key_type` is set on the project-key path and `arcade_client` on the account path, so each is absent from the other's events.

Emitted by `coordinator` — declared in `apps/coordinator/arcade/app/usage/events/_events.py`.

| Property        | Type   | Always present | On-premises |
| --------------- | ------ | -------------- | ----------- |
| `api_key_ksuid` | string | yes            | `hash`      |
| `api_key_label` | string | no             | `deny`      |
| `api_key_type`  | string | no             | `allow`     |
| `arcade_client` | string | no             | `allow`     |

### capability_gap_reported

Reports an agent telling us, in prose, what it could not do. Every free-text field is the agent's or the end user's own words about their work, so none of it leaves a self-hosted cluster; the tool and client identifiers around it are what make the report countable.

Emitted by `engine` — declared in `apps/engine/internal/usage/events/tool_recommendation.go`.

| Property                           | Type    | Always present | On-premises |
| ---------------------------------- | ------- | -------------- | ----------- |
| `$process_person_profile`          | boolean | yes            | `allow`     |
| `attempted_tool_name`              | string  | yes            | `allow`     |
| `client_name`                      | string  | yes            | `allow`     |
| `client_version`                   | string  | yes            | `allow`     |
| `customer_id`                      | string  | no             | `hash`      |
| `mcp_session`                      | object  | no             | `deny`      |
| `mcp_session.client_name`          | string  | no             | `deny`      |
| `mcp_session.client_title`         | string  | no             | `deny`      |
| `mcp_session.client_version`       | string  | no             | `deny`      |
| `mcp_session.mcp_protocol_version` | string  | no             | `deny`      |
| `mcp_session.session_id`           | string  | no             | `deny`      |
| `model_name`                       | string  | yes            | `allow`     |
| `model_version`                    | string  | yes            | `allow`     |
| `principal_type`                   | string  | no             | `allow`     |
| `project_id`                       | string  | no             | `allow`     |
| `source`                           | string  | yes            | `allow`     |
| `tenant_id`                        | string  | no             | `allow`     |
| `tool_name`                        | string  | yes            | `allow`     |
| `toolkit_name`                     | string  | yes            | `allow`     |
| `toolkit_version`                  | string  | yes            | `allow`     |
| `user_goal`                        | string  | yes            | `deny`      |
| `user_quote`                       | string  | yes            | `deny`      |
| `what_blocked_you`                 | string  | yes            | `deny`      |

### Checkout started

A customer opened a checkout session. Paired with Subscription updated, this is what makes the upgrade funnel measurable.

Emitted by `coordinator` — declared in `apps/coordinator/arcade/app/usage/events/_events.py`.

| Property           | Type   | Always present | On-premises |
| ------------------ | ------ | -------------- | ----------- |
| `amount`           | number | no             | `allow`     |
| `change_direction` | string | yes            | `allow`     |
| `currency`         | string | no             | `allow`     |
| `new_plan_cadence` | string | no             | `allow`     |
| `new_plan_id`      | string | no             | `allow`     |
| `new_plan_name`    | string | no             | `allow`     |
| `old_plan_cadence` | string | no             | `allow`     |
| `old_plan_id`      | string | no             | `allow`     |
| `old_plan_name`    | string | no             | `allow`     |

### Custom user verifier configured

Reports a deployment pointing user verification at its own endpoint. `operation` distinguishes first configuration from a change or a removal.

Emitted by `engine` — declared in `apps/engine/internal/usage/events/admin.go`.

| Property    | Type   | Always present | On-premises |
| ----------- | ------ | -------------- | ----------- |
| `operation` | string | yes            | `allow`     |

### Error confirming user

Reports a failure to confirm a user mid-flow. `error` is relayed from the Coordinator verbatim and can embed request detail, so it stays inside the cluster.

Emitted by `engine` — declared in `apps/engine/internal/usage/events/auth.go`.

| Property  | Type   | Always present | On-premises |
| --------- | ------ | -------------- | ----------- |
| `error`   | string | yes            | `deny`      |
| `flow_id` | string | yes            | `hash`      |
| `user_id` | string | yes            | `hash`      |

### Flow test failed

Reports a provider flow test that did not complete.

Emitted by `engine` — declared in `apps/engine/internal/usage/events/auth.go`.

| Property         | Type   | Always present | On-premises |
| ---------------- | ------ | -------------- | ----------- |
| `failure_reason` | string | yes            | `allow`     |
| `flow_id`        | string | yes            | `hash`      |
| `user_id`        | string | yes            | `hash`      |

### Flow test started

Reports an administrator beginning a provider flow test.

Emitted by `engine` — declared in `apps/engine/internal/usage/events/auth.go`.

| Property  | Type   | Always present | On-premises |
| --------- | ------ | -------------- | ----------- |
| `flow_id` | string | yes            | `hash`      |
| `user_id` | string | yes            | `hash`      |

### Flow test succeeded

Reports a provider flow test that completed.

Emitted by `engine` — declared in `apps/engine/internal/usage/events/auth.go`.

| Property  | Type   | Always present | On-premises |
| --------- | ------ | -------------- | ----------- |
| `flow_id` | string | yes            | `hash`      |
| `user_id` | string | yes            | `hash`      |

### Gateway created

Reports a new MCP gateway being configured.

Emitted by `engine` — declared in `apps/engine/internal/usage/events/gateway.go`.

| Property            | Type   | Always present | On-premises |
| ------------------- | ------ | -------------- | ----------- |
| `gateway_auth_type` | string | yes            | `allow`     |
| `gateway_id`        | string | yes            | `hash`      |
| `gateway_name`      | string | yes            | `deny`      |
| `gateway_slug`      | string | yes            | `hash`      |
| `user_source_id`    | string | yes            | `hash`      |

### Gateway deleted

Reports a gateway being removed.

Emitted by `engine` — declared in `apps/engine/internal/usage/events/gateway.go`.

| Property            | Type   | Always present | On-premises |
| ------------------- | ------ | -------------- | ----------- |
| `gateway_auth_type` | string | yes            | `allow`     |
| `gateway_id`        | string | yes            | `hash`      |
| `gateway_name`      | string | yes            | `deny`      |
| `gateway_slug`      | string | yes            | `hash`      |
| `user_source_id`    | string | yes            | `hash`      |

### Gateway updated

Reports a change to an existing gateway. The `previous_*` properties appear only when that field actually changed, so the per-gateway timeline can distinguish a first assignment from a reassignment or an unbind without carrying a duplicate property on every update.

Emitted by `engine` — declared in `apps/engine/internal/usage/events/gateway.go`.

| Property                     | Type   | Always present | On-premises |
| ---------------------------- | ------ | -------------- | ----------- |
| `gateway_auth_type`          | string | yes            | `allow`     |
| `gateway_id`                 | string | yes            | `hash`      |
| `gateway_name`               | string | yes            | `deny`      |
| `gateway_slug`               | string | yes            | `hash`      |
| `previous_gateway_auth_type` | string | no             | `allow`     |
| `previous_user_source_id`    | string | no             | `hash`      |
| `user_source_id`             | string | yes            | `hash`      |

### Hook execution failed

Reports a hook that could not run. `error` is the raw error from customer-supplied extension code, so it stays inside the cluster.

Emitted by `engine` — declared in `apps/engine/internal/usage/events/hooks.go`.

| Property           | Type   | Always present | On-premises |
| ------------------ | ------ | -------------- | ----------- |
| `error`            | string | yes            | `deny`      |
| `hook_binding`     | string | yes            | `allow`     |
| `hook_duration_ms` | number | yes            | `allow`     |
| `hook_family`      | string | yes            | `allow`     |
| `hook_phase`       | string | yes            | `allow`     |
| `hook_priority`    | number | yes            | `allow`     |
| `hook_reason`      | string | yes            | `allow`     |
| `hook_result`      | string | yes            | `allow`     |
| `hook_type`        | string | yes            | `allow`     |
| `hook_version`     | number | yes            | `allow`     |

### Hook execution succeeded

Reports a hook that ran, whether it allowed or denied. A denial is a successful execution — `hook_result` is what separates them.

Emitted by `engine` — declared in `apps/engine/internal/usage/events/hooks.go`.

| Property           | Type   | Always present | On-premises |
| ------------------ | ------ | -------------- | ----------- |
| `hook_binding`     | string | yes            | `allow`     |
| `hook_duration_ms` | number | yes            | `allow`     |
| `hook_family`      | string | yes            | `allow`     |
| `hook_phase`       | string | yes            | `allow`     |
| `hook_priority`    | number | yes            | `allow`     |
| `hook_reason`      | string | yes            | `allow`     |
| `hook_result`      | string | yes            | `allow`     |
| `hook_type`        | string | yes            | `allow`     |
| `hook_version`     | number | yes            | `allow`     |

### MCP apps listed

Reports an agent asking which applications are available.

Emitted by `engine` — declared in `apps/engine/internal/usage/events/mcp.go`.

| Property                          | Type    | Always present | On-premises |
| --------------------------------- | ------- | -------------- | ----------- |
| `client_name`                     | string  | no             | `allow`     |
| `client_supports_url_elicitation` | boolean | no             | `allow`     |
| `client_title`                    | string  | no             | `allow`     |
| `client_version`                  | string  | no             | `allow`     |
| `fully_qualified_tool_name`       | string  | no             | `allow`     |
| `protocol_version`                | string  | yes            | `allow`     |
| `session_id`                      | string  | no             | `hash`      |
| `tool_name`                       | string  | no             | `allow`     |
| `toolkit_name`                    | string  | no             | `allow`     |
| `toolkit_version`                 | string  | no             | `allow`     |
| `user_id`                         | string  | yes            | `hash`      |

### MCP auth challenge started

Reports a tool call that stopped to ask the caller for authorization. The tool did not run, so this is not a successful call.

Emitted by `engine` — declared in `apps/engine/internal/usage/events/mcp.go`.

| Property                          | Type    | Always present | On-premises |
| --------------------------------- | ------- | -------------- | ----------- |
| `client_name`                     | string  | no             | `allow`     |
| `client_supports_url_elicitation` | boolean | no             | `allow`     |
| `client_title`                    | string  | no             | `allow`     |
| `client_version`                  | string  | no             | `allow`     |
| `fully_qualified_tool_name`       | string  | no             | `allow`     |
| `protocol_version`                | string  | yes            | `allow`     |
| `session_id`                      | string  | no             | `hash`      |
| `tool_name`                       | string  | no             | `allow`     |
| `toolkit_name`                    | string  | no             | `allow`     |
| `toolkit_version`                 | string  | no             | `allow`     |
| `user_id`                         | string  | yes            | `hash`      |

### MCP capability gap report filed

Reports an agent telling us it could not find a tool for what it was trying to do.

Emitted by `engine` — declared in `apps/engine/internal/usage/events/mcp.go`.

| Property                          | Type    | Always present | On-premises |
| --------------------------------- | ------- | -------------- | ----------- |
| `client_name`                     | string  | no             | `allow`     |
| `client_supports_url_elicitation` | boolean | no             | `allow`     |
| `client_title`                    | string  | no             | `allow`     |
| `client_version`                  | string  | no             | `allow`     |
| `fully_qualified_tool_name`       | string  | no             | `allow`     |
| `protocol_version`                | string  | yes            | `allow`     |
| `session_id`                      | string  | no             | `hash`      |
| `tool_name`                       | string  | no             | `allow`     |
| `toolkit_name`                    | string  | no             | `allow`     |
| `toolkit_version`                 | string  | no             | `allow`     |
| `user_id`                         | string  | yes            | `hash`      |

### MCP manage auth offered

Reports authorization links being handed to an agent.

Emitted by `engine` — declared in `apps/engine/internal/usage/events/mcp.go`.

| Property                          | Type    | Always present | On-premises |
| --------------------------------- | ------- | -------------- | ----------- |
| `client_name`                     | string  | no             | `allow`     |
| `client_supports_url_elicitation` | boolean | no             | `allow`     |
| `client_title`                    | string  | no             | `allow`     |
| `client_version`                  | string  | no             | `allow`     |
| `fully_qualified_tool_name`       | string  | no             | `allow`     |
| `link_count`                      | number  | yes            | `allow`     |
| `protocol_version`                | string  | yes            | `allow`     |
| `provider_count`                  | number  | yes            | `allow`     |
| `providers`                       | array   | yes            | `allow`     |
| `session_id`                      | string  | no             | `hash`      |
| `tool_name`                       | string  | no             | `allow`     |
| `toolkit_name`                    | string  | no             | `allow`     |
| `toolkit_version`                 | string  | no             | `allow`     |
| `user_id`                         | string  | yes            | `hash`      |

### MCP manage auth status checked

Reports an agent reading authorization status without being offered anything to complete.

Emitted by `engine` — declared in `apps/engine/internal/usage/events/mcp.go`.

| Property                          | Type    | Always present | On-premises |
| --------------------------------- | ------- | -------------- | ----------- |
| `client_name`                     | string  | no             | `allow`     |
| `client_supports_url_elicitation` | boolean | no             | `allow`     |
| `client_title`                    | string  | no             | `allow`     |
| `client_version`                  | string  | no             | `allow`     |
| `fully_qualified_tool_name`       | string  | no             | `allow`     |
| `link_count`                      | number  | yes            | `allow`     |
| `protocol_version`                | string  | yes            | `allow`     |
| `provider_count`                  | number  | yes            | `allow`     |
| `providers`                       | array   | yes            | `allow`     |
| `session_id`                      | string  | no             | `hash`      |
| `tool_name`                       | string  | no             | `allow`     |
| `toolkit_name`                    | string  | no             | `allow`     |
| `toolkit_version`                 | string  | no             | `allow`     |
| `user_id`                         | string  | yes            | `hash`      |

### MCP registry suggestion created

Reports an end user filing a capability request through a gateway's Private Registry.

Emitted by `engine` — declared in `apps/engine/internal/usage/events/mcp.go`.

| Property                          | Type    | Always present | On-premises |
| --------------------------------- | ------- | -------------- | ----------- |
| `client_name`                     | string  | no             | `allow`     |
| `client_supports_url_elicitation` | boolean | no             | `allow`     |
| `client_title`                    | string  | no             | `allow`     |
| `client_version`                  | string  | no             | `allow`     |
| `fully_qualified_tool_name`       | string  | no             | `allow`     |
| `protocol_version`                | string  | yes            | `allow`     |
| `session_id`                      | string  | no             | `hash`      |
| `tool_name`                       | string  | no             | `allow`     |
| `toolkit_name`                    | string  | no             | `allow`     |
| `toolkit_version`                 | string  | no             | `allow`     |
| `user_id`                         | string  | yes            | `hash`      |

### MCP registry suggestion supported

Reports an end user supporting another user's capability request through a gateway's Private Registry.

Emitted by `engine` — declared in `apps/engine/internal/usage/events/mcp.go`.

| Property                          | Type    | Always present | On-premises |
| --------------------------------- | ------- | -------------- | ----------- |
| `client_name`                     | string  | no             | `allow`     |
| `client_supports_url_elicitation` | boolean | no             | `allow`     |
| `client_title`                    | string  | no             | `allow`     |
| `client_version`                  | string  | no             | `allow`     |
| `fully_qualified_tool_name`       | string  | no             | `allow`     |
| `outcome`                         | string  | yes            | `allow`     |
| `protocol_version`                | string  | yes            | `allow`     |
| `session_id`                      | string  | no             | `hash`      |
| `tool_name`                       | string  | no             | `allow`     |
| `toolkit_name`                    | string  | no             | `allow`     |
| `toolkit_version`                 | string  | no             | `allow`     |
| `user_id`                         | string  | yes            | `hash`      |

### MCP registry suggestions listed

Reports an end user reading either the gateway's suggestion list or their own source-scoped submissions.

Emitted by `engine` — declared in `apps/engine/internal/usage/events/mcp.go`.

| Property                          | Type    | Always present | On-premises |
| --------------------------------- | ------- | -------------- | ----------- |
| `client_name`                     | string  | no             | `allow`     |
| `client_supports_url_elicitation` | boolean | no             | `allow`     |
| `client_title`                    | string  | no             | `allow`     |
| `client_version`                  | string  | no             | `allow`     |
| `fully_qualified_tool_name`       | string  | no             | `allow`     |
| `protocol_version`                | string  | yes            | `allow`     |
| `session_id`                      | string  | no             | `hash`      |
| `tool_name`                       | string  | no             | `allow`     |
| `toolkit_name`                    | string  | no             | `allow`     |
| `toolkit_version`                 | string  | no             | `allow`     |
| `user_id`                         | string  | yes            | `hash`      |

### MCP server discovered

Reports a client listing what a gateway offers, which is the step before it calls anything.

Emitted by `engine` — declared in `apps/engine/internal/usage/events/mcp.go`.

| Property                          | Type    | Always present | On-premises |
| --------------------------------- | ------- | -------------- | ----------- |
| `client_name`                     | string  | no             | `allow`     |
| `client_supports_url_elicitation` | boolean | no             | `allow`     |
| `client_title`                    | string  | no             | `allow`     |
| `client_version`                  | string  | no             | `allow`     |
| `fully_qualified_tool_name`       | string  | no             | `allow`     |
| `protocol_version`                | string  | yes            | `allow`     |
| `session_id`                      | string  | no             | `hash`      |
| `tool_name`                       | string  | no             | `allow`     |
| `toolkit_name`                    | string  | no             | `allow`     |
| `toolkit_version`                 | string  | no             | `allow`     |
| `user_id`                         | string  | yes            | `hash`      |

### MCP session started

Reports an agent completing the MCP handshake against a gateway. `client_requested_protocol_version` is what the client asked for, which can differ from the `protocol_version` that was negotiated.

Emitted by `engine` — declared in `apps/engine/internal/usage/events/mcp.go`.

| Property                            | Type    | Always present | On-premises |
| ----------------------------------- | ------- | -------------- | ----------- |
| `client_name`                       | string  | no             | `allow`     |
| `client_requested_protocol_version` | string  | yes            | `allow`     |
| `client_supports_url_elicitation`   | boolean | no             | `allow`     |
| `client_title`                      | string  | no             | `allow`     |
| `client_version`                    | string  | no             | `allow`     |
| `fully_qualified_tool_name`         | string  | no             | `allow`     |
| `protocol_version`                  | string  | yes            | `allow`     |
| `session_id`                        | string  | no             | `hash`      |
| `tool_name`                         | string  | no             | `allow`     |
| `toolkit_name`                      | string  | no             | `allow`     |
| `toolkit_version`                   | string  | no             | `allow`     |
| `user_id`                           | string  | yes            | `hash`      |

### MCP tool call succeeded

Reports a tool invoked through an MCP gateway returning without a transport-level failure.

Emitted by `engine` — declared in `apps/engine/internal/usage/events/mcp.go`.

| Property                          | Type    | Always present | On-premises |
| --------------------------------- | ------- | -------------- | ----------- |
| `client_name`                     | string  | no             | `allow`     |
| `client_supports_url_elicitation` | boolean | no             | `allow`     |
| `client_title`                    | string  | no             | `allow`     |
| `client_version`                  | string  | no             | `allow`     |
| `fully_qualified_tool_name`       | string  | no             | `allow`     |
| `protocol_version`                | string  | yes            | `allow`     |
| `session_id`                      | string  | no             | `hash`      |
| `tool_name`                       | string  | no             | `allow`     |
| `toolkit_name`                    | string  | no             | `allow`     |
| `toolkit_version`                 | string  | no             | `allow`     |
| `user_id`                         | string  | yes            | `hash`      |

### MCP tool recommendation queried

Reports an agent asking which tools suit a task.

Emitted by `engine` — declared in `apps/engine/internal/usage/events/mcp.go`.

| Property                          | Type    | Always present | On-premises |
| --------------------------------- | ------- | -------------- | ----------- |
| `client_name`                     | string  | no             | `allow`     |
| `client_supports_url_elicitation` | boolean | no             | `allow`     |
| `client_title`                    | string  | no             | `allow`     |
| `client_version`                  | string  | no             | `allow`     |
| `fully_qualified_tool_name`       | string  | no             | `allow`     |
| `protocol_version`                | string  | yes            | `allow`     |
| `session_id`                      | string  | no             | `hash`      |
| `tool_name`                       | string  | no             | `allow`     |
| `toolkit_name`                    | string  | no             | `allow`     |
| `toolkit_version`                 | string  | no             | `allow`     |
| `user_id`                         | string  | yes            | `hash`      |

### MCP use tool invoked

Reports an agent running a tool through the recommendation builtin rather than calling it by name.

Emitted by `engine` — declared in `apps/engine/internal/usage/events/mcp.go`.

| Property                          | Type    | Always present | On-premises |
| --------------------------------- | ------- | -------------- | ----------- |
| `client_name`                     | string  | no             | `allow`     |
| `client_supports_url_elicitation` | boolean | no             | `allow`     |
| `client_title`                    | string  | no             | `allow`     |
| `client_version`                  | string  | no             | `allow`     |
| `fully_qualified_tool_name`       | string  | no             | `allow`     |
| `protocol_version`                | string  | yes            | `allow`     |
| `session_id`                      | string  | no             | `hash`      |
| `tool_name`                       | string  | no             | `allow`     |
| `toolkit_name`                    | string  | no             | `allow`     |
| `toolkit_version`                 | string  | no             | `allow`     |
| `user_id`                         | string  | yes            | `hash`      |

### OAuth client registered

A client registered against Arcade's OAuth server. Dynamic Client Registration is unauthenticated, so this is a system event with no user context. The client's self-declared name and URI come from an unauthenticated request, so neither is disclosed.

Emitted by `coordinator` — declared in `apps/coordinator/arcade/app/usage/events/_events.py`.

| Property            | Type    | Always present | On-premises |
| ------------------- | ------- | -------------- | ----------- |
| `application_type`  | string  | no             | `allow`     |
| `client_id`         | string  | yes            | `hash`      |
| `client_name`       | string  | no             | `deny`      |
| `client_uri`        | string  | no             | `deny`      |
| `grant_types`       | array   | no             | `allow`     |
| `has_jwks`          | boolean | no             | `allow`     |
| `registration_type` | string  | yes            | `allow`     |
| `scope`             | array   | no             | `allow`     |
| `software_id`       | string  | no             | `allow`     |
| `software_version`  | string  | no             | `allow`     |

### OAuth provider created

Reports an auth provider being configured. `provider_id` is the well-known provider's name, or "custom" for one Arcade does not ship.

Emitted by `engine` — declared in `apps/engine/internal/usage/events/admin.go`.

| Property      | Type   | Always present | On-premises |
| ------------- | ------ | -------------- | ----------- |
| `provider_id` | string | yes            | `allow`     |

### OAuth token issued

Arcade Auth issued a token. Emitted only where a local account record exists, which is the static Arcade Auth flow. `amr` is the authentication methods reference from the identity provider: a bounded set of method names, not user data.

Emitted by `coordinator` — declared in `apps/coordinator/arcade/app/usage/events/_events.py`.

| Property            | Type    | Always present | On-premises |
| ------------------- | ------- | -------------- | ----------- |
| `amr`               | array   | no             | `allow`     |
| `auth_time`         | number  | no             | `allow`     |
| `auth_type`         | string  | no             | `allow`     |
| `client_id`         | string  | no             | `hash`      |
| `grant_type`        | string  | yes            | `allow`     |
| `has_refresh_token` | boolean | yes            | `allow`     |
| `scope`             | string  | no             | `allow`     |
| `user_source_id`    | string  | no             | `hash`      |

### Organization updated

An organization's profile changed. Everything but the identifier is what a customer chose to call themselves, so only the identifier is disclosed.

Emitted by `coordinator` — declared in `apps/coordinator/arcade/app/usage/events/_events.py`.

| Property                   | Type   | Always present | On-premises |
| -------------------------- | ------ | -------------- | ----------- |
| `organization_color`       | string | no             | `allow`     |
| `organization_description` | string | no             | `deny`      |
| `organization_id`          | string | yes            | `allow`     |
| `organization_image_url`   | string | no             | `deny`      |
| `organization_name`        | string | no             | `deny`      |

### Project created

A project was created. Emitted both on explicit creation and for the default project that comes with a new organization.

Emitted by `coordinator` — declared in `apps/coordinator/arcade/app/usage/events/_events.py`.

| Property              | Type   | Always present | On-premises |
| --------------------- | ------ | -------------- | ----------- |
| `project_color`       | string | no             | `allow`     |
| `project_description` | string | no             | `deny`      |
| `project_id`          | string | yes            | `allow`     |
| `project_image_url`   | string | no             | `deny`      |
| `project_name`        | string | no             | `deny`      |

### Project deleted

A project was removed.

Emitted by `coordinator` — declared in `apps/coordinator/arcade/app/usage/events/_events.py`.

| Property              | Type   | Always present | On-premises |
| --------------------- | ------ | -------------- | ----------- |
| `project_color`       | string | no             | `allow`     |
| `project_description` | string | no             | `deny`      |
| `project_id`          | string | yes            | `allow`     |
| `project_image_url`   | string | no             | `deny`      |
| `project_name`        | string | no             | `deny`      |

### Project updated

A project's profile changed.

Emitted by `coordinator` — declared in `apps/coordinator/arcade/app/usage/events/_events.py`.

| Property              | Type   | Always present | On-premises |
| --------------------- | ------ | -------------- | ----------- |
| `project_color`       | string | no             | `allow`     |
| `project_description` | string | no             | `deny`      |
| `project_id`          | string | yes            | `allow`     |
| `project_image_url`   | string | no             | `deny`      |
| `project_name`        | string | no             | `deny`      |

### Secret upserted

Reports a secret being written. It deliberately carries no properties: that a deployment manages secrets is the signal, and every attribute of one would describe the secret.

Emitted by `engine` — declared in `apps/engine/internal/usage/events/admin.go`.

| Property | Type | Always present | On-premises |
| -------- | ---- | -------------- | ----------- |

### Server added

Reports a tool server being registered. `credential_mode` is the joined set of credential sources an MCP server carries, which caps the property at eight values rather than the unbounded space a per-source list would produce. It is absent for HTTP servers, which always authenticate with a shared secret.

Emitted by `engine` — declared in `apps/engine/internal/usage/events/admin.go`.

| Property          | Type   | Always present | On-premises |
| ----------------- | ------ | -------------- | ----------- |
| `credential_mode` | string | no             | `allow`     |
| `server_type`     | string | yes            | `allow`     |

### Server deployed

Reports a worker deployment, sized by how many toolkits it carries.

Emitted by `engine` — declared in `apps/engine/internal/usage/events/admin.go`.

| Property        | Type   | Always present | On-premises |
| --------------- | ------ | -------------- | ----------- |
| `toolkit_count` | number | yes            | `allow`     |

### Subscription updated

A customer's subscription changed.

Emitted by `coordinator` — declared in `apps/coordinator/arcade/app/usage/events/_events.py`.

| Property           | Type   | Always present | On-premises |
| ------------------ | ------ | -------------- | ----------- |
| `amount`           | number | no             | `allow`     |
| `change_direction` | string | yes            | `allow`     |
| `currency`         | string | no             | `allow`     |
| `new_plan_cadence` | string | no             | `allow`     |
| `new_plan_id`      | string | no             | `allow`     |
| `new_plan_name`    | string | no             | `allow`     |
| `old_plan_cadence` | string | no             | `allow`     |
| `old_plan_id`      | string | no             | `allow`     |
| `old_plan_name`    | string | no             | `allow`     |

### Tool execution failed

Reports an execution that never produced a tool response — the call to the worker itself failed. `arcade_error` is the raw Go error, which routinely embeds upstream URLs and request bodies, so it stays inside the cluster.

Emitted by `engine` — declared in `apps/engine/internal/usage/events/tool_execution.go`.

| Property                    | Type   | Always present | On-premises |
| --------------------------- | ------ | -------------- | ----------- |
| `arcade_error`              | string | yes            | `deny`      |
| `execution_id`              | string | yes            | `hash`      |
| `execution_type`            | string | yes            | `allow`     |
| `execution_type_reason`     | string | no             | `allow`     |
| `fully_qualified_tool_name` | string | yes            | `allow`     |
| `requested_tool_version`    | string | yes            | `allow`     |
| `run_id`                    | string | yes            | `hash`      |
| `tool_name`                 | string | yes            | `allow`     |
| `tool_version`              | string | yes            | `allow`     |
| `toolkit_name`              | string | yes            | `allow`     |
| `user_id`                   | string | no             | `hash`      |
| `worker_id`                 | string | yes            | `hash`      |

### Tool execution succeeded

Reports a tool the Engine ran to completion. The tool itself may still have returned an error, described by the `execution_error_*` properties; `is_execution_success` is what separates the two cases. `execution_error` and `execution_error_dev_message` relay text from the upstream provider verbatim, which in practice includes customer document IDs, URLs and request bodies, so neither leaves a self-hosted cluster. The bounded `execution_error_kind` is what makes a failure legible without them.

Emitted by `engine` — declared in `apps/engine/internal/usage/events/tool_execution.go`.

| Property                         | Type    | Always present | On-premises |
| -------------------------------- | ------- | -------------- | ----------- |
| `execution_duration_ms`          | number  | yes            | `allow`     |
| `execution_error`                | string  | no             | `deny`      |
| `execution_error_dev_message`    | string  | no             | `deny`      |
| `execution_error_kind`           | string  | no             | `allow`     |
| `execution_error_retry_after_ms` | number  | no             | `allow`     |
| `execution_id`                   | string  | yes            | `hash`      |
| `execution_type`                 | string  | yes            | `allow`     |
| `execution_type_reason`          | string  | no             | `allow`     |
| `fully_qualified_tool_name`      | string  | yes            | `allow`     |
| `is_execution_error_retryable`   | boolean | no             | `allow`     |
| `is_execution_success`           | boolean | yes            | `allow`     |
| `requested_tool_version`         | string  | yes            | `allow`     |
| `run_id`                         | string  | yes            | `hash`      |
| `tool_name`                      | string  | yes            | `allow`     |
| `tool_version`                   | string  | yes            | `allow`     |
| `toolkit_name`                   | string  | yes            | `allow`     |
| `user_id`                        | string  | no             | `hash`      |
| `worker_id`                      | string  | yes            | `hash`      |

### tr_apps_list

Reports an agent listing the applications available to it, and how many of them it is already connected to.

Emitted by `engine` — declared in `apps/engine/internal/usage/events/tool_recommendation.go`.

| Property                  | Type    | Always present | On-premises |
| ------------------------- | ------- | -------------- | ----------- |
| `$process_person_profile` | boolean | yes            | `allow`     |
| `connected_apps_count`    | number  | yes            | `allow`     |
| `customer_id`             | string  | no             | `hash`      |
| `principal_type`          | string  | no             | `allow`     |
| `project_id`              | string  | no             | `allow`     |
| `source`                  | string  | yes            | `allow`     |
| `tenant_id`               | string  | no             | `allow`     |
| `total_apps_count`        | number  | yes            | `allow`     |

### tr_search_tools

Reports a free-text search over the tool catalog.

Emitted by `engine` — declared in `apps/engine/internal/usage/events/tool_recommendation.go`.

| Property                  | Type    | Always present | On-premises |
| ------------------------- | ------- | -------------- | ----------- |
| `$process_person_profile` | boolean | yes            | `allow`     |
| `customer_id`             | string  | no             | `hash`      |
| `principal_type`          | string  | no             | `allow`     |
| `project_id`              | string  | no             | `allow`     |
| `results_count`           | number  | yes            | `allow`     |
| `source`                  | string  | yes            | `allow`     |
| `tenant_id`               | string  | no             | `allow`     |
| `top_k`                   | number  | yes            | `allow`     |

### tr_select_tools

Reports a task-driven tool selection. It opens the discovery-to-execution funnel: `query_id` is echoed back by the tr_use_tool event that follows, which is what links the two.

Emitted by `engine` — declared in `apps/engine/internal/usage/events/tool_recommendation.go`.

| Property                  | Type    | Always present | On-premises |
| ------------------------- | ------- | -------------- | ----------- |
| `$process_person_profile` | boolean | yes            | `allow`     |
| `condex_latency_ms`       | number  | yes            | `allow`     |
| `customer_id`             | string  | no             | `hash`      |
| `failed_tasks`            | number  | yes            | `allow`     |
| `hydration_latency_ms`    | number  | yes            | `allow`     |
| `principal_type`          | string  | no             | `allow`     |
| `project_id`              | string  | no             | `allow`     |
| `query_id`                | string  | yes            | `hash`      |
| `results_count`           | number  | yes            | `allow`     |
| `source`                  | string  | yes            | `allow`     |
| `task_count`              | number  | yes            | `allow`     |
| `tenant_id`               | string  | no             | `allow`     |
| `top_k`                   | number  | yes            | `allow`     |

### tr_use_tool

Reports a gateway tool execution alongside the recommendation that led to it. `result_type` distinguishes a tool that ran from one that stopped on an authorization challenge: a challenge returns successfully at the transport level but the tool never ran, so counting it as a success would inflate every execution rate with pending OAuth flows. `toolkit` and `canonical_name` are absent when the tool name does not parse, in which case `tool_name` holds the raw string rather than the event being dropped.

Emitted by `engine` — declared in `apps/engine/internal/usage/events/tool_recommendation.go`.

| Property                  | Type    | Always present | On-premises |
| ------------------------- | ------- | -------------- | ----------- |
| `$process_person_profile` | boolean | yes            | `allow`     |
| `canonical_name`          | string  | no             | `allow`     |
| `customer_id`             | string  | no             | `hash`      |
| `error_kind`              | string  | no             | `allow`     |
| `execution_id`            | string  | no             | `hash`      |
| `invocation_path`         | string  | yes            | `allow`     |
| `latency_ms`              | number  | yes            | `allow`     |
| `principal_type`          | string  | no             | `allow`     |
| `project_id`              | string  | no             | `allow`     |
| `query_id`                | string  | no             | `hash`      |
| `result_type`             | string  | yes            | `allow`     |
| `source`                  | string  | yes            | `allow`     |
| `tenant_id`               | string  | no             | `allow`     |
| `tool_name`               | string  | yes            | `allow`     |
| `toolkit`                 | string  | no             | `allow`     |

### User auth checked

Reports a poll of a user's authorization status. It is by far the highest-volume auth event, since callers poll it while a flow is pending.

Emitted by `engine` — declared in `apps/engine/internal/usage/events/auth.go`.

| Property            | Type   | Always present | On-premises |
| ------------------- | ------ | -------------- | ----------- |
| `provider_id`       | string | yes            | `allow`     |
| `status`            | string | yes            | `allow`     |
| `user_id`           | string | yes            | `hash`      |
| `waited_duration_s` | number | no             | `allow`     |

### User auth confirmed

Reports a user's identity being confirmed mid-flow, which is the step that binds an authorization to a specific end user.

Emitted by `engine` — declared in `apps/engine/internal/usage/events/auth.go`.

| Property  | Type   | Always present | On-premises |
| --------- | ------ | -------------- | ----------- |
| `auth_id` | string | yes            | `hash`      |
| `flow_id` | string | yes            | `hash`      |
| `user_id` | string | yes            | `hash`      |

### User auth failed

Reports an authorization flow that ended without a connection. `failure_reason` is a bounded set of causes, not a message from the provider.

Emitted by `engine` — declared in `apps/engine/internal/usage/events/auth.go`.

| Property         | Type   | Always present | On-premises |
| ---------------- | ------ | -------------- | ----------- |
| `failure_reason` | string | yes            | `allow`     |
| `flow_id`        | string | no             | `hash`      |
| `provider_id`    | string | yes            | `allow`     |
| `user_id`        | string | yes            | `hash`      |

### User auth revoked

Reports a connection being deleted, which is the only event that marks a user withdrawing access to an integration.

Emitted by `engine` — declared in `apps/engine/internal/usage/events/auth.go`.

| Property      | Type   | Always present | On-premises |
| ------------- | ------ | -------------- | ----------- |
| `provider_id` | string | yes            | `allow`     |
| `user_id`     | string | yes            | `hash`      |

### User auth started

Reports the beginning of an authorization flow for a user.

Emitted by `engine` — declared in `apps/engine/internal/usage/events/auth.go`.

| Property      | Type   | Always present | On-premises |
| ------------- | ------ | -------------- | ----------- |
| `flow_id`     | string | yes            | `hash`      |
| `provider_id` | string | yes            | `allow`     |
| `scopes`      | array  | yes            | `allow`     |
| `user_id`     | string | yes            | `hash`      |

### User auth succeeded

Reports an authorization flow that completed and produced a usable connection. `flow_id` is absent on the resume path, where a flow interrupted by a restart is picked back up and `waited_duration_s` reports how long that took instead.

Emitted by `engine` — declared in `apps/engine/internal/usage/events/auth.go`.

| Property            | Type   | Always present | On-premises |
| ------------------- | ------ | -------------- | ----------- |
| `flow_id`           | string | no             | `hash`      |
| `provider_id`       | string | yes            | `allow`     |
| `user_id`           | string | yes            | `hash`      |
| `waited_duration_s` | number | no             | `allow`     |

### User invited

Someone was invited to a project. The role is the only dimension: who was invited is on the event's identity, not its properties.

Emitted by `coordinator` — declared in `apps/coordinator/arcade/app/usage/events/_events.py`.

| Property | Type   | Always present | On-premises |
| -------- | ------ | -------------- | ----------- |
| `role`   | string | no             | `allow`     |

### User Source created

A federated identity source was configured.

Emitted by `coordinator` — declared in `apps/coordinator/arcade/app/usage/events/_events.py`.

| Property         | Type   | Always present | On-premises |
| ---------------- | ------ | -------------- | ----------- |
| `issuer_domain`  | string | no             | `allow`     |
| `org_id`         | string | no             | `allow`     |
| `project_id`     | string | no             | `allow`     |
| `protocol`       | string | no             | `allow`     |
| `user_source_id` | string | yes            | `hash`      |

### User Source deleted

A federated identity source was removed.

Emitted by `coordinator` — declared in `apps/coordinator/arcade/app/usage/events/_events.py`.

| Property         | Type   | Always present | On-premises |
| ---------------- | ------ | -------------- | ----------- |
| `issuer_domain`  | string | no             | `allow`     |
| `org_id`         | string | no             | `allow`     |
| `project_id`     | string | no             | `allow`     |
| `protocol`       | string | no             | `allow`     |
| `user_source_id` | string | yes            | `hash`      |

### User Source updated

A federated identity source changed.

Emitted by `coordinator` — declared in `apps/coordinator/arcade/app/usage/events/_events.py`.

| Property         | Type   | Always present | On-premises |
| ---------------- | ------ | -------------- | ----------- |
| `issuer_domain`  | string | no             | `allow`     |
| `org_id`         | string | no             | `allow`     |
| `project_id`     | string | no             | `allow`     |
| `protocol`       | string | no             | `allow`     |
| `user_source_id` | string | yes            | `hash`      |

## Not covered here

- **PostHog SDK autocapture** (`$pageview`, `$autocapture`, `$web_vitals`, `$ai_*`,
  `$identify`) is generated inside the SDK, so it has no declaration to read.
- **Marketing-site events** are emitted from a separate repository and never run
  in a customer deployment.
- **Events from services not yet migrated to declarations.** The count above is
  what is declared, not everything Arcade sends today.
