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

# Authentication

> Secure your integration with HANA using API keys, scoped tokens, and webhook verification.

## API Keys

Every HANA integration uses API keys scoped to your organization and environment. Keys follow a predictable format so you can identify them quickly in logs:

```
hana_sk_live_...   → Production key
hana_sk_test_...   → Sandbox key
```

<Warning>
  Never embed API keys in client-side code, mobile apps, or version control. Use environment variables or a secrets manager.
</Warning>

### Key Permissions

Keys are scoped to one of three permission levels:

| Scope              | Can Do                                    | Use Case                      |
| ------------------ | ----------------------------------------- | ----------------------------- |
| `full_access`      | All operations                            | Server-to-server integrations |
| `engagements_only` | Create/read engagements, receive webhooks | Limited integrations          |
| `read_only`        | Read engagements, patients, metrics       | Dashboards, reporting         |

### Creating Keys

Keys are provisioned during partner onboarding. To manage keys:

```bash theme={null}
# List active keys
GET /v1/auth/keys

# Rotate a key (old key remains valid for 24 hours)
POST /v1/auth/keys/{key_id}/rotate
```

**Response:**

```json theme={null}
{
  "id": "key_xyz789",
  "prefix": "hana_sk_live_",
  "scope": "full_access",
  "created_at": "2026-02-07T14:30:00Z",
  "expires_at": null,
  "last_used_at": "2026-02-07T14:28:00Z"
}
```

### Key Rotation

HANA supports zero-downtime key rotation. When you rotate a key:

1. A new key is issued immediately
2. The old key continues working for 24 hours
3. After 24 hours, the old key is automatically revoked

This gives your team time to update all services without disruption.

***

## Request Authentication

Include your API key in the `Authorization` header on every request:

```bash theme={null}
curl -X POST https://api.hana.health/v1/engagements \
  -H "Authorization: Bearer hana_sk_live_abc123..." \
  -H "Content-Type: application/json" \
  -d '{ ... }'
```

<CodeGroup>
  ```javascript Node.js theme={null}
  import { HanaClient } from '@hana-health/sdk';

  const hana = new HanaClient({
    apiKey: process.env.HANA_API_KEY,
    environment: 'production',
    region: 'eu'
  });

  // SDK handles authentication automatically
  const engagement = await hana.engagements.create({ ... });
  ```

  ```python Python theme={null}
  from hana_health import HanaClient

  hana = HanaClient(
      api_key=os.environ["HANA_API_KEY"],
      environment="production",
      region="eu"
  )

  # SDK handles authentication automatically
  engagement = hana.engagements.create(...)
  ```
</CodeGroup>

***

## Webhook Verification

All outbound webhooks from HANA are signed with HMAC-SHA256 using your webhook secret. Always verify the signature before processing events.

### Verification Flow

1. HANA computes `HMAC-SHA256(webhook_secret, raw_request_body)`
2. The signature is sent in the `X-Hana-Signature` header
3. Your server recomputes the HMAC and compares

<CodeGroup>
  ```javascript Node.js theme={null}
  const crypto = require('crypto');

  function verifyHanaWebhook(rawBody, signature, secret) {
    const expected = crypto
      .createHmac('sha256', secret)
      .update(rawBody)
      .digest('hex');
    return crypto.timingSafeEqual(
      Buffer.from(`sha256=${expected}`),
      Buffer.from(signature)
    );
  }

  // Express middleware
  app.post('/webhooks/hana', express.raw({ type: 'application/json' }), (req, res) => {
    const sig = req.headers['x-hana-signature'];
    if (!verifyHanaWebhook(req.body, sig, process.env.HANA_WEBHOOK_SECRET)) {
      return res.status(401).send('Invalid signature');
    }
    
    const event = JSON.parse(req.body);
    // Process event...
    res.status(200).send('ok');
  });
  ```

  ```python Python theme={null}
  import hmac
  import hashlib

  def verify_hana_webhook(raw_body: bytes, signature: str, secret: str) -> bool:
      expected = hmac.new(
          secret.encode(),
          raw_body,
          hashlib.sha256
      ).hexdigest()
      return hmac.compare_digest(f"sha256={expected}", signature)

  # Flask example
  @app.route('/webhooks/hana', methods=['POST'])
  def hana_webhook():
      sig = request.headers.get('X-Hana-Signature')
      if not verify_hana_webhook(request.data, sig, WEBHOOK_SECRET):
          abort(401)
      
      event = request.json
      # Process event...
      return 'ok', 200
  ```
</CodeGroup>

<Warning>
  Always use timing-safe comparison functions to prevent timing attacks. Never use `==` for signature comparison.
</Warning>

***

## Organization & Environment Isolation

Each organization gets fully isolated environments:

```
Organization: "Acme Health"
├── Production
│   ├── API Key: hana_sk_live_...
│   ├── Webhook Secret: whsec_live_...
│   └── Data: Production patients & engagements
└── Sandbox
    ├── API Key: hana_sk_test_...
    ├── Webhook Secret: whsec_test_...
    └── Data: Test patients only (no real calls)
```

Data never crosses environment boundaries. Sandbox keys cannot access production data.

***

## IP Allowlisting (Optional)

For organizations with strict network policies, restrict API access to specific IP ranges:

```bash theme={null}
POST /v1/auth/ip-allowlist
```

```json theme={null}
{
  "allowed_cidrs": [
    "203.0.113.0/24",
    "198.51.100.0/24"
  ]
}
```

When enabled, requests from non-allowlisted IPs receive `403 Forbidden`.

<Card title="Next: Webhooks" icon="bell" href="/developers/webhooks">
  Detailed guide to receiving and processing HANA events.
</Card>
