AI spend and cloud cost connectors
Spend connectors read what vendors billed, so Shield360 can compare it with the estimate it builds from observed usage. Every connector here is read-only: it reads cost, seat and key data, and never changes anything at the vendor.
There are two kinds:
- AI spend connectors read AI vendors’ bills: pay-per-token API costs (OpenAI, Anthropic, Azure OpenAI, Amazon Bedrock) and per-seat tools (GitHub Copilot, Cursor, ChatGPT Enterprise). They feed the spend report, Costs → Actual vs estimate, Costs → API keys and the subscriptions the connectors keep for you.
- Cloud cost connectors (AWS Cost Explorer, Azure Cost Management and the Google Cloud billing export) read the whole cloud bill for every service. Their rows are kept apart from every AI figure and appear only on Costs → Cloud.
Add a spend connector
Section titled “Add a spend connector”Open Connectors (/connectors) and select the project and environment the costs belong to. Spend connectors belong to a project and write to that environment’s database.
Select Add connector, then choose a type from the AI spend category.
Follow the section for your vendor below. Each secret field also has a … from the Vault (optional) field: name a Vault entry there instead of pasting the secret, and Shield360 reads the secret from the Vault at every sync.
Save the connector, then use its test button. The test is a real sync: it reads the vendor now and reports what it found, for example Synced $1,234.56 over 31 days, 42 seats. or the vendor’s error.
These settings are common to every spend connector:
| Field | What it does |
|---|---|
| Days to read back | 7, 31 (default) or 90. Each sync re-reads this many days, so late corrections from the vendor are picked up. Days older than that keep what earlier syncs found. |
| Import full history on the next sync | On by default for connectors that report history. The next sync reads everything the vendor keeps, once, then the switch turns itself off. Turn it on again to re-import. Seat connectors (GitHub Copilot, Cursor, ChatGPT Enterprise) don’t have it, because vendors only report today’s seat holders. |
| Sync every | 6 hours or Day (default). Vendors publish costs daily. A failed sync is retried within the hour instead of waiting the full interval. |
To change a secret later, type the new value. Leaving a secret field blank when you edit a connector keeps the stored secret.
OpenAI API costs
Section titled “OpenAI API costs”Reads daily OpenAI API costs from OpenAI’s Admin API, grouped by project, line item and API key. It also lists every project’s API keys, with each key’s name, owner and redacted hint, so Costs → API keys can match billed keys to the machines seen using them. Costs per API key are only available from a date OpenAI sets; earlier costs are imported without a key.
What you’ll need
- An OpenAI organisation owner, to create the admin key.
- An OpenAI admin key (not an ordinary project API key).
Shield360 calls these endpoints on https://api.openai.com, all with GET:
| Endpoint | Used for |
|---|---|
/v1/organization/costs | Daily costs by project, line item and API key. |
/v1/organization/projects | Project names. |
/v1/organization/projects/{project_id}/api_keys | Each project’s keys, for key attribution. |
OpenAI admin key
Section titled “OpenAI admin key”Sign in to the OpenAI platform as an organisation owner and create an admin key under your organisation’s settings (admin keys are separate from project API keys). See OpenAI’s Administration API reference and Usage and Costs API.
Copy the key when it’s shown. You can’t view it again.
Add an OpenAI API costs connector and paste the key into Admin key, or store it in the Vault and name the entry in Admin key from the Vault (optional).
Troubleshooting
| Message | Cause and fix |
|---|---|
Set the OpenAI admin key. | No key was given and the Vault entry is empty. Paste the key or fix the Vault entry. |
Reading OpenAI costs failed (needs an admin key): HTTP 401: … | The key is wrong, revoked, or an ordinary project key. Create an admin key. |
Reading OpenAI costs failed (needs an admin key): HTTP 403: … | The key can’t read organisation costs. Create the key as an organisation owner. |
Reading OpenAI costs failed (needs an admin key): HTTP 429: … | OpenAI’s rate limit for the Admin API. Shield360 paces its requests and retries once; if it still fails, the next sync retries within the hour. |
| Costs appear but Costs → API keys is empty | Listing projects or keys failed. Costs are still correct; check that the key belongs to the organisation that owns the projects. |
Anthropic API costs
Section titled “Anthropic API costs”Reads daily Anthropic API costs from the Admin API cost report, by workspace and model. Anthropic reports cost per workspace and model only, so Shield360 also reads token usage per API key and divides each day’s cost for a model between the keys that used it. The total still matches what Anthropic charged. It also lists the organisation’s API keys and workspaces.
What you’ll need
- An Anthropic Console organisation with the Admin API (API organisations only).
- A member with the admin role, to create the key.
- An Admin API key, starting
sk-ant-admin.
Shield360 calls these endpoints on https://api.anthropic.com, all with GET:
| Endpoint | Used for |
|---|---|
/v1/organizations/cost_report | Daily costs by workspace and model. |
/v1/organizations/usage_report/messages | Tokens per API key and model, to divide each day’s cost between keys. |
/v1/organizations/api_keys | The organisation’s keys, for key attribution. |
/v1/organizations/workspaces | Workspace names. |
Anthropic admin key
Section titled “Anthropic admin key”In the Anthropic Console, as an organisation admin, create an Admin API key under your organisation’s settings. Admin keys start sk-ant-admin and are separate from ordinary API keys. See Anthropic’s Admin API overview and Usage and Cost API.
Copy the key when it’s shown. You can’t view it again.
Add an Anthropic API costs connector and paste the key into Admin key, or name a Vault entry in Admin key from the Vault (optional).
Troubleshooting
| Message | Cause and fix |
|---|---|
Set the Anthropic admin key. | No key was given and the Vault entry is empty. |
Reading the Anthropic cost report failed (needs an admin key): HTTP 401: … | The key is wrong, revoked, or an ordinary API key. Use a key starting sk-ant-admin. |
Reading the Anthropic cost report failed (needs an admin key): HTTP 403: … | The organisation can’t use the Admin API, for example an individual account. |
Reading the Anthropic cost report failed (needs an admin key): HTTP 429: … | Rate limited. The next sync retries within the hour. |
| Costs appear but aren’t split by key | Reading token usage failed. Costs are still correct; they stay unattributed until usage can be read. |
Claude Enterprise spend limits and costs
Section titled “Claude Enterprise spend limits and costs”Reads every Claude Enterprise member’s effective monthly spend limit, where it comes from (set for them, their seat tier, their group, or the organisation default), and what they have spent against it this month. Nothing is entered by hand. When an administrator raises someone from $500 to $750 in claude.ai, the next sync records the change. It also adds an audit log entry and raises an alert. Limits are shown under Costs → Budgets → Limits set in vendors.
With an optional Analytics API key, the connector also reads each member’s daily costs, broken out by product (chat, Claude Code, Cowork and others) and model. Without one it reads limits only, as before.
What you’ll need
- A Claude Enterprise organisation with usage credits turned on (the primary owner turns them on in claude.ai billing settings).
- The primary owner, to create the keys.
- A Claude Enterprise Admin API key with the
read:spend_limitsscope. - Optional, for costs: a Claude Enterprise Analytics API key with the
read:analyticsscope.
Shield360 calls these endpoints on https://api.anthropic.com, page by page:
| Endpoint | Key | What it reads |
|---|---|---|
GET /v1/organizations/spend_limits/effective | Admin API key (read:spend_limits) | Each member’s limit, where it comes from, and their spend this month |
GET /v1/organizations/analytics/user_cost_report | Analytics API key (read:analytics) | Each member’s cost per day, grouped by product and model (bucket_width=1d) |
Amounts arrive in cents and are shown in dollars. An amount of No limit means the member has no cap, and $0 means nothing beyond what their plan includes.
How costs are read
Section titled “How costs are read”- Costs exist from 1 January 2026, and Anthropic serves at most the last 365 days. The first sync with an Analytics API key imports everything it can. After that, each sync re-reads the days set in Days to read back.
- Each member’s costs are stored per day as Claude Enterprise, with the product as the project and the model as the line item. The person is the member’s email address in lower case. Deleted accounts and service accounts have no email, so they’re stored under Anthropic’s user ID.
- Anthropic usually has costs ready within 4 hours, and always within 24. Every response says how current its data is (
data_refreshed_at). Shield360 stores only whole days up to that point, so a partly reported day is never saved. The next sync picks it up. - Anthropic can revise a month’s costs until about 7 days after the month ends. Keep Days to read back at 31 or more so those corrections are picked up.
- The Analytics API allows 60 requests a minute for the whole organisation, shared with anything else that uses it. Shield360 spaces its requests and waits when it is rate limited.
- If reading costs fails, the limits are still read, the sync reports
costs not read: …, and the costs already stored stay. If reading limits fails, the costs are still read and the sync reportsspend limits not read: …. The sync fails only when both fail.
Where the costs count
The amount is what Anthropic charges for usage after discounts and before credits. On a usage-based plan that’s the usage bill. On a seat-based plan it’s usage credits only, because seats are billed separately and Shield360 counts them as subscriptions. The costs count as billed API use in the AI cost total and the ledger, on the Anthropic line.
They don’t replace Shield360’s estimate for Anthropic API usage. The Enterprise bill covers claude.ai use, which the collector already reports as plan usage, outside the total. It doesn’t cover usage on Console API keys. For the same reason, they’re left out of the reconciliation and of per-key attribution. They show on each person’s page.
Claude Enterprise admin key
Section titled “Claude Enterprise admin key”As the primary owner, create an Admin API key for your Claude Enterprise organisation and give it the read:spend_limits scope only. See Anthropic’s Spend Limits API and Admin API keys.
Add a Claude Enterprise connector and paste the key into Admin key, or name a Vault entry in Admin key from the Vault (optional). Choose how often to sync. A change in claude.ai shows up within that time.
Claude Enterprise analytics key
Section titled “Claude Enterprise analytics key”As the primary owner, go to claude.ai → Organization settings → API and turn on public API access. See Anthropic’s Analytics APIs.
Create an Analytics API key. It has the read:analytics scope. Copy it when it’s shown.
In the Claude Enterprise connector, paste the key into Analytics API key (optional), or name a Vault entry in Analytics API key from the Vault (optional). Leave Import full history on the next sync on to import costs from 1 January 2026.
| Message | Cause and fix |
|---|---|
Reading Claude Enterprise spend limits failed (… read:spend_limits …): HTTP 401 | The admin key is wrong or revoked. Create a new one. |
… HTTP 403 | The key lacks the read:spend_limits scope, or the organisation isn’t on Claude Enterprise. |
… HTTP 400 or … HTTP 404 | Usage credits aren’t turned on for the organisation. |
costs not read: Reading Claude Enterprise costs failed (… read:analytics …): HTTP 401 | The Analytics API key is wrong or revoked, or an Admin API key was pasted instead. |
costs not read: … HTTP 429 | The organisation’s 60 requests a minute were used up, possibly by another tool. The next sync tries again. |
GitHub Copilot
Section titled “GitHub Copilot”Reads the organisation’s Copilot seats: who holds each seat, its plan, when it was assigned, and when it was last used. Seat cost comes from the seat price. On every sync, Shield360 keeps a matching subscription in step with the seat list.
What you’ll need
- The GitHub organisation name (the login in
github.com/<org>). - A GitHub organisation owner, to create or approve the token.
- A token that can read Copilot billing for that organisation.
- Your negotiated seat price, if it isn’t the list price.
Shield360 calls one endpoint on https://api.github.com: GET /orgs/{org}/copilot/billing/seats.
When Enterprise is set, it also reads the enterprise’s billing budgets with GET /enterprises/{enterprise}/settings/billing/budgets. These include enterprise, organisation, repository, cost centre and per-user budgets, such as a person’s monthly Copilot premium-request budget, each with what it has consumed. They’re shown under Limits set in vendors. Reading budgets needs an enterprise owner or billing manager’s token. If it fails, the sync still records seats and reports spend limits not read: ….
GitHub Copilot token
Section titled “GitHub Copilot token”Create one of these, as or with the approval of an organisation owner:
- A fine-grained personal access token whose resource owner is the organisation, with the organisation permission GitHub Copilot Business set to Read-only. Fine-grained tokens can be limited to this one permission, so prefer them.
- A classic personal access token with the
manage_billing:copilotscope.
See GitHub’s reference for listing Copilot seats for the permissions it accepts, and managing personal access tokens.
Organisations can require owners to approve fine-grained tokens before they work. Approve the request in the organisation’s settings.
Add a GitHub Copilot connector and fill in:
| Field | Value |
|---|---|
| GitHub organisation | The organisation login, for example contoso. |
| Price per seat per month (USD) | Leave blank for the list price (Business $19, Enterprise $39). Set your negotiated price so seat cost matches your invoice. |
| Token | The token, or name a Vault entry in Token from the Vault (optional). |
Set an expiry on the token and a reminder to replace it before it expires.
Troubleshooting
| Message | Cause and fix |
|---|---|
Set the GitHub organisation. or That is not a GitHub organisation name. | Enter the organisation’s login (letters, digits and hyphens), not its display name or URL. |
Reading Copilot seats failed (the token needs Copilot billing read access): HTTP 401: … | The token is wrong or expired. Create a new one. |
… HTTP 403: … | The token lacks the permission, is awaiting owner approval, or its owner is not an organisation owner. |
… HTTP 404: … | The organisation name is wrong, or a fine-grained token’s resource owner is a different account. |
… HTTP 422: … | Copilot Business or Enterprise isn’t enabled for the organisation, or seats are managed somewhere this endpoint can’t read. |
Cursor
Section titled “Cursor”Reads the Cursor team’s members as seats, each member’s last active day, and usage billed beyond the included requests for the current billing cycle, per member. On every sync, Shield360 keeps a matching subscription in step with the member list.
What you’ll need
- A Cursor team on a plan with the Admin API.
- A Cursor team admin, to create the key.
- A Cursor Admin API key.
- Your negotiated seat price, if it isn’t the list price.
Shield360 calls these endpoints on https://api.cursor.com, sending the key as the HTTP basic-auth user name:
| Endpoint | Used for |
|---|---|
GET /teams/members | Team members (seats). |
POST /teams/spend | Usage beyond the included requests, per member, for the billing cycle. |
POST /teams/daily-usage-data | Each member’s last active day. |
The same POST /teams/spend response carries each member’s effective monthly limit and whether an administrator set it for them. Shield360 shows these under Limits set in vendors and records each change, so a limit raised in Cursor needs no re-entry.
Cursor admin API key
Section titled “Cursor admin API key”As a team admin, open the Cursor dashboard’s team settings and create an Admin API key (the app labels it Team settings → Advanced → Admin API keys). See Cursor’s Admin API documentation.
Add a Cursor connector and fill in:
| Field | Value |
|---|---|
| Price per seat per month (USD) | Leave blank for the list price ($40 for Business). Set your negotiated price to match your invoice. |
| Admin API key | The key, or name a Vault entry in Admin API key from the Vault (optional). |
Troubleshooting
| Message | Cause and fix |
|---|---|
Set the Cursor admin API key. | No key was given and the Vault entry is empty. |
Reading Cursor members failed (needs an admin API key): HTTP 401: … | The key is wrong or revoked, or it isn’t an Admin API key. |
Reading Cursor spend failed (needs an admin API key): HTTP 403: … | The key can’t read team spend. Create it as a team admin. |
| Seats appear with no last active day | Daily usage couldn’t be read, or the member wasn’t active in the window. Seats and spend are still correct. |
Microsoft 365 Copilot
Section titled “Microsoft 365 Copilot”Microsoft 365 Copilot is an AI spend connector too: its licences become seats and a kept subscription, as here. It also reads use by app and, if you switch them on, audited interactions, so it has its own guide: Microsoft 365 Copilot connector.
ChatGPT Enterprise
Section titled “ChatGPT Enterprise”Reads the ChatGPT Enterprise workspace’s active users as seats, through OpenAI’s Enterprise Compliance API. ChatGPT Enterprise bills per seat, so cost comes from the seat price. On every sync, Shield360 keeps a matching subscription in step with the user list.
What you’ll need
- A ChatGPT Enterprise or Edu workspace.
- The Compliance API enabled for the workspace (OpenAI does this on request).
- The workspace id.
- A Compliance API key.
- Your contracted seat price.
Shield360 calls one endpoint on https://api.chatgpt.com: GET /v1/compliance/workspaces/{workspace_id}/users.
ChatGPT Enterprise compliance API key
Section titled “ChatGPT Enterprise compliance API key”The Compliance API is enabled per workspace by OpenAI. Ask your OpenAI account team, or search the OpenAI Help Center for the Compliance API for Enterprise.
Once it’s enabled, a workspace owner creates a Compliance API key as OpenAI’s instructions describe, and notes the workspace id. Copy the key when it’s shown.
Add a ChatGPT Enterprise connector and fill in:
| Field | Value |
|---|---|
| Workspace id | The workspace id (letters, digits, _ and -). |
| Price per seat per month (USD) | Your contracted price. Enterprise is priced by contract; the default is $60. |
| Compliance API key | The key, or name a Vault entry in Compliance API key from the Vault (optional). |
Troubleshooting
| Message | Cause and fix |
|---|---|
Set the ChatGPT workspace id. or That is not a ChatGPT workspace id. | Enter the workspace id itself, not a URL or name. |
Set the workspace id and the Compliance API key. | The key is missing or the Vault entry is empty. |
Reading ChatGPT Enterprise users failed (needs the Compliance API): HTTP 401: … | The key is wrong or revoked. |
… HTTP 403: … | The Compliance API isn’t enabled for this workspace, or the key belongs to another workspace. |
… HTTP 404: … | The workspace id is wrong. |
Azure OpenAI
Section titled “Azure OpenAI”Reads daily Azure OpenAI and Azure AI Foundry costs from Azure Cost Management, as actual cost, by resource group and meter. It keeps only these services: Azure OpenAI, Cognitive Services, Foundry Models, Azure AI Foundry Models and Azure AI services. Full history is read a year at a time, going back until Cost Management has nothing more.
What you’ll need
- A Microsoft Entra app registration with a client secret.
- The Cost Management Reader role for the app on each subscription.
- The directory (tenant) id and application (client) id.
- The ids of the subscriptions that hold your Azure OpenAI resources.
Shield360 makes two kinds of request:
| Request | Used for |
|---|---|
POST https://login.microsoftonline.com/{tenant}/oauth2/v2.0/token | Client-credentials sign-in, with the scope https://management.azure.com/.default. |
POST https://management.azure.com/subscriptions/{subscription}/providers/Microsoft.CostManagement/query?api-version=2023-11-01 | The cost query, once per subscription per year of history. |
Azure OpenAI app registration and role
Section titled “Azure OpenAI app registration and role”In the Azure portal, open Microsoft Entra ID → App registrations → New registration. Give it a name such as shield360-cost-reader, keep it single-tenant and leave the redirect URI empty. See Register an application.
On the app’s Overview page, copy the Directory (tenant) ID and the Application (client) ID. Subscription ids are on the Subscriptions page.
Open Certificates & secrets → Client secrets → New client secret and choose an expiry. Copy the secret’s Value (not its ID) when it’s shown; you can’t see it again. Note the expiry date: the connector stops working when the secret expires.
For each subscription, open Access control (IAM) → Add role assignment, choose Cost Management Reader, and assign it to the app. Assign it at subscription scope; it grants read access to cost data and nothing else. See Assign Azure roles using the Azure portal and Cost Management access.
With the Azure CLI, one command does the app, secret and role together for one subscription:
az ad sp create-for-rbac --name shield360-cost-reader \ --role "Cost Management Reader" \ --scopes /subscriptions/<subscription-id>Its output’s appId is the client id, password the client secret and tenant the tenant id. Run az role assignment create for each additional subscription.
Add an Azure OpenAI (Cost Management) connector and fill in:
| Field | Value |
|---|---|
| Directory (tenant) id | The tenant id. |
| Application (client) id | The client id. |
| Subscription ids | Comma separated, for example 0b1c…, 7d2e…. Each must be a full 36-character subscription id. |
| Client secret | The secret’s value, or name a Vault entry in Client secret from the Vault (optional). |
Role assignments can take a few minutes to apply. Azure troubleshooting is shared with Azure Cost Management; see Azure troubleshooting.
Amazon Bedrock
Section titled “Amazon Bedrock”Reads daily Amazon Bedrock costs from AWS Cost Explorer, as unblended cost, by service and usage type. It keeps every service with “Bedrock” in its name, which includes models bought through AWS Marketplace (for example “Claude … (Amazon Bedrock Edition)”).
What you’ll need
- Cost Explorer enabled for the AWS account.
- An IAM user with an access key, allowed
ce:GetCostAndUsageand, optionally,ce:GetDimensionValues. - Ideally, that user in the management (payer) account, to see all accounts.
Shield360 calls two AWS API actions, signed with Signature Version 4 against https://ce.us-east-1.amazonaws.com (Cost Explorer’s single endpoint):
ce:GetDimensionValues(optional) looks up the Cost Explorer services with “Bedrock” in their name for the window, so the cost query asks for those services only.ce:GetCostAndUsagereads the daily costs, filtered to those services.
If the key isn’t allowed ce:GetDimensionValues, the sync still works: Shield360 reads the costs of every service and keeps the Bedrock ones, which can take more pages.
Amazon Bedrock IAM policy
Section titled “Amazon Bedrock IAM policy”If nobody has opened Cost Explorer in the account before, open it once in the AWS Billing and Cost Management console to enable it. Data can take up to 24 hours to appear. See Enabling Cost Explorer.
Create an IAM policy with this content. Cost Explorer doesn’t support resource-level permissions, so the resource is *. ce:GetDimensionValues is optional; leave it out and the connector reads every service’s costs and keeps the Bedrock ones.
{ "Version": "2012-10-17", "Statement": [ { "Sid": "Shield360CostExplorerRead", "Effect": "Allow", "Action": ["ce:GetCostAndUsage", "ce:GetDimensionValues"], "Resource": "*" } ]}Create an IAM user with no console access, attach the policy, and create an access key for it (use case: application running outside AWS). Create it in the management account of your AWS Organization to see every member account’s costs; in a member account it sees that account only. See Managing access keys for IAM users.
Temporary credentials from an assumed role or IAM Identity Center need a session token, which the connector doesn’t take. Use the IAM user’s long-term key, and rotate it on a schedule.
Add an Amazon Bedrock (Cost Explorer) connector and fill in:
| Field | Value |
|---|---|
| Access key id | The access key id, starting AKIA. |
| Secret access key | The secret, or name a Vault entry in Secret access key from the Vault (optional). |
Cost Explorer keeps 14 months of history, or 38 months when multi-year data is turned on in Cost Explorer’s preferences. The full-history import starts as early as the account allows and steps forward automatically if AWS refuses the range. AWS troubleshooting is shared with AWS Cost Explorer; see AWS troubleshooting.
AWS Cost Explorer
Section titled “AWS Cost Explorer”Reads daily AWS costs for every service from Cost Explorer, as unblended cost, grouped by service and by a cost allocation tag you name, or by linked account when no tag is set. Shown on Costs → Cloud and kept out of the AI figures.
What you’ll need
- Cost Explorer enabled for the AWS account.
- An IAM user with an access key, allowed
ce:GetCostAndUsageonly. - That user in the management (payer) account, to see every account.
- Optionally, an activated cost allocation tag that names the project or team.
Shield360 calls one AWS API action: ce:GetCostAndUsage, against https://ce.us-east-1.amazonaws.com. It doesn’t call GetDimensionValues, GetTags or any other action, so it needs no other permission.
To follow the account’s AWS Budgets as well, set AWS account id (for budgets) and allow budgets:ViewBudget. Both AWS connectors then call DescribeBudgets and show each cost budget, with its actual spend, under Limits set in vendors. Leave the account id blank to skip budgets.
AWS IAM policy
Section titled “AWS IAM policy”Follow the Amazon Bedrock IAM policy steps. This connector needs only ce:GetCostAndUsage, so you can leave ce:GetDimensionValues out of the policy, and one IAM user can serve both connectors. Create it in the management account so the bill covers every linked account. Then add an AWS (Cost Explorer, all services) connector and fill in Access key id and Secret access key (or Secret access key from the Vault (optional)).
AWS cost allocation tag
Section titled “AWS cost allocation tag”The Cost allocation tag field names the tag that says which project or team a resource belongs to, for example Project. Leave it blank to group by linked account instead.
Use one tag key consistently across resources, for example Project=payments.
In the management account, open Billing and Cost Management → Cost allocation tags, find the user-defined tag and activate it. Activated tags can take up to 24 hours to appear, and apply from activation onward. See Activating user-defined cost allocation tags.
Type the tag key exactly as it’s spelled in AWS (it’s case-sensitive) into Cost allocation tag.
Costs on resources without the tag, and costs from before the tag was activated, are shown as (untagged).
AWS troubleshooting
Section titled “AWS troubleshooting”| Message | Cause and fix |
|---|---|
Set the AWS access key id. | The access key id is missing or malformed. Paste the id (upper-case letters and digits, starting AKIA). |
Set the access key id and secret access key. | The secret is missing or the Vault entry is empty. |
The tag key has characters a tag cannot have. | Check the Cost allocation tag spelling. |
AWS Cost Explorer failed (needs ce:GetCostAndUsage): HTTP 400: … AccessDeniedException … | The user’s policy doesn’t allow ce:GetCostAndUsage, or an organisation policy (SCP) denies it. Attach the policy above. A Bedrock key without ce:GetDimensionValues doesn’t cause this error; the connector falls back to reading every service. |
… UnrecognizedClientException … or … InvalidClientTokenId … | The access key id is wrong or deleted. |
… InvalidSignatureException … or … SignatureDoesNotMatch … | The secret access key doesn’t match the key id. |
… DataUnavailableException … | Cost Explorer isn’t enabled yet, or has no data for the account. Enable it and wait up to 24 hours. |
… LimitExceededException … | Too many Cost Explorer requests. The next sync retries within the hour. |
Everything is (untagged) | The tag isn’t activated as a cost allocation tag, or the key is spelled differently. |
| Only one account’s costs | The key belongs to a member account. Create the user in the management account. |
A ValidationException about the time period during the full-history import is handled automatically: Shield360 retries with the latest start date AWS keeps.
Azure Cost Management
Section titled “Azure Cost Management”Reads daily Azure costs for every service from Cost Management, as actual cost, grouped by service and by a tag you name, or by resource group when no tag is set. Shown on Costs → Cloud and kept out of the AI figures.
What you’ll need
- A Microsoft Entra app registration with a client secret.
- The Cost Management Reader role for the app on each subscription.
- The directory (tenant) id and application (client) id.
- The subscription ids to read.
- Optionally, a resource tag key that names the project or team.
Shield360 makes the same two requests as the Azure OpenAI connector: a client-credentials token from login.microsoftonline.com, then a Microsoft.CostManagement/query per subscription, a year of history at a time.
Both Azure connectors also read each subscription’s cost budgets, with their current spend, from Microsoft.Consumption/budgets. The Cost Management Reader role already allows this. Budgets are shown under Limits set in vendors. Turn off Read budgets to skip them.
Azure app registration and role
Section titled “Azure app registration and role”Follow the Azure OpenAI app registration and role steps; one app can serve both connectors. Assign Cost Management Reader on every subscription you list. Then add an Azure (Cost Management, all services) connector and fill in:
| Field | Value |
|---|---|
| Directory (tenant) id | The tenant id from the app’s Overview page. |
| Application (client) id | The client id from the app’s Overview page. |
| Subscription ids | Comma separated, full 36-character ids. |
| Tag | The resource tag key that names the project or team, for example project. Leave blank to group by resource group. Costs on untagged resources are shown as (untagged). |
| Client secret | The secret’s value, or name a Vault entry in Client secret from the Vault (optional). |
Azure troubleshooting
Section titled “Azure troubleshooting”These apply to both Azure connectors.
| Message | Cause and fix |
|---|---|
Set the directory (tenant) id and the application (client) id. | Fill in both ids. |
Give one or more Azure subscription ids, comma separated. | Each id must be a full subscription id (36 characters with hyphens), not a subscription name. |
Set the tenant id, client id and client secret. | The client secret is missing or the Vault entry is empty. |
Azure sign-in failed: HTTP 400: … AADSTS90002 … | The tenant id is wrong. |
Azure sign-in failed: HTTP 400: … AADSTS700016 … | The client id is wrong, or the app is in another tenant. |
Azure sign-in failed: HTTP 401: … AADSTS7000215 … | The client secret is wrong. Paste the secret’s Value, not its Secret ID. |
Azure sign-in failed: … AADSTS7000222 … | The client secret has expired. Create a new one and update the connector. |
Azure Cost Management failed for <subscription> (needs Cost Management Reader): HTTP 401: … or HTTP 403: … | The app has no Cost Management Reader role on that subscription, or the assignment hasn’t applied yet. Assign it and wait a few minutes. |
Azure Cost Management failed for <subscription> …: HTTP 429: … | Cost Management is throttling queries. The next sync retries within the hour. |
Azure Cost Management failed for <subscription> …: HTTP 400: … | The subscription type may not be supported by Cost Management. See Understand Cost Management data. |
Google Cloud billing export
Section titled “Google Cloud billing export”Reads daily Google Cloud costs for every service, including Vertex AI and Gemini, from the Cloud Billing export in BigQuery. Costs are grouped by service and by a resource label you name, or by Google Cloud project when no label is set, with credits applied so the figure matches the invoice. Shown on Costs → Cloud and kept out of the AI figures.
Google Cloud has no cost API of its own; the BigQuery export is the supported source. The export starts when it’s turned on, so earlier days are absent.
What you’ll need
- Cloud Billing export to BigQuery, standard usage cost, turned on.
- The export table’s full name, as
project.dataset.table. - A service account with a JSON key.
- BigQuery Job User on the table’s project, Data Viewer on the dataset.
Shield360 signs in as the service account with the read-only scope https://www.googleapis.com/auth/bigquery.readonly, then calls:
| Request | Used for |
|---|---|
POST https://bigquery.googleapis.com/bigquery/v2/projects/{project}/queries | Runs one read-only SELECT over the export table for the window. |
GET https://bigquery.googleapis.com/bigquery/v2/projects/{project}/queries/{job_id} | Waits for a query BigQuery hasn’t finished within its 60-second wait (up to 10 more waits), then reads further pages of results. |
{project} is the project named first in the table path, so the query job runs and is billed there.
Google Cloud billing export table
Section titled “Google Cloud billing export table”A billing account administrator opens Billing → Billing export → BigQuery export and turns on Standard usage cost export to a dataset. See Set up Cloud Billing data export to BigQuery.
Google creates a table named gcp_billing_export_v1_<billing account id> in that dataset once data arrives, which can take a few hours. Its full name is <project id>.<dataset>.<table>, for example acme-billing.billing.gcp_billing_export_v1_0123AB_4567CD_89EF01.
Google Cloud service account
Section titled “Google Cloud service account”Create a service account, for example shield360-billing-reader, in the project that holds the export dataset. See Create service accounts.
Grant BigQuery Job User (roles/bigquery.jobUser) on the project named first in the table path. Shield360 runs its query there, so this is the project that needs the role, whichever project the service account lives in.
Grant BigQuery Data Viewer (roles/bigquery.dataViewer) on the export dataset only, not the whole project. See Control access to datasets.
Create a JSON key for the service account and download it. If your organisation policy blocks service account keys, an administrator needs to allow keys for this account. See Create and delete service account keys.
Add a Google Cloud (billing export) connector and fill in:
| Field | Value |
|---|---|
| Billing export table | The table as project.dataset.table. The project id must be the plain id (lower-case letters, digits and hyphens), not a domain-scoped id. |
| Label | The resource label key that names the project or team, for example team. Leave blank to group by Google Cloud project. Resources without the label are shown as (unlabelled). |
| Service account key (JSON) | The whole contents of the JSON key file, or name a Vault entry in Service account key from the Vault (optional). |
Delete the downloaded key file once it’s stored.
Troubleshooting
| Message | Cause and fix |
|---|---|
Set the billing export table as project.dataset.table. | Enter the full three-part name, without backticks or a domain prefix. |
A Google Cloud label key is lower case letters, digits, hyphens and underscores. | Check the Label spelling; label keys start with a lower-case letter. |
The service account key is not the JSON key file's contents. | Paste the whole file, from { to }. |
The service account key needs client_email and private_key. | The JSON isn’t a service account key. Create a JSON key for the service account. |
Google sign-in failed for <account>: … | The key was deleted or disabled, or the service account was removed. Create a new key. |
Reading the billing export failed (needs BigQuery Job User and Data Viewer): HTTP 403: … bigquery.jobs.create … | Grant BigQuery Job User on the project named in the table path. |
… HTTP 403: Access Denied: Table … | Grant BigQuery Data Viewer on the export dataset. |
… HTTP 403: … BigQuery API has not been used … | Enable the BigQuery API in the project named in the table path. |
… HTTP 404: Not found: Table … or … Dataset … | The table name is wrong, or the export hasn’t written its first data yet. |
… The request timed out. | A large export took too long for the first read. Run the test again; BigQuery usually answers a repeated query from its cache. |
BigQuery: … | BigQuery rejected the query. The message says why, for example a table that isn’t a standard usage cost export. |
BigQuery did not finish the billing export query (job <id>) in 11 minutes; nothing was recorded. … | The query was still running after every wait. The connector is marked as failed and no costs are written for the window. Run the sync again (BigQuery often answers a repeated query from its cache), or lower Days to read back so each sync reads fewer days. |
BigQuery did not finish the billing export query and returned no job to wait on; nothing was recorded. | BigQuery returned an unfinished result with no job to poll. Run the sync again. |
LiteLLM budgets
Section titled “LiteLLM budgets”Reads the max budget on each LiteLLM virtual key, user and team that has one, how often it resets, and the spend LiteLLM counts against it. Shown under Limits set in vendors, with every change recorded. This connector reads limits only, not costs.
What you’ll need
- The LiteLLM proxy’s URL, reachable from Shield360. For a proxy on a private network, turn on Allow private network.
- The proxy’s master key, or a key with the proxy admin role.
Shield360 calls GET /key/list, GET /user/list and GET /team/list on the proxy. Keys are named by their alias. LiteLLM’s key ids are hashed again before they’re kept, and the masked key names are never read.
LiteLLM admin key
Section titled “LiteLLM admin key”Use the master key (LITELLM_MASTER_KEY), or create a key for a user with the proxy_admin role. See LiteLLM’s budgets and virtual keys.
Add a LiteLLM budgets connector, set Proxy URL, and paste the key into Admin key, or name a Vault entry in Admin key from the Vault (optional).
OpenRouter key limits
Section titled “OpenRouter key limits”Reads the credit limit on each OpenRouter API key, how often it resets (daily, weekly, monthly or never), and the key’s usage in that period. Credits are US dollars. Disabled keys and keys without a limit are left out. Shown under Limits set in vendors. This connector reads limits only, not costs.
Shield360 calls GET https://openrouter.ai/api/v1/keys, 100 keys at a time. The masked key OpenRouter returns is never read.
OpenRouter management key
Section titled “OpenRouter management key”In OpenRouter, open Settings → Provisioning keys and create a key. It can list keys but can’t call models. See OpenRouter’s provisioning keys.
Add an OpenRouter key limits connector and paste the key into Management key, or name a Vault entry in Management key from the Vault (optional).
Errors any connector can return
Section titled “Errors any connector can return”| Message | Cause and fix |
|---|---|
Set the <field>, or name the Vault entry holding it. | Saving without a secret. Paste the secret or name a Vault entry. |
The vault has no entry called <name>. | The Vault entry named in … from the Vault (optional) doesn’t exist in this environment. Create it or fix the name. |
That is not a valid vault entry name. | Vault entry names use letters, digits, ., _, / and -. |
No database in <environment> to write to. | The environment has no database configuration. Add one to the project’s environment first. |