Skip to content

Claude Cowork

Claude Cowork routes Claude Code's inference through Corveil using a gateway inference config plus a small credential helper. Your Claude Code OAuth token flows upstream to Anthropic, while a separate x-citadel-api-key header authenticates with Corveil for tracking and guardrails — the same passthrough model as the Claude Code guide, but driven by a JSON config file and a keychain helper script instead of environment variables.

Prerequisites

  • A running Corveil instance (see deployment options)
  • A Corveil virtual API key (sk-citadel-...) — create one under Corveil → Settings → API Keys
  • macOS — the helper reads from the macOS Keychain
  • Claude Code installed and logged in, so the Claude Code-credentials keychain entry exists
  • jq installed (brew install jq)

Mode

Passthrough mode. Unlike the Claude Code guide's ANTHROPIC_CUSTOM_HEADERS approach, Cowork reads a versioned JSON inference config and sources the upstream token from a helper script at request time.

1. Configure the inference passthrough

Add the following to your Claude Cowork inference config. The credential.command is an absolute path to the helper script from the next step.

json
{
  "$schemaVersion": 2,
  "inference": {
    "provider": "gateway",
    "baseUrl": "https://corveil.io",
    "customHeaders": {
      "x-citadel-api-key": "sk-citadel-your-key-here"
    },
    "credential": {
      "kind": "helper-script",
      "command": "/absolute/path/to/claudecode.sh"
    }
  },
  "chatSurface": {
    "enabled": true
  }
}

Field reference

  • $schemaVersion — config schema version; use 2.
  • inference.provider — must be gateway to route inference through Corveil.
  • inference.baseUrl — your Corveil host (https://corveil.io for the hosted gateway).
  • inference.customHeaders.x-citadel-api-key — your Corveil virtual API key (sk-citadel-...). This is what authenticates the call with Corveil.
  • inference.credential.kindhelper-script; tells Cowork to run a command to fetch the upstream token.
  • inference.credential.command — absolute path to claudecode.sh (below). Cowork runs it and uses stdout as the upstream bearer token.
  • chatSurface.enabledtrue turns on the Cowork chat surface.

2. Install the credential helper

Save this as claudecode.sh. It reads the Claude Code OAuth token from the Claude Code-credentials keychain entry and prints the access token on stdout:

sh
#!/bin/sh
security find-generic-password -s "Claude Code-credentials" -w | jq -r '.claudeAiOauth.accessToken'

Then make it executable and point credential.command at its absolute path:

bash
chmod +x claudecode.sh
# use the absolute path, e.g. /Users/you/bin/claudecode.sh, in credential.command
pwd
  • The command must be an absolute path — a relative path won't resolve when Cowork runs it.
  • Requires jq on PATH to extract the token from the keychain JSON.
  • The token comes from the Claude Code-credentials keychain entry, so Claude Code must be logged in on this machine.

3. Confirm passthrough

  1. Sanity-check the helper by itself. Running it should print a long, non-empty token — not null and not an error:

    bash
    ./claudecode.sh
  2. Launch Cowork (or Claude Code with this config) and send a simple prompt.

  3. A successful passthrough returns a normal completion and the request shows up in Corveil. Confirm via the CLI or the request-log / spend dashboard in the UI:

    bash
    corveil logs --tail 5

If the prompt answers and the call appears in the Corveil logs, inference is round-tripping through the gateway.

Troubleshooting

  • jq: command not found / empty token: install jq (brew install jq) and re-run the helper.
  • Helper prints null or errors: Claude Code isn't logged in, or the token expired — re-login to Claude Code. A stale token surfaces as a 401 from the upstream provider.
  • 401 from Corveil: the x-citadel-api-key is missing or invalid — verify the value under Settings → API Keys.
  • "permission denied" running the helper: run chmod +x claudecode.sh, and confirm credential.command is the correct absolute path.
  • Connection refused: ensure the Corveil instance is running and inference.baseUrl is reachable.