On 17 October 2026, OpenCode emailed some Console users under the subject "Action Required: Potential provider key breach." It said it had found unauthorized access to a legacy system it was decommissioning, and that third party provider keys such as OpenAI and Anthropic keys saved in Console workspaces may have been exposed. Users were told to revoke and replace those keys through each provider's dashboard.
Revoking a key takes a minute. The slow part is knowing which keys exist, who owns each one and what breaks when it goes away. This guide builds that inventory with the providers' own admin APIs, so the next rotation is a lookup instead of a search.
What's affected
Any organization where developers create provider keys by hand is exposed to this problem. The usual signs:
- Keys created in a console by whoever needed one, named "test" or "my key".
- Keys pasted into third party tools that store them on the vendor side, as OpenCode Console did.
- Keys with no expiry and no record of who uses them.
- Google API keys with no API restriction, which Google's own documentation calls insecure.
Each provider exposes enough metadata to fix this, but not the same metadata. OpenAI's project key and admin key objects include created_at, last_used_at and an owner. Anthropic's key object includes created_at, created_by, expires_at, status and a scope naming the workspace, but no last used field. Google's Key resource has createTime, displayName, restrictions and free form annotations, and Google's reference shows no last used field either.
How to check if you're exposed
You need an OpenAI admin key, an Anthropic Admin API credential, and a Google account that can read Cloud Asset Inventory. Anthropic notes the Admin API is unavailable for individual accounts.
1. OpenAI: every project, every key. List projects, then list keys per project. Pass owner_project_access=any; OpenAI's reference says that without it the endpoint applies membership based visibility rules "which may exclude some enabled keys."
curl -s "https://api.openai.com/v1/organization/projects?limit=100" \
-H "Authorization: Bearer $OPENAI_ADMIN_KEY" | jq -r '.data[].id' > projects.txt
while read -r p; do
curl -s "https://api.openai.com/v1/organization/projects/$p/api_keys?limit=100&owner_project_access=any" \
-H "Authorization: Bearer $OPENAI_ADMIN_KEY" |
jq -r --arg p "$p" '.data[] | [$p, .id, .name, .redacted_value,
(.created_at | todate),
(if .last_used_at then (.last_used_at | todate) else "never" end),
(.owner.user.email // .owner.service_account.name)] | @csv'
done < projects.txt > openai_keys.csv
Run the same pattern against GET /v1/organization/admin_api_keys for admin keys, which carry the same timestamps. Both lists paginate with after, so loop while has_more is true.
2. Anthropic: keys plus real usage. List active keys across the organization:
curl -s "https://api.anthropic.com/v1/organizations/api_keys?status=active&limit=1000" \
-H "x-api-key: $ANTHROPIC_ADMIN_KEY" -H "anthropic-version: 2023-06-01" |
jq -r '.data[] | [.id, .name, .partial_key_hint, .created_at,
(.created_by.id // "unknown"), (.scope.workspace_id // "organization"),
(.expires_at // "never")] | @csv' > anthropic_keys.csv
Since there is no last used field, ask the Usage API which keys actually made requests. Daily buckets allow up to 31 days per call:
curl -s --globoff "https://api.anthropic.com/v1/organizations/usage_report/messages?starting_at=2026-09-17T00:00:00Z&ending_at=2026-10-18T00:00:00Z&bucket_width=1d&limit=31&group_by[]=api_key_id" \
-H "x-api-key: $ANTHROPIC_ADMIN_KEY" -H "anthropic-version: 2023-06-01" |
jq -r '[.data[] | .starting_at as $d | .results[] | select(.api_key_id) | {k: .api_key_id, d: $d}]
| group_by(.k)[] | [.[0].k, (map(.d) | max)] | @tsv' > anthropic_last_seen.tsv
Any active key missing from that file made no requests in 31 days. Console playground usage reports a null api_key_id, so it never shows up here.
3. Google: the whole organization at once. Cloud Asset Inventory lists keys across every project in scope. This filter shows keys with no API restriction:
gcloud asset list --organization=ORGANIZATION_ID \
--asset-types=apikeys.googleapis.com/Key \
--content-type=resource \
--filter="-resource.data.restrictions.apiTargets:*"
Drop the filter for the full list. Gemini keys belong to a Google Cloud project, so they appear here too.
4. GitHub: where keys leaked into code. If secret scanning is enabled, an org admin or security manager can pull every open alert for these providers:
curl -s -H "Authorization: Bearer $GH_TOKEN" -H "Accept: application/vnd.github+json" \
"https://api.github.com/orgs/YOUR_ORG/secret-scanning/alerts?state=open&secret_type=openai_api_key,anthropic_api_key,anthropic_admin_api_key,google_gemini_api_key,google_api_key&hide_secret=true&per_page=100" |
jq -r '.[] | [.repository.full_name, .secret_type, .validity, .created_at, .html_url] | @csv'
An alert with validity of active is a key that still works and sits in a repository.
What to do now
- Merge the four outputs into one sheet. Columns: provider, key ID, name, hint, project or workspace, owner, created, last used, consumers, next review date.
- Give every key an owner where the provider stores it. Anthropic lets you rename a key through
POST /v1/organizations/api_keys/{id}with anamefield, so put the team in the name. Google keys take annotations:gcloud services api-keys update KEY_ID --annotations=owner=payments,ticket=SEC-412. For OpenAI, keep the owner in your sheet; theownerfield already names the user or service account. - Turn off what nobody uses. For Anthropic, send
{"status": "inactive"}to the same endpoint, which you can reverse if something breaks. For OpenAI, the update endpoint acceptsexpires_in_seconds, so a seven day expiry gives owners a deadline before you delete withDELETE /v1/organization/projects/{project_id}/api_keys/{key_id}. For Google,gcloud services api-keys delete KEY_ID, withgcloud services api-keys undelete KEY_IDavailable for 30 days. - Restrict surviving Google keys.
gcloud services api-keys update KEY_ID --api-target=service=generativelanguage.googleapis.comlimits a key to the Gemini API. Each update replaces the service list, so include every API the key needs. Google also says the Gemini API now rejects requests from unrestricted standard keys, and since 7 May 2026 it has blocked unrestricted keys that sat dormant for an extended period. - Close GitHub alerts properly. After revoking, mark each alert resolved with resolution
revokedviaPATCH /repos/{owner}/{repo}/secret-scanning/alerts/{alert_number}. - Schedule the review. Run the four scripts quarterly. Any key with no use in 90 days, no owner or no expiry goes to its owner with a two week deadline, then gets disabled.
Why it matters
A provider key is a bill and a data path. When a vendor that stores your keys has a bad night, the clock starts on your side, and the inventory decides whether you spend that time rotating or searching.
The admin APIs make the inventory cheap. A key with no owner and no recent use is pure risk: nothing depends on it, and anyone holding it can still spend.
Try it yourself
Run step 4 against your GitHub organization, or a single repository, and count the active AI provider keys it returns. Then check whether each one appears in your provider inventory. Keys found in code but missing from your sheet are the ones nobody would have rotated.
Click through it at your own pace, or open it full screen.
Quick check
Why add owner_project_access=any when listing OpenAI project keys?
OpenAI's reference says that without it the endpoint applies membership based visibility rules that may exclude some enabled keys. With any, you get all enabled project keys.
How do I find unused Anthropic keys if the key object has no last used date?
Group the Usage API's messages report by api_key_id over the last 31 days. Active keys that never appear made no requests in that window.
Is deleting a Google API key permanent?
Not right away. Google lets you undelete a key within 30 days, and gcloud services api-keys list --show-deleted shows soft deleted keys.
Sources
- DEV Community: OpenCode email excerpts and timeline
- OfficeChai: OpenCode discloses security breach
- OpenAI API reference: List project API keys
- OpenAI API reference: Admin API keys
- OpenAI API reference: List projects
- Claude API reference: List API Keys
- Claude docs: Admin API
- Claude docs: Usage and Cost API
- Google Cloud: Manage API keys
- Google Cloud: API keys REST resource
- gcloud services api-keys update
- gcloud services api-keys list
- Google AI for Developers: Using Gemini API keys
- GitHub REST API: Secret scanning
- GitHub Docs: Supported secret scanning patterns