# Authentication
URL: https://backworkhealth.com/docs/authentication

Learn how to authenticate with the Backwork API

<Callout title="Looking for the Backwork Minecraft mod?">
  That's a different project — the mod needs an AI provider key, like a free Groq key from [console.groq.com](https://console.groq.com). This site is Backwork, a healthcare coverage-policy API.
</Callout>

The Backwork API authenticates each request with an API key in the `Authorization` header. MCP clients use OAuth instead; see [MCP Authentication](#mcp-authentication).

## Getting an API Key [#getting-an-api-key]

Live keys are self-serve:

1. [Sign up](https://backworkhealth.com/sign-up) and create your organization. It starts with 100 free starter credits.
2. Open [API usage and keys](https://backworkhealth.com/api-usage).
3. Under **Live API keys**, enter a name and choose **Create live key**.
4. Copy the key. Backwork shows the full key once and stores only its hash.

Only organization owners and admins can create and revoke keys. Every key in the organization spends the same request credits and shares one per-minute limit. See [Pricing and credits](/docs/pricing) for packs and the request cost of each endpoint, and the [Quickstart](/docs/quickstart) for a first call.

Keys for `GET /api/v1/releases/{releaseId}/decisions` are separate from live keys: they carry only the `certified:derived_decision:read` scope and work only on that endpoint.

To try the API first, get a free sandbox test key. No account is needed:

```bash
curl -X POST https://backworkhealth.com/api/sandbox/v1/keys
```

See [Try it in the sandbox](/docs/sandbox).

## Using Your API Key [#using-your-api-key]

Include your API key in the `Authorization` header using the Bearer scheme:

```bash
curl -X GET "https://backworkhealth.com/api/v1/health" \
  -H "Authorization: Bearer bwk_live_xxxx"
```

<Callout type="warn" title="Keep Your Key Secret">
  Never expose your API key in client-side code, public repositories, or logs. If you suspect your key has been compromised, revoke it in [API usage and keys](https://backworkhealth.com/api-usage) and create a new one.
</Callout>

## Key Format [#key-format]

API keys follow this format:

```
bwk_{mode}_{random}_checksum
```

* `bwk` - Prefix identifying Backwork API keys
* `mode` - Either `live` (production) or `test` (development)
* `random` - Cryptographically random string
* `checksum` - 4-character checksum for validation

## Test vs Live Keys [#test-vs-live-keys]

| Key Type | Prefix      | Works on                                         | Data               |
| -------- | ----------- | ------------------------------------------------ | ------------------ |
| Live     | `bwk_live_` | `https://backworkhealth.com/api/v1`              | Backwork data      |
| Test     | `bwk_test_` | `https://backworkhealth.com/api/sandbox/v1` only | Synthetic fixtures |

The two key types do not overlap. Production routes reject test keys with `AUTH_TEST_KEY_SANDBOX_ONLY`, and the sandbox rejects live keys with `AUTH_SANDBOX_TEST_KEY_REQUIRED`. Test keys are self-serve and expire after 7 days.

## Earlier API keys [#earlier-api-keys]

Keys issued before the current `bwk_` format start with `vrt_`. They still work. To rotate one, create a new live key in [API usage and keys](https://backworkhealth.com/api-usage), move your integration to it, and revoke the old key.

## API Key Scopes [#api-key-scopes]

Keys created in [API usage and keys](https://backworkhealth.com/api-usage) have the `read` scope. It covers every lookup, policy, coverage, prior-auth, claim, and compliance read endpoint, and the read-only agent tools.

Endpoints that change data require a key with the `write` or `admin` scope. The API usage page does not create `write` keys yet:

| Endpoint                       | Required Scope     |
| ------------------------------ | ------------------ |
| `POST /v1/webhooks`            | `write` or `admin` |
| `PATCH /v1/webhooks/{id}`      | `write` or `admin` |
| `DELETE /v1/webhooks/{id}`     | `write` or `admin` |
| `POST /v1/webhooks/{id}/test`  | `write` or `admin` |
| `POST /v1/compliance/ack`      | `write` or `admin` |
| `POST /v1/compliance/ack/bulk` | `write` or `admin` |

Use the narrowest scope each integration needs. The `admin` scope satisfies all read and write checks. `GET /v1/webhooks` also needs `write`, and every webhook endpoint needs the Scale pack.

## MCP Authentication [#mcp-authentication]

Direct API integrations use API keys. MCP clients connect to the hosted server at `https://backworkhealth.com/mcp` with OAuth, so you do not copy a key into the client.

The OAuth flow signs the user in to Backwork, asks for consent to the read-only scopes `backwork:mcp read`, and issues a 1-hour access token for the MCP resource plus a rotating refresh token. The client uses the refresh token to get new access tokens for up to 30 days without a new consent. MCP requests spend the organization's request credits like any other API request, and are attributed to the signed-in user, the OAuth client, the organization, and the MCP tool.

See [MCP Server](/docs/mcp) for client setup.

## Code Examples [#code-examples]

The [Go CLI](/docs/sdks/go) and the examples on [SDKs and HTTP clients](/docs/sdks) read `BACKWORK_API_KEY`.

<Tabs items="[&#x22;Python&#x22;, &#x22;JavaScript&#x22;, &#x22;cURL&#x22;]">
  <Tab value="Python">
    ```python
    import requests
    import os

    response = requests.get(
        'https://backworkhealth.com/api/v1/health',
        headers={'Authorization': f'Bearer {os.environ["BACKWORK_API_KEY"]}'}
    )
    ```
  </Tab>

  <Tab value="JavaScript">
    ```javascript
    const response = await fetch('https://backworkhealth.com/api/v1/health', {
      headers: {
        'Authorization': `Bearer ${process.env.BACKWORK_API_KEY}`
      }
    });
    ```
  </Tab>

  <Tab value="cURL">
    ```bash
    curl -X GET "https://backworkhealth.com/api/v1/health" \
      -H "Authorization: Bearer $BACKWORK_API_KEY"
    ```
  </Tab>
</Tabs>

## Error Responses [#error-responses]

If authentication fails, you'll receive a `401 Unauthorized` response:

```json
{
  "success": false,
  "error": {
    "code": "AUTH_KEY_NOT_FOUND",
    "message": "API key not found",
    "hint": "Check that your API key is correct",
    "docUrl": "https://backworkhealth.com/docs/errors#auth-key-not-found",
    "retryable": false
  }
}
```

### Common Authentication Errors [#common-authentication-errors]

| Error Code                                                                      | Description                                                     |
| ------------------------------------------------------------------------------- | --------------------------------------------------------------- |
| [`AUTH_MISSING`](/docs/errors#auth-missing)                                     | No Authorization header provided                                |
| [`AUTH_INVALID_FORMAT`](/docs/errors#auth-invalid-format)                       | Authorization header format is wrong                            |
| [`AUTH_INVALID_KEY_FORMAT`](/docs/errors#auth-invalid-key-format)               | API key does not start with `bwk_live_`, `bwk_test_`, or `vrt_` |
| [`AUTH_KEY_NOT_FOUND`](/docs/errors#auth-key-not-found)                         | API key not found                                               |
| [`AUTH_KEY_REVOKED`](/docs/errors#auth-key-revoked)                             | API key has been revoked                                        |
| [`AUTH_KEY_SUSPENDED`](/docs/errors#auth-key-suspended)                         | API key is suspended                                            |
| [`AUTH_KEY_EXPIRED`](/docs/errors#auth-key-expired)                             | API key has expired                                             |
| [`AUTH_SANDBOX_TEST_KEY_REQUIRED`](/docs/errors#auth-sandbox-test-key-required) | A live key or OAuth token was sent to the sandbox               |
| [`AUTH_TEST_KEY_SANDBOX_ONLY`](/docs/errors#auth-test-key-sandbox-only)         | A test key was sent to a production route                       |

See [Error Handling](/docs/errors) for every code, its hint, and whether a retry can help.
