Your first request
Call `/v1/whoami`, interpret every field, and know your key is healthy before you build anything else.
Every Partner API client makes the same first call: GET /v1/whoami. It's the cheapest endpoint on the surface and it tells you four things at once — that your key authenticates, what org it's bound to, what tier it's on, and whether the kill switch is on. Run it once in your bootstrap and again in CI. If it returns 200 with the shape below, the rest of the API is yours to use.
The call
curl -i https://api.layers.com/v1/whoami \
-H "X-Api-Key: $LAYERS_API_KEY"const res = await fetch("https://api.layers.com/v1/whoami", {
headers: { "X-Api-Key": process.env.LAYERS_API_KEY! },
});
if (!res.ok) {
throw new Error(`whoami failed: ${res.status} ${await res.text()}`);
}
const me = await res.json();
console.log(me);import os, requests
res = requests.get(
"https://api.layers.com/v1/whoami",
headers={"X-Api-Key": os.environ["LAYERS_API_KEY"]},
)
res.raise_for_status()
me = res.json()
print(me)The response
A healthy key returns 200 OK:
{
"organizationId": "2481fa5c-a404-44ed-a561-565392499abc",
"workspaceId": "2481fa5c-a404-44ed-a561-565392499abc",
"organizationName": "Acme Growth",
"scopes": [],
"rateLimitTier": "standard",
"killSwitch": false,
"apiAccessRevoked": false,
"apiKeyId": "c2037bb9-354d-4662-96b7-97a28ad6b6e1",
"creditBalance": 6000
}Every field is load-bearing.
organizationId and workspaceId
The UUIDs your key is pinned to. Every project, content container, social account, and metric you can touch hangs off organizationId. workspaceId will diverge from organizationId in a future workspace split — today they resolve to the same value for partner keys. Persist organizationId in your config; you'll want it in log lines when filing a support ticket.
organizationName
Human-readable label for the org. Safe to render in a "you are authenticated as" UI element.
scopes
Present for forward-compat. Partner keys today carry org-level access and the returned array is empty. A future release will populate granular scopes — don't key behavior off scopes.length until that ships.
rateLimitTier
standard by default. Partner-tier keys (pilot, partner) are provisioned by Layers for enterprise partners. The tier sets per-endpoint-class rpm limits; if you're hitting 429 RATE_LIMITED in normal operation, this field is the first thing to check. Full tier table lives in Rate limits.
killSwitch and apiAccessRevoked
Two "stop the world" signals.
killSwitch: true→ this specific key is disabled. Clearable by Layers without re-issuing.apiAccessRevoked: true→ the whole org lost API access (billing lapse, policy violation). Cleared when the underlying issue is resolved.
Either one means every other call returns 503 KILL_SWITCH until cleared. Treat both as hard stops — no retry, no exponential back-off. Stop your worker, page your Layers contact, find out why.
If either killSwitch or apiAccessRevoked is true, do not retry the call you were about to make. The switch is binary and the only recovery is human. Log the incident and stop.
apiKeyId
Safe to log. It's the public identifier that Layers uses for rate-limit attribution and audit events. You'll see it echoed in every partner_audit log entry.
What can go wrong
A small set of failure modes accounts for nearly every onboarding ticket. If /v1/whoami doesn't return 200, you'll see one of these:
| Status | error.code | What happened |
|---|---|---|
401 | UNAUTHENTICATED | Header missing, malformed, or the secret doesn't match. Check X-Api-Key: <key> is set and the key is the full string (not just key_id). |
401 | UNAUTHENTICATED (message: "API key has been revoked.") | The key was revoked. Create a new one with your Layers contact. |
402 | BILLING_EXHAUSTED | Your plan doesn't include partner API access. error.details.minTier names the tier you need. |
503 | KILL_SWITCH | Per-key or org-wide kill switch is engaged. See Authentication → kill switch. |
5xx | INTERNAL | Bad day on our side. Include X-Request-Id when you page us. |
Every error response carries the same shape — { error: { code, message, requestId, details? } }. Branch on code, never on message. Full vocabulary in Errors.
A boot-time sanity check
Run whoami at process startup and refuse to serve traffic until it returns clean. The pattern below catches every one of the failure modes above before your first real request hits the wire.
#!/usr/bin/env bash
set -euo pipefail
response=$(curl -sS -w "\n%{http_code}" \
https://api.layers.com/v1/whoami \
-H "X-Api-Key: $LAYERS_API_KEY")
body=$(echo "$response" | sed '$d')
status=$(echo "$response" | tail -n1)
if [ "$status" != "200" ]; then
echo "whoami failed: HTTP $status"
echo "$body"
exit 1
fi
if echo "$body" | jq -e '.killSwitch == true or .apiAccessRevoked == true' > /dev/null; then
echo "access revoked or kill switch engaged — refusing to start"
exit 1
fi
echo "key healthy: $(echo "$body" | jq -r '.organizationId')"export async function assertKeyHealthy() {
const res = await fetch("https://api.layers.com/v1/whoami", {
headers: { "X-Api-Key": process.env.LAYERS_API_KEY! },
});
if (!res.ok) {
const body = await res.text();
throw new Error(`whoami ${res.status}: ${body}`);
}
const me = await res.json();
if (me.killSwitch || me.apiAccessRevoked) {
throw new Error(
`api access blocked: killSwitch=${me.killSwitch} apiAccessRevoked=${me.apiAccessRevoked}`,
);
}
console.log(
`key healthy — org=${me.organizationId} name=${me.organizationName} tier=${me.rateLimitTier}`,
);
return me;
}import os, requests
def assert_key_healthy() -> dict:
res = requests.get(
"https://api.layers.com/v1/whoami",
headers={"X-Api-Key": os.environ["LAYERS_API_KEY"]},
timeout=10,
)
res.raise_for_status()
me = res.json()
if me.get("killSwitch") or me.get("apiAccessRevoked"):
raise RuntimeError(
f"api access blocked: killSwitch={me.get('killSwitch')} "
f"apiAccessRevoked={me.get('apiAccessRevoked')}"
)
print(
f"key healthy — org={me['organizationId']} "
f"name={me['organizationName']} tier={me['rateLimitTier']}"
)
return meWire this into your service's health check or your CI smoke suite. It's a single network round trip and it will catch a misconfigured secret faster than any downstream 403 ever will.
What's next
- Authentication — rotation, kill switch, rate limits.
- Common patterns — idempotency, pagination, errors, async jobs.
- Quickstart — six calls end to end, from key check to first ingest job.