swagger: '2.0' info: title: Agent Pulse API version: 1.31.0 description: Read what your organisation's agents spent, for a dashboard of your own. Every request carries a reading api key, its name in the X-Api-Key header and its secret in X-Api-Secret, and names your organisation in the path; a request naming any other is refused. Every method reads except Report a bill, which takes a recording key; recording calls happens through the recorder library over gRPC. Every reply carries X-Request-Id and, where a rate limit applies, RateLimit-Limit, RateLimit-Remaining and RateLimit-Reset. tags: - name: Spend - name: People - name: Agents - name: Outcomes - name: Rate card - name: Bill check consumes: - application/json produces: - application/json paths: /v1/priceableUnits: get: summary: Lists the rate card. operationId: PriceableUnitsService_ListPriceableUnits responses: '200': description: A successful response. schema: $ref: '#/definitions/v1ListPriceableUnitsResponse' '400': description: 'The request could not be read: a missing window, one that ends before it starts, a groupBy or filter field not served, or a parameter the method has no field for.' schema: $ref: '#/definitions/Error' '401': description: The key or secret is missing, unknown or revoked. schema: $ref: '#/definitions/Error' '403': description: A recording key was used to read, or the request named an organisation the key does not belong to. schema: $ref: '#/definitions/Error' '429': description: Over the plan's rate limit. Retry after the seconds in Retry-After. schema: $ref: '#/definitions/Error' parameters: - name: pageSize description: Maximum priceable units to return. Defaults to `100`, capped at `1000`. in: query required: false type: integer format: int32 - name: pageToken description: A page token from a previous response, to retrieve the next page. in: query required: false type: string tags: - Rate card /v1/{parent_1}/activities: get: summary: Lists activities under an organisation, most recent first by default. operationId: ActivitiesService_ListActivities2 responses: '200': description: A successful response. schema: $ref: '#/definitions/v1ListActivitiesResponse' '400': description: 'The request could not be read: a missing window, one that ends before it starts, a groupBy or filter field not served, or a parameter the method has no field for.' schema: $ref: '#/definitions/Error' '401': description: The key or secret is missing, unknown or revoked. schema: $ref: '#/definitions/Error' '403': description: A recording key was used to read, or the request named an organisation the key does not belong to. schema: $ref: '#/definitions/Error' '429': description: Over the plan's rate limit. Retry after the seconds in Retry-After. schema: $ref: '#/definitions/Error' parameters: - name: parent_1 description: 'The organisation, or the one workspace within it, whose activities to list. Format: `organisations/{organisation}` or `organisations/{organisation}/workspaces/{workspace}` An organisation reads every workspace under it; a workspace reads only its own. This is the whole of the access decision, and it is why the two are a different `parent` rather than the same read with a filter term.' in: path required: true type: string pattern: organisations/[^/]+/workspaces/[^/]+ - name: pageSize description: Maximum activities to return. Defaults to `100`, capped at `1000`. in: query required: false type: integer format: int32 - name: pageToken description: A page token from a previous response, to retrieve the next page. in: query required: false type: string - name: filter description: 'Optional filter, confining the page to activities matching every term. A filter is one or more terms joined by `AND`, each `field = value`, e.g. `agent = "organisations/acme/agents/atlas" AND status = FAILED`. A value may be quoted; it must be when it holds a space. The fields are `workspace`, `project`, `agent`, `request`, `model`, `provider`, `user`, `caller_service`, `caller_component`, `sub_agent`, `skill`, `framework`, `tool`, `status`, `error_code`, `error_class`, `kind` and `environment`, each also accepted in camelCase. A `workspace` or `agent` is its full name or its short id, such as `default` or `atlas`. A `status`, `error_class` or `kind` is matched in any case. Only equality is served, and only `AND`; the window is `start_time` and `end_time`, not a term. An unknown field, an unknown value of a fixed set, or any other operator is refused rather than ignored. Cannot widen the organisation: scope always comes from `parent`, and a term naming an agent outside it matches nothing.' in: query required: false type: string - name: orderBy description: Optional sort order. The one order served is `occurred_at desc`, which is also the default, so a page is the most recent work first. in: query required: false type: string - name: startTime description: Start of the window to list, inclusive. With `end_time`, confines the page to when the work happened; without either, every activity under `parent` is listed. in: query required: false type: string format: date-time - name: endTime description: End of the window to list, exclusive. in: query required: false type: string format: date-time tags: - Spend /v1/{parent_1}/activities:aggregate: get: summary: Returns spend grouped by one or more dimensions. description: 'This is what cost dashboards read. Results are always confined to the organisation named in `parent`; the organisation is never taken from `filter`, so a caller cannot widen its own scope.' operationId: ActivitiesService_AggregateActivities2 responses: '200': description: A successful response. schema: $ref: '#/definitions/v1AggregateActivitiesResponse' '400': description: 'The request could not be read: a missing window, one that ends before it starts, a groupBy or filter field not served, or a parameter the method has no field for.' schema: $ref: '#/definitions/Error' '401': description: The key or secret is missing, unknown or revoked. schema: $ref: '#/definitions/Error' '403': description: A recording key was used to read, or the request named an organisation the key does not belong to. schema: $ref: '#/definitions/Error' '429': description: Over the plan's rate limit. Retry after the seconds in Retry-After. schema: $ref: '#/definitions/Error' parameters: - name: parent_1 description: 'The organisation, or the one workspace within it, whose spend to aggregate. Format: `organisations/{organisation}` or `organisations/{organisation}/workspaces/{workspace}` An organisation aggregates across every workspace under it, which grouped by `workspace` is one row per tenant. A workspace aggregates only its own.' in: path required: true type: string pattern: organisations/[^/]+/workspaces/[^/]+ - name: groupBy description: 'Dimensions to group by, e.g. `["agent", "model"]`. Valid dimensions are `workspace`, `project`, `agent`, `request`, `model`, `provider`, `user`, `caller_service`, `caller_component`, `sub_agent`, `skill`, `framework`, `tool`, `status`, `error_code`, `error_class`, `kind`, `agent_kind`, `environment`, `date` and `hour`, each also accepted in camelCase. A row''s `dimensions` are keyed by these snake_case names whichever spelling was asked for. `environment` is the one the record was filed under, or where it carries none its agent''s `environment`, and empty for a record with neither. `kind` is `model`, `tool` or `call`: a tool call is one recorded under a `tool:` component, a model call one that names a model or counted tokens, and a call is anything else. `agent_kind` is `agent` or `service`: the kind the activity''s agent was registered with, or where it was registered with none, the activity''s `observed_as`, and an agent with neither is an agent. `date` is `YYYY-MM-DD` and `hour` is `YYYY-MM-DDTHH:00`, both in `time_zone`, UTC when it is empty, with no offset written. A call that did not fail groups under an `error_class` of `ERROR_CLASS_UNSPECIFIED`. Rows come most expensive first. Grouping by `workspace` under an organisation is one row per tenant; under a single workspace it is one row. An empty list returns a single total for whatever `parent` named.' in: query required: false type: array items: type: string collectionFormat: multi - name: filter description: 'Optional filter, with the same terms as [ListActivitiesRequest.filter], so a panel asks for exactly the rows it draws: `user = "8c21e0b4"` grouped by `date` is one person''s spend by day, without the rest of the organisation''s rows being read and discarded. Cannot widen the organisation. `agent` below is the same as a filter term on `agent`, kept for callers that only ever confine to one.' in: query required: false type: string - name: startTime description: Start of the window to aggregate, inclusive. in: query required: true type: string format: date-time - name: endTime description: End of the window to aggregate, exclusive. in: query required: true type: string format: date-time - name: pageSize description: 'Maximum rows to return. Defaults to `100`, capped at `1000`. Grouping by several dimensions at once can produce a great many rows, so this method paginates like a list rather than returning everything.' in: query required: false type: integer format: int32 - name: pageToken description: A page token from a previous response, to retrieve the next page. in: query required: false type: string - name: agent description: 'Optional. Confines the aggregate to one agent''s activities. Format: `organisations/{organisation}/agents/{agent}`; must sit under `parent`.' in: query required: false type: string - name: timeZone description: The IANA time zone `date` and `hour` groups are cut in, such as `Africa/Nairobi`, so a day is the reader's organisation's day. The day a request with no request id is counted by is cut in it too. Empty means UTC. in: query required: false type: string - name: readMask description: 'Optional. The fields of [AggregateRow] to fill in, such as `estimated_cost_micros` and `activity_count`. Empty fills every field. `dimensions` is always filled. A field left out reads as zero. `distinct_requests`, `distinct_users`, `failed_requests`, `ended_in_failure_requests`, `charges`, `lost_records` and `unrecognised_calls` each cost a further pass over the window, so a caller that names only sums and counts is answered at a fraction of the cost of one that names everything.' in: query required: false type: string tags: - Spend /v1/{parent_1}/outcomes: get: summary: Lists recorded outcomes. operationId: OutcomesService_ListOutcomes2 responses: '200': description: A successful response. schema: $ref: '#/definitions/v1ListOutcomesResponse' '400': description: 'The request could not be read: a missing window, one that ends before it starts, a groupBy or filter field not served, or a parameter the method has no field for.' schema: $ref: '#/definitions/Error' '401': description: The key or secret is missing, unknown or revoked. schema: $ref: '#/definitions/Error' '403': description: A recording key was used to read, or the request named an organisation the key does not belong to. schema: $ref: '#/definitions/Error' '429': description: Over the plan's rate limit. Retry after the seconds in Retry-After. schema: $ref: '#/definitions/Error' parameters: - name: parent_1 description: 'The organisation, or the one workspace within it, whose outcomes to list. Format: `organisations/{organisation}` or `organisations/{organisation}/workspaces/{workspace}`' in: path required: true type: string pattern: organisations/[^/]+/workspaces/[^/]+ - name: pageSize description: Maximum outcomes to return. Defaults to `100`, capped at `1000`. in: query required: false type: integer format: int32 - name: pageToken description: A page token from a previous response, to retrieve the next page. in: query required: false type: string tags: - Outcomes /v1/{parent_1}/users: get: summary: Lists the users an organisation has named. operationId: UsersService_ListUsers2 responses: '200': description: A successful response. schema: $ref: '#/definitions/v1ListUsersResponse' '400': description: 'The request could not be read: a missing window, one that ends before it starts, a groupBy or filter field not served, or a parameter the method has no field for.' schema: $ref: '#/definitions/Error' '401': description: The key or secret is missing, unknown or revoked. schema: $ref: '#/definitions/Error' '403': description: A recording key was used to read, or the request named an organisation the key does not belong to. schema: $ref: '#/definitions/Error' '429': description: Over the plan's rate limit. Retry after the seconds in Retry-After. schema: $ref: '#/definitions/Error' parameters: - name: parent_1 description: 'The organisation, or the one workspace within it, whose users to list. Format: `organisations/{organisation}` or `organisations/{organisation}/workspaces/{workspace}`' in: path required: true type: string pattern: organisations/[^/]+/workspaces/[^/]+ - name: pageSize description: Maximum users to return. Defaults to `100`, capped at `1000`. in: query required: false type: integer format: int32 - name: pageToken description: A page token from a previous response, to retrieve the next page. in: query required: false type: string tags: - People /v1/{parent}/activities: get: summary: Lists activities under an organisation, most recent first by default. operationId: ActivitiesService_ListActivities responses: '200': description: A successful response. schema: $ref: '#/definitions/v1ListActivitiesResponse' '400': description: 'The request could not be read: a missing window, one that ends before it starts, a groupBy or filter field not served, or a parameter the method has no field for.' schema: $ref: '#/definitions/Error' '401': description: The key or secret is missing, unknown or revoked. schema: $ref: '#/definitions/Error' '403': description: A recording key was used to read, or the request named an organisation the key does not belong to. schema: $ref: '#/definitions/Error' '429': description: Over the plan's rate limit. Retry after the seconds in Retry-After. schema: $ref: '#/definitions/Error' parameters: - name: parent description: 'The organisation, or the one workspace within it, whose activities to list. Format: `organisations/{organisation}` or `organisations/{organisation}/workspaces/{workspace}` An organisation reads every workspace under it; a workspace reads only its own. This is the whole of the access decision, and it is why the two are a different `parent` rather than the same read with a filter term.' in: path required: true type: string pattern: organisations/[^/]+ - name: pageSize description: Maximum activities to return. Defaults to `100`, capped at `1000`. in: query required: false type: integer format: int32 - name: pageToken description: A page token from a previous response, to retrieve the next page. in: query required: false type: string - name: filter description: 'Optional filter, confining the page to activities matching every term. A filter is one or more terms joined by `AND`, each `field = value`, e.g. `agent = "organisations/acme/agents/atlas" AND status = FAILED`. A value may be quoted; it must be when it holds a space. The fields are `workspace`, `project`, `agent`, `request`, `model`, `provider`, `user`, `caller_service`, `caller_component`, `sub_agent`, `skill`, `framework`, `tool`, `status`, `error_code`, `error_class`, `kind` and `environment`, each also accepted in camelCase. A `workspace` or `agent` is its full name or its short id, such as `default` or `atlas`. A `status`, `error_class` or `kind` is matched in any case. Only equality is served, and only `AND`; the window is `start_time` and `end_time`, not a term. An unknown field, an unknown value of a fixed set, or any other operator is refused rather than ignored. Cannot widen the organisation: scope always comes from `parent`, and a term naming an agent outside it matches nothing.' in: query required: false type: string - name: orderBy description: Optional sort order. The one order served is `occurred_at desc`, which is also the default, so a page is the most recent work first. in: query required: false type: string - name: startTime description: Start of the window to list, inclusive. With `end_time`, confines the page to when the work happened; without either, every activity under `parent` is listed. in: query required: false type: string format: date-time - name: endTime description: End of the window to list, exclusive. in: query required: false type: string format: date-time tags: - Spend /v1/{parent}/activities:aggregate: get: summary: Returns spend grouped by one or more dimensions. description: 'This is what cost dashboards read. Results are always confined to the organisation named in `parent`; the organisation is never taken from `filter`, so a caller cannot widen its own scope.' operationId: ActivitiesService_AggregateActivities responses: '200': description: A successful response. schema: $ref: '#/definitions/v1AggregateActivitiesResponse' '400': description: 'The request could not be read: a missing window, one that ends before it starts, a groupBy or filter field not served, or a parameter the method has no field for.' schema: $ref: '#/definitions/Error' '401': description: The key or secret is missing, unknown or revoked. schema: $ref: '#/definitions/Error' '403': description: A recording key was used to read, or the request named an organisation the key does not belong to. schema: $ref: '#/definitions/Error' '429': description: Over the plan's rate limit. Retry after the seconds in Retry-After. schema: $ref: '#/definitions/Error' parameters: - name: parent description: 'The organisation, or the one workspace within it, whose spend to aggregate. Format: `organisations/{organisation}` or `organisations/{organisation}/workspaces/{workspace}` An organisation aggregates across every workspace under it, which grouped by `workspace` is one row per tenant. A workspace aggregates only its own.' in: path required: true type: string pattern: organisations/[^/]+ - name: groupBy description: 'Dimensions to group by, e.g. `["agent", "model"]`. Valid dimensions are `workspace`, `project`, `agent`, `request`, `model`, `provider`, `user`, `caller_service`, `caller_component`, `sub_agent`, `skill`, `framework`, `tool`, `status`, `error_code`, `error_class`, `kind`, `agent_kind`, `environment`, `date` and `hour`, each also accepted in camelCase. A row''s `dimensions` are keyed by these snake_case names whichever spelling was asked for. `environment` is the one the record was filed under, or where it carries none its agent''s `environment`, and empty for a record with neither. `kind` is `model`, `tool` or `call`: a tool call is one recorded under a `tool:` component, a model call one that names a model or counted tokens, and a call is anything else. `agent_kind` is `agent` or `service`: the kind the activity''s agent was registered with, or where it was registered with none, the activity''s `observed_as`, and an agent with neither is an agent. `date` is `YYYY-MM-DD` and `hour` is `YYYY-MM-DDTHH:00`, both in `time_zone`, UTC when it is empty, with no offset written. A call that did not fail groups under an `error_class` of `ERROR_CLASS_UNSPECIFIED`. Rows come most expensive first. Grouping by `workspace` under an organisation is one row per tenant; under a single workspace it is one row. An empty list returns a single total for whatever `parent` named.' in: query required: false type: array items: type: string collectionFormat: multi - name: filter description: 'Optional filter, with the same terms as [ListActivitiesRequest.filter], so a panel asks for exactly the rows it draws: `user = "8c21e0b4"` grouped by `date` is one person''s spend by day, without the rest of the organisation''s rows being read and discarded. Cannot widen the organisation. `agent` below is the same as a filter term on `agent`, kept for callers that only ever confine to one.' in: query required: false type: string - name: startTime description: Start of the window to aggregate, inclusive. in: query required: true type: string format: date-time - name: endTime description: End of the window to aggregate, exclusive. in: query required: true type: string format: date-time - name: pageSize description: 'Maximum rows to return. Defaults to `100`, capped at `1000`. Grouping by several dimensions at once can produce a great many rows, so this method paginates like a list rather than returning everything.' in: query required: false type: integer format: int32 - name: pageToken description: A page token from a previous response, to retrieve the next page. in: query required: false type: string - name: agent description: 'Optional. Confines the aggregate to one agent''s activities. Format: `organisations/{organisation}/agents/{agent}`; must sit under `parent`.' in: query required: false type: string - name: timeZone description: The IANA time zone `date` and `hour` groups are cut in, such as `Africa/Nairobi`, so a day is the reader's organisation's day. The day a request with no request id is counted by is cut in it too. Empty means UTC. in: query required: false type: string - name: readMask description: 'Optional. The fields of [AggregateRow] to fill in, such as `estimated_cost_micros` and `activity_count`. Empty fills every field. `dimensions` is always filled. A field left out reads as zero. `distinct_requests`, `distinct_users`, `failed_requests`, `ended_in_failure_requests`, `charges`, `lost_records` and `unrecognised_calls` each cost a further pass over the window, so a caller that names only sums and counts is answered at a fraction of the cost of one that names everything.' in: query required: false type: string tags: - Spend /v1/{parent}/agents: get: summary: Lists the agents registered to an organisation. operationId: AgentsService_ListAgents responses: '200': description: A successful response. schema: $ref: '#/definitions/v1ListAgentsResponse' '400': description: 'The request could not be read: a missing window, one that ends before it starts, a groupBy or filter field not served, or a parameter the method has no field for.' schema: $ref: '#/definitions/Error' '401': description: The key or secret is missing, unknown or revoked. schema: $ref: '#/definitions/Error' '403': description: A recording key was used to read, or the request named an organisation the key does not belong to. schema: $ref: '#/definitions/Error' '429': description: Over the plan's rate limit. Retry after the seconds in Retry-After. schema: $ref: '#/definitions/Error' parameters: - name: parent description: 'The organisation whose agents to list. Format: `organisations/{organisation}`' in: path required: true type: string pattern: organisations/[^/]+ - name: pageSize description: Maximum agents to return. Defaults to `100`, capped at `1000`. in: query required: false type: integer format: int32 - name: pageToken description: A page token from a previous response, to retrieve the next page. in: query required: false type: string tags: - Agents /v1/{parent}/outcomes: get: summary: Lists recorded outcomes. operationId: OutcomesService_ListOutcomes responses: '200': description: A successful response. schema: $ref: '#/definitions/v1ListOutcomesResponse' '400': description: 'The request could not be read: a missing window, one that ends before it starts, a groupBy or filter field not served, or a parameter the method has no field for.' schema: $ref: '#/definitions/Error' '401': description: The key or secret is missing, unknown or revoked. schema: $ref: '#/definitions/Error' '403': description: A recording key was used to read, or the request named an organisation the key does not belong to. schema: $ref: '#/definitions/Error' '429': description: Over the plan's rate limit. Retry after the seconds in Retry-After. schema: $ref: '#/definitions/Error' parameters: - name: parent description: 'The organisation, or the one workspace within it, whose outcomes to list. Format: `organisations/{organisation}` or `organisations/{organisation}/workspaces/{workspace}`' in: path required: true type: string pattern: organisations/[^/]+ - name: pageSize description: Maximum outcomes to return. Defaults to `100`, capped at `1000`. in: query required: false type: integer format: int32 - name: pageToken description: A page token from a previous response, to retrieve the next page. in: query required: false type: string tags: - Outcomes /v1/{parent}/reconciliations:reportBill: post: summary: Compares one window of an organisation's bill with what was recorded for it, and stores the result. description: 'The lines are the whole bill for `billed_by` in the window: reporting the same window again replaces what was stored for it, so a provider''s later correction is sent the same way as the first report, and a group no longer on the bill is removed. Each recorded call in a group that matches carries its share of the group''s bill as `Activity.billed_cost_micros`; a call in a group that does not match carries none.' operationId: ReconciliationsService_ReportBill responses: '200': description: A successful response. schema: $ref: '#/definitions/v1ReportBillResponse' '400': description: 'The bill could not be read: a window not on the hour or longer than a day, a line starting outside it, more than 20,000 lines, or a field the request has no field for.' schema: $ref: '#/definitions/Error' '401': description: The key or secret is missing, unknown or revoked. schema: $ref: '#/definitions/Error' '403': description: A reading key was used to report, a key confined to one workspace was used for the whole organisation's bill, or the path names an organisation the key does not belong to. schema: $ref: '#/definitions/Error' '429': description: Over the plan's rate limit. Retry after the seconds in Retry-After. schema: $ref: '#/definitions/Error' parameters: - name: parent description: 'The organisation whose bill this is. Format: `organisations/{organisation}`' in: path required: true type: string pattern: organisations/[^/]+ - name: body in: body required: true schema: $ref: '#/definitions/ReconciliationsServiceReportBillBody' tags: - Bill check /v1/{parent}/users: get: summary: Lists the users an organisation has named. operationId: UsersService_ListUsers responses: '200': description: A successful response. schema: $ref: '#/definitions/v1ListUsersResponse' '400': description: 'The request could not be read: a missing window, one that ends before it starts, a groupBy or filter field not served, or a parameter the method has no field for.' schema: $ref: '#/definitions/Error' '401': description: The key or secret is missing, unknown or revoked. schema: $ref: '#/definitions/Error' '403': description: A recording key was used to read, or the request named an organisation the key does not belong to. schema: $ref: '#/definitions/Error' '429': description: Over the plan's rate limit. Retry after the seconds in Retry-After. schema: $ref: '#/definitions/Error' parameters: - name: parent description: 'The organisation, or the one workspace within it, whose users to list. Format: `organisations/{organisation}` or `organisations/{organisation}/workspaces/{workspace}`' in: path required: true type: string pattern: organisations/[^/]+ - name: pageSize description: Maximum users to return. Defaults to `100`, capped at `1000`. in: query required: false type: integer format: int32 - name: pageToken description: A page token from a previous response, to retrieve the next page. in: query required: false type: string tags: - People definitions: ActivityErrorClass: type: string enum: - ERROR_CLASS_RATE_LIMITED - ERROR_CLASS_TIMEOUT - ERROR_CLASS_UNAVAILABLE - ERROR_CLASS_INVALID_REQUEST - ERROR_CLASS_NOT_FOUND - ERROR_CLASS_PERMISSION_DENIED - ERROR_CLASS_SAFETY_BLOCKED - ERROR_CLASS_CONTEXT_OVERFLOW - ERROR_CLASS_TOOL_ERROR - ERROR_CLASS_CANCELLED - ERROR_CLASS_BILLING - ERROR_CLASS_UNKNOWN description: "What kind of failure a call ended in.\n\n - ERROR_CLASS_RATE_LIMITED:\ \ The provider refused because the caller was over a quota or a rate. The call\ \ is worth retrying, later.\n - ERROR_CLASS_TIMEOUT: The call ran out of time,\ \ whether the deadline was the caller's or the transport's.\n - ERROR_CLASS_UNAVAILABLE:\ \ The provider or the tool was down or unreachable. Nothing about the request\ \ was wrong.\n - ERROR_CLASS_INVALID_REQUEST: The request itself was wrong:\ \ a malformed argument, an unsupported combination, a schema the provider refused.\ \ Retrying it unchanged fails again, which is what makes this class worth separating\ \ from the ones above.\n - ERROR_CLASS_NOT_FOUND: What the call asked for does\ \ not exist.\n - ERROR_CLASS_PERMISSION_DENIED: The caller was not allowed to\ \ do it. A credential or a grant, never a quota.\n - ERROR_CLASS_SAFETY_BLOCKED:\ \ The provider's safety system stopped the call.\n - ERROR_CLASS_CONTEXT_OVERFLOW:\ \ The request did not fit: too many tokens for the model, or a context window\ \ already full.\n - ERROR_CLASS_TOOL_ERROR: The tool itself raised an error\ \ while running. The call reached it and it failed on its own terms.\n - ERROR_CLASS_CANCELLED:\ \ The caller went away before the call finished — a closed tab, a cancelled\ \ turn, a parent that gave up. Nothing was wrong with the call, and it is separated\ \ from a timeout because the two are fixed in different places.\n - ERROR_CLASS_BILLING:\ \ The account cannot pay: a spend limit reached, a prepaid balance at zero,\ \ billing not enabled. Separated from a rate limit because the two look identical\ \ on the wire and are nothing alike to the person reading the report — OpenAI\ \ returns 429 for an exhausted credit balance and for throttling, and only one\ \ of them clears by waiting.\n - ERROR_CLASS_UNKNOWN: A code the server has\ \ no class for. Counted, and worth looking at: a class this row lands in often\ \ is a mapping that needs adding." ActivityProvider: type: string enum: - VERTEX_AI - ANTHROPIC - OPENAI - PERPLEXITY description: "The provider that billed for a call.\n\n - VERTEX_AI: Google Vertex\ \ AI. The only provider whose charges can be reconciled\nagainst a Google invoice.\n\ \ - ANTHROPIC: Anthropic, called directly rather than through Vertex AI.\n -\ \ OPENAI: OpenAI.\n - PERPLEXITY: Perplexity." ReconciliationResult: type: string enum: - MATCHED - BILL_HIGHER - BILL_LOWER - UNRECORDED - AWAITING_BILL description: "How the bill and the recorded calls compared.\n\n - MATCHED: The\ \ bill is within the tolerance of what was recorded. Each recorded call in the\ \ group carries its share of the bill as `Activity.billed_cost_micros`.\n -\ \ BILL_HIGHER: The bill is higher than what was recorded: calls were billed\ \ that Agent Pulse never received. The recorded calls are not stretched to cover\ \ the gap, so it stays visible here.\n - BILL_LOWER: The bill is lower than\ \ what was recorded, which usually means the rate card overprices the model.\n\ \ - UNRECORDED: The bill has a line and Agent Pulse recorded nothing for it.\n\ \ - AWAITING_BILL: Agent Pulse recorded calls and the bill has no line for them\ \ yet." ReconciliationsServiceReportBillBody: type: object properties: billedBy: type: string description: Who billed for the calls, as `Activity.billed_by` names it, e.g. `VERTEX_AI`. Only calls billed by this provider are compared. environment: type: string title: 'The environment the calls were made in. Only calls recorded in it are compared. Empty compares calls from every environment. Format: `organisations/{organisation}/environments/{environment}`' startTime: type: string format: date-time description: The start of the window the lines cover, on the hour. endTime: type: string format: date-time description: The end of the window, exclusive, on the hour and at most 24 hours after `start_time`. lines: type: array items: type: object $ref: '#/definitions/v1BillLine' description: Every line of the bill for `billed_by` in the window, at most 20,000. A window with more is reported an hour at a time. Each line starts within the window. required: - billedBy - startTime - endTime v1Activity: type: object properties: name: type: string description: The workspace in it is whose the spend is, and it comes from the `parent` the batch was recorded under rather than from any field here, so there is one place a record's tenant is stated and no way for the two to disagree. title: 'The resource name of the activity. Format: `organisations/{organisation}/workspaces/{workspace}/activities/{activity}`' agent: type: string description: 'A registered resource rather than a free string, so that spend can always be attributed to something with a known owner.' title: 'The agent that performed the work. Format: `organisations/{organisation}/agents/{agent}`' request: type: string description: 'Opaque identifier of the end-user request this activity belongs to, as the caller''s own tracing names it. Every activity produced while serving one user request carries the same value, including across a handoff from one agent to another. This is what makes cost per unit of business work possible: outcomes attach to the request, and the request''s activities sum to its cost. Optional. A call made outside any request, such as a service generating a report on a schedule, carries none and is recorded all the same, attributed by its agent, component, user and workspace. It counts towards no request and no outcome, and is never refused for the lack of one: the spend is real whether or not it can be tied to a unit of work.' session: type: string description: 'Opaque identifier of the conversation or session, where the product has one. Empty for work that is not conversational.' user: type: string description: 'Opaque identifier of the person the work was done for. An identifier, never an email or a name. Recorders take it from the verified request context rather than accepting it as an argument, so that one person''s spend cannot be attributed to another. The name behind it, where the organisation supplies one, is held once at `organisations/{organisation}/users/{user}` with this value as the last segment, so it cannot contain `/`.' callerService: type: string description: The service that made the call, e.g. `atlas-agent`, `deep-research`. callerComponent: type: string description: 'The part of the calling service that spent the money, e.g. `chat_turn`, `tool:web_search`, `research_merger`. Derived by the recorder from the surrounding framework call, so adopting teams do not set it per call site. A free string on the wire: adding a new component never requires a contract change. Values an agent did not declare in `Agent.components` are still accepted, and reported, so that drift is visible rather than silent.' subAgent: type: string description: 'The agent inside a multi-agent run that made this call, as the framework names it, e.g. `meetings_researcher`: the specialist a parallel or delegating agent handed the work to, or the root agent where nothing was handed on. Set on model calls and tool calls alike, so a tool call is filed under the sub-agent that ran it. `caller_component` cannot say this for a tool call, which it names `tool:` and the tool, and changing that would break every reader that tells a tool call apart by its prefix. Derived by the recorder from the framework''s own context, so adopting teams set nothing. Empty for a call made outside an agent framework, and on records from a recorder that does not state it.' environment: type: string description: 'Written by the gateway from the api key the record arrived with, and whatever a recorder put here is replaced, so a record is filed under the environment its key was issued for and no other. Empty where the key was issued for none, and on every record written before environments existed. A record with none reads as its agent''s `environment`, where an administrator has said where that agent''s unlabelled records belong, and as unassigned otherwise. Worked out when read rather than stored, so saying where an agent''s history belongs needs nothing rewritten.' title: 'The environment the call was made in, e.g. production or staging. Format: `organisations/{organisation}/environments/{environment}`' skill: type: string description: 'The area of the product the person was using, e.g. `canvas`, `doc_viewer`, `social`. Distinct from `caller_component`, which is a code path. This is a product concept the framework cannot infer, so it is set once on the request context by products that want to slice cost this way, and left empty otherwise.' model: type: string description: 'The model that was called, as the provider names it, e.g. `gemini-2.5-pro`.' billedBy: type: string description: 'The provider that billed for the call, as a stable uppercase token, e.g. `VERTEX_AI`, `ANTHROPIC`, `OPENAI`, `PERPLEXITY`, `DEEPSEEK`. A free string rather than an enum, because a provider nobody has called before has to be recordable the day someone calls it, and a contract change is too slow to stand between a new provider and a correct bill. An unrecognised value is accepted, priced at nothing and reported, never refused — the rule `caller_component` follows, for the same reason. The vocabulary `PriceableUnit.provider` already carries, so a record and the rate card that prices it name a provider identically.' usageFormat: type: string description: 'The reporting convention the counts on this record arrived in, e.g. `VERTEX`, `ANTHROPIC`, `OPENAI_CHAT`, `OPENAI_RESPONSES`, `OTLP_GENAI`. Which shape the counts are in decides how they are read, and that is a different question from who billed. Claude served through Vertex is billed by Google and reports Vertex-shaped usage; a proxy callback reports OpenAI-shaped usage whoever served the call. It has to be stated because providers disagree about what their own headline numbers contain. One reports a prompt count that already includes the tokens served from cache and another reports the two apart; one counts reasoning inside the generated output and another counts it beside. Reading one convention as another charges for the same token twice, at two different rates. Free for the same reason as `billed_by`. A convention the server does not recognise leaves the counts exactly as reported and normalised by nothing, which is a visible gap rather than a wrong number.' provider: $ref: '#/definitions/ActivityProvider' description: 'Deprecated: use `billed_by`, which is not limited to the four providers this enum can name.' promptTokens: type: integer format: int32 description: Tokens in the prompt, excluding any served from or written to the cache. candidateTokens: type: integer format: int32 description: 'Tokens the model generated and was billed for at its output rate, including any it spent reasoning. Reasoning is inside this figure rather than beside it because that is how every provider bills it, whichever way they report it. A provider that counts reasoning apart from the visible response has it added here; one that counts it within leaves it where it is.' cachedTokens: type: integer format: int32 description: 'Prompt tokens served from the provider''s cache, billed at the cache read rate. Held apart from `prompt_tokens` rather than within it because the two rates differ by roughly ten times on every provider that caches.' cacheWriteTokens: type: integer format: int32 description: Prompt tokens written to the provider's cache, billed at the cache write rate, which is usually a premium over the ordinary prompt rate. reasoningTokens: type: integer format: int32 description: 'How many of `candidate_tokens` the model spent reasoning, where the provider says. A breakdown, not a class: it is already counted in `candidate_tokens` and is never priced on its own. Recorded because it answers why an answer cost what it did, which a total cannot.' totalTokens: type: integer format: int32 description: 'Total tokens the provider reported for the call. Recorded as reported rather than summed locally, which is what makes it a check: the classes above are read out of a provider''s own convention, and a sum that disagrees with this figure means the convention was read wrongly or the provider changed it.' reportedUsage: type: array items: type: object $ref: '#/definitions/v1ReportedQuantity' description: 'Every quantity the provider reported, under the provider''s own name for it, before anything was added to it or taken from it. Kept because the classes above are an interpretation and this is the evidence. An interpretation found to be wrong later can only be corrected if what was actually said survived it, and a quantity that has no class and no price today is still recorded here rather than lost at the moment it arrived. This is also what makes a new charged thing visible. A provider that starts reporting something nobody has priced shows up as a quantity with no charge against it, rather than as silence.' serviceTier: type: string description: 'How the call was served, where a provider prices the same tokens differently by tier, e.g. `BATCH`, `FLEX`, `PRIORITY`. Empty is the standard tier. Worth recording because batch traffic is priced at roughly half, so a batched call charged at the standard rate is wrong by that much. Free for the same reason as `billed_by`: a tier a provider introduces must not need a contract change before it can be priced.' cacheWriteTtlSeconds: type: integer format: int32 description: 'How long a cache entry written by this call lives, in seconds. Recorded because a longer-lived write is charged a premium: an hour-long entry is priced apart from a five-minute one across much of the Anthropic and Vertex catalogue. Zero where the call wrote no cache, or where the duration was not stated.' region: type: string description: 'Where the call was processed, as the client named it, e.g. `global`, `us-central1`, `us`, `eu`. Empty or `global` is the provider''s default. Recorded because a regional endpoint or a data-residency region is charged a premium, about a tenth more on Vertex and OpenAI.' framework: type: string description: 'The agent framework or SDK that made the call and its version, e.g. `google/adk-go` and `v1.7.0`, as the recorder observed it. Empty where the call was reported directly. Recorded so news about a framework can be shown to the teams that run it.' frameworkVersion: type: string providerCostMicros: type: string format: int64 description: 'What the provider itself said the call cost, in millionths of a US dollar. Where a provider returns a price there is no reason to estimate one over the top of it. Zero where the provider said nothing, which is all but one of them today. Distinct from `billed_cost_micros`, which is what an invoice later confirmed. This is what the provider asserted at the time of the call.' estimatedCostMicros: type: string format: int64 description: 'What the call is expected to cost, in millionths of a US dollar. Computed by the server from token counts, `charges` and the rate card in force at `occurred_at`. Available immediately, and approximate. Money is an integer count of millionths rather than a floating-point amount so that sums are exact.' readOnly: true billedCostMicros: type: string format: int64 description: 'The call''s share of what Google billed, in millionths of a US dollar. Written when the organisation reports its Google bill for the call''s hour and that hour and model come within 5% of what was recorded, and zero otherwise: before the bill is reported, where the hour did not match, and for a provider called directly, whose calls are never billed by Google. Where both figures are present, this one is what was billed and `estimated_cost_micros` the estimate it confirmed.' readOnly: true rateCardVersion: type: string description: 'The version of the rate card used to price this activity. Recorded so that a later change to provider pricing does not silently rewrite what history cost.' readOnly: true charges: type: array items: type: object $ref: '#/definitions/v1Charge' description: 'Everything else the call was charged for: a per-request search fee, audio or image tokens, a citation token, a grounding query. This is where a charged thing lives when it has no column of its own, and deliberately so. What a provider bills for is a list that keeps growing, and a fixed set of fields can only ever describe the providers we had in mind when we froze it — a charge with nowhere to go is a charge that is silently free. Repeated because one call can incur several. A call billed only on the token classes has none.' durationMs: type: string format: int64 description: How long the call took, in milliseconds. status: $ref: '#/definitions/v1ActivityStatus' description: How the call ended. errorCode: type: string description: 'The provider or platform error code, where the call did not succeed. A code, never a message: provider exception messages can quote the prompt.' errorClass: $ref: '#/definitions/ActivityErrorClass' description: 'What kind of failure `error_code` is, as a value that can be grouped by. Derived by the server from the provider and the code, not stated by the recorder: a taxonomy every recorder decides for itself is a taxonomy that disagrees with itself, and one held here can be corrected without redeploying a single agent. A code the server does not recognise is reported as `ERROR_CLASS_UNKNOWN` rather than forced into the nearest class, so that a provider''s new code is visible as drift instead of quietly counted as something else. The code stays beside it exactly as the provider wrote it. The class is for asking how often calls are rate limited; the code is for looking up what the provider said.' readOnly: true errorDetail: type: string description: 'A short description of the failure, where the product chose to record one, e.g. `query must include a date range`. Off unless an organisation turns it on, and never filled in from a provider''s own message: a provider error routinely quotes the prompt, which is the one thing this contract must never carry. What a product declares about its own tool is a different thing, and it is sometimes the only place the fix is written down — a code says a request was invalid, and this says which argument was wrong. Bounded by the server and truncated rather than refused, so that a product which puts too much here loses the tail and not the record.' errorFormat: type: string description: 'The convention the failure on this record was reported in, e.g. `VERTEX`, `ANTHROPIC`, `OPENAI`, `PERPLEXITY`. The same question `usage_format` answers, asked of the failure instead of the counts, and it has to be asked separately because providers disagree about where a failure''s identity lives. Gemini states a `status` and a `reason` beneath it; OpenAI states a `type` and a `code`, which are two different things; Anthropic states one `type`. Perplexity uses OpenAI''s field names and puts an http status in the one OpenAI fills with a word, so a reader that matched on names alone would read both and be wrong about one. Free for the same reason as `usage_format`. A convention the server does not recognise leaves `reported_error` exactly as it arrived and classifies nothing from it, which is a gap someone can close rather than a class that is quietly wrong.' reportedError: type: array items: type: object $ref: '#/definitions/v1ReportedField' description: 'What the provider said about the failure, under its own names. Identifiers and status codes only, never a message: a provider''s error text routinely quotes the prompt back, which is the one thing this contract must never carry. What is useful is what is nameable — the status, the reason beneath it, whether a retry-after header was present — and none of that is prose. Stated raw for the reason `reported_usage` is: the recorder is a library inside somebody else''s agent, so a reading baked in there can only be corrected by every adopter upgrading, while one made here is corrected by one deploy and reapplied to records already written. A failure reduced to a single word before it leaves the agent cannot be reread, and a word the recorder could not place is a record that says `Unknown` for ever. Some of this is unreachable through a framework that flattens an error to its message before a recorder sees it. Such a record carries what survived and says so, rather than carrying a guess.' failure: $ref: '#/definitions/v1FailureDescription' description: 'How the failure on this record reads to a person: a label, one sentence, the one value that makes it specific, and everything kept about it. Empty for a record that did not fail. Written by the server on every read, from `error_format`, `reported_error` and `error_code`, so every product''s dashboard shows the same failure in the same words and none has to translate five providers'' vocabularies for itself. Worked out rather than stored, so a wording corrected here reads back on records already written.' readOnly: true observedAs: $ref: '#/definitions/v1AgentKind' description: 'Whether the recorder observed this call through an agent framework or from a service''s own code: `AGENT` where it was listening to an agent''s callbacks, `SERVICE` where the call was made directly on an instrumented client. Stated by the recorder because it is the one thing that knows which way it was set up, and nothing else on a record tells the two apart. It is what an agent is listed as until an administrator says otherwise: the kind an agent was registered with wins, then this, and a record that carries neither reads as `AGENT`. Left unset by a recorder that does not state it.' project: type: string description: 'The unit of work inside a workspace that this call served, e.g. `acme-q3-refresh`, `matter-1183`. A product''s own identifier for a piece of work, set once on the request context beside the person, and empty for a product that has no such concept. Grouped and filtered like any other dimension. A label and never a boundary: it is not part of any resource name, nothing is authorised against it, and an unrecognised value is accepted and reported rather than refused. Everyone who can read the workspace can read every project in it, so a project must never be used to keep one reader away from another''s figures.' tool: type: string description: 'The tool that ran, where this call is a tool call, e.g. `web_search`. Its own field rather than only the `tool:` prefix on `caller_component`, because which tools fail, which are slow and which return too much are questions asked of the tool by name, and a dimension that has to be parsed out of another field is a dimension that gets parsed differently by every reader. The prefixed component stays as it is, so nothing reading it breaks.' attempt: type: integer format: int32 description: 'Which attempt at the same piece of work this call was, counting from one. A retry costs what the call it repeats cost, and is invisible to a report that counts it as new work. Left at zero by a recorder that does not know, which reads the same as one attempt.' retryOf: type: string description: Set only where the recorder knows which earlier call this repeats. It makes the chain readable — three attempts at one thing rather than three unrelated calls — and it is what separates the cost of retrying from the cost of working. title: 'The activity this call retried. Format: `organisations/{organisation}/workspaces/{workspace}/activities/{activity}`' resultBytes: type: string format: int64 description: 'How much the tool handed back, in bytes and in tokens. A size, never the content. A tool that returns two hundred thousand characters is paid for on this call and on every later turn that carries it, which makes this the field that finds the most expensive tool in an agent. `result_tokens` is left at zero where nothing counted them; the bytes are always countable.' resultTokens: type: integer format: int32 description: How much the tool handed back, in tokens, where something counted them. emptyResult: type: boolean description: 'The call succeeded and returned nothing the agent could use. Kept apart from a failure, because that is what it is to everyone except the agent: the status is `OK`, nothing was refused, and the turn still went on to retry or to wander. A tool with a high share of these is a tool that should be saying what to try instead of saying nothing.' argsBytes: type: string format: int64 description: 'How large the tool''s arguments were, and a fingerprint of them. The fingerprint is a hash under a salt held per organisation, so it can be compared with another call''s and read back as nothing. It exists for one question — the same tool called twice with the same arguments inside one request — which is the cheapest saving an agent has and cannot be seen any other way without storing the arguments themselves. Both are off unless the organisation turns them on. A hash is not content, but it is derived from what a person typed, and that is a choice an organisation makes rather than one made for it.' argsFingerprint: type: string description: A salted fingerprint of the tool's arguments, where the organisation has turned it on. occurredAt: type: string format: date-time description: 'When the work finished, as reported by the recorder. It started `duration_ms` before this. The finish rather than the start because that is when a recorder knows the call happened at all, and when its spend was incurred. A span ingested over OTLP is stamped with its end time by the same rule. Preserved as supplied. The server falls back to its own clock only when this is omitted, so that a batch delayed in transit is still priced and reported against the time it actually occurred.' etag: type: string description: Entity tag. readOnly: true createTime: type: string format: date-time description: When this activity was created. readOnly: true updateTime: type: string format: date-time description: 'When this activity was last updated, which for most activities is when reconciliation wrote `billed_cost_micros`.' readOnly: true description: One priced unit of agent work. required: - agent v1ActivityStatus: type: string enum: - OK - FAILED - DENIED - TRUNCATED description: "How a call ended.\n\n - OK: The call completed.\n - FAILED: The\ \ call failed. Cost is still recorded, because a provider charges for\nwork\ \ done before a failure.\n - DENIED: The call was refused before it reached\ \ the provider, by a spend limit or\na guardrail. Usually costs nothing, but\ \ is recorded so that refusals are\ncountable.\n - TRUNCATED: The call was cut\ \ short, by a token limit or a deadline. Partial work was\ndone and is charged\ \ for." v1Agent: type: object properties: name: type: string title: 'The resource name of the agent. Format: `organisations/{organisation}/agents/{agent}`' displayName: type: string description: Human-readable name, e.g. `Atlas`. product: type: string description: 'Deprecated: no longer a budget-matching dimension. Organisation identifies the customer whose spend is governed; workspace, agent and user may still narrow a budget within that organisation. This field remains solely for wire and stored-data compatibility — new callers should leave it empty. Existing implementations may continue using it until the downstream consumer update is deployed.' service: type: string description: 'The service the agent runs in, e.g. `atlas-agent`. Matches `Activity.caller_service` for activities this agent produces.' owner: type: string description: 'Who to ask about this agent''s spend. A team name or a role, not a person, so it survives people changing jobs.' components: type: array items: type: string description: 'The components this agent expects to report, e.g. `chat_turn`, `tool:web_search`. Advisory, not enforced. An activity naming a component that is not listed here is still recorded, and is counted in a drift report, so that a team adding a component never has a write rejected but a team whose components have quietly diverged can be told.' tools: type: array items: type: string description: 'The tools this agent has, e.g. `web_search`, `lookup_booking`. Advisory in the same way `components` is: a tool that is not listed is still recorded and is counted as drift. What it adds is the other direction — a tool listed here and never called is a tool the agent is carrying and not using, and an agent offered fewer tools picks better among the ones it has.' kind: $ref: '#/definitions/v1AgentKind' description: 'Whether this records a model-driven agent or an ordinary service that calls a model directly, such as a report generator or a document processor. Both are registered the same way and their spend is recorded, priced, budgeted and grouped the same way, because every record needs an owner someone can be asked about, and a service calling a model has one just as an agent does. The kind only says which of the two it is, so the two can be shown apart rather than a document processor being listed as an agent. Left unstated, an agent is listed as what its recorder observed it as, `Activity.observed_as`, and as `AGENT` where the recorder states nothing.' environment: type: string description: 'The environment this agent''s records that carry none are read as, e.g. `organisations/acme/environments/development`. For the records written before environments existed, and by a key issued for none: an administrator says where the agent actually ran, and its history reads that way from then on. It never overrides a record that carries an environment of its own, and it is applied when read, so changing it moves those records without rewriting any.' etag: type: string description: Entity tag. readOnly: true createTime: type: string format: date-time description: When this agent was registered. readOnly: true updateTime: type: string format: date-time description: When this agent was last updated. readOnly: true description: An agent whose spend is recorded. required: - displayName - owner v1AgentKind: type: string enum: - AGENT - SERVICE description: "What an agent resource records.\n\n - AGENT: A model-driven agent:\ \ it decides which model and tool calls to make, e.g. `Atlas`, `Russell`.\n\ \ - SERVICE: A service that calls a model directly from its own code, e.g. a\ \ report generator, a document processor, a prompt generator. Its calls are\ \ recorded under components of its code rather than an agent's tools." v1AggregateActivitiesResponse: type: object properties: rows: type: array items: type: object $ref: '#/definitions/v1AggregateRow' description: One row per combination of the requested dimensions. nextPageToken: type: string description: Token to retrieve the next page, empty when there are no more. description: Response for [ActivitiesService.AggregateActivities]. v1AggregateRow: type: object properties: dimensions: type: object additionalProperties: type: string description: 'The dimension values for this row, keyed by dimension name, e.g. `{"agent": "organisations/acme/agents/atlas", "model": "gemini-2.5-pro"}`.' estimatedCostMicros: type: string format: int64 description: Sum of `estimated_cost_micros` over the matching activities. billedCostMicros: type: string format: int64 description: 'Sum of `billed_cost_micros` over the matching activities. Lower than the estimate wherever part of the window has no matched bill.' totalTokens: type: string format: int64 description: Sum of `total_tokens` over the matching activities. activityCount: type: string format: int64 description: How many activities the row covers. promptTokens: type: string format: int64 description: Sums of each kind of token, kept apart because each is priced at its own rate and a total cannot be taken back apart once summed. candidateTokens: type: string format: int64 description: Sum of `candidate_tokens`, the output tokens, over the matching activities. cachedTokens: type: string format: int64 description: Sum of `cached_tokens` over the matching activities. cacheWriteTokens: type: string format: int64 description: Sum of `cache_write_tokens` over the matching activities. reasoningTokens: type: string format: int64 description: Sum of `reasoning_tokens` over the matching activities. failedCount: type: string format: int64 description: 'How many of the activities failed or were denied: neither produced the work the call was made for.' truncatedCount: type: string format: int64 description: How many hit a limit and did partial work. unpricedCount: type: string format: int64 description: How many model calls used tokens and were priced at nothing, which is what a call looks like when the rate card could not match its model. modelCalls: type: string format: int64 description: 'How many of the activities were model calls, and how many tool calls, by the same rule as the `kind` dimension. The two do not have to sum to `activity_count`: a call that names no model, counted no tokens and ran no tool is neither. A tool call carries no model, so a row grouped by `model` holds the tool calls under an empty value rather than leaving them out.' toolCalls: type: string format: int64 description: How many of the activities were tool calls. distinctRequests: type: string format: int64 description: 'How many distinct requests and people the activities belong to. Exact for the window asked for, and not additive across windows: a person active on two days is one person in a two-day window. Activities that carry no request belong to one request per caller component per day, the day cut in `time_zone`, so a component''s work that names no request is counted by the days it ran. Those that carry no person count towards no person.' distinctUsers: type: string format: int64 description: How many distinct people the activities were done for. p50DurationMs: type: string format: int64 description: 'Duration at the 50th and 95th percentile, in milliseconds, over the activities that reported one. Approximate, and not additive across windows. Never populated: a percentile over a group needs every duration in it, which a grouped read does not have. The mean and the slowest below are what it does have. Kept so that a reader written against them keeps compiling, and so that the field numbers are not reused for something that is not a percentile.' p95DurationMs: type: string format: int64 description: Duration at the 95th percentile, in milliseconds. Never populated, for the reason given on `p50_duration_ms`. avgDurationMs: type: string format: int64 description: 'The mean and the slowest duration, in milliseconds, over the activities that reported one. A call that reported none is left out of the mean rather than counted as instant. Both are what the store actually computes, which is why they are here and the percentiles above are not.' maxDurationMs: type: string format: int64 description: The slowest duration, in milliseconds, over the activities that reported one. failedCostMicros: type: string format: int64 description: 'What the failed activities cost, and what the retries cost. The first is money spent on work that produced nothing — a provider charges for what it did before it failed — and the second is money spent doing the same piece of work again. Both are sums over the same rows `failed_count` counts, offered here because a reader that had to page every row to add them up would be doing the scan this method exists to avoid.' retryCostMicros: type: string format: int64 description: 'What the retries cost: money spent doing the same piece of work again, in millionths of a US dollar.' emptyResultCount: type: string format: int64 description: How many calls succeeded and returned nothing usable. firstOccurredAt: type: string format: date-time description: When the earliest and latest of the activities finished. lastOccurredAt: type: string format: date-time description: When the latest of the activities finished. firstStartedAt: type: string format: date-time description: 'When the earliest of the activities started: the least of `occurred_at` less `duration_ms`. A request runs from here to `last_occurred_at`. `first_occurred_at` is when its first call finished, which for a request that opens on a long model call can be most of the request later.' readOnly: true charges: type: array items: type: object $ref: '#/definitions/v1ChargeTotal' description: 'Sums of each charged line beyond the token classes, one entry per priceable unit, e.g. audio output tokens, web searches, citation tokens. Here rather than as a column each because what a provider bills for is open-ended, and a panel that can only draw the charges we thought of when we froze the contract cannot show a customer where their money went.' unnormalisedCount: type: string format: int64 description: 'How many of the activities carried counts the server could not read, because `usage_format` named a convention it does not know. Their quantities are stored as reported and priced at nothing. Counted here so that a figure known to be incomplete says so on the row, rather than reading as spend that did not happen.' lostRecords: type: string format: int64 description: Records the recorders of the matching agents reported losing in the window, such as records dropped when their queue was full. Filled when a row is grouped by agent or not grouped at all, because a loss has no model, user or status to be grouped by. unrecognisedCalls: type: string format: int64 description: Calls the recorders of the matching agents saw through an instrumented client and did not recognise, such as embeddings, so nothing was recorded for them. Filled as `lost_records` is. errorClassLabel: type: string description: The label for the row's `error_class`, e.g. `Rate limit exceeded`, where the rows are grouped by it; the same label `Activity.failure` carries, so a failures panel and a single failure read in the same words. readOnly: true errorClassExplanation: type: string description: The sentence for the row's `error_class` where the rows are grouped by it, e.g. `The provider rejected the request as sent. It fails the same way until the request changes.`; the same one `Activity.failure` carries. readOnly: true errorClassOwner: type: string description: 'Whose the row''s `error_class` is, where the rows are grouped by it: `ours`, `provider` or `unknown`. See `FailureDescription.owner`.' readOnly: true failedRequests: type: string format: int64 description: 'How many of the requests the activities belong to had at least one call that failed or was denied, whether or not the request went on to finish. Weighed by the request rather than the call because a failed call that works on retry still left the user with an answer, and what a person reading a report needs to know is how many of their users'' requests were touched by a failure.' readOnly: true endedInFailureRequests: type: string format: int64 description: 'How many of the requests ended in failure: the last call recorded for the request, across the whole window rather than this row alone, failed or was denied. The one figure that says a user was left without an answer. A request whose calls fall partly outside the window is judged by its last call inside it. Activities that carry no request are judged as their component''s request for that day, the same as in `distinct_requests`.' readOnly: true maxPromptTokens: type: string format: int64 description: 'The largest prompt of any one call in the row, in tokens, cached tokens included. What a model must accept to serve these calls: one whose context window is smaller could not have. Compare it with `Model.max_input_tokens`.' readOnly: true maxCandidateTokens: type: string format: int64 description: The longest answer of any one call in the row, in tokens, reasoning included. Compare it with `Model.max_output_tokens`. readOnly: true description: Spend for one combination of grouping dimensions. v1BillLine: type: object properties: billingAccount: type: string description: 'Who the line was charged to: a Google Cloud project id, an OpenAI project, an Anthropic workspace.' startTime: type: string format: date-time description: The start of the usage the line charges for. endTime: type: string format: date-time description: The end of that usage, exclusive. product: type: string description: The provider's own name for what was charged, e.g. Google's SKU description `Gemini 3 Flash Text Input - Predictions`. costMicros: type: string format: int64 description: The charge in millionths of a US dollar, after credits and converted from the bill's currency. usageAmount: type: number format: double description: How much of the product was charged for, e.g. tokens for a token product, in the provider's unit. description: One line of a provider's bill, as the organisation read it from the provider. required: - billingAccount - startTime - product v1Charge: type: object properties: priceableUnit: type: string title: 'The priceable unit being charged for. Format: `priceableUnits/{priceable_unit}`' quantity: type: string format: int64 description: How many of the unit were consumed, e.g. `3` web searches, `1840` audio output tokens. estimatedCostMicros: type: string format: int64 description: 'What this charge is expected to cost, in millionths of a US dollar. Computed by the server from the rate card. Zero where the rate card holds no price for the unit. A line the card cannot price is kept and counted rather than dropped, because an unpriced charge is a gap someone can close and a missing one is a gap nobody can see.' readOnly: true description: A charge that is not measured in tokens. required: - priceableUnit - quantity v1ChargeTotal: type: object properties: priceableUnit: type: string title: 'The priceable unit being charged for. Format: `priceableUnits/{priceable_unit}`' quantity: type: string format: int64 description: Sum of `quantity` over the matching activities. estimatedCostMicros: type: string format: int64 description: Sum of `estimated_cost_micros` over the matching activities. Zero where the rate card holds no price for the unit, which is why the quantity is reported beside it rather than the money alone. description: Sum of one charged line over the activities in a row. v1FailureDescription: type: object properties: label: type: string description: The failure in a few words, e.g. `Rate limit exceeded`, `Invalid request`, `Tool execution failed`. One per `error_class`, and fixed, so a list of failures can be read and grouped by eye. explanation: type: string description: One sentence saying what the failure means and whether retrying helps, e.g. `The provider rejected the request as sent. It fails the same way until the request changes.` One per `error_class`. detail: type: string description: 'The one value that makes the label specific, e.g. `retry in 34s`, `tools[11].function.name`, `lookup_listing`, `authentication`. Empty where the provider stated nothing it could be taken from, which is common: OpenAI often leaves the parameter out, and Anthropic names one only inside its message, which is never stored. Never guessed.' provider: type: string description: Whose convention `facts` are in, e.g. `Google`, `OpenAI`, `Anthropic`. Empty for a failure no provider reported, such as a spend limit refusing the call. providerCode: type: string description: What the provider called the failure, on one line, e.g. `RESOURCE_EXHAUSTED · 429`, `string_above_max_length · 400`. For looking the failure up in the provider's own documentation. facts: type: array items: type: object $ref: '#/definitions/v1FailureFact' description: 'Everything kept about the failure, in reading order, under names a person reads, e.g. `Quota`, `Retry after`, `Parameter`, `Request ID`. Which facts appear depends on what the provider stated. A convention the server has no reader for is shown under the provider''s own names rather than dropped.' owner: type: string description: 'Whose failure it is, so a reader knows whether it is theirs to fix: `ours` for a request that was wrong or not allowed, such as an invalid request, access denied or a context window exceeded; `provider` for the provider refusing or failing, such as a rate limit, an outage or a timeout; `unknown` where the kind cannot say. Empty for a record that did not fail.' description: 'How one failure reads to a person, the same whichever provider it came from. Nothing here is a provider''s message, because none is stored: a provider''s error text routinely quotes the prompt. Every value is an identifier, a code, a count or a name, and the sentences are ours.' v1FailureFact: type: object properties: name: type: string description: What it is, e.g. `Quota`, `Retry after`, `Parameter`. value: type: string description: Its value, e.g. `GenerateRequestsPerMinutePerProjectPerModel`, `34s`, `tools[11].function.name`. description: One thing known about a failure. v1ListActivitiesResponse: type: object properties: activities: type: array items: type: object $ref: '#/definitions/v1Activity' description: The activities in this page. nextPageToken: type: string description: Token to retrieve the next page, empty when there are no more. description: Response for [ActivitiesService.ListActivities]. v1ListAgentsResponse: type: object properties: agents: type: array items: type: object $ref: '#/definitions/v1Agent' description: The agents in this page. nextPageToken: type: string description: Token to retrieve the next page, empty when there are no more. description: Response for [AgentsService.ListAgents]. v1ListOutcomesResponse: type: object properties: outcomes: type: array items: type: object $ref: '#/definitions/v1Outcome' description: The outcomes in this page. nextPageToken: type: string description: Token to retrieve the next page, empty when there are no more. description: Response for [OutcomesService.ListOutcomes]. v1ListPriceableUnitsResponse: type: object properties: priceableUnits: type: array items: type: object $ref: '#/definitions/v1PriceableUnit' description: The priceable units in this page. nextPageToken: type: string description: Token to retrieve the next page, empty when there are no more. description: Response for [PriceableUnitsService.ListPriceableUnits]. v1ListUsersResponse: type: object properties: users: type: array items: type: object $ref: '#/definitions/v1User' description: The users in this page. nextPageToken: type: string description: Token to retrieve the next page, empty when there are no more. description: Response for [UsersService.ListUsers]. v1Outcome: type: object properties: name: type: string title: 'The resource name of the outcome. Format: `organisations/{organisation}/workspaces/{workspace}/outcomes/{outcome}`' outcomeDefinition: type: string title: 'The kind of outcome being counted. Format: `organisations/{organisation}/outcomeDefinitions/{outcome_definition}`' request: type: string description: 'The request that produced it, matching `Activity.request`. The join between work done and money spent. A request with no outcome recorded simply has no cost-per-outcome figure; it is not an error.' quantity: type: string format: int64 description: 'How many were produced, e.g. `12` candidates on one shortlist. Zero is meaningful: it records a request that produced nothing.' occurredAt: type: string format: date-time description: When the outcome was produced. etag: type: string description: Entity tag. readOnly: true createTime: type: string format: date-time description: When this outcome was recorded. readOnly: true updateTime: type: string format: date-time description: When this outcome was last updated. readOnly: true description: A count of business outcomes produced by one request. required: - outcomeDefinition - request - quantity v1PriceableUnit: type: object properties: name: type: string title: 'The resource name of the priceable unit. Format: `priceableUnits/{priceable_unit}`' displayName: type: string description: Human-readable name, e.g. `Gemini 2.5 Pro output tokens`. provider: type: string description: The provider that charges for it. model: type: string description: 'The model this rate applies to, where the charge is per model. Empty for a charge that is not model-specific, such as a web search.' kind: $ref: '#/definitions/v1PriceableUnitKind' description: What is being counted. unitCostMicros: type: string format: int64 description: 'Deprecated: too coarse to hold a real rate. Use `unit_cost_nanos`. A single token often costs a fraction of one millionth of a dollar — Gemini input is 1.25 millionths — so a whole number of millionths cannot express it and rounds every such rate down. Left in place rather than removed, and never read.' unitCostNanos: type: string format: int64 description: 'Cost of one unit, in billionths of a US dollar. Billionths rather than millionths because a per-token rate is routinely a fraction of a millionth, and rounding one to a whole millionth undercounts every token priced against it. An activity''s cost is still stored in millionths; the extra precision belongs to the rate, not to the money.' serviceTier: type: string description: 'The service tier this rate is for, e.g. `BATCH`, `FLEX`, `PRIORITY`. Empty applies to the standard tier and to a call that named no tier. A free string, matching `Activity.service_tier`, so a tier a provider introduces can be priced without a contract change.' minPromptTokens: type: integer format: int32 description: 'The smallest prompt this rate applies to, in tokens. Zero is every prompt. A provider that charges more above a context threshold has a second rate at that threshold, and the higher one wins for a call that crosses it. Measured against the whole prompt the call sent, cached tokens included, because that is the size the provider is reacting to.' minCacheWriteTtlSeconds: type: integer format: int32 description: 'The shortest cache lifetime this rate applies to, in seconds. Zero is any lifetime. Only meaningful on a cache write, where a provider charges a premium for an entry that lives longer — an hour against the usual five minutes.' modality: type: string description: 'The modality this rate is for, e.g. `AUDIO`, `IMAGE`, `VIDEO`, `DOCUMENT`. Empty is text, and is what an ordinary rate states. Audio and image tokens are counted in the same classes as text and charged at several times the rate, so a card with no modality on it prices a voice turn as though it had been typed.' region: type: string description: 'The processing region this rate is for, matched against `Activity.region`, e.g. `us`, `eu`. Empty applies to any region. `REGIONAL` applies to any region other than empty or `global`, which is how Vertex prices a regional endpoint.' effectiveFrom: type: string format: date-time description: When this rate takes effect, inclusive. effectiveTo: type: string format: date-time description: When this rate stops applying, exclusive. Empty while it is current. rateCardVersion: type: string description: 'The rate card version this belongs to, recorded on every activity priced with it, e.g. `2026-09`.' source: type: string description: 'Where the rate came from, e.g. a link to the provider''s public price list. Kept so a figure can be traced back to a published price.' etag: type: string description: Entity tag. readOnly: true createTime: type: string format: date-time description: When this rate was created. readOnly: true updateTime: type: string format: date-time description: When this rate was last updated. readOnly: true description: One line of the rate card. required: - displayName - provider - kind - unitCostNanos - effectiveFrom - rateCardVersion v1PriceableUnitKind: type: string enum: - PROMPT_TOKENS - CANDIDATE_TOKENS - CACHED_TOKENS - CACHE_WRITE_TOKENS - REASONING_TOKENS - REQUEST description: "What a rate is charged against.\n\n - PROMPT_TOKENS: Tokens in the\ \ prompt.\n - CANDIDATE_TOKENS: Tokens the model generated.\n - CACHED_TOKENS:\ \ Prompt tokens served from the provider's cache.\n - CACHE_WRITE_TOKENS: Prompt\ \ tokens written to the provider's cache.\n - REASONING_TOKENS: Tokens the model\ \ spent reasoning.\n - REQUEST: A whole request, e.g. one web search." v1Reconciliation: type: object properties: name: type: string description: The id is derived from the group itself, so reading the same bill twice lands on the same reconciliation rather than a second one. title: 'The resource name of the reconciliation. Format: `organisations/{organisation}/reconciliations/{reconciliation}`' readOnly: true billedBy: type: string description: Who billed for the calls, as `Activity.billed_by` names it, e.g. `VERTEX_AI`, `OPENAI`, `ANTHROPIC`. readOnly: true environment: type: string title: 'The environment the calls were made in, as the report that produced this group named it. Empty where the report named none. Format: `organisations/{organisation}/environments/{environment}`' readOnly: true billingAccount: type: string description: 'The account the bill line was charged to, as the provider names it: a Google Cloud project id, an OpenAI project or api key, an Anthropic workspace.' readOnly: true startTime: type: string format: date-time description: 'The start of the window the bill line covers: an hour for Google, a day for OpenAI and Anthropic.' readOnly: true endTime: type: string format: date-time description: The end of that window, exclusive. readOnly: true model: type: string description: The model, as the rate card names it, read from the bill's own product name. readOnly: true agent: type: string description: Empty where the group holds calls from more than one agent, or no calls at all. A bill names no agent, so this comes from the recorded calls and never from the bill. title: 'The agent that made every recorded call in the group. Format: `organisations/{organisation}/agents/{agent}`' readOnly: true component: type: string description: The component that made every recorded call in the group, in the form a billing label stores it. Empty where the calls name more than one, or none. readOnly: true billedCostMicros: type: string format: int64 description: What the bill charged for the group, in millionths of a US dollar, after credits and converted from the bill's currency. readOnly: true estimatedCostMicros: type: string format: int64 description: What Agent Pulse estimated for the calls it recorded in the group, in millionths of a US dollar. readOnly: true activityCount: type: string format: int64 description: How many recorded calls fell in the group. readOnly: true billedTokens: $ref: '#/definitions/v1Tokens' description: The tokens the bill charged for, by kind. readOnly: true recordedTokens: $ref: '#/definitions/v1Tokens' description: The tokens Agent Pulse recorded for the same group, by kind. readOnly: true result: $ref: '#/definitions/ReconciliationResult' description: How the two compared. readOnly: true readTime: type: string format: date-time description: When the bill holding this group was last reported. A provider that corrects a line later has the window reported again, and this moves. readOnly: true description: One group of a provider's bill, compared with the calls Agent Pulse recorded for it. v1ReportBillResponse: type: object properties: reconciliations: type: array items: type: object $ref: '#/definitions/v1Reconciliation' description: The groups the window was compared in, oldest first. v1ReportedField: type: object properties: name: type: string description: 'The provider''s own name for it, verbatim, e.g. `status`, `reason`, `type`, `code`, `http_status`, `retry_after`. Not translated into our vocabulary, for the same reason a reported quantity''s name is not: the translation is what this exists to make checkable. A name the server does not recognise is stored all the same.' value: type: string description: 'What the provider said it was, verbatim. Never a message, and bounded by the server: a value longer than a failure is ever named in is a caller sending prose, and it is truncated rather than refusing the record.' description: 'One thing a provider said about a failure, under the provider''s own name for it. A string pair rather than the numeric one the counts use, because a failure is identified by words: `RESOURCE_EXHAUSTED`, `rateLimitExceeded`, `invalid_request_error`, `content_filter`. The http status is carried here too, as the digits it was, so that everything about one failure reads from one list.' required: - name - value v1ReportedQuantity: type: object properties: unit: type: string description: 'The provider''s own name for it, verbatim, e.g. `cachedContentTokenCount`, `cache_read_input_tokens`, `output_tokens_details.reasoning_tokens`. Not translated into our vocabulary, because the translation is exactly what this exists to make checkable. A name the server does not recognise is stored all the same.' quantity: type: string format: int64 description: How many, as the provider stated it. description: One quantity a provider reported, in the provider's own words. required: - unit - quantity v1Tokens: type: object properties: promptTokens: type: string format: int64 description: Input tokens not read from a cache. cachedTokens: type: string format: int64 description: Input tokens read from a cache. cacheWriteTokens: type: string format: int64 description: Input tokens written to a cache. candidateTokens: type: string format: int64 description: Output tokens, reasoning included. description: Tokens by kind, as a bill and a recorder can both state them. v1User: type: object properties: name: type: string description: 'The directory sits under the workspace because an identifier is only unique within the sign-in that issued it, and two tenants of the same organisation may each run their own. A person is looked up in the workspace their activities were recorded under, and nowhere else. `{user}` is the identifier exactly as the organisation''s agents stamp it on `Activity.user`; that equality is the whole join. It is the bare identifier the organisation''s sign-in issued, never an email, and it cannot contain `/`.' title: 'The resource name of the user. Format: `organisations/{organisation}/workspaces/{workspace}/users/{user}`' displayName: type: string description: The person's name as the organisation would show it, e.g. `Ada Lovelace`. email: type: string description: The person's email address, where the organisation chooses to send it. createTime: type: string format: date-time description: When this user was first named. readOnly: true updateTime: type: string format: date-time description: When this user was last written. readOnly: true description: A person an organisation's agents do work for, named. Error: type: object description: How a refusal is answered. The code is the gRPC status by name, the reason a fixed name a program can check, and the message says what to change. properties: error: type: object properties: code: type: string example: InvalidArgument reason: type: string example: UNKNOWN_DIMENSION description: A fixed name for the refusal, listed on the error codes page. A refusal without one of its own carries its code in capitals, such as PERMISSION_DENIED. message: type: string field: type: string example: groupBy description: The request field at fault, where one is. requestId: type: string description: The id the reply's X-Request-Id carries, to quote to support. host: gateway.agentspulse.ai schemes: - https securityDefinitions: ApiKey: type: apiKey in: header name: X-Api-Key description: A read key's name, issued on the console's API keys page. A write key is refused here. ApiSecret: type: apiKey in: header name: X-Api-Secret description: The same key's secret, shown once when the key is created. security: - ApiKey: [] ApiSecret: []