> ## Documentation Index
> Fetch the complete documentation index at: https://docs.gravixlayer.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Get started with Claude Code

> Create a Gravix Layer Claude sandbox with Anthropic credentials and a network allowlist, then run your first interactive and headless Claude Code sessions.

This guide gets Claude Code talking to Anthropic from a Gravix Layer runtime. If you already opened a shell and saw:

```text theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
Failed to connect to api.anthropic.com: ETIMEDOUT
```

you are in the right place. Gravix sandboxes **block outbound traffic by default**. Claude is installed; it just cannot reach the API until you attach a network policy (and provide `ANTHROPIC_API_KEY`).

## Prerequisites

```bash theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
export GRAVIXLAYER_API_KEY="your-gravixlayer-api-key"
export ANTHROPIC_API_KEY="sk-ant-..."
# Template built from agent-claude.Dockerfile (name is yours)
export GRAVIXLAYER_CLAUDE_TEMPLATE="agent-claude"
```

```bash theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
pip install gravixlayer
# or use the Gravix CLI: https://github.com/gravixlayer/gravixlayer-cli
```

## End-to-end: create, smoke-test, clean up

One script creates an Identity provider, an allowlist for `api.anthropic.com`, a Claude runtime, runs a headless prompt, then tears down.

<CodeGroup>
  ```python Python theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
  import os
  import uuid

  from gravixlayer import GravixLayer

  TEMPLATE = os.environ["GRAVIXLAYER_CLAUDE_TEMPLATE"]
  ANTHROPIC_API_KEY = os.environ["ANTHROPIC_API_KEY"]

  client = GravixLayer()

  # 1. Identity provider — secrets inject into run_cmd / run_code
  provider = client.identity.providers.create(
      name=f"anthropic-claude-{uuid.uuid4().hex[:8]}",
      provider_type="api_key",
      secrets=[{"key": "ANTHROPIC_API_KEY", "value": ANTHROPIC_API_KEY}],
  )

  # 2. Network allowlist — required or api.anthropic.com times out
  policy = client.network_policies.create(
      name=f"claude-anthropic-{uuid.uuid4().hex[:8]}",
      egress_mode="allowlist",
      description="Claude Code → Anthropic API",
      rules=[
          {
              "destination": "api.anthropic.com",
              "port": 443,
              "protocol": "tcp",
              "description": "Anthropic Messages API",
          },
      ],
  )

  # 3. Runtime — env_vars also set the key for interactive shell / SSH
  #    (Identity secrets are not auto-injected into shell sessions)
  runtime = client.runtime.create(
      template=TEMPLATE,
      cloud="azure",
      region="eastus2",
      providers=[provider.id],
      network_policy_ids=[policy.id],
      env_vars={"ANTHROPIC_API_KEY": ANTHROPIC_API_KEY},
      timeout=3600,
  )
  print("runtime_id:", runtime.runtime_id)

  # 4. Headless smoke test
  result = runtime.run_cmd(
      command="claude",
      args=[
          "--dangerously-skip-permissions",
          "-p",
          "Reply with exactly: pong",
          "--output-format",
          "text",
      ],
      timeout=300,
  )
  print(result.stdout)
  print("exit_code:", result.exit_code)
  assert result.success, result.stderr
  assert "pong" in (result.stdout or "").lower()

  # 5. Clean up (keep provider/policy if you will reuse them)
  runtime.kill()
  # client.identity.providers.delete(provider.id)
  # client.network_policies.delete(policy.id)
  ```

  ```bash CLI theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
  # Requires: GRAVIXLAYER_API_KEY, ANTHROPIC_API_KEY, GRAVIXLAYER_CLAUDE_TEMPLATE, jq

  PROVIDER_ID=$(gravixlayer provider create \
    "anthropic-claude-$(openssl rand -hex 4)" \
    --type api_key \
    --secret "ANTHROPIC_API_KEY=$ANTHROPIC_API_KEY" \
    --output json | jq -r '.id')

  POLICY_ID=$(gravixlayer network-policy create \
    "claude-anthropic-$(openssl rand -hex 4)" \
    --egress-mode allowlist \
    --description "Claude Code → Anthropic API" \
    --output json | jq -r '.id')

  gravixlayer network-policy add-rule "$POLICY_ID" \
    --destination api.anthropic.com \
    --port 443 \
    --protocol tcp \
    --description "Anthropic Messages API"

  RT=$(gravixlayer runtime create \
    --template "$GRAVIXLAYER_CLAUDE_TEMPLATE" \
    --cloud azure \
    --region eastus2 \
    --provider "$PROVIDER_ID" \
    --network-policy "$POLICY_ID" \
    --env "ANTHROPIC_API_KEY=$ANTHROPIC_API_KEY" \
    --timeout 3600 \
    --wait \
    --output json | jq -r '.runtime_id')

  echo "runtime_id=$RT"

  gravixlayer runtime exec "$RT" --timeout 300 -- \
    claude --dangerously-skip-permissions \
    -p 'Reply with exactly: pong' \
    --output-format text

  # Interactive (optional):
  # gravixlayer runtime shell "$RT"

  gravixlayer runtime kill "$RT" -y
  # gravixlayer provider delete "$PROVIDER_ID" -y
  # gravixlayer network-policy delete "$POLICY_ID" -y
  ```

  ```bash cURL theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
  # Requires: GRAVIXLAYER_API_KEY, ANTHROPIC_API_KEY, GRAVIXLAYER_CLAUDE_TEMPLATE, jq
  API="https://api.gravixlayer.ai/v1"
  AUTH=(-H "Authorization: Bearer $GRAVIXLAYER_API_KEY" -H "Content-Type: application/json")

  PROVIDER_ID=$(curl -sS -X POST "$API/identity/providers" "${AUTH[@]}" \
    -d "{
      \"name\": \"anthropic-claude-$(openssl rand -hex 4)\",
      \"provider_type\": \"api_key\",
      \"secrets\": [{\"key\": \"ANTHROPIC_API_KEY\", \"value\": \"$ANTHROPIC_API_KEY\"}]
    }" | jq -r '.provider.id')

  POLICY_ID=$(curl -sS -X POST "$API/network-policies" "${AUTH[@]}" \
    -d "{
      \"name\": \"claude-anthropic-$(openssl rand -hex 4)\",
      \"egress_mode\": \"allowlist\",
      \"description\": \"Claude Code → Anthropic API\"
    }" | jq -r '.policy.id')

  curl -sS -X POST "$API/network-policies/$POLICY_ID/rules" "${AUTH[@]}" \
    -d '{
      "destination": "api.anthropic.com",
      "port": 443,
      "protocol": "tcp",
      "description": "Anthropic Messages API"
    }' >/dev/null

  RT=$(curl -sS -X POST "$API/agents/runtime" "${AUTH[@]}" \
    -d "{
      \"template\": \"$GRAVIXLAYER_CLAUDE_TEMPLATE\",
      \"cloud\": \"azure\",
      \"region\": \"eastus2\",
      \"timeout\": 3600,
      \"providers\": [\"$PROVIDER_ID\"],
      \"network_policy_ids\": [\"$POLICY_ID\"],
      \"env_vars\": {\"ANTHROPIC_API_KEY\": \"$ANTHROPIC_API_KEY\"}
    }" | jq -r '.runtime_id')

  # Wait until running
  for i in $(seq 1 60); do
    STATUS=$(curl -sS "$API/agents/runtime/$RT" \
      -H "Authorization: Bearer $GRAVIXLAYER_API_KEY" | jq -r '.status')
    [ "$STATUS" = "running" ] && break
    sleep 2
  done
  echo "runtime_id=$RT status=$STATUS"

  # timeout is milliseconds on the HTTP API
  curl -sS -X POST "$API/agents/runtime/$RT/commands/run" "${AUTH[@]}" \
    -d '{
      "command": "claude",
      "args": [
        "--dangerously-skip-permissions",
        "-p", "Reply with exactly: pong",
        "--output-format", "text"
      ],
      "timeout": 300000
    }' | jq .

  curl -sS -X DELETE "$API/agents/runtime/$RT" \
    -H "Authorization: Bearer $GRAVIXLAYER_API_KEY"
  ```
