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

# Restrict Anthropic Sign-Ins to One Org

> Inject Anthropic tenant restrictions so users can only sign in with your organization ID

export const G = ({term, anchor, children}) => {
  const href = anchor ? `/docs/glossary/index#${anchor}` : `/docs/glossary/index`;
  return <a href={href} className="glossary-link" style={{
    textDecoration: "underline",
    textDecorationLine: "underline",
    textDecorationColor: "#6b7280",
    textDecorationThickness: "1px",
    textUnderlineOffset: "2px",
    color: "inherit",
    transition: "text-decoration-color 0.2s ease",
    borderBottom: "none"
  }} onMouseEnter={e => e.target.style.textDecorationColor = "#fff"} onMouseLeave={e => e.target.style.textDecorationColor = "#6b7280"}>
  {children || term}
</a>;
};

## Overview

Anthropic [Tenant Restrictions](https://support.claude.com/en/articles/13198485-enforce-network-level-access-control-with-tenant-restrictions) let a proxy allowlist organization IDs. Your proxy injects `anthropic-allowed-org-ids` on Claude traffic. Anthropic then allows only those orgs for sign-in and API use.

Formal adds that header with a request-stage `rewrite` <G anchor="policy">policy</G>. Users on the Formal Endpoint can sign in only with your Anthropic org. Personal Claude accounts and other orgs receive a 403 from Anthropic.

This guide uses the Formal Endpoint on macOS.

## Find your Anthropic organization ID

You need the UUID Anthropic shows for your org.

<Steps>
  <Step title="Open Anthropic settings">
    For Enterprise, go to **Settings** → **Account**, or **Organization settings** → **Organization**. For Console orgs, go to **Settings** → **Organization**.
  </Step>

  <Step title="Copy the Organization ID">
    Copy the UUID. It looks like `000a0000-a00a-00a0-a000-000000000000`.
  </Step>
</Steps>

See Anthropic's [Tenant Restrictions](https://support.claude.com/en/articles/13198485-enforce-network-level-access-control-with-tenant-restrictions) article for where the ID appears in each plan.

## Route Anthropic traffic through Formal

The Formal Endpoint must intercept Anthropic requests so the rewrite can run.

<Steps>
  <Step title="Enable the transparent proxy">
    On macOS, install and start the [transparent proxy](/docs/guides/client-apps/desktop-app#transparent-proxy):

    ```bash theme={"languages":{"custom":["/languages/cel.json","/languages/rego.json"]}}
    formal transparent-proxy install
    formal transparent-proxy enable
    formal transparent-proxy status
    ```

    **Verify:** the status command reports that the proxy is running.
  </Step>

  <Step title="Connect the Anthropic resource">
    ```bash theme={"languages":{"custom":["/languages/cel.json","/languages/rego.json"]}}
    formal connect anthropic
    ```

    Replace `anthropic` with your resource name from `formal ls`.
  </Step>
</Steps>

Apps can keep using `api.anthropic.com`. The Endpoint intercepts that hostname and applies policies.

## Create the rewrite policy

The policy merges `anthropic-allowed-org-ids` into outgoing LLM request headers. `object.union` puts your value second, so it overwrites a client-supplied header.

Replace `<YOUR_ORG_ID>` with the UUID you copied.

<Tabs>
  <Tab title="Web Console">
    1. Navigate to [Policies](https://app.formal.ai/policies)
    2. Click **Create Policy**
    3. In **Choose a Template**, select **Restrict Anthropic Sign-Ins To One Org**
    4. Replace `<YOUR_ORG_ID>` with the UUID you copied
    5. Click **Create Policy** to save

    You can also paste this Rego if you skip the template:

    ```rego theme={"languages":{"custom":["/languages/cel.json","/languages/rego.json"]}}
    package formal.v2

    import future.keywords.if

    rewritten_headers := object.union(input.http.headers, {
      "anthropic-allowed-org-ids": ["<YOUR_ORG_ID>"],
    })

    request := {
      "action": "rewrite",
      "headers": rewritten_headers,
    } if {
      input.resource.technology == "llm"
    }
    ```
  </Tab>

  <Tab title="Terraform">
    ```hcl theme={"languages":{"custom":["/languages/cel.json","/languages/rego.json"]}}
    resource "formal_policy" "restrict_anthropic_org" {
      name        = "restrict-anthropic-org"
      description = "Allow Anthropic sign-ins only for the approved organization ID"
      status      = "active"

      module = <<-EOT
        package formal.v2

        import future.keywords.if

        rewritten_headers := object.union(input.http.headers, {
          "anthropic-allowed-org-ids": ["<YOUR_ORG_ID>"],
        })

        request := {
          "action": "rewrite",
          "headers": rewritten_headers,
        } if {
          input.resource.technology == "llm"
        }
      EOT
    }
    ```
  </Tab>
</Tabs>

Header values must be arrays of strings. For more than one org, use a comma-separated list in a single value. Do not put spaces between IDs:

```rego theme={"languages":{"custom":["/languages/cel.json","/languages/rego.json"]}}
"anthropic-allowed-org-ids": ["<ORG_ID_1>,<ORG_ID_2>"],
```

<Warning>
  Anthropic rejects duplicate `anthropic-allowed-org-ids` headers. Keep one header value. Do not list each org as a separate array element.
</Warning>

This policy matches every LLM resource. If you also proxy other providers, scope the rule to Anthropic hostnames:

```rego theme={"languages":{"custom":["/languages/cel.json","/languages/rego.json"]}}
request := {
  "action": "rewrite",
  "headers": rewritten_headers,
} if {
  input.resource.technology == "llm"
  input.resource.hostname == "api.anthropic.com"
}
```

Create the policy in **Draft** or **Dry-run** first. Then set it to **Active**.

## Verify

**Verify an allowed org:**

1. Sign in to Claude with an account in your Anthropic org.
2. Send a Messages API request, or use Claude Code as usual.
3. Open [Logs](https://app.formal.ai/logs) and confirm the rewrite ran.

Look for `triggered_policies.type:rewrite` on LLM requests. Sent headers should include `anthropic-allowed-org-ids` with your UUID.

**Verify a blocked org:**

Sign in with a personal Claude account, or call the API with a key from another org. Anthropic should return HTTP 403:

```json theme={"languages":{"custom":["/languages/cel.json","/languages/rego.json"]}}
{
  "type": "error",
  "error": {
    "type": "permission_error",
    "message": "Access restricted by network policy. Contact IT Administrator.",
    "error_code": "tenant_restriction_violation"
  }
}
```

You can also probe the API with your org key after the header is in place:

```bash theme={"languages":{"custom":["/languages/cel.json","/languages/rego.json"]}}
curl https://api.anthropic.com/v1/messages \
  -H "x-api-key: $ANTHROPIC_API_KEY" \
  -H "anthropic-version: 2023-06-01" \
  -H "content-type: application/json" \
  -d '{"model":"claude-sonnet-4-6","max_tokens":32,"messages":[{"role":"user","content":"Hello"}]}'
```

**Expected:** HTTP 200 when the key belongs to `<YOUR_ORG_ID>`. HTTP 403 `tenant_restriction_violation` otherwise.

## Troubleshooting

<AccordionGroup>
  <Accordion title="Personal accounts still sign in">
    **Possible causes:**

    * Transparent proxy or `formal connect` is not active
    * Policy is Draft or Dry-run, not Active
    * Traffic uses a host you do not intercept (`claude.ai` vs `api.anthropic.com`)

    **Fix:**

    1. Run `formal transparent-proxy status`
    2. Set the policy to **Active**
    3. Confirm Formal intercepts the Anthropic hostname the app uses
  </Accordion>

  <Accordion title="Requests return 403 tenant_restriction_violation">
    **Possible causes:**

    * The UUID is not the org that owns the account or API key
    * Extra spaces or quotes in the header value
    * You are testing with a personal account (this is expected)

    **Fix:**

    1. Re-copy the Organization ID from Anthropic settings
    2. Keep a single UUID string in the header array
    3. Sign in with an account in that org
  </Accordion>

  <Accordion title="Requests return 400 for the header">
    **Possible causes:**

    * More than one `anthropic-allowed-org-ids` header on the request
    * Multiple array elements instead of a comma-separated string

    **Fix:**

    1. Use `object.union` so Formal overwrites the header
    2. Put every org ID in one comma-separated value
  </Accordion>
</AccordionGroup>

## Next Steps

<CardGroup cols={2}>
  <Card title="LLM Resources" icon="brain-circuit" href="/docs/guides/core-concepts/resources/llm">
    Proxy Anthropic and other LLM APIs
  </Card>

  <Card title="Rewrite Action" icon="gavel" href="/docs/guides/policies/enforcement#example-rewrite-llm-request-headers">
    Header rewrite reference
  </Card>

  <Card title="LLM Policy Inputs" icon="magnifying-glass" href="/docs/guides/policies/evaluation#example-rewrite-request-headers">
    Request-stage LLM fields and examples
  </Card>

  <Card title="Formal Endpoint" icon="desktop" href="/docs/guides/client-apps/desktop-app#transparent-proxy">
    Enable the transparent proxy
  </Card>
</CardGroup>
