{
  "openapi": "3.1.0",
  "info": {
    "title": "Tailpipe API Documentation",
    "description": "The Tailpipe API serves the same emissions, cost and recommendation data that powers the\nTailpipe dashboard. It is intended for pulling your own figures into another platform, a\nreport, or a carbon dashboard of your own.\n\nEverything here is read-only and `GET`. API key management, billing and account endpoints are\nnot part of this API.\n\n## Getting a token\n\nTailpipe uses OAuth2 `client_credentials`. Your client id and secret are in the **Setup** tab\nin Tailpipe.\n\n```bash\ncurl -sS -X POST 'https://uat.tailpipe.cloud/auth/oauth2/token' \\\n  -H 'Content-Type: application/x-www-form-urlencoded' \\\n  -d 'grant_type=client_credentials' \\\n  -d 'client_id=YOUR_CLIENT_ID' \\\n  -d 'client_secret=YOUR_CLIENT_SECRET' \\\n  -d 'scope=API'\n```\n\nThen send the token as a bearer header:\n\n```bash\ncurl -sS -H \"Authorization: Bearer $TOKEN\" \\\n  'https://uat.tailpipe.cloud/api/co2/summary'\n```\n\nThree things about authentication that are easy to get wrong:\n\n1. **Credentials go in the form body**, as `client_secret_post`. Putting them in the URL path\n   does not fail cleanly — the request misses the token route entirely and you get a `302`\n   redirect to a login page, plus your secret written to the access log in plaintext.\n2. **HTTP Basic is not registered.** `client_secret_basic` is not enabled for these clients, so\n   moving the credentials into an `Authorization: Basic` header will not fix a failing request.\n3. **Tokens last 30 minutes** and there is no refresh token. Cache the token, reuse it until it\n   is close to expiry, and re-request once on a `401`.\n\nThe token carries your tenant identity. Every endpoint is scoped to it automatically — there is\nno tenant parameter, and no way to reach another tenant's data.\n\n**Trying requests from this page.** The interactive \"Test Request\" panel talks straight from\nyour browser to the host this page is served from — nothing transits any third party, and the\nserver selector defaults to the host you are browsing. That also means it can only reach\n*this* host. On UAT the token endpoint shares the host (under `/auth`), so the whole flow —\ntoken included — works in-page. On production the token endpoint is a separate host\n(`login.tailpipe.ai`), so fetch your token with the curl command above and paste it into the\nAuthorization field here. Picking the other environment from the server dropdown fails\nsilently in-page (the browser blocks the cross-origin call) — use curl for cross-environment\nchecks.\n\n## Environments\n\n| | Data | Token |\n|---|---|---|\n| Production | `https://platform.tailpipe.ai/api/...` | `https://login.tailpipe.ai/oauth2/token` |\n| UAT | `https://uat.tailpipe.cloud/api/...` | `https://uat.tailpipe.cloud/auth/oauth2/token` |\n\nNote the asymmetry: the UAT token endpoint sits under an `/auth` context path, production does\nnot. The data paths are identical between the two.\n\n## Units\n\n**This is the single most important thing to get right, and the two families differ.**\n\n| | Emissions | Energy |\n|---|---|---|\n| `/api/co2/*` — cloud | **kg CO₂e** | Wh (`powerConsumption`) |\n| `/api/ai/*` — AI | **grams CO₂e** | Wh (`energyConsumptionWh`) |\n\nAdding a cloud figure to an AI figure without converting overstates the AI side by 1000×. Grid\ncarbon intensities, where they appear, are gCO₂/kWh.\n\nThe cloud path reports embodied and operational emissions. The AI path is **operational only** —\n`embodiedEmissionCo2` is present on AI responses but always null, and `totalEmission` equals the\noperational figure. That is a deliberate methodology decision, not missing data.\n\nAI `totalCost` is an **API-equivalent estimate** derived from published list prices. It is not a\nbilled amount, and it will not match an invoice for anyone on a subscription or committed-spend\nplan.\n\n## Filtering\n\n`from` and `to` are `YYYY-MM-DD` and must be sent **bare**. A quoted value —\n`from=%222026-06-28%22` — fails validation and returns `400`. Omit both for the default window\nof one month. The span may not exceed 366 days.\n\nThe `provider`, `account`, `location`, `model`, `application` and `environment` filters take\ncomma-separated values. **Omit a filter to disable it.** Sending an empty or quoted value\nfilters for a literal empty string and returns `200` with an empty array — which looks exactly\nlike having no emissions. Take valid values from `/api/co2/filteroptions` and\n`/api/ai/filteroptions` first.\n\n## Errors\n\n`401` means the token is missing, malformed or expired. `403` means the token is valid but its\nscope does not cover the endpoint — every endpoint in this document is reachable by a\n`scope=API` token, and endpoints outside it are not. `429` is rate limiting from either the\napplication or the Cloudflare edge; back off and retry. `5xx` is retryable.\n\nError bodies are not uniform. Some `400`s return JSON with `error` and `message`; a malformed\ndate is rejected by parameter validation and returns **plain text** — a bracketed list of\nconstraint messages. Branch on the status code, not on the body.\n\n## Empty responses\n\nAn empty array is not the same as a zero measurement. If `/api/ai/*` returns `200 []` while\n`/api/co2/*` returns data, AI telemetry has not been ingested for your tenant — that is a setup\nquestion, not an emissions figure. Check `/api/co2/coverage` and `/api/ai/coverage` to see how\nmuch of your spend and usage the figures actually cover.\n\n## Changelog\n\n**1.1.0** — 2026-07-28\n\n- Documented the eleven endpoints that were reachable but unpublished: the `/api/ai/*` family\n  (`daily`, `provider`, `model`, `summary`, `coverage`, `filteroptions`, `spendmap`, `pricing`,\n  `pue`), `/api/ai/recommendations`, and `/api/co2/spendmap`.\n- Rewrote the authentication section around `client_secret_post`, token lifetime and the UAT\n  `/auth` context path.\n- Added the UAT server to every operation.\n- Corrected `/api/co2/summary`: it returns `yearlyFrom` and `yearData` only. `monthlyFrom` and\n  `monthData` were documented but have never existed.\n- Corrected `/api/recommendations`: the body is an object, not an array; added the missing\n  `services`, `currency` and `exchangeRate` fields and sixteen `summary` fields; documented the\n  `convertCurrency` and `objective` parameters, including that `convertCurrency` is a no-op for\n  `scope=API` tokens.\n- Added `embodiedAccel`, `embodiedOther` and `powerConsumption` to the cloud metric schemas, and\n  `providerFilterMappings` to the filter options.\n- Removed the two `400 \"Invalid request parameters.\"` responses on `summary` and\n  `filteroptions`; neither endpoint takes a parameter.\n- Documented `401`, `403`, `429` and `5xx` throughout.\n- Dropped the incorrect `required: true` on the `Content-Type` header of the three `GET`s that\n  carried it.\n- Stated the units, the empty-filter trap and the bare-date requirement explicitly.\n- Wired `components.schemas` through `$ref`; the named schemas were previously orphaned and\n  every operation inlined a duplicate.\n\n**1.0.1** — the hand-maintained baseline. Six endpoints.",
    "version": "1.1.0"
  },
  "servers": [
    {
      "url": "https://platform.tailpipe.ai",
      "description": "Production"
    },
    {
      "url": "https://uat.tailpipe.cloud",
      "description": "UAT"
    }
  ],
  "tags": [
    {
      "name": "Authentication",
      "description": "Getting and using an access token."
    },
    {
      "name": "Cloud emissions",
      "description": "Emissions and cost from cloud infrastructure. Values in kg CO₂e."
    },
    {
      "name": "AI emissions",
      "description": "Emissions, energy and cost from AI/LLM usage. Values in grams CO₂e."
    },
    {
      "name": "Recommendations",
      "description": "Reduction recommendations for cloud and AI workloads."
    },
    {
      "name": "Reference data",
      "description": "Tenant-independent reference data behind the figures."
    }
  ],
  "paths": {
    "/api/ai/coverage": {
      "get": {
        "parameters": [
          {
            "in": "query",
            "name": "from",
            "required": false,
            "schema": {
              "pattern": "(^$|^\\d\\d\\d\\d-\\d\\d-\\d\\d$)",
              "type": "string"
            },
            "description": "First day of the window, `YYYY-MM-DD`. Must be on or before `to`, and the span must not exceed 366 days. Send it bare: `from=2026-06-28`. A quoted value — `from=%222026-06-28%22` — fails the pattern and returns 400. Omit both `from` and `to` for the default window (the last month)."
          },
          {
            "in": "query",
            "name": "provider",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Comma-separated AI provider names to include. Omit the parameter to disable this filter. Sending an empty or quoted value — `provider=` or `provider=%22%22` — filters for a literal empty string and returns `200 []`, which is indistinguishable from having no data. Take valid values from `/filteroptions`."
          },
          {
            "in": "query",
            "name": "to",
            "required": false,
            "schema": {
              "pattern": "(^$|^\\d\\d\\d\\d-\\d\\d-\\d\\d$)",
              "type": "string"
            },
            "description": "Last day of the window, `YYYY-MM-DD`. Must be on or after `from`. Send it bare: `from=2026-06-28`. A quoted value — `from=%222026-06-28%22` — fails the pattern and returns 400."
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AiCoverageResponse"
                }
              }
            },
            "description": "Coverage for the window."
          },
          "400": {
            "description": "Invalid request parameters: `from` later than `to`, or a range exceeding 366 days.\n\nA malformed date is rejected earlier, by parameter validation, and that response is **plain text, not JSON** — a bracketed list of constraint messages such as `[must match \"(^$|^\\d\\d\\d\\d-\\d\\d-\\d\\d$)\"]`. Do not assume a JSON body on a 400; branch on the status code.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "No token, a malformed token, or an expired one. Tokens last 30 minutes; request a new one and retry. The body is empty — a `WWW-Authenticate` header carries the reason."
          },
          "403": {
            "description": "The token is valid but its scope does not cover this endpoint. A `scope=API` token reaches every endpoint in this document and nothing else; `/user`, `/apikey`, billing and account endpoints are not part of the public API and will 403. The body is empty."
          },
          "429": {
            "description": "Rate limited. Back off and retry. Two limiters can produce this: an application-level limiter, and the Cloudflare edge in front of production. Neither publishes its threshold; treat 429 as retryable with exponential backoff rather than tuning to a fixed rate."
          },
          "500": {
            "description": "Unhandled server error. Retryable. If it persists, the request is probably hitting a defect rather than a transient fault — report it with the timestamp and path."
          }
        },
        "tags": [
          "AI emissions"
        ],
        "operationId": "getAiCoverage",
        "summary": "AI pricing coverage",
        "description": "Per day, which models Tailpipe priced from a model-specific reference row and which fell back to a provider default. Use it to qualify the AI cost and energy figures.\n\nNote this takes `provider` but not `model`, `application` or `environment`.\n\n**An empty response means no AI telemetry has been ingested for your tenant, not zero emissions.** The AI endpoints only return data once the Tailpipe AI SDK or AI proxy is sending telemetry. If you get `200 []` here while `/api/co2/*` returns data, AI ingestion is not yet wired up — that is a setup question, not an emissions figure.",
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -sS -H \"Authorization: Bearer $TOKEN\" \\\n  'https://uat.tailpipe.cloud/api/ai/coverage?from=2026-06-01&to=2026-06-30'"
          }
        ]
      }
    },
    "/api/ai/daily": {
      "get": {
        "parameters": [
          {
            "in": "query",
            "name": "application",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Comma-separated application labels, as tagged by the SDK. Omit the parameter to disable this filter. Sending an empty or quoted value — `provider=` or `provider=%22%22` — filters for a literal empty string and returns `200 []`, which is indistinguishable from having no data. Take valid values from `/filteroptions`."
          },
          {
            "in": "query",
            "name": "environment",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Comma-separated environment labels, as tagged by the SDK. Omit the parameter to disable this filter. Sending an empty or quoted value — `provider=` or `provider=%22%22` — filters for a literal empty string and returns `200 []`, which is indistinguishable from having no data. Take valid values from `/filteroptions`."
          },
          {
            "in": "query",
            "name": "from",
            "required": false,
            "schema": {
              "pattern": "(^$|^\\d\\d\\d\\d-\\d\\d-\\d\\d$)",
              "type": "string"
            },
            "description": "First day of the window, `YYYY-MM-DD`. Must be on or before `to`, and the span must not exceed 366 days. Send it bare: `from=2026-06-28`. A quoted value — `from=%222026-06-28%22` — fails the pattern and returns 400. Omit both `from` and `to` for the default window (the last month)."
          },
          {
            "in": "query",
            "name": "model",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Comma-separated model identifiers to include. Omit the parameter to disable this filter. Sending an empty or quoted value — `provider=` or `provider=%22%22` — filters for a literal empty string and returns `200 []`, which is indistinguishable from having no data. Take valid values from `/filteroptions`."
          },
          {
            "in": "query",
            "name": "provider",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Comma-separated AI provider names to include. Omit the parameter to disable this filter. Sending an empty or quoted value — `provider=` or `provider=%22%22` — filters for a literal empty string and returns `200 []`, which is indistinguishable from having no data. Take valid values from `/filteroptions`."
          },
          {
            "in": "query",
            "name": "to",
            "required": false,
            "schema": {
              "pattern": "(^$|^\\d\\d\\d\\d-\\d\\d-\\d\\d$)",
              "type": "string"
            },
            "description": "Last day of the window, `YYYY-MM-DD`. Must be on or after `from`. Send it bare: `from=2026-06-28`. A quoted value — `from=%222026-06-28%22` — fails the pattern and returns 400."
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "items": {
                    "$ref": "#/components/schemas/DailyAiUsageResponse"
                  },
                  "type": "array"
                }
              }
            },
            "description": "One entry per day with telemetry in the window."
          },
          "400": {
            "description": "Invalid request parameters: `from` later than `to`, or a range exceeding 366 days.\n\nA malformed date is rejected earlier, by parameter validation, and that response is **plain text, not JSON** — a bracketed list of constraint messages such as `[must match \"(^$|^\\d\\d\\d\\d-\\d\\d-\\d\\d$)\"]`. Do not assume a JSON body on a 400; branch on the status code.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "No token, a malformed token, or an expired one. Tokens last 30 minutes; request a new one and retry. The body is empty — a `WWW-Authenticate` header carries the reason."
          },
          "403": {
            "description": "The token is valid but its scope does not cover this endpoint. A `scope=API` token reaches every endpoint in this document and nothing else; `/user`, `/apikey`, billing and account endpoints are not part of the public API and will 403. The body is empty."
          },
          "429": {
            "description": "Rate limited. Back off and retry. Two limiters can produce this: an application-level limiter, and the Cloudflare edge in front of production. Neither publishes its threshold; treat 429 as retryable with exponential backoff rather than tuning to a fixed rate."
          },
          "500": {
            "description": "Unhandled server error. Retryable. If it persists, the request is probably hitting a defect rather than a transient fault — report it with the timestamp and path."
          }
        },
        "tags": [
          "AI emissions"
        ],
        "operationId": "getDailyAiUsage",
        "summary": "Daily AI usage and emissions",
        "description": "AI token usage, cost and emissions per day. Defaults to the last month.\n\n**The path is `/api/ai/daily`** — there is no `/api/ai/co2/daily`.\n\nEmissions are in **grams** CO₂e and energy in Wh. The cloud endpoints report kg.\n\n**An empty response means no AI telemetry has been ingested for your tenant, not zero emissions.** The AI endpoints only return data once the Tailpipe AI SDK or AI proxy is sending telemetry. If you get `200 []` here while `/api/co2/*` returns data, AI ingestion is not yet wired up — that is a setup question, not an emissions figure.",
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -sS -H \"Authorization: Bearer $TOKEN\" \\\n  'https://uat.tailpipe.cloud/api/ai/daily?from=2026-06-01&to=2026-06-30'"
          }
        ]
      }
    },
    "/api/ai/filteroptions": {
      "get": {
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AiFilterOptionResponse"
                }
              }
            },
            "description": "Filter options retrieved."
          },
          "401": {
            "description": "No token, a malformed token, or an expired one. Tokens last 30 minutes; request a new one and retry. The body is empty — a `WWW-Authenticate` header carries the reason."
          },
          "403": {
            "description": "The token is valid but its scope does not cover this endpoint. A `scope=API` token reaches every endpoint in this document and nothing else; `/user`, `/apikey`, billing and account endpoints are not part of the public API and will 403. The body is empty."
          },
          "429": {
            "description": "Rate limited. Back off and retry. Two limiters can produce this: an application-level limiter, and the Cloudflare edge in front of production. Neither publishes its threshold; treat 429 as retryable with exponential backoff rather than tuning to a fixed rate."
          },
          "500": {
            "description": "Unhandled server error. Retryable. If it persists, the request is probably hitting a defect rather than a transient fault — report it with the timestamp and path."
          }
        },
        "tags": [
          "AI emissions"
        ],
        "operationId": "getAiFilterOptions",
        "summary": "AI filter options",
        "description": "The provider, model, application and environment values present in your AI telemetry, plus a cascading mapping. Takes no parameters.\n\n**An empty response means no AI telemetry has been ingested for your tenant, not zero emissions.** The AI endpoints only return data once the Tailpipe AI SDK or AI proxy is sending telemetry. If you get `200 []` here while `/api/co2/*` returns data, AI ingestion is not yet wired up — that is a setup question, not an emissions figure.",
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -sS -H \"Authorization: Bearer $TOKEN\" \\\n  'https://uat.tailpipe.cloud/api/ai/filteroptions'"
          }
        ]
      }
    },
    "/api/ai/model": {
      "get": {
        "parameters": [
          {
            "in": "query",
            "name": "application",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Comma-separated application labels, as tagged by the SDK. Omit the parameter to disable this filter. Sending an empty or quoted value — `provider=` or `provider=%22%22` — filters for a literal empty string and returns `200 []`, which is indistinguishable from having no data. Take valid values from `/filteroptions`."
          },
          {
            "in": "query",
            "name": "environment",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Comma-separated environment labels, as tagged by the SDK. Omit the parameter to disable this filter. Sending an empty or quoted value — `provider=` or `provider=%22%22` — filters for a literal empty string and returns `200 []`, which is indistinguishable from having no data. Take valid values from `/filteroptions`."
          },
          {
            "in": "query",
            "name": "from",
            "required": false,
            "schema": {
              "pattern": "(^$|^\\d\\d\\d\\d-\\d\\d-\\d\\d$)",
              "type": "string"
            },
            "description": "First day of the window, `YYYY-MM-DD`. Must be on or before `to`, and the span must not exceed 366 days. Send it bare: `from=2026-06-28`. A quoted value — `from=%222026-06-28%22` — fails the pattern and returns 400. Omit both `from` and `to` for the default window (the last month)."
          },
          {
            "in": "query",
            "name": "model",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Comma-separated model identifiers to include. Omit the parameter to disable this filter. Sending an empty or quoted value — `provider=` or `provider=%22%22` — filters for a literal empty string and returns `200 []`, which is indistinguishable from having no data. Take valid values from `/filteroptions`."
          },
          {
            "in": "query",
            "name": "provider",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Comma-separated AI provider names to include. Omit the parameter to disable this filter. Sending an empty or quoted value — `provider=` or `provider=%22%22` — filters for a literal empty string and returns `200 []`, which is indistinguishable from having no data. Take valid values from `/filteroptions`."
          },
          {
            "in": "query",
            "name": "to",
            "required": false,
            "schema": {
              "pattern": "(^$|^\\d\\d\\d\\d-\\d\\d-\\d\\d$)",
              "type": "string"
            },
            "description": "Last day of the window, `YYYY-MM-DD`. Must be on or after `from`. Send it bare: `from=2026-06-28`. A quoted value — `from=%222026-06-28%22` — fails the pattern and returns 400."
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "items": {
                    "$ref": "#/components/schemas/AiModelSummaryResponse"
                  },
                  "type": "array"
                }
              }
            },
            "description": "One entry per model."
          },
          "400": {
            "description": "Invalid request parameters: `from` later than `to`, or a range exceeding 366 days.\n\nA malformed date is rejected earlier, by parameter validation, and that response is **plain text, not JSON** — a bracketed list of constraint messages such as `[must match \"(^$|^\\d\\d\\d\\d-\\d\\d-\\d\\d$)\"]`. Do not assume a JSON body on a 400; branch on the status code.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "No token, a malformed token, or an expired one. Tokens last 30 minutes; request a new one and retry. The body is empty — a `WWW-Authenticate` header carries the reason."
          },
          "403": {
            "description": "The token is valid but its scope does not cover this endpoint. A `scope=API` token reaches every endpoint in this document and nothing else; `/user`, `/apikey`, billing and account endpoints are not part of the public API and will 403. The body is empty."
          },
          "429": {
            "description": "Rate limited. Back off and retry. Two limiters can produce this: an application-level limiter, and the Cloudflare edge in front of production. Neither publishes its threshold; treat 429 as retryable with exponential backoff rather than tuning to a fixed rate."
          },
          "500": {
            "description": "Unhandled server error. Retryable. If it persists, the request is probably hitting a defect rather than a transient fault — report it with the timestamp and path."
          }
        },
        "tags": [
          "AI emissions"
        ],
        "operationId": "getAiUsageByModel",
        "summary": "AI usage by model",
        "description": "AI usage aggregated per model over the window, including the prefill/decode/cache energy split.\n\n**An empty response means no AI telemetry has been ingested for your tenant, not zero emissions.** The AI endpoints only return data once the Tailpipe AI SDK or AI proxy is sending telemetry. If you get `200 []` here while `/api/co2/*` returns data, AI ingestion is not yet wired up — that is a setup question, not an emissions figure.",
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -sS -H \"Authorization: Bearer $TOKEN\" \\\n  'https://uat.tailpipe.cloud/api/ai/model?from=2026-06-01&to=2026-06-30'"
          }
        ]
      }
    },
    "/api/ai/pricing": {
      "get": {
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "items": {
                    "$ref": "#/components/schemas/AiModelPricing"
                  },
                  "type": "array"
                }
              }
            },
            "description": "Pricing rows retrieved."
          },
          "401": {
            "description": "No token, a malformed token, or an expired one. Tokens last 30 minutes; request a new one and retry. The body is empty — a `WWW-Authenticate` header carries the reason."
          },
          "403": {
            "description": "The token is valid but its scope does not cover this endpoint. A `scope=API` token reaches every endpoint in this document and nothing else; `/user`, `/apikey`, billing and account endpoints are not part of the public API and will 403. The body is empty."
          },
          "429": {
            "description": "Rate limited. Back off and retry. Two limiters can produce this: an application-level limiter, and the Cloudflare edge in front of production. Neither publishes its threshold; treat 429 as retryable with exponential backoff rather than tuning to a fixed rate."
          },
          "500": {
            "description": "Unhandled server error. Retryable. If it persists, the request is probably hitting a defect rather than a transient fault — report it with the timestamp and path."
          }
        },
        "tags": [
          "Reference data"
        ],
        "operationId": "getAiModelPricing",
        "summary": "AI model pricing and energy reference data",
        "description": "The full pricing and energy reference table Tailpipe uses to turn token counts into cost and emissions. Tenant-independent: every caller sees the same rows.\n\nUse it to reproduce our arithmetic. Cost columns refresh daily from LiteLLM; energy columns are curated and carry their own provenance (`energyTier`, `energySource`, `energyBasis`, and a cross-check against an independent anchor).\n\nTakes no parameters and returns every row, including historical `effectiveDate` versions and the `default-<provider>` fallback rows.",
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -sS -H \"Authorization: Bearer $TOKEN\" \\\n  'https://uat.tailpipe.cloud/api/ai/pricing'"
          }
        ]
      }
    },
    "/api/ai/provider": {
      "get": {
        "parameters": [
          {
            "in": "query",
            "name": "application",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Comma-separated application labels, as tagged by the SDK. Omit the parameter to disable this filter. Sending an empty or quoted value — `provider=` or `provider=%22%22` — filters for a literal empty string and returns `200 []`, which is indistinguishable from having no data. Take valid values from `/filteroptions`."
          },
          {
            "in": "query",
            "name": "environment",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Comma-separated environment labels, as tagged by the SDK. Omit the parameter to disable this filter. Sending an empty or quoted value — `provider=` or `provider=%22%22` — filters for a literal empty string and returns `200 []`, which is indistinguishable from having no data. Take valid values from `/filteroptions`."
          },
          {
            "in": "query",
            "name": "from",
            "required": false,
            "schema": {
              "pattern": "(^$|^\\d\\d\\d\\d-\\d\\d-\\d\\d$)",
              "type": "string"
            },
            "description": "First day of the window, `YYYY-MM-DD`. Must be on or before `to`, and the span must not exceed 366 days. Send it bare: `from=2026-06-28`. A quoted value — `from=%222026-06-28%22` — fails the pattern and returns 400. Omit both `from` and `to` for the default window (the last month)."
          },
          {
            "in": "query",
            "name": "model",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Comma-separated model identifiers to include. Omit the parameter to disable this filter. Sending an empty or quoted value — `provider=` or `provider=%22%22` — filters for a literal empty string and returns `200 []`, which is indistinguishable from having no data. Take valid values from `/filteroptions`."
          },
          {
            "in": "query",
            "name": "provider",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Comma-separated AI provider names to include. Omit the parameter to disable this filter. Sending an empty or quoted value — `provider=` or `provider=%22%22` — filters for a literal empty string and returns `200 []`, which is indistinguishable from having no data. Take valid values from `/filteroptions`."
          },
          {
            "in": "query",
            "name": "to",
            "required": false,
            "schema": {
              "pattern": "(^$|^\\d\\d\\d\\d-\\d\\d-\\d\\d$)",
              "type": "string"
            },
            "description": "Last day of the window, `YYYY-MM-DD`. Must be on or after `from`. Send it bare: `from=2026-06-28`. A quoted value — `from=%222026-06-28%22` — fails the pattern and returns 400."
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "items": {
                    "$ref": "#/components/schemas/AiProviderSummaryResponse"
                  },
                  "type": "array"
                }
              }
            },
            "description": "One entry per provider."
          },
          "400": {
            "description": "Invalid request parameters: `from` later than `to`, or a range exceeding 366 days.\n\nA malformed date is rejected earlier, by parameter validation, and that response is **plain text, not JSON** — a bracketed list of constraint messages such as `[must match \"(^$|^\\d\\d\\d\\d-\\d\\d-\\d\\d$)\"]`. Do not assume a JSON body on a 400; branch on the status code.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "No token, a malformed token, or an expired one. Tokens last 30 minutes; request a new one and retry. The body is empty — a `WWW-Authenticate` header carries the reason."
          },
          "403": {
            "description": "The token is valid but its scope does not cover this endpoint. A `scope=API` token reaches every endpoint in this document and nothing else; `/user`, `/apikey`, billing and account endpoints are not part of the public API and will 403. The body is empty."
          },
          "429": {
            "description": "Rate limited. Back off and retry. Two limiters can produce this: an application-level limiter, and the Cloudflare edge in front of production. Neither publishes its threshold; treat 429 as retryable with exponential backoff rather than tuning to a fixed rate."
          },
          "500": {
            "description": "Unhandled server error. Retryable. If it persists, the request is probably hitting a defect rather than a transient fault — report it with the timestamp and path."
          }
        },
        "tags": [
          "AI emissions"
        ],
        "operationId": "getAiUsageByProvider",
        "summary": "AI usage by provider",
        "description": "AI usage aggregated per provider over the window.\n\n**An empty response means no AI telemetry has been ingested for your tenant, not zero emissions.** The AI endpoints only return data once the Tailpipe AI SDK or AI proxy is sending telemetry. If you get `200 []` here while `/api/co2/*` returns data, AI ingestion is not yet wired up — that is a setup question, not an emissions figure.",
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -sS -H \"Authorization: Bearer $TOKEN\" \\\n  'https://uat.tailpipe.cloud/api/ai/provider?from=2026-06-01&to=2026-06-30'"
          }
        ]
      }
    },
    "/api/ai/pue": {
      "get": {
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AiProviderPueResponse"
                }
              }
            },
            "description": "PUE registry retrieved."
          },
          "401": {
            "description": "No token, a malformed token, or an expired one. Tokens last 30 minutes; request a new one and retry. The body is empty — a `WWW-Authenticate` header carries the reason."
          },
          "403": {
            "description": "The token is valid but its scope does not cover this endpoint. A `scope=API` token reaches every endpoint in this document and nothing else; `/user`, `/apikey`, billing and account endpoints are not part of the public API and will 403. The body is empty."
          },
          "429": {
            "description": "Rate limited. Back off and retry. Two limiters can produce this: an application-level limiter, and the Cloudflare edge in front of production. Neither publishes its threshold; treat 429 as retryable with exponential backoff rather than tuning to a fixed rate."
          },
          "500": {
            "description": "Unhandled server error. Retryable. If it persists, the request is probably hitting a defect rather than a transient fault — report it with the timestamp and path."
          }
        },
        "tags": [
          "Reference data"
        ],
        "operationId": "getAiProviderPue",
        "summary": "AI provider PUE registry",
        "description": "The power usage effectiveness figures applied to AI energy, with citations, and whether provider-specific PUE is currently switched on. Tenant-independent.\n\nWhen `enabled` is false, `globalDefault` is the figure actually in use and the per-provider `entries` are reference only.\n\nTakes no parameters.",
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -sS -H \"Authorization: Bearer $TOKEN\" \\\n  'https://uat.tailpipe.cloud/api/ai/pue'"
          }
        ]
      }
    },
    "/api/ai/recommendations": {
      "get": {
        "parameters": [
          {
            "in": "query",
            "name": "convertCurrency",
            "required": false,
            "schema": {
              "default": false,
              "type": "boolean"
            },
            "description": "Convert monetary fields into the signed-in user's configured currency.\n\n**This parameter is a no-op for `scope=API` tokens.** A machine client always receives `USD` with `exchangeRate` 1.0, whatever it sends, and no error is raised. Conversion is driven by the `uid` claim, which `client_credentials` tokens do not carry. Read the `currency` field on the response rather than assuming the request was honoured."
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AiRecommendationResponse"
                }
              }
            },
            "description": "Recommendations retrieved."
          },
          "401": {
            "description": "No token, a malformed token, or an expired one. Tokens last 30 minutes; request a new one and retry. The body is empty — a `WWW-Authenticate` header carries the reason."
          },
          "403": {
            "description": "The token is valid but its scope does not cover this endpoint. A `scope=API` token reaches every endpoint in this document and nothing else; `/user`, `/apikey`, billing and account endpoints are not part of the public API and will 403. The body is empty."
          },
          "404": {
            "description": "Tailpipe does not hold enough usage history (30 days) to produce recommendations."
          },
          "429": {
            "description": "Rate limited. Back off and retry. Two limiters can produce this: an application-level limiter, and the Cloudflare edge in front of production. Neither publishes its threshold; treat 429 as retryable with exponential backoff rather than tuning to a fixed rate."
          },
          "500": {
            "description": "Unhandled server error. Retryable. If it persists, the request is probably hitting a defect rather than a transient fault — report it with the timestamp and path."
          }
        },
        "tags": [
          "Recommendations"
        ],
        "operationId": "getAiRecommendations",
        "summary": "AI reduction recommendations",
        "description": "Caching, cost, model-switch, token-optimisation and region-move insights for AI workloads.\n\nThe response is a single object. The five insight arrays are always present and may be empty. There is no `objective` parameter here.\n\n**An empty response means no AI telemetry has been ingested for your tenant, not zero emissions.** The AI endpoints only return data once the Tailpipe AI SDK or AI proxy is sending telemetry. If you get `200 []` here while `/api/co2/*` returns data, AI ingestion is not yet wired up — that is a setup question, not an emissions figure.",
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -sS -H \"Authorization: Bearer $TOKEN\" \\\n  'https://uat.tailpipe.cloud/api/ai/recommendations'"
          }
        ]
      }
    },
    "/api/ai/spendmap": {
      "get": {
        "parameters": [
          {
            "in": "query",
            "name": "application",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Comma-separated application labels, as tagged by the SDK. Omit the parameter to disable this filter. Sending an empty or quoted value — `provider=` or `provider=%22%22` — filters for a literal empty string and returns `200 []`, which is indistinguishable from having no data. Take valid values from `/filteroptions`."
          },
          {
            "in": "query",
            "name": "environment",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Comma-separated environment labels, as tagged by the SDK. Omit the parameter to disable this filter. Sending an empty or quoted value — `provider=` or `provider=%22%22` — filters for a literal empty string and returns `200 []`, which is indistinguishable from having no data. Take valid values from `/filteroptions`."
          },
          {
            "in": "query",
            "name": "from",
            "required": false,
            "schema": {
              "pattern": "(^$|^\\d\\d\\d\\d-\\d\\d-\\d\\d$)",
              "type": "string"
            },
            "description": "First day of the window, `YYYY-MM-DD`. Must be on or before `to`, and the span must not exceed 366 days. Send it bare: `from=2026-06-28`. A quoted value — `from=%222026-06-28%22` — fails the pattern and returns 400. Omit both `from` and `to` for the default window (the last month)."
          },
          {
            "in": "query",
            "name": "model",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Comma-separated model identifiers to include. Omit the parameter to disable this filter. Sending an empty or quoted value — `provider=` or `provider=%22%22` — filters for a literal empty string and returns `200 []`, which is indistinguishable from having no data. Take valid values from `/filteroptions`."
          },
          {
            "in": "query",
            "name": "provider",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Comma-separated AI provider names to include. Omit the parameter to disable this filter. Sending an empty or quoted value — `provider=` or `provider=%22%22` — filters for a literal empty string and returns `200 []`, which is indistinguishable from having no data. Take valid values from `/filteroptions`."
          },
          {
            "in": "query",
            "name": "to",
            "required": false,
            "schema": {
              "pattern": "(^$|^\\d\\d\\d\\d-\\d\\d-\\d\\d$)",
              "type": "string"
            },
            "description": "Last day of the window, `YYYY-MM-DD`. Must be on or after `from`. Send it bare: `from=2026-06-28`. A quoted value — `from=%222026-06-28%22` — fails the pattern and returns 400."
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "items": {
                    "$ref": "#/components/schemas/AiSpendMapResponse"
                  },
                  "type": "array"
                }
              }
            },
            "description": "One entry per provider and model family."
          },
          "400": {
            "description": "Invalid request parameters: `from` later than `to`, or a range exceeding 366 days.\n\nA malformed date is rejected earlier, by parameter validation, and that response is **plain text, not JSON** — a bracketed list of constraint messages such as `[must match \"(^$|^\\d\\d\\d\\d-\\d\\d-\\d\\d$)\"]`. Do not assume a JSON body on a 400; branch on the status code.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "No token, a malformed token, or an expired one. Tokens last 30 minutes; request a new one and retry. The body is empty — a `WWW-Authenticate` header carries the reason."
          },
          "403": {
            "description": "The token is valid but its scope does not cover this endpoint. A `scope=API` token reaches every endpoint in this document and nothing else; `/user`, `/apikey`, billing and account endpoints are not part of the public API and will 403. The body is empty."
          },
          "429": {
            "description": "Rate limited. Back off and retry. Two limiters can produce this: an application-level limiter, and the Cloudflare edge in front of production. Neither publishes its threshold; treat 429 as retryable with exponential backoff rather than tuning to a fixed rate."
          },
          "500": {
            "description": "Unhandled server error. Retryable. If it persists, the request is probably hitting a defect rather than a transient fault — report it with the timestamp and path."
          }
        },
        "tags": [
          "AI emissions"
        ],
        "operationId": "getAiSpendMap",
        "summary": "AI spend map",
        "description": "AI cost and emissions grouped by provider and model family.\n\nUnlike `/api/co2/spendmap` this has no `category` parameter and returns a narrower object.\n\n**An empty response means no AI telemetry has been ingested for your tenant, not zero emissions.** The AI endpoints only return data once the Tailpipe AI SDK or AI proxy is sending telemetry. If you get `200 []` here while `/api/co2/*` returns data, AI ingestion is not yet wired up — that is a setup question, not an emissions figure.",
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -sS -H \"Authorization: Bearer $TOKEN\" \\\n  'https://uat.tailpipe.cloud/api/ai/spendmap?from=2026-06-01&to=2026-06-30'"
          }
        ]
      }
    },
    "/api/ai/summary": {
      "get": {
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AiUsageSummaryResponse"
                }
              }
            },
            "description": "Summary retrieved."
          },
          "401": {
            "description": "No token, a malformed token, or an expired one. Tokens last 30 minutes; request a new one and retry. The body is empty — a `WWW-Authenticate` header carries the reason."
          },
          "403": {
            "description": "The token is valid but its scope does not cover this endpoint. A `scope=API` token reaches every endpoint in this document and nothing else; `/user`, `/apikey`, billing and account endpoints are not part of the public API and will 403. The body is empty."
          },
          "429": {
            "description": "Rate limited. Back off and retry. Two limiters can produce this: an application-level limiter, and the Cloudflare edge in front of production. Neither publishes its threshold; treat 429 as retryable with exponential backoff rather than tuning to a fixed rate."
          },
          "500": {
            "description": "Unhandled server error. Retryable. If it persists, the request is probably hitting a defect rather than a transient fault — report it with the timestamp and path."
          }
        },
        "tags": [
          "AI emissions"
        ],
        "operationId": "getAiUsageSummary",
        "summary": "AI usage summary",
        "description": "Year-to-date AI totals. Takes no parameters.\n\n**An empty response means no AI telemetry has been ingested for your tenant, not zero emissions.** The AI endpoints only return data once the Tailpipe AI SDK or AI proxy is sending telemetry. If you get `200 []` here while `/api/co2/*` returns data, AI ingestion is not yet wired up — that is a setup question, not an emissions figure.",
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -sS -H \"Authorization: Bearer $TOKEN\" \\\n  'https://uat.tailpipe.cloud/api/ai/summary'"
          }
        ]
      }
    },
    "/api/co2/coverage": {
      "get": {
        "parameters": [
          {
            "in": "query",
            "name": "account",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Comma-separated cloud account ids to include. Omit the parameter to disable this filter. Sending an empty or quoted value — `provider=` or `provider=%22%22` — filters for a literal empty string and returns `200 []`, which is indistinguishable from having no data. Take valid values from `/filteroptions`."
          },
          {
            "in": "query",
            "name": "from",
            "required": false,
            "schema": {
              "pattern": "(^$|^\\d\\d\\d\\d-\\d\\d-\\d\\d$)",
              "type": "string"
            },
            "description": "First day of the window, `YYYY-MM-DD`. Must be on or before `to`, and the span must not exceed 366 days. Send it bare: `from=2026-06-28`. A quoted value — `from=%222026-06-28%22` — fails the pattern and returns 400. Omit both `from` and `to` for the default window (the last month)."
          },
          {
            "in": "query",
            "name": "location",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Comma-separated data centre locations to include. Omit the parameter to disable this filter. Sending an empty or quoted value — `provider=` or `provider=%22%22` — filters for a literal empty string and returns `200 []`, which is indistinguishable from having no data. Take valid values from `/filteroptions`."
          },
          {
            "in": "query",
            "name": "provider",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Comma-separated provider names to include. Omit the parameter to disable this filter. Sending an empty or quoted value — `provider=` or `provider=%22%22` — filters for a literal empty string and returns `200 []`, which is indistinguishable from having no data. Take valid values from `/filteroptions`."
          },
          {
            "in": "query",
            "name": "to",
            "required": false,
            "schema": {
              "pattern": "(^$|^\\d\\d\\d\\d-\\d\\d-\\d\\d$)",
              "type": "string"
            },
            "description": "Last day of the window, `YYYY-MM-DD`. Must be on or after `from`. Send it bare: `from=2026-06-28`. A quoted value — `from=%222026-06-28%22` — fails the pattern and returns 400."
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UsageCoverageResponse"
                }
              }
            },
            "description": "Coverage for the window."
          },
          "400": {
            "description": "Invalid request parameters: `from` later than `to`, or a range exceeding 366 days.\n\nA malformed date is rejected earlier, by parameter validation, and that response is **plain text, not JSON** — a bracketed list of constraint messages such as `[must match \"(^$|^\\d\\d\\d\\d-\\d\\d-\\d\\d$)\"]`. Do not assume a JSON body on a 400; branch on the status code.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "No token, a malformed token, or an expired one. Tokens last 30 minutes; request a new one and retry. The body is empty — a `WWW-Authenticate` header carries the reason."
          },
          "403": {
            "description": "The token is valid but its scope does not cover this endpoint. A `scope=API` token reaches every endpoint in this document and nothing else; `/user`, `/apikey`, billing and account endpoints are not part of the public API and will 403. The body is empty."
          },
          "429": {
            "description": "Rate limited. Back off and retry. Two limiters can produce this: an application-level limiter, and the Cloudflare edge in front of production. Neither publishes its threshold; treat 429 as retryable with exponential backoff rather than tuning to a fixed rate."
          },
          "500": {
            "description": "Unhandled server error. Retryable. If it persists, the request is probably hitting a defect rather than a transient fault — report it with the timestamp and path."
          }
        },
        "tags": [
          "Cloud emissions"
        ],
        "operationId": "getCloudCoverage",
        "summary": "Cloud spend coverage",
        "description": "How much of your cloud spend Tailpipe's methodology covers, split in-scope versus out-of-scope, plus provider financial adjustments. Use it to qualify the emissions figures: a large `outOfScope` total means the emissions are computed over a fraction of your bill.",
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -sS -H \"Authorization: Bearer $TOKEN\" \\\n  'https://uat.tailpipe.cloud/api/co2/coverage?from=2026-06-01&to=2026-06-30'"
          }
        ]
      }
    },
    "/api/co2/daily": {
      "get": {
        "parameters": [
          {
            "in": "query",
            "name": "account",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Comma-separated cloud account ids to include. Omit the parameter to disable this filter. Sending an empty or quoted value — `provider=` or `provider=%22%22` — filters for a literal empty string and returns `200 []`, which is indistinguishable from having no data. Take valid values from `/filteroptions`."
          },
          {
            "in": "query",
            "name": "from",
            "required": false,
            "schema": {
              "pattern": "(^$|^\\d\\d\\d\\d-\\d\\d-\\d\\d$)",
              "type": "string"
            },
            "description": "First day of the window, `YYYY-MM-DD`. Must be on or before `to`, and the span must not exceed 366 days. Send it bare: `from=2026-06-28`. A quoted value — `from=%222026-06-28%22` — fails the pattern and returns 400. Omit both `from` and `to` for the default window (the last month)."
          },
          {
            "in": "query",
            "name": "location",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Comma-separated data centre locations to include. Omit the parameter to disable this filter. Sending an empty or quoted value — `provider=` or `provider=%22%22` — filters for a literal empty string and returns `200 []`, which is indistinguishable from having no data. Take valid values from `/filteroptions`."
          },
          {
            "in": "query",
            "name": "provider",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Comma-separated provider names to include. Omit the parameter to disable this filter. Sending an empty or quoted value — `provider=` or `provider=%22%22` — filters for a literal empty string and returns `200 []`, which is indistinguishable from having no data. Take valid values from `/filteroptions`."
          },
          {
            "in": "query",
            "name": "to",
            "required": false,
            "schema": {
              "pattern": "(^$|^\\d\\d\\d\\d-\\d\\d-\\d\\d$)",
              "type": "string"
            },
            "description": "Last day of the window, `YYYY-MM-DD`. Must be on or after `from`. Send it bare: `from=2026-06-28`. A quoted value — `from=%222026-06-28%22` — fails the pattern and returns 400."
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "items": {
                    "$ref": "#/components/schemas/DailyCo2Response"
                  },
                  "type": "array"
                }
              }
            },
            "description": "One entry per day with data in the window."
          },
          "400": {
            "description": "Invalid request parameters: `from` later than `to`, or a range exceeding 366 days.\n\nA malformed date is rejected earlier, by parameter validation, and that response is **plain text, not JSON** — a bracketed list of constraint messages such as `[must match \"(^$|^\\d\\d\\d\\d-\\d\\d-\\d\\d$)\"]`. Do not assume a JSON body on a 400; branch on the status code.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "No token, a malformed token, or an expired one. Tokens last 30 minutes; request a new one and retry. The body is empty — a `WWW-Authenticate` header carries the reason."
          },
          "403": {
            "description": "The token is valid but its scope does not cover this endpoint. A `scope=API` token reaches every endpoint in this document and nothing else; `/user`, `/apikey`, billing and account endpoints are not part of the public API and will 403. The body is empty."
          },
          "429": {
            "description": "Rate limited. Back off and retry. Two limiters can produce this: an application-level limiter, and the Cloudflare edge in front of production. Neither publishes its threshold; treat 429 as retryable with exponential backoff rather than tuning to a fixed rate."
          },
          "500": {
            "description": "Unhandled server error. Retryable. If it persists, the request is probably hitting a defect rather than a transient fault — report it with the timestamp and path."
          }
        },
        "tags": [
          "Cloud emissions"
        ],
        "operationId": "getDailyCloudEmissions",
        "summary": "Daily cloud emissions",
        "description": "Cloud emissions and cost per day. Defaults to the last month when `from` and `to` are omitted.\n\nEmission values are in **kg CO₂e**. The AI endpoints use grams — do not mix them in one total without converting.",
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -sS -H \"Authorization: Bearer $TOKEN\" \\\n  'https://uat.tailpipe.cloud/api/co2/daily?from=2026-06-01&to=2026-06-30'"
          }
        ]
      }
    },
    "/api/co2/filteroptions": {
      "get": {
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FilterOptionResponse"
                }
              }
            },
            "description": "Filter options retrieved."
          },
          "401": {
            "description": "No token, a malformed token, or an expired one. Tokens last 30 minutes; request a new one and retry. The body is empty — a `WWW-Authenticate` header carries the reason."
          },
          "403": {
            "description": "The token is valid but its scope does not cover this endpoint. A `scope=API` token reaches every endpoint in this document and nothing else; `/user`, `/apikey`, billing and account endpoints are not part of the public API and will 403. The body is empty."
          },
          "429": {
            "description": "Rate limited. Back off and retry. Two limiters can produce this: an application-level limiter, and the Cloudflare edge in front of production. Neither publishes its threshold; treat 429 as retryable with exponential backoff rather than tuning to a fixed rate."
          },
          "500": {
            "description": "Unhandled server error. Retryable. If it persists, the request is probably hitting a defect rather than a transient fault — report it with the timestamp and path."
          }
        },
        "tags": [
          "Cloud emissions"
        ],
        "operationId": "getCloudFilterOptions",
        "summary": "Cloud filter options",
        "description": "The provider, account and location values that actually appear in your data, plus a cascading mapping between them. Call this before filtering: a value that is not in these lists returns an empty result rather than an error.\n\nThis endpoint takes no parameters.",
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -sS -H \"Authorization: Bearer $TOKEN\" \\\n  'https://uat.tailpipe.cloud/api/co2/filteroptions'"
          }
        ]
      }
    },
    "/api/co2/region": {
      "get": {
        "parameters": [
          {
            "in": "query",
            "name": "account",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Comma-separated cloud account ids to include. Omit the parameter to disable this filter. Sending an empty or quoted value — `provider=` or `provider=%22%22` — filters for a literal empty string and returns `200 []`, which is indistinguishable from having no data. Take valid values from `/filteroptions`."
          },
          {
            "in": "query",
            "name": "from",
            "required": false,
            "schema": {
              "pattern": "(^$|^\\d\\d\\d\\d-\\d\\d-\\d\\d$)",
              "type": "string"
            },
            "description": "First day of the window, `YYYY-MM-DD`. Must be on or before `to`, and the span must not exceed 366 days. Send it bare: `from=2026-06-28`. A quoted value — `from=%222026-06-28%22` — fails the pattern and returns 400. Omit both `from` and `to` for the default window (the last month)."
          },
          {
            "in": "query",
            "name": "location",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Comma-separated data centre locations to include. Omit the parameter to disable this filter. Sending an empty or quoted value — `provider=` or `provider=%22%22` — filters for a literal empty string and returns `200 []`, which is indistinguishable from having no data. Take valid values from `/filteroptions`."
          },
          {
            "in": "query",
            "name": "provider",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Comma-separated provider names to include. Omit the parameter to disable this filter. Sending an empty or quoted value — `provider=` or `provider=%22%22` — filters for a literal empty string and returns `200 []`, which is indistinguishable from having no data. Take valid values from `/filteroptions`."
          },
          {
            "in": "query",
            "name": "to",
            "required": false,
            "schema": {
              "pattern": "(^$|^\\d\\d\\d\\d-\\d\\d-\\d\\d$)",
              "type": "string"
            },
            "description": "Last day of the window, `YYYY-MM-DD`. Must be on or after `from`. Send it bare: `from=2026-06-28`. A quoted value — `from=%222026-06-28%22` — fails the pattern and returns 400."
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "items": {
                    "$ref": "#/components/schemas/LocationCo2Response"
                  },
                  "type": "array"
                }
              }
            },
            "description": "One entry per location with data in the window."
          },
          "400": {
            "description": "Invalid request parameters: `from` later than `to`, or a range exceeding 366 days.\n\nA malformed date is rejected earlier, by parameter validation, and that response is **plain text, not JSON** — a bracketed list of constraint messages such as `[must match \"(^$|^\\d\\d\\d\\d-\\d\\d-\\d\\d$)\"]`. Do not assume a JSON body on a 400; branch on the status code.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "No token, a malformed token, or an expired one. Tokens last 30 minutes; request a new one and retry. The body is empty — a `WWW-Authenticate` header carries the reason."
          },
          "403": {
            "description": "The token is valid but its scope does not cover this endpoint. A `scope=API` token reaches every endpoint in this document and nothing else; `/user`, `/apikey`, billing and account endpoints are not part of the public API and will 403. The body is empty."
          },
          "429": {
            "description": "Rate limited. Back off and retry. Two limiters can produce this: an application-level limiter, and the Cloudflare edge in front of production. Neither publishes its threshold; treat 429 as retryable with exponential backoff rather than tuning to a fixed rate."
          },
          "500": {
            "description": "Unhandled server error. Retryable. If it persists, the request is probably hitting a defect rather than a transient fault — report it with the timestamp and path."
          }
        },
        "tags": [
          "Cloud emissions"
        ],
        "operationId": "getCloudEmissionsByRegion",
        "summary": "Cloud emissions by region",
        "description": "Cloud emissions and cost aggregated by data centre location over the window.",
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -sS -H \"Authorization: Bearer $TOKEN\" \\\n  'https://uat.tailpipe.cloud/api/co2/region?from=2026-06-01&to=2026-06-30'"
          }
        ]
      }
    },
    "/api/co2/spendmap": {
      "get": {
        "parameters": [
          {
            "in": "query",
            "name": "account",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Comma-separated cloud account ids to include. Omit the parameter to disable this filter. Sending an empty or quoted value — `provider=` or `provider=%22%22` — filters for a literal empty string and returns `200 []`, which is indistinguishable from having no data. Take valid values from `/filteroptions`."
          },
          {
            "in": "query",
            "name": "category",
            "required": false,
            "schema": {
              "default": "all",
              "type": "string"
            },
            "description": "Taxonomy category to restrict the map to. Defaults to `all`."
          },
          {
            "in": "query",
            "name": "from",
            "required": false,
            "schema": {
              "pattern": "(^$|^\\d\\d\\d\\d-\\d\\d-\\d\\d$)",
              "type": "string"
            },
            "description": "First day of the window, `YYYY-MM-DD`. Must be on or before `to`, and the span must not exceed 366 days. Send it bare: `from=2026-06-28`. A quoted value — `from=%222026-06-28%22` — fails the pattern and returns 400. Omit both `from` and `to` for the default window (the last month)."
          },
          {
            "in": "query",
            "name": "location",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Comma-separated data centre locations to include. Omit the parameter to disable this filter. Sending an empty or quoted value — `provider=` or `provider=%22%22` — filters for a literal empty string and returns `200 []`, which is indistinguishable from having no data. Take valid values from `/filteroptions`."
          },
          {
            "in": "query",
            "name": "provider",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Comma-separated provider names to include. Omit the parameter to disable this filter. Sending an empty or quoted value — `provider=` or `provider=%22%22` — filters for a literal empty string and returns `200 []`, which is indistinguishable from having no data. Take valid values from `/filteroptions`."
          },
          {
            "in": "query",
            "name": "to",
            "required": false,
            "schema": {
              "pattern": "(^$|^\\d\\d\\d\\d-\\d\\d-\\d\\d$)",
              "type": "string"
            },
            "description": "Last day of the window, `YYYY-MM-DD`. Must be on or after `from`. Send it bare: `from=2026-06-28`. A quoted value — `from=%222026-06-28%22` — fails the pattern and returns 400."
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "items": {
                    "$ref": "#/components/schemas/SpendMapResponse"
                  },
                  "type": "array"
                }
              }
            },
            "description": "One entry per service."
          },
          "400": {
            "description": "Invalid request parameters: `from` later than `to`, or a range exceeding 366 days.\n\nA malformed date is rejected earlier, by parameter validation, and that response is **plain text, not JSON** — a bracketed list of constraint messages such as `[must match \"(^$|^\\d\\d\\d\\d-\\d\\d-\\d\\d$)\"]`. Do not assume a JSON body on a 400; branch on the status code.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "No token, a malformed token, or an expired one. Tokens last 30 minutes; request a new one and retry. The body is empty — a `WWW-Authenticate` header carries the reason."
          },
          "403": {
            "description": "The token is valid but its scope does not cover this endpoint. A `scope=API` token reaches every endpoint in this document and nothing else; `/user`, `/apikey`, billing and account endpoints are not part of the public API and will 403. The body is empty."
          },
          "429": {
            "description": "Rate limited. Back off and retry. Two limiters can produce this: an application-level limiter, and the Cloudflare edge in front of production. Neither publishes its threshold; treat 429 as retryable with exponential backoff rather than tuning to a fixed rate."
          },
          "500": {
            "description": "Unhandled server error. Retryable. If it persists, the request is probably hitting a defect rather than a transient fault — report it with the timestamp and path."
          }
        },
        "tags": [
          "Cloud emissions"
        ],
        "operationId": "getCloudSpendMap",
        "summary": "Cloud spend map",
        "description": "Cloud cost and emissions grouped by service, for a treemap or a cost-versus-carbon breakdown.\n\n**The path is `/api/co2/spendmap`.** There is no bare `/api/spendmap`; the AI equivalent is `/api/ai/spendmap` and returns a different shape.",
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -sS -H \"Authorization: Bearer $TOKEN\" \\\n  'https://uat.tailpipe.cloud/api/co2/spendmap?from=2026-06-01&to=2026-06-30'"
          }
        ]
      }
    },
    "/api/co2/summary": {
      "get": {
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Co2SummaryResponse"
                }
              }
            },
            "description": "Summary retrieved."
          },
          "401": {
            "description": "No token, a malformed token, or an expired one. Tokens last 30 minutes; request a new one and retry. The body is empty — a `WWW-Authenticate` header carries the reason."
          },
          "403": {
            "description": "The token is valid but its scope does not cover this endpoint. A `scope=API` token reaches every endpoint in this document and nothing else; `/user`, `/apikey`, billing and account endpoints are not part of the public API and will 403. The body is empty."
          },
          "429": {
            "description": "Rate limited. Back off and retry. Two limiters can produce this: an application-level limiter, and the Cloudflare edge in front of production. Neither publishes its threshold; treat 429 as retryable with exponential backoff rather than tuning to a fixed rate."
          },
          "500": {
            "description": "Unhandled server error. Retryable. If it persists, the request is probably hitting a defect rather than a transient fault — report it with the timestamp and path."
          }
        },
        "tags": [
          "Cloud emissions"
        ],
        "operationId": "getCloudEmissionsSummary",
        "summary": "Cloud emissions summary",
        "description": "Year-to-date cloud totals, with embodied, operational and combined roll-ups.\n\nThis endpoint takes no parameters — the window is fixed.",
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -sS -H \"Authorization: Bearer $TOKEN\" \\\n  'https://uat.tailpipe.cloud/api/co2/summary'"
          }
        ]
      }
    },
    "/api/recommendations": {
      "get": {
        "parameters": [
          {
            "in": "query",
            "name": "convertCurrency",
            "required": false,
            "schema": {
              "default": false,
              "type": "boolean"
            },
            "description": "Convert monetary fields into the signed-in user's configured currency.\n\n**This parameter is a no-op for `scope=API` tokens.** A machine client always receives `USD` with `exchangeRate` 1.0, whatever it sends, and no error is raised. Conversion is driven by the `uid` claim, which `client_credentials` tokens do not carry. Read the `currency` field on the response rather than assuming the request was honoured."
          },
          {
            "in": "query",
            "name": "objective",
            "required": false,
            "schema": {
              "default": "carbon",
              "type": "string"
            },
            "description": "Which objective targets the move lever. `carbon` (default) moves toward the cleanest valid region; `cost` moves toward the cheapest valid region that clears a saving gate. The delete and resize levers are objective-independent. Case-insensitive. Any other value returns 400."
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RecommendationResponse"
                }
              }
            },
            "description": "Recommendations retrieved."
          },
          "400": {
            "description": "`objective` was neither `carbon` nor `cost`. The reason text is not included in the response body — only the status code tells you."
          },
          "401": {
            "description": "No token, a malformed token, or an expired one. Tokens last 30 minutes; request a new one and retry. The body is empty — a `WWW-Authenticate` header carries the reason."
          },
          "403": {
            "description": "The token is valid but its scope does not cover this endpoint. A `scope=API` token reaches every endpoint in this document and nothing else; `/user`, `/apikey`, billing and account endpoints are not part of the public API and will 403. The body is empty."
          },
          "404": {
            "description": "Tailpipe does not hold enough usage history (30 days) to produce recommendations."
          },
          "429": {
            "description": "Rate limited. Back off and retry. Two limiters can produce this: an application-level limiter, and the Cloudflare edge in front of production. Neither publishes its threshold; treat 429 as retryable with exponential backoff rather than tuning to a fixed rate."
          },
          "500": {
            "description": "Unhandled server error. Retryable. If it persists, the request is probably hitting a defect rather than a transient fault — report it with the timestamp and path."
          }
        },
        "tags": [
          "Recommendations"
        ],
        "operationId": "getCloudRecommendations",
        "summary": "Cloud reduction recommendations",
        "description": "Instance and service level recommendations for reducing cloud emissions and cost.\n\n**The path is plural.** `/api/recommendation` does not exist and returns 404.\n\nThe response is a single object with `summary`, `instances` and `services`, not an array.",
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -sS -H \"Authorization: Bearer $TOKEN\" \\\n  'https://uat.tailpipe.cloud/api/recommendations?objective=carbon'"
          }
        ]
      }
    },
    "/oauth2/token": {
      "post": {
        "tags": [
          "Authentication"
        ],
        "summary": "Get an access token",
        "description": "Exchange your client id and secret for a 30-minute bearer token.\n\n**Send the credentials in the form body.** The registered client authentication method is `client_secret_post`, so `client_id` and `client_secret` are ordinary `application/x-www-form-urlencoded` fields alongside `grant_type` and `scope`.\n\nTwo things that do not work, both of which have cost a real integration weeks:\n\n- **Credentials as URL path segments.** `POST /oauth2/token/<id>/<secret>` does not match the token route at all. The server falls through to the login page and answers `302`, not an error you can diagnose. It also writes your client secret into the access log in plaintext.\n- **HTTP Basic.** `client_secret_basic` is *not* registered for these clients, so moving the credentials into an `Authorization: Basic` header fails too. It is a natural thing to reach for when the first attempt fails; it is not the fix.\n\nThe token carries a `tid` claim identifying your tenant. Every data endpoint scopes its results to it — there is no tenant parameter to pass and no way to read another tenant's data.\n\n**Refreshing.** There is no refresh token for `client_credentials`; request a new token. Cache it and reuse it for its lifetime rather than minting one per call. A reasonable pattern is to re-request when the cached token is within a minute of expiry, and to re-request once on a `401`.",
        "servers": [
          {
            "url": "https://login.tailpipe.ai",
            "description": "Production"
          },
          {
            "url": "https://uat.tailpipe.cloud/auth",
            "description": "UAT. Note the `/auth` context path — the UAT token URL is https://uat.tailpipe.cloud/auth/oauth2/token, whereas production has no context path. The data endpoints have no such asymmetry."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/x-www-form-urlencoded": {
              "schema": {
                "type": "object",
                "required": [
                  "client_id",
                  "client_secret",
                  "grant_type",
                  "scope"
                ],
                "properties": {
                  "grant_type": {
                    "type": "string",
                    "enum": [
                      "client_credentials"
                    ],
                    "default": "client_credentials",
                    "description": "Must be `client_credentials`."
                  },
                  "client_id": {
                    "type": "string",
                    "description": "From the Setup tab in Tailpipe."
                  },
                  "client_secret": {
                    "type": "string",
                    "description": "From the Setup tab in Tailpipe."
                  },
                  "scope": {
                    "type": "string",
                    "enum": [
                      "API"
                    ],
                    "default": "API",
                    "description": "Must be `API`."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Token issued.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TokenResponse"
                }
              }
            }
          },
          "400": {
            "description": "Malformed request — a missing or unsupported `grant_type`, or a `scope` the client is not allowed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OAuthError"
                }
              }
            }
          },
          "401": {
            "description": "`invalid_client`. Wrong id or secret, or the credentials were not sent as form fields.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OAuthError"
                }
              }
            }
          },
          "302": {
            "description": "**Not a real response — a symptom.** A redirect to `/auth/login` means the request did not match the token route: almost always credentials in the path instead of the body, or a missing `grant_type`. Fix the request shape; there is nothing to follow."
          }
        },
        "security": [],
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "curl",
            "source": "curl -sS -X POST 'https://uat.tailpipe.cloud/auth/oauth2/token' \\\n  -H 'Content-Type: application/x-www-form-urlencoded' \\\n  -d 'grant_type=client_credentials' \\\n  -d 'client_id=YOUR_CLIENT_ID' \\\n  -d 'client_secret=YOUR_CLIENT_SECRET' \\\n  -d 'scope=API'"
          }
        ],
        "operationId": "getAccessToken"
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "JWT",
        "description": "The `access_token` from `POST /oauth2/token`, sent as `Authorization: Bearer <token>`. Valid for 30 minutes."
      }
    },
    "schemas": {
      "AiCachingInsight": {
        "properties": {
          "cacheHitRatio": {
            "format": "double",
            "type": "number",
            "description": "Cache-read tokens as a fraction of input tokens over the window, 0-1."
          },
          "creationToReadRatio": {
            "format": "double",
            "type": "number",
            "description": "Cache-creation tokens per cache-read token. High values mean the cache is written but rarely reused."
          },
          "message": {
            "type": "string",
            "description": "Human-readable statement of the finding."
          },
          "model": {
            "type": "string",
            "description": "Model identifier as reported by the caller."
          },
          "potentialCostSavings": {
            "format": "double",
            "type": "number",
            "description": "Monthly, USD."
          },
          "potentialEmissionSavings": {
            "format": "double",
            "type": "number",
            "description": "Monthly, grams CO₂e."
          },
          "priority": {
            "type": "string",
            "description": "`HIGH`, `MEDIUM` or `LOW`."
          },
          "priorityScore": {
            "format": "double",
            "type": "number",
            "description": "Dimensionless sort score computed from USD values. Never currency-converted."
          },
          "provider": {
            "type": "string",
            "description": "Normalised provider name, e.g. `anthropic`, `openai`, `aws_bedrock`."
          },
          "rule": {
            "type": "string",
            "description": "Identifier of the rule that fired."
          }
        },
        "type": "object",
        "description": "Prompt-cache efficiency insight. Null fields are omitted."
      },
      "AiCostInsight": {
        "properties": {
          "alternativeCostPerMToken": {
            "format": "double",
            "type": "number",
            "description": "USD per million tokens."
          },
          "alternativeModel": {
            "type": "string",
            "description": "Cheaper model suggested for this workload, when one exists."
          },
          "currentCostPerMToken": {
            "format": "double",
            "type": "number",
            "description": "USD per million tokens."
          },
          "errorRate": {
            "format": "double",
            "type": "number",
            "description": "Failed requests as a fraction of all requests over the window, 0-1."
          },
          "message": {
            "type": "string",
            "description": "Human-readable statement of the finding."
          },
          "model": {
            "type": "string",
            "description": "Model identifier as reported by the caller."
          },
          "monthlyCost": {
            "format": "double",
            "type": "number",
            "description": "USD."
          },
          "monthlyTokens": {
            "format": "int64",
            "type": "integer",
            "description": "Tokens over the trailing window the insight was sized from."
          },
          "priority": {
            "type": "string",
            "description": "`HIGH`, `MEDIUM` or `LOW`."
          },
          "priorityScore": {
            "format": "double",
            "type": "number",
            "description": "Dimensionless sort score computed from USD values. Never currency-converted."
          },
          "provider": {
            "type": "string",
            "description": "Normalised provider name, e.g. `anthropic`, `openai`, `aws_bedrock`."
          },
          "rule": {
            "type": "string",
            "description": "Identifier of the rule that fired."
          },
          "wastedCost": {
            "format": "double",
            "type": "number",
            "description": "Cost attributed to failed requests, USD."
          }
        },
        "type": "object",
        "description": "Cost efficiency insight. Null fields are omitted."
      },
      "AiCoverageResponse": {
        "properties": {
          "coverage": {
            "items": {
              "$ref": "#/components/schemas/DailyAiUsageCoverage"
            },
            "type": "array",
            "description": "One entry per day in the window; empty when no AI telemetry has been ingested."
          }
        },
        "type": "object",
        "description": "A wrapper object, not a bare array."
      },
      "AiFilterOptionResponse": {
        "properties": {
          "applications": {
            "items": {
              "type": "string"
            },
            "type": "array",
            "description": "Application labels present in your telemetry, as tagged by the SDK."
          },
          "environments": {
            "items": {
              "type": "string"
            },
            "type": "array",
            "description": "Environment labels present in your telemetry, as tagged by the SDK."
          },
          "models": {
            "items": {
              "type": "string"
            },
            "type": "array",
            "description": "Model values present in your telemetry — valid inputs for the `model` filter."
          },
          "providerFilterMappings": {
            "items": {
              "$ref": "#/components/schemas/AiProviderFilterMapping"
            },
            "type": "array",
            "description": "Cascading mapping: which models and applications were seen under each provider."
          },
          "providers": {
            "items": {
              "type": "string"
            },
            "type": "array",
            "description": "Provider values present in your telemetry — valid inputs for the `provider` filter."
          }
        },
        "type": "object",
        "description": "Valid filter values for the AI endpoints."
      },
      "AiModelPricing": {
        "properties": {
          "cacheReadPricePerMToken": {
            "format": "double",
            "type": "number",
            "description": "USD per million cache-read tokens."
          },
          "cacheWritePricePerMToken": {
            "format": "double",
            "type": "number",
            "description": "USD per million cache-write tokens."
          },
          "confidenceTier": {
            "format": "int32",
            "type": "integer",
            "description": "GHG Protocol confidence: 1 measured, 2 modelled, 3 estimated."
          },
          "crosscheckAnchorWh": {
            "format": "double",
            "type": "number",
            "description": "Independent anchor the figure was cross-checked against."
          },
          "crosscheckOutcome": {
            "type": "string",
            "description": "Result of the cross-check."
          },
          "crosscheckRatio": {
            "format": "double",
            "type": "number",
            "description": "Ratio of the stored figure to the anchor."
          },
          "effectiveDate": {
            "type": "string",
            "description": "The row's effective date. Serialised as a string; the newest row at or before a usage date wins."
          },
          "embodiedFactor": {
            "format": "double",
            "type": "number",
            "description": "Retained but unused: the AI methodology is operational-only."
          },
          "energyBasis": {
            "type": "string",
            "description": "What the energy figure was derived from."
          },
          "energyPerMCacheCreationWh": {
            "format": "double",
            "type": "number",
            "description": "Wh per million cache-creation tokens. Equal to the input coefficient."
          },
          "energyPerMCacheReadWh": {
            "format": "double",
            "type": "number",
            "description": "Wh per million cache-read tokens. Modelled as 2% of the input coefficient — an explicit estimate, not a measurement."
          },
          "energyPerMInputWh": {
            "format": "double",
            "type": "number",
            "description": "Wh per million input tokens."
          },
          "energyPerMOutputWh": {
            "format": "double",
            "type": "number",
            "description": "Wh per million output tokens."
          },
          "energyRangeHighFactor": {
            "format": "double",
            "type": "number",
            "description": "Multiplier giving the high bound."
          },
          "energyRangeLowFactor": {
            "format": "double",
            "type": "number",
            "description": "Multiplier giving the low bound of the energy estimate."
          },
          "energySource": {
            "type": "string",
            "description": "Citation for the energy figure."
          },
          "energyTier": {
            "format": "int32",
            "type": "integer",
            "description": "ADR-0002 energy waterfall tier, 1–4. Distinct from `confidenceTier`."
          },
          "inputPricePerMToken": {
            "format": "double",
            "type": "number",
            "description": "USD per million input tokens."
          },
          "model": {
            "type": "string",
            "description": "Model or model-family key. Rows named `default-<provider>` are the fallback used when a model has no row of its own."
          },
          "outputPricePerMToken": {
            "format": "double",
            "type": "number",
            "description": "USD per million output tokens."
          },
          "provider": {
            "type": "string",
            "description": "Normalised provider name, e.g. `anthropic`, `openai`, `aws_bedrock`."
          }
        },
        "type": "object",
        "description": "One pricing and energy reference row. Cost columns are refreshed daily from LiteLLM; energy and confidence columns are curated and are not touched by that sync. Rows named `default-<provider>` are the fallback used when a model has no row of its own."
      },
      "AiModelSummaryResponse": {
        "properties": {
          "cacheCreationTokens": {
            "format": "int64",
            "type": "integer",
            "description": "Tokens written to the prompt cache."
          },
          "cacheEnergyConsumptionWh": {
            "format": "double",
            "type": "number",
            "description": "Cache read + cache creation energy, Wh."
          },
          "cacheReadTokens": {
            "format": "int64",
            "type": "integer",
            "description": "Tokens served from the prompt cache."
          },
          "embodiedEmissionCo2": {
            "format": "double",
            "type": "number",
            "description": "Always null on the AI path. The column is retained but the agreed AI methodology is operational-only (ADR-0002)."
          },
          "energyConsumptionWh": {
            "format": "double",
            "type": "number",
            "description": "Energy, Wh."
          },
          "errorCount": {
            "format": "int32",
            "type": "integer",
            "description": "Requests that returned an error."
          },
          "inputCost": {
            "format": "double",
            "type": "number",
            "description": "Portion of `totalCost` from input tokens, USD."
          },
          "inputEnergyConsumptionWh": {
            "format": "double",
            "type": "number",
            "description": "Prefill energy, Wh."
          },
          "inputTokens": {
            "format": "int64",
            "type": "integer",
            "description": "Prompt tokens."
          },
          "model": {
            "type": "string",
            "description": "Model identifier as reported by the caller."
          },
          "modelFamily": {
            "type": "string",
            "description": "Family key used to join pricing and energy reference data."
          },
          "operationalEmissionCo2": {
            "format": "double",
            "type": "number",
            "description": "Operational emissions, grams CO₂e."
          },
          "outputEnergyConsumptionWh": {
            "format": "double",
            "type": "number",
            "description": "Decode energy, Wh."
          },
          "outputTokens": {
            "format": "int64",
            "type": "integer",
            "description": "Completion tokens."
          },
          "provider": {
            "type": "string",
            "description": "Normalised provider name, e.g. `anthropic`, `openai`, `aws_bedrock`."
          },
          "reasoningTokens": {
            "format": "int64",
            "type": "integer",
            "description": "Reasoning tokens. A **subset of `outputTokens`, never additive** — do not add it to the total. Null for telemetry recorded before SDK v1.3.0."
          },
          "requestCount": {
            "format": "int32",
            "type": "integer",
            "description": "Requests in the period."
          },
          "successCount": {
            "format": "int32",
            "type": "integer",
            "description": "Requests that returned successfully."
          },
          "totalCost": {
            "format": "double",
            "type": "number",
            "description": "API-equivalent cost, USD. An estimate derived from published list prices, not a billed amount — a customer on a subscription plan pays something different."
          },
          "totalEmission": {
            "format": "double",
            "type": "number",
            "description": "Equal to `operationalEmissionCo2`, or 0 when that is null, grams CO₂e. This is the field to report."
          },
          "totalTokens": {
            "format": "int64",
            "type": "integer",
            "description": "Total tokens."
          }
        },
        "type": "object",
        "description": "AI usage aggregated per model over the window, with the energy split."
      },
      "AiModelSwitchInsight": {
        "properties": {
          "complexity": {
            "type": "string",
            "description": "Adoption effort band."
          },
          "confidence": {
            "format": "double",
            "type": "number",
            "description": "0–1."
          },
          "currentEnergyPerMToken": {
            "format": "double",
            "type": "number",
            "description": "Blended Wh per million tokens for the in-use model."
          },
          "message": {
            "type": "string",
            "description": "Human-readable statement of the finding."
          },
          "model": {
            "type": "string",
            "description": "The in-use model."
          },
          "modelFamily": {
            "type": "string",
            "description": "Family key used to join pricing and energy reference data."
          },
          "monthlyTokens": {
            "format": "int64",
            "type": "integer",
            "description": "Tokens over the trailing window the insight was sized from."
          },
          "priority": {
            "type": "string",
            "description": "`HIGH`, `MEDIUM` or `LOW`."
          },
          "projectedCostSavings": {
            "format": "double",
            "type": "number",
            "description": "Monthly API-equivalent saving, USD. Null when either model has no token pricing."
          },
          "projectedEmissionSavings": {
            "format": "double",
            "type": "number",
            "description": "Monthly operational saving, grams CO₂e."
          },
          "provider": {
            "type": "string",
            "description": "Normalised provider name, e.g. `anthropic`, `openai`, `aws_bedrock`."
          },
          "recommendedEnergyPerMToken": {
            "format": "double",
            "type": "number",
            "description": "Blended Wh per million tokens for the recommendation."
          },
          "recommendedModel": {
            "type": "string",
            "description": "The lower-energy family to switch to."
          },
          "rule": {
            "type": "string",
            "description": "Identifier of the rule that fired."
          }
        },
        "type": "object",
        "description": "A same-provider, same-line model with materially lower measured energy is available. Null fields are omitted."
      },
      "AiProviderFilterMapping": {
        "properties": {
          "applications": {
            "items": {
              "type": "string"
            },
            "type": "array",
            "description": "Applications seen under this provider."
          },
          "models": {
            "items": {
              "type": "string"
            },
            "type": "array",
            "description": "Models seen under this provider."
          },
          "provider": {
            "type": "string",
            "description": "Normalised provider name, e.g. `anthropic`, `openai`, `aws_bedrock`."
          }
        },
        "type": "object",
        "description": "Cascading filter mapping: the models and applications seen under one provider."
      },
      "AiProviderPueResponse": {
        "properties": {
          "enabled": {
            "type": "boolean",
            "description": "Whether provider-specific PUE is currently applied. When false, `globalDefault` is what the figures actually use."
          },
          "entries": {
            "items": {
              "$ref": "#/components/schemas/ProviderPueEntry"
            },
            "type": "array",
            "description": "Per-provider figures with provenance. Reference only while `enabled` is false."
          },
          "globalDefault": {
            "$ref": "#/components/schemas/ProviderPueEntry",
            "description": "The PUE applied when no provider-specific figure is active."
          }
        },
        "type": "object",
        "description": "The provider PUE registry with provenance, and whether provider-specific PUE is active."
      },
      "AiProviderSummaryResponse": {
        "properties": {
          "cacheCreationTokens": {
            "format": "int64",
            "type": "integer",
            "description": "Tokens written to the prompt cache."
          },
          "cacheReadTokens": {
            "format": "int64",
            "type": "integer",
            "description": "Tokens served from the prompt cache."
          },
          "embodiedEmissionCo2": {
            "format": "double",
            "type": "number",
            "description": "Always null on the AI path. The column is retained but the agreed AI methodology is operational-only (ADR-0002)."
          },
          "energyConsumptionWh": {
            "format": "double",
            "type": "number",
            "description": "Energy, Wh."
          },
          "errorCount": {
            "format": "int32",
            "type": "integer",
            "description": "Requests that returned an error."
          },
          "inputTokens": {
            "format": "int64",
            "type": "integer",
            "description": "Prompt tokens."
          },
          "operationalEmissionCo2": {
            "format": "double",
            "type": "number",
            "description": "Operational emissions, grams CO₂e."
          },
          "outputTokens": {
            "format": "int64",
            "type": "integer",
            "description": "Completion tokens."
          },
          "provider": {
            "type": "string",
            "description": "Normalised provider name, e.g. `anthropic`, `openai`, `aws_bedrock`."
          },
          "reasoningTokens": {
            "format": "int64",
            "type": "integer",
            "description": "Reasoning tokens. A **subset of `outputTokens`, never additive** — do not add it to the total. Null for telemetry recorded before SDK v1.3.0."
          },
          "requestCount": {
            "format": "int32",
            "type": "integer",
            "description": "Requests in the period."
          },
          "successCount": {
            "format": "int32",
            "type": "integer",
            "description": "Requests that returned successfully."
          },
          "totalCost": {
            "format": "double",
            "type": "number",
            "description": "API-equivalent cost, USD. An estimate derived from published list prices, not a billed amount — a customer on a subscription plan pays something different."
          },
          "totalEmission": {
            "format": "double",
            "type": "number",
            "description": "Equal to `operationalEmissionCo2`, or 0 when that is null, grams CO₂e. This is the field to report."
          },
          "totalTokens": {
            "format": "int64",
            "type": "integer",
            "description": "Total tokens."
          }
        },
        "type": "object",
        "description": "AI usage aggregated per provider over the window."
      },
      "AiRecommendationResponse": {
        "properties": {
          "cachingInsights": {
            "items": {
              "$ref": "#/components/schemas/AiCachingInsight"
            },
            "type": "array",
            "description": "Prompt-cache efficiency findings: low hit ratios and cache churn."
          },
          "costInsights": {
            "items": {
              "$ref": "#/components/schemas/AiCostInsight"
            },
            "type": "array",
            "description": "Cost-efficiency findings: expensive models, spend wasted on failed requests, cheaper alternatives."
          },
          "currency": {
            "type": "string",
            "description": "Always `USD` for a `scope=API` token."
          },
          "exchangeRate": {
            "format": "double",
            "type": "number",
            "description": "Always 1.0 for a `scope=API` token."
          },
          "modelSwitchInsights": {
            "items": {
              "$ref": "#/components/schemas/AiModelSwitchInsight"
            },
            "type": "array",
            "description": "Same-provider, same-line models with materially lower measured energy."
          },
          "regionMoveInsights": {
            "items": {
              "$ref": "#/components/schemas/AiRegionMoveInsight"
            },
            "type": "array",
            "description": "Region-selectable inference running on a dirtier grid than an available candidate."
          },
          "summary": {
            "$ref": "#/components/schemas/AiRecommendationSummary",
            "description": "Totals and priority counts across all insight types."
          },
          "tokenOptimizationInsights": {
            "items": {
              "$ref": "#/components/schemas/AiTokenOptimizationInsight"
            },
            "type": "array",
            "description": "Output-heavy workloads where concise prompting or structured output would cut decode energy."
          }
        },
        "type": "object",
        "description": "A single object. The five insight arrays are always present, possibly empty. Null fields are omitted from the response."
      },
      "AiRecommendationSummary": {
        "properties": {
          "annualCost": {
            "format": "double",
            "type": "number",
            "description": "Annualised API-equivalent cost, USD."
          },
          "annualEmission": {
            "format": "double",
            "type": "number",
            "description": "Annualised operational emissions, grams CO₂e."
          },
          "endDate": {
            "type": "string",
            "description": "Last day of the observation window, `YYYY-MM-DD`."
          },
          "forecastCost": {
            "format": "double",
            "type": "number",
            "description": "Annualised cost if every insight is adopted, USD."
          },
          "forecastEmission": {
            "format": "double",
            "type": "number",
            "description": "Annualised emissions if every insight is adopted, grams CO₂e."
          },
          "highPriorityCount": {
            "format": "int32",
            "type": "integer",
            "description": "Insights with priority `HIGH`."
          },
          "lowPriorityCount": {
            "format": "int32",
            "type": "integer",
            "description": "Insights with priority `LOW`."
          },
          "mediumPriorityCount": {
            "format": "int32",
            "type": "integer",
            "description": "Insights with priority `MEDIUM`."
          },
          "potentialCostReduction": {
            "format": "double",
            "type": "number",
            "description": "USD."
          },
          "potentialEmissionReduction": {
            "format": "double",
            "type": "number",
            "description": "grams CO₂e."
          },
          "startDate": {
            "type": "string",
            "description": "First day of the observation window, `YYYY-MM-DD`."
          }
        },
        "type": "object",
        "description": "Totals, projected reductions and priority counts across the AI insights."
      },
      "AiRegionMoveInsight": {
        "properties": {
          "complexity": {
            "type": "string",
            "description": "Always the highest band — this is an infrastructure change."
          },
          "confidence": {
            "format": "double",
            "type": "number",
            "description": "0–1."
          },
          "currentGridIntensity": {
            "format": "double",
            "type": "number",
            "description": "gCO₂/kWh."
          },
          "inferenceRegion": {
            "type": "string",
            "description": "The in-use region."
          },
          "message": {
            "type": "string",
            "description": "Human-readable statement of the finding."
          },
          "monthlyTokens": {
            "format": "int64",
            "type": "integer",
            "description": "Tokens over the trailing window the insight was sized from."
          },
          "priority": {
            "type": "string",
            "description": "`HIGH`, `MEDIUM` or `LOW`."
          },
          "projectedCostSavings": {
            "format": "double",
            "type": "number",
            "description": "Always null: cross-region price differences are out of scope."
          },
          "projectedEmissionSavings": {
            "format": "double",
            "type": "number",
            "description": "Monthly central estimate, grams CO₂e."
          },
          "projectedEmissionSavingsHigh": {
            "format": "double",
            "type": "number",
            "description": "High bound, grams CO₂e."
          },
          "projectedEmissionSavingsLow": {
            "format": "double",
            "type": "number",
            "description": "Low bound, grams CO₂e. Render the saving as a range, not a point: the grid-intensity ratio is exact but the underlying energy figure is not."
          },
          "provider": {
            "type": "string",
            "description": "Normalised provider name, e.g. `anthropic`, `openai`, `aws_bedrock`."
          },
          "recommendedGridIntensity": {
            "format": "double",
            "type": "number",
            "description": "gCO₂/kWh."
          },
          "recommendedRegion": {
            "type": "string",
            "description": "Lower-carbon candidate in the same platform namespace."
          },
          "rule": {
            "type": "string",
            "description": "Identifier of the rule that fired."
          }
        },
        "type": "object",
        "description": "A region-selectable inference workload (Bedrock, Vertex, Azure OpenAI) is running in a higher-carbon region than an available candidate. Never emitted for direct provider APIs, where the region is not the customer's to choose. Null fields are omitted."
      },
      "AiSpendMapResponse": {
        "properties": {
          "modelFamily": {
            "type": "string",
            "description": "Family key used to join pricing and energy reference data."
          },
          "provider": {
            "type": "string",
            "description": "Normalised provider name, e.g. `anthropic`, `openai`, `aws_bedrock`."
          },
          "totalCost": {
            "format": "double",
            "type": "number",
            "description": "API-equivalent cost, USD."
          },
          "totalEmission": {
            "format": "double",
            "type": "number",
            "description": "Operational emissions, grams CO₂e."
          },
          "totalTokens": {
            "format": "int64",
            "type": "integer",
            "description": "Total tokens for this provider and model family."
          }
        },
        "type": "object",
        "description": "AI cost and emissions grouped by provider and model family. Note this carries fewer fields than the cloud `SpendMapResponse` and has no `category`."
      },
      "AiTokenOptimizationInsight": {
        "properties": {
          "complexity": {
            "type": "string",
            "description": "Adoption-effort band for this lever."
          },
          "confidence": {
            "format": "double",
            "type": "number",
            "description": "0–1."
          },
          "inputEnergyPerMToken": {
            "format": "double",
            "type": "number",
            "description": "Prefill, Wh per million tokens."
          },
          "message": {
            "type": "string",
            "description": "Human-readable statement of the finding."
          },
          "model": {
            "type": "string",
            "description": "Model identifier as reported by the caller."
          },
          "modelFamily": {
            "type": "string",
            "description": "Family key used to join pricing and energy reference data."
          },
          "monthlyTokens": {
            "format": "int64",
            "type": "integer",
            "description": "Tokens over the trailing window the insight was sized from."
          },
          "outputEnergyPerMToken": {
            "format": "double",
            "type": "number",
            "description": "Decode, Wh per million tokens."
          },
          "outputInputRatio": {
            "format": "double",
            "type": "number",
            "description": "Output tokens per input token over the window that triggered the insight."
          },
          "priority": {
            "type": "string",
            "description": "`HIGH`, `MEDIUM` or `LOW`."
          },
          "projectedCostSavings": {
            "format": "double",
            "type": "number",
            "description": "Monthly saving, USD. Null when token pricing is unknown."
          },
          "projectedEmissionSavings": {
            "format": "double",
            "type": "number",
            "description": "Monthly operational saving, grams CO₂e."
          },
          "provider": {
            "type": "string",
            "description": "Normalised provider name, e.g. `anthropic`, `openai`, `aws_bedrock`."
          },
          "rule": {
            "type": "string",
            "description": "Identifier of the rule that fired."
          }
        },
        "type": "object",
        "description": "An output-heavy workload where concise prompting or structured output would trim verbose responses. Null fields are omitted."
      },
      "AiUsageSummary": {
        "properties": {
          "cacheCreationTokens": {
            "format": "int64",
            "type": "integer",
            "description": "Tokens written to the prompt cache."
          },
          "cacheReadTokens": {
            "format": "int64",
            "type": "integer",
            "description": "Tokens served from the prompt cache."
          },
          "embodiedEmissionCo2": {
            "format": "double",
            "type": "number",
            "description": "Always null on the AI path. The column is retained but the agreed AI methodology is operational-only (ADR-0002)."
          },
          "energyConsumptionWh": {
            "format": "double",
            "type": "number",
            "description": "Energy, Wh."
          },
          "errorCount": {
            "format": "int32",
            "type": "integer",
            "description": "Requests that returned an error."
          },
          "inputTokens": {
            "format": "int64",
            "type": "integer",
            "description": "Prompt tokens."
          },
          "operationalEmissionCo2": {
            "format": "double",
            "type": "number",
            "description": "Operational emissions, grams CO₂e."
          },
          "outputTokens": {
            "format": "int64",
            "type": "integer",
            "description": "Completion tokens."
          },
          "reasoningTokens": {
            "format": "int64",
            "type": "integer",
            "description": "Reasoning tokens. A **subset of `outputTokens`, never additive** — do not add it to the total. Null for telemetry recorded before SDK v1.3.0."
          },
          "requestCount": {
            "format": "int32",
            "type": "integer",
            "description": "Requests in the period."
          },
          "successCount": {
            "format": "int32",
            "type": "integer",
            "description": "Requests that returned successfully."
          },
          "totalCost": {
            "format": "double",
            "type": "number",
            "description": "API-equivalent cost, USD. An estimate derived from published list prices, not a billed amount — a customer on a subscription plan pays something different."
          },
          "totalEmission": {
            "format": "double",
            "type": "number",
            "description": "Equal to `operationalEmissionCo2`, or 0 when that is null, grams CO₂e. This is the field to report."
          },
          "totalTokens": {
            "format": "int64",
            "type": "integer",
            "description": "Total tokens."
          }
        },
        "type": "object",
        "description": "Year-to-date AI totals. Same field set as `AiUsageMetrics`."
      },
      "AiUsageSummaryResponse": {
        "properties": {
          "yearData": {
            "$ref": "#/components/schemas/AiUsageSummary",
            "description": "Year-to-date AI totals."
          },
          "yearlyFrom": {
            "type": "string",
            "description": "First day covered by `yearData`."
          }
        },
        "type": "object",
        "description": "Mirrors `Co2SummaryResponse` — two fields, no monthly pair."
      },
      "Co2Summary": {
        "properties": {
          "cost": {
            "format": "double",
            "type": "number",
            "description": "Unblended cost for the period, USD."
          },
          "embodiedAccel": {
            "format": "double",
            "type": "number",
            "description": "Embodied emissions attributed to accelerators (GPU/other), kg CO₂e."
          },
          "embodiedAssembly": {
            "format": "double",
            "type": "number",
            "description": "Embodied emissions attributed to assembly, kg CO₂e."
          },
          "embodiedCpu": {
            "format": "double",
            "type": "number",
            "description": "Embodied emissions attributed to CPU, kg CO₂e."
          },
          "embodiedEmission": {
            "format": "double",
            "type": "number",
            "description": "Sum of the twelve `embodied*` fields, kg CO₂e."
          },
          "embodiedEnclosure": {
            "format": "double",
            "type": "number",
            "description": "Embodied emissions attributed to the chassis, kg CO₂e."
          },
          "embodiedHdd": {
            "format": "double",
            "type": "number",
            "description": "Embodied emissions attributed to spinning disk, kg CO₂e."
          },
          "embodiedMotherboard": {
            "format": "double",
            "type": "number",
            "description": "Embodied emissions attributed to the mainboard, kg CO₂e."
          },
          "embodiedNetworkStorage": {
            "format": "double",
            "type": "number",
            "description": "Embodied emissions attributed to network storage, kg CO₂e."
          },
          "embodiedOther": {
            "format": "double",
            "type": "number",
            "description": "Embodied emissions not attributed to a named component, kg CO₂e."
          },
          "embodiedPsu": {
            "format": "double",
            "type": "number",
            "description": "Embodied emissions attributed to the power supply, kg CO₂e."
          },
          "embodiedRam": {
            "format": "double",
            "type": "number",
            "description": "Embodied emissions attributed to RAM, kg CO₂e."
          },
          "embodiedSsd": {
            "format": "double",
            "type": "number",
            "description": "Embodied emissions attributed to SSD, kg CO₂e."
          },
          "embodiedSwitch": {
            "format": "double",
            "type": "number",
            "description": "Embodied emissions attributed to switching, kg CO₂e."
          },
          "operationalAccel": {
            "format": "double",
            "type": "number",
            "description": "Operational emissions attributed to accelerators, kg CO₂e."
          },
          "operationalCpu": {
            "format": "double",
            "type": "number",
            "description": "Operational emissions attributed to CPU, kg CO₂e."
          },
          "operationalEmission": {
            "format": "double",
            "type": "number",
            "description": "Sum of the fourteen `operational*` fields, kg CO₂e."
          },
          "operationalExternalInstanceNetwork": {
            "format": "double",
            "type": "number",
            "description": "Operational emissions, instance egress to the internet, kg CO₂e."
          },
          "operationalExternalNetwork": {
            "format": "double",
            "type": "number",
            "description": "Operational emissions, egress to the internet, kg CO₂e."
          },
          "operationalHdd": {
            "format": "double",
            "type": "number",
            "description": "Operational emissions attributed to spinning disk, kg CO₂e."
          },
          "operationalInterInstanceNetwork": {
            "format": "double",
            "type": "number",
            "description": "Operational emissions, inter-instance network traffic, kg CO₂e."
          },
          "operationalInterNetwork": {
            "format": "double",
            "type": "number",
            "description": "Operational emissions, inter-region network traffic, kg CO₂e."
          },
          "operationalIntraInstanaceNetwork": {
            "format": "double",
            "type": "number",
            "description": "Operational emissions, intra-instance network traffic, kg CO₂e. Note the spelling: `Instanace` is a long-standing typo in the column and the field name. It is part of the contract — do not correct it in your client."
          },
          "operationalIntraNetwork": {
            "format": "double",
            "type": "number",
            "description": "Operational emissions, intra-region network traffic, kg CO₂e."
          },
          "operationalMotherboard": {
            "format": "double",
            "type": "number",
            "description": "Operational emissions attributed to the mainboard, kg CO₂e."
          },
          "operationalNetworkStorage": {
            "format": "double",
            "type": "number",
            "description": "Operational emissions attributed to network storage, kg CO₂e."
          },
          "operationalNic": {
            "format": "double",
            "type": "number",
            "description": "Operational emissions attributed to the network interface, kg CO₂e."
          },
          "operationalRam": {
            "format": "double",
            "type": "number",
            "description": "Operational emissions attributed to RAM, kg CO₂e."
          },
          "operationalSsd": {
            "format": "double",
            "type": "number",
            "description": "Operational emissions attributed to SSD, kg CO₂e."
          },
          "powerConsumption": {
            "format": "double",
            "type": "number",
            "description": "Energy attributed to this row, Wh."
          },
          "processedCost": {
            "format": "double",
            "type": "number",
            "description": "Cost of the subset of line items Tailpipe could attribute emissions to, USD. Compare against `cost` to judge coverage."
          },
          "totalEmission": {
            "format": "double",
            "type": "number",
            "description": "`embodiedEmission` + `operationalEmission`, kg CO₂e. On the cloud path Tailpipe reports both; contrast the AI path, which is operational-only."
          }
        },
        "type": "object",
        "description": "Year-to-date cloud totals, with the three roll-ups Tailpipe computes."
      },
      "Co2SummaryResponse": {
        "properties": {
          "yearData": {
            "$ref": "#/components/schemas/Co2Summary",
            "description": "Year-to-date cloud totals, with embodied, operational and combined roll-ups."
          },
          "yearlyFrom": {
            "type": "string",
            "description": "First day covered by `yearData`."
          }
        },
        "type": "object",
        "description": "**This response has exactly two fields.** Versions of this document before 1.1.0 also listed `monthlyFrom` and `monthData`; those have never existed on this endpoint."
      },
      "DailyAiUsageCoverage": {
        "properties": {
          "date": {
            "type": "string",
            "description": "The day this coverage entry describes, `YYYY-MM-DD`."
          },
          "estimatedModels": {
            "type": "string",
            "description": "Models that fell back to a `default-<provider>` row."
          },
          "processedModels": {
            "type": "string",
            "description": "Models resolved against a pricing row."
          },
          "summary": {
            "type": "string",
            "description": "Free-text digestion summary."
          }
        },
        "type": "object",
        "description": "Which models Tailpipe priced from reference data and which it estimated, for one day. The entity's `tenantId`, `provider` and `fileSequence` columns are not serialised."
      },
      "DailyAiUsageResponse": {
        "properties": {
          "cacheCreationTokens": {
            "format": "int64",
            "type": "integer",
            "description": "Tokens written to the prompt cache."
          },
          "cacheReadTokens": {
            "format": "int64",
            "type": "integer",
            "description": "Tokens served from the prompt cache."
          },
          "date": {
            "type": "string",
            "description": "The day these totals cover, `YYYY-MM-DD`."
          },
          "embodiedEmissionCo2": {
            "format": "double",
            "type": "number",
            "description": "Always null on the AI path. The column is retained but the agreed AI methodology is operational-only (ADR-0002)."
          },
          "energyConsumptionWh": {
            "format": "double",
            "type": "number",
            "description": "Energy, Wh."
          },
          "errorCount": {
            "format": "int32",
            "type": "integer",
            "description": "Requests that returned an error."
          },
          "inputTokens": {
            "format": "int64",
            "type": "integer",
            "description": "Prompt tokens."
          },
          "operationalEmissionCo2": {
            "format": "double",
            "type": "number",
            "description": "Operational emissions, grams CO₂e."
          },
          "outputTokens": {
            "format": "int64",
            "type": "integer",
            "description": "Completion tokens."
          },
          "reasoningTokens": {
            "format": "int64",
            "type": "integer",
            "description": "Reasoning tokens. A **subset of `outputTokens`, never additive** — do not add it to the total. Null for telemetry recorded before SDK v1.3.0."
          },
          "requestCount": {
            "format": "int32",
            "type": "integer",
            "description": "Requests in the period."
          },
          "successCount": {
            "format": "int32",
            "type": "integer",
            "description": "Requests that returned successfully."
          },
          "totalCost": {
            "format": "double",
            "type": "number",
            "description": "API-equivalent cost, USD. An estimate derived from published list prices, not a billed amount — a customer on a subscription plan pays something different."
          },
          "totalEmission": {
            "format": "double",
            "type": "number",
            "description": "Equal to `operationalEmissionCo2`, or 0 when that is null, grams CO₂e. This is the field to report."
          },
          "totalTokens": {
            "format": "int64",
            "type": "integer",
            "description": "Total tokens."
          }
        },
        "type": "object",
        "description": "One day of AI usage."
      },
      "DailyCo2Response": {
        "properties": {
          "cost": {
            "format": "double",
            "type": "number",
            "description": "Unblended cost for the period, USD."
          },
          "date": {
            "type": "string",
            "description": "The day these totals cover, `YYYY-MM-DD`."
          },
          "embodiedAccel": {
            "format": "double",
            "type": "number",
            "description": "Embodied emissions attributed to accelerators (GPU/other), kg CO₂e."
          },
          "embodiedAssembly": {
            "format": "double",
            "type": "number",
            "description": "Embodied emissions attributed to assembly, kg CO₂e."
          },
          "embodiedCpu": {
            "format": "double",
            "type": "number",
            "description": "Embodied emissions attributed to CPU, kg CO₂e."
          },
          "embodiedEnclosure": {
            "format": "double",
            "type": "number",
            "description": "Embodied emissions attributed to the chassis, kg CO₂e."
          },
          "embodiedHdd": {
            "format": "double",
            "type": "number",
            "description": "Embodied emissions attributed to spinning disk, kg CO₂e."
          },
          "embodiedMotherboard": {
            "format": "double",
            "type": "number",
            "description": "Embodied emissions attributed to the mainboard, kg CO₂e."
          },
          "embodiedNetworkStorage": {
            "format": "double",
            "type": "number",
            "description": "Embodied emissions attributed to network storage, kg CO₂e."
          },
          "embodiedOther": {
            "format": "double",
            "type": "number",
            "description": "Embodied emissions not attributed to a named component, kg CO₂e."
          },
          "embodiedPsu": {
            "format": "double",
            "type": "number",
            "description": "Embodied emissions attributed to the power supply, kg CO₂e."
          },
          "embodiedRam": {
            "format": "double",
            "type": "number",
            "description": "Embodied emissions attributed to RAM, kg CO₂e."
          },
          "embodiedSsd": {
            "format": "double",
            "type": "number",
            "description": "Embodied emissions attributed to SSD, kg CO₂e."
          },
          "embodiedSwitch": {
            "format": "double",
            "type": "number",
            "description": "Embodied emissions attributed to switching, kg CO₂e."
          },
          "operationalAccel": {
            "format": "double",
            "type": "number",
            "description": "Operational emissions attributed to accelerators, kg CO₂e."
          },
          "operationalCpu": {
            "format": "double",
            "type": "number",
            "description": "Operational emissions attributed to CPU, kg CO₂e."
          },
          "operationalExternalInstanceNetwork": {
            "format": "double",
            "type": "number",
            "description": "Operational emissions, instance egress to the internet, kg CO₂e."
          },
          "operationalExternalNetwork": {
            "format": "double",
            "type": "number",
            "description": "Operational emissions, egress to the internet, kg CO₂e."
          },
          "operationalHdd": {
            "format": "double",
            "type": "number",
            "description": "Operational emissions attributed to spinning disk, kg CO₂e."
          },
          "operationalInterInstanceNetwork": {
            "format": "double",
            "type": "number",
            "description": "Operational emissions, inter-instance network traffic, kg CO₂e."
          },
          "operationalInterNetwork": {
            "format": "double",
            "type": "number",
            "description": "Operational emissions, inter-region network traffic, kg CO₂e."
          },
          "operationalIntraInstanaceNetwork": {
            "format": "double",
            "type": "number",
            "description": "Operational emissions, intra-instance network traffic, kg CO₂e. Note the spelling: `Instanace` is a long-standing typo in the column and the field name. It is part of the contract — do not correct it in your client."
          },
          "operationalIntraNetwork": {
            "format": "double",
            "type": "number",
            "description": "Operational emissions, intra-region network traffic, kg CO₂e."
          },
          "operationalMotherboard": {
            "format": "double",
            "type": "number",
            "description": "Operational emissions attributed to the mainboard, kg CO₂e."
          },
          "operationalNetworkStorage": {
            "format": "double",
            "type": "number",
            "description": "Operational emissions attributed to network storage, kg CO₂e."
          },
          "operationalNic": {
            "format": "double",
            "type": "number",
            "description": "Operational emissions attributed to the network interface, kg CO₂e."
          },
          "operationalRam": {
            "format": "double",
            "type": "number",
            "description": "Operational emissions attributed to RAM, kg CO₂e."
          },
          "operationalSsd": {
            "format": "double",
            "type": "number",
            "description": "Operational emissions attributed to SSD, kg CO₂e."
          },
          "powerConsumption": {
            "format": "double",
            "type": "number",
            "description": "Energy attributed to this row, Wh."
          },
          "processedCost": {
            "format": "double",
            "type": "number",
            "description": "Cost of the subset of line items Tailpipe could attribute emissions to, USD. Compare against `cost` to judge coverage."
          }
        },
        "type": "object",
        "description": "One day of cloud emissions."
      },
      "FilterOptionResponse": {
        "properties": {
          "accounts": {
            "items": {
              "type": "string"
            },
            "type": "array",
            "description": "Cloud account ids present in your data — valid inputs for the `account` filter."
          },
          "locations": {
            "items": {
              "type": "string"
            },
            "type": "array",
            "description": "Data centre locations present in your data — valid inputs for the `location` filter."
          },
          "providerFilterMappings": {
            "items": {
              "$ref": "#/components/schemas/ProviderFilterMapping"
            },
            "type": "array",
            "description": "Cascading filter mappings. Present since before 1.0.1 but undocumented until 1.1.0."
          },
          "providers": {
            "items": {
              "type": "string"
            },
            "type": "array",
            "description": "Provider values present in your data — valid inputs for the `provider` filter."
          }
        },
        "type": "object",
        "description": "Valid filter values for the cloud endpoints."
      },
      "InstanceRecommendation": {
        "properties": {
          "instance": {
            "$ref": "#/components/schemas/InstanceUsageRecord",
            "description": "The observed instance this recommendation is about."
          },
          "recommendation": {
            "$ref": "#/components/schemas/InstanceRecommendationDetails",
            "description": "The levers that fired for it. Absent fields mean the lever did not fire."
          }
        },
        "type": "object",
        "description": "One observed instance paired with the levers that fired for it."
      },
      "InstanceRecommendationDetails": {
        "properties": {
          "commit": {
            "type": "string",
            "description": "Recommended commitment term, e.g. `1yr-no-upfront`."
          },
          "costBasis": {
            "type": "string",
            "description": "`CATALOG` or `INDEX` — how the move cost delta was priced. Set only when a move fired."
          },
          "costSavings": {
            "format": "double",
            "type": "number",
            "description": "Annualised saving across delete + resize + move for this row."
          },
          "delete": {
            "type": "string",
            "description": "Set when deletion is recommended."
          },
          "modernise": {
            "type": "string",
            "description": "Same-size modern sibling, e.g. `m6g.large`."
          },
          "moderniseArch": {
            "type": "string",
            "description": "Target architecture, e.g. `arm64`. A migration caveat, not a drop-in swap."
          },
          "move": {
            "type": "string",
            "description": "Region code to move to."
          },
          "priority": {
            "type": "string",
            "description": "`CRITICAL`, `HIGH`, `MEDIUM` or `LOW`."
          },
          "priorityScore": {
            "format": "double",
            "type": "number",
            "description": "Dimensionless sort score computed from USD values. Never currency-converted."
          },
          "resize": {
            "type": "string",
            "description": "Recommended smaller instance type."
          },
          "resizeCostBasis": {
            "type": "string",
            "description": "`CATALOG` or `FIXED`. Set only when a resize fired."
          },
          "rule": {
            "type": "string",
            "description": "Identifier of the rule that fired."
          },
          "upsize": {
            "type": "string",
            "description": "Recommended larger instance type."
          }
        },
        "type": "object",
        "description": "The levers that fired for one instance. **Every field is omitted when null**, so absence means the lever did not fire."
      },
      "InstanceUsageRecord": {
        "properties": {
          "account": {
            "type": "string",
            "description": "Cloud account the instance belongs to."
          },
          "awsInstanceType": {
            "type": "string",
            "description": "Provider's own type string."
          },
          "cost": {
            "format": "double",
            "type": "number",
            "description": "USD over the window. Converted when `currency` on the enclosing response is not `USD`."
          },
          "cpuUtilisation": {
            "format": "double",
            "type": "number",
            "description": "Mean CPU utilisation over the window, percent."
          },
          "daysPresent": {
            "format": "int64",
            "type": "integer",
            "description": "Days the instance was seen in the window."
          },
          "embodiedEmissionCo2": {
            "format": "double",
            "type": "number",
            "description": "kg CO₂e."
          },
          "energyConsumptionWh": {
            "format": "double",
            "type": "number",
            "description": "Wh."
          },
          "instanceId": {
            "type": "string",
            "description": "Provider's identifier for the instance."
          },
          "instanceType": {
            "type": "string",
            "description": "Tailpipe's normalised type."
          },
          "location": {
            "type": "string",
            "description": "Provider region code, e.g. `eu-west-2`."
          },
          "operationalEmissionCo2": {
            "format": "double",
            "type": "number",
            "description": "kg CO₂e."
          },
          "provider": {
            "type": "string",
            "description": "Cloud provider the instance runs on."
          }
        },
        "type": "object",
        "description": "The observed instance a recommendation is about."
      },
      "LocationCo2Response": {
        "properties": {
          "cost": {
            "format": "double",
            "type": "number",
            "description": "Unblended cost for the period, USD."
          },
          "embodiedAccel": {
            "format": "double",
            "type": "number",
            "description": "Embodied emissions attributed to accelerators (GPU/other), kg CO₂e."
          },
          "embodiedAssembly": {
            "format": "double",
            "type": "number",
            "description": "Embodied emissions attributed to assembly, kg CO₂e."
          },
          "embodiedCpu": {
            "format": "double",
            "type": "number",
            "description": "Embodied emissions attributed to CPU, kg CO₂e."
          },
          "embodiedEnclosure": {
            "format": "double",
            "type": "number",
            "description": "Embodied emissions attributed to the chassis, kg CO₂e."
          },
          "embodiedHdd": {
            "format": "double",
            "type": "number",
            "description": "Embodied emissions attributed to spinning disk, kg CO₂e."
          },
          "embodiedMotherboard": {
            "format": "double",
            "type": "number",
            "description": "Embodied emissions attributed to the mainboard, kg CO₂e."
          },
          "embodiedNetworkStorage": {
            "format": "double",
            "type": "number",
            "description": "Embodied emissions attributed to network storage, kg CO₂e."
          },
          "embodiedOther": {
            "format": "double",
            "type": "number",
            "description": "Embodied emissions not attributed to a named component, kg CO₂e."
          },
          "embodiedPsu": {
            "format": "double",
            "type": "number",
            "description": "Embodied emissions attributed to the power supply, kg CO₂e."
          },
          "embodiedRam": {
            "format": "double",
            "type": "number",
            "description": "Embodied emissions attributed to RAM, kg CO₂e."
          },
          "embodiedSsd": {
            "format": "double",
            "type": "number",
            "description": "Embodied emissions attributed to SSD, kg CO₂e."
          },
          "embodiedSwitch": {
            "format": "double",
            "type": "number",
            "description": "Embodied emissions attributed to switching, kg CO₂e."
          },
          "location": {
            "type": "string",
            "description": "Provider region code, e.g. `eu-west-2`."
          },
          "operationalAccel": {
            "format": "double",
            "type": "number",
            "description": "Operational emissions attributed to accelerators, kg CO₂e."
          },
          "operationalCpu": {
            "format": "double",
            "type": "number",
            "description": "Operational emissions attributed to CPU, kg CO₂e."
          },
          "operationalExternalInstanceNetwork": {
            "format": "double",
            "type": "number",
            "description": "Operational emissions, instance egress to the internet, kg CO₂e."
          },
          "operationalExternalNetwork": {
            "format": "double",
            "type": "number",
            "description": "Operational emissions, egress to the internet, kg CO₂e."
          },
          "operationalHdd": {
            "format": "double",
            "type": "number",
            "description": "Operational emissions attributed to spinning disk, kg CO₂e."
          },
          "operationalInterInstanceNetwork": {
            "format": "double",
            "type": "number",
            "description": "Operational emissions, inter-instance network traffic, kg CO₂e."
          },
          "operationalInterNetwork": {
            "format": "double",
            "type": "number",
            "description": "Operational emissions, inter-region network traffic, kg CO₂e."
          },
          "operationalIntraInstanaceNetwork": {
            "format": "double",
            "type": "number",
            "description": "Operational emissions, intra-instance network traffic, kg CO₂e. Note the spelling: `Instanace` is a long-standing typo in the column and the field name. It is part of the contract — do not correct it in your client."
          },
          "operationalIntraNetwork": {
            "format": "double",
            "type": "number",
            "description": "Operational emissions, intra-region network traffic, kg CO₂e."
          },
          "operationalMotherboard": {
            "format": "double",
            "type": "number",
            "description": "Operational emissions attributed to the mainboard, kg CO₂e."
          },
          "operationalNetworkStorage": {
            "format": "double",
            "type": "number",
            "description": "Operational emissions attributed to network storage, kg CO₂e."
          },
          "operationalNic": {
            "format": "double",
            "type": "number",
            "description": "Operational emissions attributed to the network interface, kg CO₂e."
          },
          "operationalRam": {
            "format": "double",
            "type": "number",
            "description": "Operational emissions attributed to RAM, kg CO₂e."
          },
          "operationalSsd": {
            "format": "double",
            "type": "number",
            "description": "Operational emissions attributed to SSD, kg CO₂e."
          },
          "powerConsumption": {
            "format": "double",
            "type": "number",
            "description": "Energy attributed to this row, Wh."
          },
          "processedCost": {
            "format": "double",
            "type": "number",
            "description": "Cost of the subset of line items Tailpipe could attribute emissions to, USD. Compare against `cost` to judge coverage."
          }
        },
        "type": "object",
        "description": "Cloud emissions aggregated over the window for one location."
      },
      "ProviderFilterMapping": {
        "properties": {
          "accountLocations": {
            "additionalProperties": {
              "items": {
                "type": "string"
              },
              "type": "array"
            },
            "type": "object",
            "description": "Account id → the locations seen for it."
          },
          "accounts": {
            "items": {
              "type": "string"
            },
            "type": "array",
            "description": "Accounts seen under this provider."
          },
          "provider": {
            "type": "string",
            "description": "Cloud provider name."
          }
        },
        "type": "object",
        "description": "Which accounts belong to a provider, and which locations to each account. Use this to build cascading filters instead of taking the cross product of the flat lists."
      },
      "ProviderPueEntry": {
        "properties": {
          "asOf": {
            "type": "string",
            "description": "Date the figure was published or last confirmed."
          },
          "basis": {
            "type": "string",
            "description": "What the figure covers, e.g. fleet-wide annual average."
          },
          "citation": {
            "type": "string",
            "description": "Where the figure can be checked — document title or URL."
          },
          "provider": {
            "type": "string",
            "description": "Null on the global default entry."
          },
          "pue": {
            "format": "double",
            "type": "number",
            "description": "Power usage effectiveness."
          },
          "region": {
            "type": "string",
            "description": "Serving region. Region keying ships inert — this is null in practice today."
          },
          "source": {
            "type": "string",
            "description": "Publisher of the figure, e.g. a provider sustainability report."
          }
        },
        "type": "object",
        "description": "One PUE figure with its provenance."
      },
      "RecommendationResponse": {
        "properties": {
          "currency": {
            "type": "string",
            "description": "Currency the monetary fields are expressed in. Always `USD` for a `scope=API` token — see the `convertCurrency` parameter."
          },
          "exchangeRate": {
            "format": "double",
            "type": "number",
            "description": "Rate applied to the monetary fields. Always 1.0 for a `scope=API` token."
          },
          "instances": {
            "items": {
              "$ref": "#/components/schemas/InstanceRecommendation"
            },
            "type": "array",
            "description": "Per-instance recommendations (compute)."
          },
          "services": {
            "items": {
              "$ref": "#/components/schemas/ServiceRecommendation"
            },
            "type": "array",
            "description": "Per-service recommendations (non-compute)."
          },
          "summary": {
            "$ref": "#/components/schemas/RecommendationSummary",
            "description": "Totals, reductions and priority counts across all recommendations."
          }
        },
        "type": "object",
        "description": "**A single object, not an array.** Versions of this document before 1.1.0 described the 200 body as an array and omitted `services`, `currency` and `exchangeRate`."
      },
      "RecommendationSummary": {
        "properties": {
          "annualCost": {
            "format": "double",
            "type": "number",
            "description": "Annualised current cost, USD."
          },
          "annualEmission": {
            "format": "double",
            "type": "number",
            "description": "Annualised current emissions, kg CO₂e."
          },
          "co2ReductionPercent": {
            "format": "double",
            "type": "number",
            "description": "Total reduction as a percentage of `annualEmission`."
          },
          "co2SavingsSignificance": {
            "type": "string",
            "description": "Band for `co2ReductionPercent`: `HIGH`, `MEDIUM`, `LOW`, or null when there is no reduction."
          },
          "commitCostReduction": {
            "format": "double",
            "type": "number",
            "description": "Annual cost reduction from commitment recommendations, USD. Catalog-priced only."
          },
          "costReductionPercent": {
            "format": "double",
            "type": "number",
            "description": "Total cost reduction as a percentage of `annualCost`."
          },
          "costSavingsSignificance": {
            "type": "string",
            "description": "Band for `costReductionPercent`: `HIGH`, `MEDIUM`, `LOW`, or null when there is no reduction."
          },
          "criticalCount": {
            "format": "int32",
            "type": "integer",
            "description": "Instance recommendations with priority `CRITICAL`."
          },
          "deleteCo2Reduction": {
            "format": "double",
            "type": "number",
            "description": "Annual reduction from the delete lever, kg CO₂e."
          },
          "endDate": {
            "type": "string",
            "description": "Last day of the observation window, `YYYY-MM-DD`."
          },
          "forecastCost": {
            "format": "double",
            "type": "number",
            "description": "Annualised cost if every recommendation is adopted, USD."
          },
          "forecastEmission": {
            "format": "double",
            "type": "number",
            "description": "Annualised emissions if every recommendation is adopted, kg CO₂e."
          },
          "forwardProjection": {
            "type": "boolean",
            "description": "True when the window was short enough that annual figures are projected rather than observed."
          },
          "highCount": {
            "format": "int32",
            "type": "integer",
            "description": "Instance recommendations with priority `HIGH`."
          },
          "lowCount": {
            "format": "int32",
            "type": "integer",
            "description": "Instance recommendations with priority `LOW`."
          },
          "mediumCount": {
            "format": "int32",
            "type": "integer",
            "description": "Instance recommendations with priority `MEDIUM`."
          },
          "moderniseCostReduction": {
            "format": "double",
            "type": "number",
            "description": "Annual cost reduction from modernisation recommendations, USD. Catalog-priced only."
          },
          "moveCo2Reduction": {
            "format": "double",
            "type": "number",
            "description": "Annual reduction from the instance-move lever, kg CO₂e."
          },
          "moveCostReduction": {
            "format": "double",
            "type": "number",
            "description": "Annual cost reduction from instance moves, USD. May sum `CATALOG`- and `INDEX`-priced rows; the per-row `costBasis` is the disclosure."
          },
          "objective": {
            "type": "string",
            "description": "Which objective produced this response, `carbon` or `cost`. Echoes the request parameter."
          },
          "resizeCo2Reduction": {
            "format": "double",
            "type": "number",
            "description": "Annual reduction from the resize lever, kg CO₂e."
          },
          "serviceMoveCo2Reduction": {
            "format": "double",
            "type": "number",
            "description": "Annual reduction from service moves, kg CO₂e."
          },
          "serviceMoveCostReduction": {
            "format": "double",
            "type": "number",
            "description": "Annual cost reduction from service moves, USD."
          },
          "serviceRecommendationCount": {
            "format": "int32",
            "type": "integer",
            "description": "Service recommendations in `services`."
          },
          "startDate": {
            "type": "string",
            "description": "First day of the observation window, `YYYY-MM-DD`."
          },
          "totalRecommendations": {
            "format": "int32",
            "type": "integer",
            "description": "Instance recommendations in `instances`."
          }
        },
        "type": "object",
        "description": "Versions of this document before 1.1.0 listed only the first ten fields."
      },
      "ServiceRecommendation": {
        "properties": {
          "category": {
            "type": "string",
            "description": "Taxonomy category, e.g. `Compute`, `Storage`."
          },
          "cost": {
            "format": "double",
            "type": "number",
            "description": "USD."
          },
          "costBasis": {
            "type": "string",
            "description": "`CATALOG` or `INDEX`."
          },
          "costSavings": {
            "format": "double",
            "type": "number",
            "description": "Annualised saving, USD."
          },
          "location": {
            "type": "string",
            "description": "Provider region code the service usage is in."
          },
          "move": {
            "type": "string",
            "description": "Region code to move to."
          },
          "priority": {
            "type": "string",
            "description": "`HIGH`, `MEDIUM` or `LOW`."
          },
          "priorityScore": {
            "format": "double",
            "type": "number",
            "description": "Dimensionless sort score computed from USD values. Never currency-converted."
          },
          "provider": {
            "type": "string",
            "description": "Cloud provider name."
          },
          "rule": {
            "type": "string",
            "description": "Identifier of the rule that fired."
          },
          "service": {
            "type": "string",
            "description": "Provider's service name."
          },
          "serviceCode": {
            "type": "string",
            "description": "Provider's service code, e.g. `AmazonS3`."
          },
          "totalEmission": {
            "format": "double",
            "type": "number",
            "description": "kg CO₂e."
          }
        },
        "type": "object",
        "description": "A non-compute service recommendation. `move`, `costSavings`, `priorityScore` and `costBasis` are omitted when null."
      },
      "SpendMapResponse": {
        "properties": {
          "category": {
            "type": "string",
            "description": "Taxonomy category, e.g. `Compute`."
          },
          "cost": {
            "format": "double",
            "type": "number",
            "description": "USD."
          },
          "provider": {
            "type": "string",
            "description": "Cloud provider name."
          },
          "service": {
            "type": "string",
            "description": "Provider's service name."
          },
          "totalEmissions": {
            "format": "double",
            "type": "number",
            "description": "Total emissions for the service, kg CO₂e. Note the plural `totalEmissions` — the cloud recommendations and every AI endpoint use the singular `totalEmission`."
          }
        },
        "type": "object",
        "description": "Cost and emissions grouped by service, for a treemap."
      },
      "UsageCoverageResponse": {
        "properties": {
          "financial": {
            "additionalProperties": {
              "format": "double",
              "type": "number"
            },
            "type": "object",
            "description": "Provider financial adjustments (credits, refunds, tax), USD."
          },
          "inScope": {
            "additionalProperties": {
              "format": "double",
              "type": "number"
            },
            "type": "object",
            "description": "Service name → USD spend that Tailpipe attributes emissions to."
          },
          "outOfScope": {
            "additionalProperties": {
              "format": "double",
              "type": "number"
            },
            "type": "object",
            "description": "Service name → USD spend outside the methodology."
          }
        },
        "type": "object",
        "description": "Spend split by whether Tailpipe's methodology covers it. Each map is keyed by service name with a USD amount as the value. The keys `inScope` and `outOfScope` are the wire contract."
      },
      "ErrorResponse": {
        "type": "object",
        "description": "Error body returned by Tailpipe's own handlers. **Not every error uses this shape** — see the description on each response.",
        "properties": {
          "error": {
            "type": [
              "string",
              "null"
            ],
            "description": "e.g. `400 BAD_REQUEST`."
          },
          "message": {
            "type": [
              "string",
              "null"
            ],
            "description": "What was wrong with the request, e.g. the objective validation text."
          }
        }
      },
      "TokenResponse": {
        "type": "object",
        "properties": {
          "access_token": {
            "type": "string",
            "description": "The JWT to send as `Authorization: Bearer <token>`."
          },
          "token_type": {
            "type": "string",
            "description": "Always `Bearer`."
          },
          "expires_in": {
            "type": "integer",
            "description": "Lifetime in seconds. 1800 (30 minutes)."
          },
          "scope": {
            "type": "string",
            "description": "Granted scope. `API`."
          }
        }
      },
      "OAuthError": {
        "type": "object",
        "description": "Standard OAuth2 error body from the authorization server.",
        "properties": {
          "error": {
            "type": "string",
            "description": "e.g. `invalid_client`, `invalid_scope`."
          },
          "error_description": {
            "type": [
              "string",
              "null"
            ],
            "description": "Human-readable detail for the error code, when the server provides one."
          }
        }
      }
    }
  },
  "security": [
    {
      "bearerAuth": []
    }
  ]
}
