{
  "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.\nFormat: `organisations/{organisation}` or `organisations/{organisation}/workspaces/{workspace}`\n\nAn 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.\n\nA 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.\n\nCannot 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\norganisation named in `parent`; the organisation is never taken from\n`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.\nFormat: `organisations/{organisation}` or `organisations/{organisation}/workspaces/{workspace}`\n\nAn 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\"]`.\n\nValid 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`.\n\nGrouping by several dimensions at once can produce a great many rows, so\nthis 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.\nFormat: `organisations/{organisation}/agents/{agent}`; must sit under\n`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.\n\nA 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.\nFormat: `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.\nFormat: `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.\nFormat: `organisations/{organisation}` or `organisations/{organisation}/workspaces/{workspace}`\n\nAn 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.\n\nA 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.\n\nCannot 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\norganisation named in `parent`; the organisation is never taken from\n`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.\nFormat: `organisations/{organisation}` or `organisations/{organisation}/workspaces/{workspace}`\n\nAn 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\"]`.\n\nValid 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`.\n\nGrouping by several dimensions at once can produce a great many rows, so\nthis 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.\nFormat: `organisations/{organisation}/agents/{agent}`; must sit under\n`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.\n\nA 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.\nFormat: `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.\nFormat: `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.\nFormat: `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.\nFormat: `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 \u2014 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 \u2014 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.\nFormat: `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.\nFormat: `organisations/{organisation}/workspaces/{workspace}/activities/{activity}`"
        },
        "agent": {
          "type": "string",
          "description": "A registered resource rather than a free string, so that spend can always\nbe attributed to something with a known owner.",
          "title": "The agent that performed the work.\nFormat: `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.\n\nEvery activity produced while serving one user request carries the same\nvalue, including across a handoff from one agent to another. This is what\nmakes cost per unit of business work possible: outcomes attach to the\nrequest, and the request's activities sum to its cost.\n\nOptional. 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\none. Empty for work that is not conversational."
        },
        "user": {
          "type": "string",
          "description": "Opaque identifier of the person the work was done for.\n\nAn identifier, never an email or a name. Recorders take it from the\nverified request context rather than accepting it as an argument, so that\none person's spend cannot be attributed to another.\n\nThe name behind it, where the organisation supplies one, is held once at\n`organisations/{organisation}/users/{user}` with this value as the last\nsegment, 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`,\n`tool:web_search`, `research_merger`.\n\nDerived by the recorder from the surrounding framework call, so adopting\nteams do not set it per call site. A free string on the wire: adding a new\ncomponent never requires a contract change. Values an agent did not\ndeclare in `Agent.components` are still accepted, and reported, so that\ndrift 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.\n\nSet 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.\n\nDerived 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.\n\nA 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.\nFormat: `organisations/{organisation}/environments/{environment}`"
        },
        "skill": {
          "type": "string",
          "description": "The area of the product the person was using, e.g. `canvas`,\n`doc_viewer`, `social`.\n\nDistinct from `caller_component`, which is a code path. This is a product\nconcept the framework cannot infer, so it is set once on the request\ncontext by products that want to slice cost this way, and left empty\notherwise."
        },
        "model": {
          "type": "string",
          "description": "The model that was called, as the provider names it,\ne.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`.\n\nA 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 \u2014 the rule `caller_component` follows, for the same reason.\n\nThe 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`.\n\nWhich 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.\n\nIt 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.\n\nFree 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.\n\nReasoning 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.\n\nHeld 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.\n\nA 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.\n\nRecorded 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.\n\nKept 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.\n\nThis 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`.\n\nEmpty 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.\n\nFree 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.\n\nRecorded 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`.\n\nEmpty 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.\n\nEmpty 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.\n\nWhere 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.\n\nDistinct 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.\n\nComputed by the server from token counts, `charges` and the rate card in\nforce at `occurred_at`. Available immediately, and approximate. Money is\nan integer count of millionths rather than a floating-point amount so that\nsums are exact.",
          "readOnly": true
        },
        "billedCostMicros": {
          "type": "string",
          "format": "int64",
          "description": "The call's share of what Google billed, in millionths of a US dollar.\n\nWritten when the organisation reports its Google bill for the call's hour\nand that hour and model come within 5% of what was recorded, and zero\notherwise: before the bill is reported, where the hour did not match, and\nfor a provider called directly, whose calls are never billed by Google.\nWhere both figures are present, this one is what was billed and\n`estimated_cost_micros` the estimate it confirmed.",
          "readOnly": true
        },
        "rateCardVersion": {
          "type": "string",
          "description": "The version of the rate card used to price this activity.\n\nRecorded so that a later change to provider pricing does not silently\nrewrite 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.\n\nThis 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 \u2014 a charge with nowhere to go is a charge that is silently free.\n\nRepeated 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.\n\nA 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.\n\nDerived 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.\n\nThe 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`.\n\nOff 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 \u2014 a code says a request was invalid, and this says which argument was wrong.\n\nBounded 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`.\n\nThe 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.\n\nFree 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.\n\nIdentifiers 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 \u2014 the status, the reason beneath it, whether a retry-after header was present \u2014 and none of that is prose.\n\nStated 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.\n\nSome 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.\n\nWritten 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.\n\nStated 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`.\n\nLeft 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`.\n\nA 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.\n\nA 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`.\n\nIts 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.\n\nA 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 \u2014 three attempts at one thing rather than three unrelated calls \u2014 and it is what separates the cost of retrying from the cost of working.",
          "title": "The activity this call retried.\nFormat: `organisations/{organisation}/workspaces/{workspace}/activities/{activity}`"
        },
        "resultBytes": {
          "type": "string",
          "format": "int64",
          "description": "How much the tool handed back, in bytes and in tokens.\n\nA 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.\n\nKept 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.\n\nThe 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 \u2014 the same tool called twice with the same arguments inside one request \u2014 which is the cheapest saving an agent has and cannot be seen any other way without storing the arguments themselves.\n\nBoth 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.\n\nThe 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.\n\nPreserved as supplied. The server falls back to its own clock only when\nthis is omitted, so that a batch delayed in transit is still priced and\nreported 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\nreconciliation 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.\nFormat: `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\nidentifies the customer whose spend is governed; workspace, agent and\nuser may still narrow a budget within that organisation. This field\nremains solely for wire and stored-data compatibility \u2014 new callers\nshould leave it empty. Existing implementations may continue using it\nuntil the downstream consumer update is deployed."
        },
        "service": {
          "type": "string",
          "description": "The service the agent runs in, e.g. `atlas-agent`. Matches\n`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,\nso it survives people changing jobs."
        },
        "components": {
          "type": "array",
          "items": {
            "type": "string"
          },
          "description": "The components this agent expects to report, e.g. `chat_turn`,\n`tool:web_search`.\n\nAdvisory, not enforced. An activity naming a component that is not listed\nhere is still recorded, and is counted in a drift report, so that a team\nadding a component never has a write rejected but a team whose components\nhave quietly diverged can be told."
        },
        "tools": {
          "type": "array",
          "items": {
            "type": "string"
          },
          "description": "The tools this agent has, e.g. `web_search`, `lookup_booking`.\n\nAdvisory 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 \u2014 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.\n\nBoth 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.\n\nLeft 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`.\n\nFor 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.\n`{\"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\nestimate 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.\n\nA 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.\n\nNever 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.\n\nA 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.\n\nThe first is money spent on work that produced nothing \u2014 a provider charges for what it did before it failed \u2014 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`.\n\nA 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.\n\nHere 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.\n\nTheir 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.\n\nWeighed 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.\n\nThe 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.\n\nWhat 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.\nFormat: `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.\nComputed by the server from the rate card.\n\nZero 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.\nFormat: `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`.\n\nEmpty 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 \u00b7 429`, `string_above_max_length \u00b7 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`.\n\nWhich 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.\n\nNothing 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.\nFormat: `organisations/{organisation}/workspaces/{workspace}/outcomes/{outcome}`"
        },
        "outcomeDefinition": {
          "type": "string",
          "title": "The kind of outcome being counted.\nFormat: `organisations/{organisation}/outcomeDefinitions/{outcome_definition}`"
        },
        "request": {
          "type": "string",
          "description": "The request that produced it, matching `Activity.request`.\n\nThe join between work done and money spent. A request with no outcome\nrecorded 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\nmeaningful: 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.\nFormat: `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\ncharge 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`.\n\nA single token often costs a fraction of one millionth of a dollar \u2014\nGemini input is 1.25 millionths \u2014 so a whole number of millionths cannot\nexpress it and rounds every such rate down. Left in place rather than\nremoved, and never read."
        },
        "unitCostNanos": {
          "type": "string",
          "format": "int64",
          "description": "Cost of one unit, in billionths of a US dollar.\n\nBillionths rather than millionths because a per-token rate is routinely a\nfraction of a millionth, and rounding one to a whole millionth undercounts\nevery token priced against it. An activity's cost is still stored in\nmillionths; 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`.\n\nEmpty 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.\n\nZero 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.\n\nZero is any lifetime. Only meaningful on a cache write, where a provider charges a premium for an entry that lives longer \u2014 an hour against the usual five minutes."
        },
        "modality": {
          "type": "string",
          "description": "The modality this rate is for, e.g. `AUDIO`, `IMAGE`, `VIDEO`, `DOCUMENT`.\n\nEmpty 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`.\n\nEmpty 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\nwith 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.\nKept 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.\nFormat: `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.\nFormat: `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.\nFormat: `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`.\n\nNot 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.\n\nNever 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.\n\nA 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`.\n\nNot 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.\n\n`{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.\nFormat: `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": []
    }
  ]
}