# Authentication

<BackToOverview />

Every request to the Kwik Nkap Payments API is authenticated with a single secret API key. The key you send also decides which environment you are talking to: sandbox or live. There is no publishable key and no separate client-side key.

## The API key

You authenticate by sending your secret key in a request header. The canonical header is `X-API-Key`:

```bash title="Authenticate a request"
curl https://api.kwiknkap.com/v1/payments \
  -H "X-API-Key: kn_sk_test_4f8a2c9e1b7d6035a2e9c4f1b8d70a36"
```

For convenience the gateway also accepts the same key in two alternative headers, so you can reuse an existing HTTP client without rewriting it:

| Header | Example |
| --- | --- |
| `X-API-Key` (canonical) | `X-API-Key: kn_sk_test_...` |
| `Authorization` (bearer) | `Authorization: Bearer kn_sk_test_...` |
| `api-key` | `api-key: kn_sk_test_...` |

All three are equivalent. Pick one. If you send more than one, `X-API-Key` is what we read first.

A request with a missing or invalid key returns `401 Unauthorized`.

:::info
Keys are created in the dashboard **Developers** section and the full secret is shown only once. Store it immediately. See [API keys](/en/payments/api-keys) for creating, revoking, and rotating keys.
:::

## Environments

Kwik Nkap has two fully separate environments. **The key prefix selects the environment** — you never pass an environment flag, header, or query parameter.

| Prefix | Environment |
| --- | --- |
| `kn_sk_test_...` | Sandbox |
| `kn_sk_live_...` | Live |

Both environments share the same base URL — you do not switch hosts. The key prefix alone routes the request:

<ApiBaseUrl />

Sandbox and live are backed by **separate databases**. A payment created with a `kn_sk_test_` key never appears under a `kn_sk_live_` key, and vice versa. Sandbox simulates Mobile Money outcomes with no real phone prompt and moves no real money; live charges real wallets. Build and test against sandbox, then swap the key prefix to go live.

:::note
Object IDs also carry the environment. A sandbox payment looks like `knpay_test_9f2c41a7b8e04d6fa1c3e58b7d92f014`; a live payment looks like `knpay_...` without the `test_` segment. See [Testing](/en/payments/testing) for sandbox behavior and the guaranteed-success / guaranteed-failure test phone numbers.
:::

## There is no publishable key

Kwik Nkap does **not** use a Stripe-style `sk_`/`pk_` pair. There is exactly **one secret key per environment**, and it is always a secret. There is nothing safe to expose in a browser or a mobile app.

:::warning
Your API key is a secret. It can move real money. Keep it on your server only.

- Never embed a key in a browser bundle, mobile app, single-page app, or any client the public can inspect.
- Never commit a key to source control. Load it from an environment variable or a secrets manager.
- Make all Payments API calls from your backend. If a customer-facing surface needs to start a payment, have it call your server, and let your server call Kwik Nkap.
- If a key leaks, **rotate it immediately** in the dashboard and revoke the old one. See [API keys](/en/payments/api-keys).
:::

## IP allowlisting (optional)

Each business can optionally restrict which source IPs may use its keys. If your business has any active allowlist entries (CIDR ranges), requests from any other IP are rejected with `403 Forbidden`. An empty allowlist means all IPs are allowed. You manage this in the dashboard Developers section alongside your keys.

## What to read next

- [API keys](/en/payments/api-keys) — create, revoke, and rotate keys, and configure the IP allowlist.
- [Testing](/en/payments/testing) — sandbox behavior, simulated outcomes, and test phone numbers.
- [Create a payment](/en/api/direct-payments) — your first authenticated call.
