Study guides / CCDV-F / Domain 1

Applications & Integration · Lesson 9 of 11

1.9 - API Keys, Auth and Config Management

Keep credentials and model configuration out of source and out of the prompt, and manage them the way any production secret is managed.

An API key is a bearer credential sent as a request header (x-api-key) — anyone who has it can spend on your account, up to whatever limits and permissions that key carries. It belongs in environment variables or a secrets manager, never committed to source control, never logged in plaintext, and never placed inside a prompt (where it could end up echoed back in a response or stored in a logging pipeline downstream that wasn't built with credential handling in mind). A key committed to a public repository, even briefly and even if deleted in a later commit, should be treated as compromised and rotated — git history retains it, and automated scanners actively look for exactly this pattern across public repos.

Workspaces, scoping and least privilege

Where the platform supports it, scope keys as narrowly as the use case allows: a separate key per environment (development, staging, production) so a leaked staging key doesn't expose production spend, and organisational workspace boundaries so one team's usage and billing are isolated from another's. This mirrors ordinary least-privilege practice for any other credential type — a database password scoped to one service rather than a shared superuser credential is the same underlying principle applied to a different kind of secret.

Config as a separate axis from code

Model name/version, max_tokens, temperature, which tools are enabled, and the extended-thinking budget are configuration, not code — keeping them in environment-specific config rather than hardcoded lets you roll a model version forward in staging before production (Lesson 1.11 covers this rollout discipline directly), or dial down cost in a lower environment, without a code change and redeploy for every tweak. Treating a model-behaviour knob as a config value rather than a magic literal buried in application logic also makes it something you can audit, diff between environments, and roll back cleanly if a change causes a regression.

Key concept

The same discipline that applies to a database password applies to an API key: least-privilege scoping where the platform supports it, rotation on suspected exposure, and never in a client-side/browser or mobile-app context where any user could read it out of network traffic or extract it from the compiled binary.

The correct architecture: a backend proxy

A client application (browser or mobile) that needs Claude output should call your own backend, which holds the real API key server-side and forwards the request — never call the Messages API directly from client code. This gives you a place to enforce rate limiting per user, apply your own business logic (including the hook-style policy enforcement covered in Domain 3), and rotate or revoke the underlying key without shipping a new client build. It's more infrastructure than a direct client call, but it's the only architecture where the credential actually stays a secret.

Organisation and workspace structure

An organisation on the platform can contain multiple workspaces, each with its own API keys, usage limits, and billing visibility — a natural boundary for separating teams, products, or environments so that one workspace's runaway usage or leaked key doesn't silently exhaust another's budget or blur cost attribution. Admin-level keys and roles that can manage workspace membership, create or revoke other keys, and view organisation-wide billing are a materially higher-privilege credential than a workspace-scoped key used purely to call the Messages API, and should be handled with correspondingly tighter access control — a small number of trusted people, not embedded in any application code path at all.

Rotation as a routine practice, not just an incident response

Key rotation is often framed only as something you do after a suspected leak, but a mature setup rotates keys on a routine schedule regardless of any known incident, precisely because not every exposure is detected — a key that leaked through a channel nobody's monitoring (a misconfigured log aggregator, a screen-shared terminal, a support ticket with a debug output pasted in) doesn't announce itself. Scheduled rotation bounds the exposure window for a leak nobody has yet noticed, the same logic that underlies routine password rotation policies for other credential types.

Common exam distractor

An answer that treats "the code doesn't currently contain the key" as sufficient proof a credential is safe is a trap. Version control history, chat logs, ticket attachments, and log aggregators can all retain a secret long after it's gone from the current working files — the question is whether it was ever exposed anywhere, not whether it's visible right now.

Exam traps

Practice question

A mobile app calls the Messages API directly from the device, with the API key embedded in the app's compiled code, to avoid running a backend server. What's the core problem with this design?

  • A Mobile devices can't make HTTPS requests reliably.

    This isn't a real networking limitation - mobile HTTPS requests work fine.

  • B The embedded API key is extractable from the compiled app and can be used by anyone who pulls it out, at the developer's expense. Correct

    A key shipped inside client code is not secret - it can be extracted and abused, running up usage on the developer's account with no server-side control.

  • C The Messages API only accepts requests from server IP ranges.

    There's no such server-only IP restriction inherent to the API - the real issue is the exposed credential, not network origin.

  • D Mobile apps cannot send the required Authorization header format.

    Sending a standard auth header from a mobile client isn't a technical limitation - the design flaw is exposing the key itself.

Build exercise: Move a hardcoded API key into environment-based config, and add a minimal backend proxy

Beginner · 30 minutes

You'll practice:

  1. Take any small script you have that calls the Messages API with a hardcoded key, and move the key and the model name into environment variables loaded at runtime.

    This is the minimum bar for not shipping a credential into version control by accident.

    You should see: The script runs identically, but git diff on the file shows no key or model string left in it.

    Hints
    1. What extra file, besides removing the key from your source, should you make sure never gets committed either?
    2. Add the env file itself to .gitignore if you're using one - the goal is the secret never touches a commit, including the file that holds it locally.
    3. // before: const apiKey = "sk-ant-...";
      // after:
      const apiKey = process.env.ANTHROPIC_API_KEY;
      const model = process.env.CLAUDE_MODEL ?? "claude-sonnet-5";
  2. Check whether the key you were using has ever appeared in a git commit (via git log -p or a secret-scanning tool), and if so, rotate it.

    This confirms the exposure risk is real and teaches the rotate-don't-just-delete lesson directly instead of abstractly.

    You should see: Either a clean history, or a confirmed exposure followed by generating a fresh key and revoking the old one.

    Hints
    1. Does deleting a secret from the latest version of a file remove it from the project's history?
    2. git retains every version of every commit - search the full history, not just the current file, and if you find the key anywhere, treat it as compromised regardless of whether it's in the current code.
    3. git log -p --all -- your-file.js | grep -i "sk-ant"
  3. Write a minimal backend endpoint (even a single local server route) that holds the API key server-side and forwards a request from a stubbed client, so the client never sees the key.

    This is the architecture the lesson insists on for any client-facing app, and building even a toy version of it makes the pattern concrete.

    You should see: A client-side call to your own local endpoint (with no API key anywhere in the client code) that returns a Claude response, proxied through your backend.

    Hints
    1. Which side of this two-part system - the client stub or the backend route - should hold the real Anthropic API key?
    2. Only the backend route reads process.env.ANTHROPIC_API_KEY and calls the Messages API; the client stub calls your backend's own endpoint with no credential of its own.
    3. // backend route
      app.post("/chat", async (req, res) => {
        const response = await client.messages.create({ model, max_tokens: 200, messages: req.body.messages });
        res.json(response);
      });
      // client stub calls fetch("/chat", { method: "POST", body: JSON.stringify({ messages }) }) - no key present

Sources