# Authentication

> Secret API keys, the test and live environments, scopes, rotation and IP allowlists.

Source: https://rais.app/docs/authentication

Every request to the API carries a **secret key** as a bearer token. A key belongs to one store and one environment, can be limited to some operations, and can be restricted to the addresses your servers use.

## Send the key


**Request**

```bash
curl https://api.rais.app/v1/account \
  -H "Authorization: Bearer rais_sk_test_..."
```


The header is `Authorization: Bearer <key>`. There are no other credentials: no client id, no signature, no session. If the key is missing or wrong, the answer is `401` with the code `invalid_api_key`.

## Key format

| Key | Environment |
| --- | --- |
| `rais_sk_test_` followed by 43 characters | Sandbox |
| `rais_sk_live_` followed by 43 characters | Live |

Raïs stores only a one-way hash of your key, so it cannot show the key again after you create it. Copy it into your secret manager when it appears. If you lose it, create a new one and revoke the old one. The dashboard lists keys by their prefix and last four characters.

## Test and live

The environment of a request is decided by the key and nothing else: there is no header or parameter to switch.

| | Sandbox key | Live key |
| --- | --- | --- |
| Payments | Test payments, `livemode: false` | Real payments, `livemode: true` |
| Cards | SATIM test cards only | Real CIB and Edahabia cards |
| Money | Never moves, no balance changes | Credited to your balance |
| Availability | Always | After business verification: the dashboard creates no live key before |

Sandbox and live data are separate worlds. A sandbox key never sees a live payment and the other way round: asking for one answers `404`.

## Scopes

A key carries one or more scopes. A request that needs a scope the key lacks answers `403` with `insufficient_scope`. New keys get every scope; give a key only what its job needs.

| Scope | Allows |
| --- | --- |
| `payments:read` | Retrieve and list payments and refunds, and fetch receipts |
| `payments:write` | Create and cancel payments |
| `refunds:write` | Refund payments |
| `events:read` | Retrieve and list events |

`GET /account` works with any valid key, whatever its scopes, which makes it the right call for checking a key.

## Create, roll and revoke keys

In the dashboard, open **Developers**, then **API keys**.

- **Create.** Choose the store and the environment (**Test** or **Live**; **Live** is offered for a store approved for live payments only), a name and the scopes, and optionally an IP allowlist. The secret is shown once. A key has no expiry date: it works until you roll or revoke it.
- **Roll.** Replace a key without downtime: the new key has the same store, environment, scopes and allowlist. The old key stops working at once, or after a grace period of **1 hour, 24 hours or 7 days** that gives you time to deploy the new key first; the dashboard shows until when the old key is valid, and after that it is expired and answers `401 invalid_api_key`. The new secret is shown once, like any new key.
- **Revoke.** The key stops working for good. A revoked key is refused on the server that handles the revocation immediately and everywhere else within 10 seconds.

A store can have up to 50 keys in each environment: revoke the ones you no longer use.


> [!TIP]
> **Roll keys on a schedule**
>
> Rolling with a grace period needs no maintenance window: deploy the new key, check your logs for the old key's prefix going quiet, then let the grace period end.


## IP allowlist

A key can be limited to up to 50 IP addresses or CIDR ranges, IPv4 or IPv6, for example `203.0.113.7` or `198.51.100.0/24`. A request from any other address is refused exactly like an unknown key: the answer does not say why. Use the public address your server connects from, which behind a NAT gateway is the gateway's address.

## One key, one store

A key can only read and change the payments, refunds and events of **its own store** in **its own environment**.


> [!NOTE]
> **What a 404 means**
>
> `payment_not_found`, `refund_not_found` and `event_not_found` are returned when the object does not exist, belongs to another store, or belongs to the other environment. The three cases are deliberately indistinguishable, so a key cannot be used to find out what exists elsewhere.


Refunds follow the same rule: a key can refund only payments of its own store and environment. If you run several stores, give each one its own keys.

## Authentication errors

| Status | Code | Meaning |
| --- | --- | --- |
| 401 | `invalid_api_key` | The key is missing, malformed, unknown, revoked or expired; it is used from an address outside its allowlist; or its store is suspended, disabled or, for a live key, not approved for live payments. The answer is the same in every case, so that a key cannot be used to learn anything. |
| 403 | `insufficient_scope` | The key lacks the scope this endpoint needs. |
| 403 | `account_disabled`, `live_mode_not_enabled` | Listed in the OpenAPI document, but the key check answers `401 invalid_api_key` before they can happen. Handle a `403` like any other refusal and contact support if it persists. See [Going live](https://rais.app/docs/going-live.md). |
| 429 | `rate_limit_exceeded` | Too many requests from this key, or more than 30 failed authentications in a minute from one address. While an address is throttled for failed authentications, every request from it answers `429`, even with a valid key. Wait for `Retry-After`. |

A `401` carries a `WWW-Authenticate: Bearer realm="Rais API"` header. See [Errors](https://rais.app/docs/errors.md) for the envelope.

## Keeping keys safe

- Keep keys on your server, in environment variables or a secret manager. Never in a web page, a mobile app, a repository, a log or a support ticket.
- The API sends no CORS headers: a browser cannot call it with a key, by design. Give your front end the `checkout_url` of a payment instead.
- Use a separate key for each service, with only the scopes it needs, and an allowlist when your servers have fixed addresses.
- If a key may have leaked, roll or revoke it first and investigate afterwards. See [Security and safety](https://rais.app/docs/security.md).