</CodeGroup>

<Note>
  Identity secrets are injected for `run_cmd` / `run_code`. They are **not** automatically present in an interactive SSH / `runtime shell` session. Pass `env_vars` at create time (as above) or `export ANTHROPIC_API_KEY=...` inside the shell.
</Note>

## Interactive shell

After create (keep the runtime alive):

```bash theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
gravixlayer runtime shell "$RT"
```

Inside the sandbox:

```bash theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
claude --version
claude
```

You should get the Claude Code TUI, not `ETIMEDOUT`.

## Extra allowlist hosts (optional)

| Destination                          | Why                           |
| ------------------------------------ | ----------------------------- |
| `api.anthropic.com`                  | Model API (required)          |
| `github.com`                         | `git clone` / push over HTTPS |
| `registry.npmjs.org`                 | `npm install`                 |
| `pypi.org`, `files.pythonhosted.org` | `pip` / `uv`                  |

For a first smoke test only, you can use `egress_mode="allow_all"` / `--egress-mode allow_all`, then tighten. See [Network policies](/documentation/agentruntime/getting-started/network-policies).

## Troubleshooting

| Symptom                      | Likely cause                                                            |
| ---------------------------- | ----------------------------------------------------------------------- |
| `ETIMEDOUT` / cannot connect | No network policy, or allowlist missing `api.anthropic.com:443`         |
| Auth / invalid API key       | Missing `ANTHROPIC_API_KEY` in that session (shell vs `run_cmd`)        |
| `claude: command not found`  | Wrong template (not built from `agent-claude.Dockerfile`)               |
| Permission / bypass refused  | Rebuild Claude template (`IS_SANDBOX=1`) or ensure exec runs as `agent` |

## Next steps

* [Headless automation](/guides/claude-code/headless)
* [Repo & git workflows](/guides/claude-code/repo-workflows)
* [Long-running & parallel agents](/guides/claude-code/long-running-agents)
