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

# Authenticate API requests with Borga

> Borga uses API keys to authenticate requests. Learn about secret and publishable keys, the Merchant ID header, idempotency, and how to handle auth errors.

Borga authenticates every API request using API keys. You include your secret key in the `Authorization` header of each request. Requests without a valid key are rejected before they reach any endpoint. There are two key types — one for server-side calls and one for browser-side flows — and you should never mix them up.

<Warning>
  Never commit a secret key to source control, expose it in client-side JavaScript, or share it in a public channel. If a secret key is compromised, rotate it immediately from the dashboard under **Settings → API Keys**.
</Warning>

## Key types

Borga issues two distinct key types, each with a different scope and environment variant.

| Key prefix  | Type                    | Where to use                              |
| ----------- | ----------------------- | ----------------------------------------- |
| `sk_live_…` | Secret — live mode      | Server-side only. Processes real charges. |
| `sk_test_…` | Secret — test mode      | Server-side only. No real charges.        |
| `pk_live_…` | Publishable — live mode | Browser-side embedded checkout only.      |
| `pk_test_…` | Publishable — test mode | Browser-side embedded checkout only.      |

**Secret keys** have full API access and must only ever appear in server-side code — environment variables, secrets managers, or backend configuration files. **Publishable keys** have a restricted scope limited to initiating embedded checkout sessions, making them safe to include in browser-side JavaScript.

## Passing your secret key

Include your secret key as a Bearer token in the `Authorization` header of every request:

```bash curl theme={null}
curl --request GET \
  --url https://api.borga.is/v1/merchants/current \
  --header "Authorization: Bearer sk_test_YOUR_SECRET_KEY" \
  --header "X-Merchant-Id: mer_YOUR_MERCHANT_ID"
```

There is no separate login step or token exchange — the key itself is the credential.

## Merchant ID header

Most Borga endpoints require an `X-Merchant-Id` header identifying which merchant account the request is for. Your Merchant ID has the format `mer_…` and is visible in the dashboard under **Settings → API Keys**.

You can also retrieve it programmatically:

```bash curl theme={null}
curl --request GET \
  --url https://api.borga.is/v1/merchants/current \
  --header "Authorization: Bearer sk_test_YOUR_SECRET_KEY"
```

```json theme={null}
{
  "id": "mer_01HXYZ9876ABCDEF",
  "name": "Acme ehf.",
  "created_at": "2026-01-15T08:00:00Z"
}
```

<Note>
  The `GET /v1/merchants/current` endpoint does not require the `X-Merchant-Id` header — it infers the merchant from the API key itself. Use it to look up your Merchant ID during initial setup.
</Note>

Set the Merchant ID as an environment variable alongside your secret key so both are available to your server at runtime:

```bash theme={null}
BORGA_SECRET_KEY=sk_test_YOUR_SECRET_KEY
BORGA_MERCHANT_ID=mer_YOUR_MERCHANT_ID
```

## Idempotency

Network failures can leave you uncertain whether a request was processed. To safely retry a `POST` request without risking duplicate charges, include an `Idempotency-Key` header with a unique string (a UUID works well). Borga stores the result of the first request and returns the same response for any subsequent requests with the same key, without executing the operation again.

```bash curl theme={null}
curl --request POST \
  --url https://api.borga.is/v1/payments \
  --header "Authorization: Bearer sk_test_YOUR_SECRET_KEY" \
  --header "X-Merchant-Id: mer_YOUR_MERCHANT_ID" \
  --header "Idempotency-Key: a1b2c3d4-e5f6-7890-abcd-ef1234567890" \
  --header "Content-Type: application/json" \
  --data '{
    "amount": 5000,
    "currency": "ISK",
    "description": "Order #1042"
  }'
```

<Tip>
  Generate a fresh UUID for each distinct operation. Reusing the same key for a different payload will return the original response, not a new one — Borga deduplicates on the key alone, not the combination of key and body.
</Tip>

## Test vs. live mode

The key prefix determines the mode of every request:

* **`sk_test_…`** — test mode. No real charges are made. Card numbers, payments, and invoices are all simulated. Use this during development and staging.
* **`sk_live_…`** — live mode. Requests process real ISK charges immediately.

You switch modes simply by swapping the key. No other configuration changes are required.

## Authentication errors

| HTTP status        | Meaning                                                                       | What to do                                                                                                                     |
| ------------------ | ----------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------ |
| `401 Unauthorized` | The `Authorization` header is missing, malformed, or contains an invalid key. | Verify the key is correct, has not been rotated, and is being passed as `Bearer sk_…` in the header.                           |
| `403 Forbidden`    | The key is valid but does not have permission to perform this action.         | Check that you are using a secret key (not a publishable key) for server-side calls, and that the key has the required scopes. |

A `401` response body looks like this:

```json theme={null}
{
  "error": {
    "code": "unauthorized",
    "message": "Invalid API key provided."
  }
}
```

Inspect the `error.message` field for a human-readable explanation before contacting support.
