<!-- Source: https://docs.infraio.xyz/en/security/api-keys -->
<!-- Last updated: 2026-10-04 -->

# API keys

Three kinds of credentials, three threat models.

## `pk_` — Publishable

- Designed to ship to the browser. Sent as the `X-Client-ID` header
  on every signed B2B request, also embedded in the SDK bundle for
  client-side checkout opens.
- Can identify your account; cannot create sessions, read other
  merchants' data, or trigger anything destructive.
- A leaked publishable key is a **low-severity** event.

## `sk_` — Secret (HMAC signing key)

- The HMAC-SHA256 signing key for all B2B API calls — see
  [Authentication](https://docs.infraio.xyz/en/api-reference/authentication).
- Never travels over the wire. Only its derived per-request
  signature does. So you only have to worry about leaks at the
  *storage* layer (env vars, git, logs), not at the transport layer.
- Server-only. Should never appear in a browser bundle, public
  repo, screenshot, or chat message.
- A leaked secret is a **high-severity** event.

## `whsec_` — Webhook signing secret

- Used to verify the signature on **inbound** webhook deliveries
  from us to your server. See
  [Signature verification](https://docs.infraio.xyz/en/webhooks/signature-verification).
- Separate per webhook endpoint — if you have 3 registered endpoints,
  you have 3 distinct `whsec_` secrets. Environment is encoded in
  the prefix: `whsec_live_…` / `whsec_test_…`.
- Server-only. Like `sk_`, never travels over the wire — only used
  to verify HMACs locally.
- **Rotation has a 24-hour grace window.** Click Rotate and the
  previous secret stays accepted for 24h alongside the new one
  (deliveries carry both `X-Signature` and `X-Signature-Prev`), so
  you can redeploy your verifier without holding traffic.
- **Reveal-existing-secret** is available, gated by fresh 2FA and
  recorded in the audit log — for the case where the secret was
  lost and rotation isn't acceptable. The dashboard's default
  posture is "rotate, don't reveal".
- A leaked webhook secret lets an attacker fake events to your URL.
  **Medium-to-high severity** depending on how much you trust the
  event payload.

## Scopes

Secret keys are scoped. The dashboard lets you mint keys with one of
these scope bundles:

| Scope | Can do | Used for |
| --- | --- | --- |
| `read` | List/read orders, sessions, refunds, balances | Read-only integrations (analytics, BI) |
| `write_order` | All `read` + create sessions, create orders, cancel orders | Storefront backend |
| `write_refund` | All `read` + create refunds, mark refunds executed | Customer support tools |
| `webhook_manage` | All `read` + manage webhook endpoints | DevOps tooling |

A default-mint "full access" key gets all four. Minting per-purpose
keys is still good practice because it documents intent, but read the caveat below before you
treat scope as a security boundary.

> **Warning:**
>
> **Scopes are not enforced yet.** A leaked `sk_` key of *any* scope can
> call *any* `/b2b/v1/*` endpoint for your merchant. A `read` key is
> not prevented from creating a refund. Narrow scopes do **not** limit
> the damage of a leak yet: for security planning, treat every secret
> key as full access and rely on fast rotation and revocation
> (below) to contain a leak.

## Rotation

1. **Generate a new key.** Dashboard → **Developers → API keys** →
   **+ Add key**. Pick scope. The dashboard displays the secret
   **once** — store it immediately.
2. **Roll your env vars** to the new value across all environments.
   Deploy.
3. **Verify traffic.** Dashboard shows per-key request counts in
   real time. Wait for the old key's count to drop to zero.
4. **Revoke the old key.** Same screen → kebab menu → **Revoke**.

> **Warning:**
>
> There is **no automatic overlap window today** — once you revoke a
> key, any in-flight request signed with it gets `401`. Plan your
> rotation accordingly: deploy the new key first, drain traffic from
> the old, then revoke.

## Emergency revocation

If a key has leaked (in git history, in a public bundle, in a
logged stack trace, in a partner's pen-test report) — revoke it
immediately, even at the cost of some failed requests. Better to
fail loudly than to let an attacker hold a valid credential.

Steps:

1. **Dashboard → Developers → API keys → [key] → Revoke now.**
   Effect is instant; no grace period.
2. Mint a replacement and deploy.
3. Audit recent activity — dashboard shows the last 30 days of
   requests per key with IPs and endpoints hit.

If you suspect the breach is broader than one key, contact
contact@lartech.xyz to:

- Get a full audit log export for your merchant account
- Rotate webhook secrets in bulk
- Optionally freeze the account while you investigate

## Storage best practices

- **Env vars only.** Never commit secrets to git, even in a
  `.env.example` that says "REPLACE ME".
- **Per-environment keys.** Different `sk_test_…` and `sk_live_…`
  for dev/staging/prod, sourced from your secrets manager (AWS
  Secrets Manager, Vault, Doppler, …).
- **Restrict env var access.** In Kubernetes, mount as a `Secret`,
  not a `ConfigMap`. In Vercel/Netlify, use environment-variable
  scoping not project-wide globals.
- **Don't log requests with bodies.** Even when debugging — your
  HMAC signature in `X-Signature` is single-use but your business
  payload may include PII.

## What's NOT supported today

- ❌ **IP allowlisting** for secret keys.
- ❌ **OAuth-style scoped per-user tokens.** The current key model
  is per-merchant, not per-user.
- ❌ **Automatic key rotation** (for example, weekly rotation enforced by
  the platform). Rotation is manual.

## What's next

- [Authentication](https://docs.infraio.xyz/en/api-reference/authentication) — exact signing
  algorithm for B2B calls.
- [Webhooks → Signature verification](https://docs.infraio.xyz/en/webhooks/signature-verification)
  — how `whsec_` is used on incoming events.
