API Credential Verification
Use before trusting a stored API key. For any "I configured <service> at some point — can you use it?" request against a stored third-party API key (mail, payments, data, SaaS). The job is to establish, with live evidence, whether the credential authenticates, what it is allowed to do, and whether the resource it must act on exists — before attempting the user-visible action.
The partner skill github-token-access-verification covers the same discipline for GitHub
tokens specifically. This one is the provider-agnostic class.
Always-on rules
- Never print, echo, or repeat a secret value. Print only length, a SHA-256 prefix, a character-class structure mask, or HTTP status codes.
- Never fabricate a successful action. A send/write is "done" only when the provider returned a confirmed id for it. Otherwise report the blocker.
- Never send a real payload to a real recipient as a "test" unless the user asked for that exact action. Probe with a deliberately nonexistent resource id instead (Step 5).
- Report in the shortest honest form: what is fixed, what is verified held, what is missing, and the one or two concrete things the user can supply to unblock it. Name the blocker and its owner; do not bury it under process narration.
Procedure
Step 1 — Locate every copy of the credential
Search the agent home, profile directories, state snapshots, and project .env files for
the variable name. Compare copies by hash, not by eye:
import hashlib
for p in paths:
for line in open(p, errors="ignore"):
if line.strip().startswith(VAR + "="):
v = line.split("=", 1)[1].strip().strip('"').strip("'")
print(p, hashlib.sha256(v.encode()).hexdigest()[:12], len(v))
Identical hashes across profiles mean one fix must be applied everywhere; differing hashes mean you must determine which copy the running process actually reads.
Step 2 — Structure-check the value before blaming the provider
Before concluding a key is revoked or wrong, inspect the value's shape:
- Does it start with the variable name again (
VAR=VAR=...)? A shell append that duplicated the prefix is invisible in a listing and authenticates as garbage. - Does it carry surrounding quotes, trailing whitespace, a trailing inline
# comment, or an embedded newline? - Does its length and prefix match the provider's documented key format?
import re
mask = re.sub(r"[a-z]", "a", re.sub(r"[A-Z]", "A", re.sub(r"[0-9]", "9", v)))
print(len(v), mask[:60], v.startswith(VAR + "="))
Strip the defect in code and re-test before asking the user for a new key.
Step 3 — Run control probes to locate the failure layer
Call one cheap read endpoint three times: with the real key, with an obviously bogus key, and with an empty key. Compare the bodies, not just the codes.
- Identical, unstructured bodies for all three (e.g. a bare
{"message":"Forbidden"}from a CDN/edge) → the request never reached the provider's auth layer. Suspect a malformed value (Step 2), wrong host, or wrong API version prefix. - A structured error that names a permission or reason for the real key only → the key authenticated; the problem is scope, and you can now enumerate it.
Use scripts/probe_api_key.py in this skill to run this sweep without hand-typing it.
Step 4 — Get the exact endpoint from current provider docs
Consult Context7 first per the standing library-documentation gate. When Context7 is
unavailable or over quota, say so and go to the provider's own docs rather than memory:
many modern docs sites serve clean markdown by appending .md to any page URL and publish
/llms.txt and /llms-full.txt indexes at the docs root. Take the HTTP method, full path,
and required body fields from that page — guessing between /v0 and /v1, or between a
collection path and a nested /{resource_id}/... path, produces a not_found that is easy
to misread as a permission failure.
Step 5 — Map the permission scope with read and write probes
Sweep the documented read endpoints and record each result. Then probe the write path using a deliberately nonexistent resource id. The status code discriminates cleanly:
403naming a missing permission → the permission itself is absent.404 not foundfor the fake resource → the write permission is held; only the resource is missing or invisible to this credential's scope.
This distinction decides what you ask the user for: a broader key vs. just a resource id.
See references/scoped-key-permission-probes.md for the full probe table and
interpretation rules.
Step 6 — Confirm the resource id exists before promising the action
Most write actions need an id the key cannot always list: a sender inbox, an account, a project, a from-address. Search the filesystem and config for a stored id before assuming one. If it is absent everywhere and the key lacks the permission to list or create it, that is the blocker — report it instead of attempting the action.
Step 7 — Report
State, in order: the defect you fixed and the evidence it is fixed (a real status code), the permissions verified held, the permissions verified missing, the missing resource id, and the one or two alternatives that unblock it. Explicitly say that the user-visible action was not performed. Offer the remaining safe cleanup (e.g. correcting the malformed value in every copy) as a question rather than doing it unasked when the user's request was only "try it".
Pitfalls
- Treat an opaque, detail-free 4xx as a malformed-credential signal, not a revocation.
Edge proxies reject a badly formed
Authorizationheader before the application sees it, so the body carries no permission detail; a key that reaches the auth layer almost always gets a structured error naming what is missing. - Compare every stored copy by hash before fixing one. A malformed secret propagates
into profile and snapshot
.envfiles, so repairing a single file leaves the next process reading the broken value and reproducing the same failure. - Do not read secrets by sourcing the file into a shell you also log. Parse the line and strip quotes/whitespace in code; sourcing masks prefix and whitespace defects that are exactly what you are testing for.
- A credential that authenticates is not a working integration. Authentication, scope, and resource existence are three independent checks; report them separately so the user knows which one to fix.
- Use the provider's SDK as a second opinion on error detail. A raw HTTP call and the official client can surface different amounts of error context for the same request, and the richer message often names the exact missing permission.
- Bound filesystem searches for a resource id. A recursive walk of the whole home
directory for an address or id pattern will run for minutes; scope it to config, profile,
and project directories and skip
node_modules,.git, and cache trees.
Support files
references/scoped-key-permission-probes.md— probe table, status-code interpretation, and how to turn results into a precise ask.scripts/probe_api_key.py— re-runnable control-probe and scope sweep that never prints the secret.
Supporting files: this skill's supporting files are held in the docsite at
docs/15-skills/_support/engineering/api-credential-verification/— fetch them fresh fromjknash/docsitemain alongside this page. Source:jknash/hermes-shared-skills· branchhermes-jkdev001@1d0d545c3970·skills/engineering/api-credential-verification/· view source · Imported 2026-10-04. Supporting files (references, scripts) remain in the source repository.
version 1.0.0 · author Hermes Agent · license MIT.
Published by Muse · 2026-10-04.