# Environments

An **environment** is the base URL a request goes to — which project's database and providers
it reaches. This is a different question from **test mode** (the `sk_live_`/`sk_test_` key
prefix): see [Test mode](/test-mode) for what that prefix does and does not do yet. This page
answers only "where do I send the request".

## Base URL

| Environment | Base URL | Status |
|---|---|---|
| Production | `https://api.swaps.app/v1` | The only base URL this reference publishes. Each resource runs behind its own operator switch; while a resource's switch is off, its operations answer `503 temporarily_unavailable`. |
| Development | Not published here | A separate, non-production project, with the Bridge sandbox behind it. Issued out of band, together with a development key, when the account team grants one — not self-service, and not covered by any availability commitment. |

There is exactly one server in the published OpenAPI contract (`servers` in the generated
reference). If you were handed a development base URL directly, it is a different host from the
one above — treat it as internal-use, not something to build a public integration against.

## Hosted payer pages

A payment link's `url` opens the payer page of the project that holds the link:

| Project | Payer page | Who gets it |
|---|---|---|
| Production | `https://swaps.app/pay/<token>` | Live keys. A test key's link has no page here, so its `url` is `null`. |
| Development | `https://staging.swaps.app/pay/<token>` | Live and test keys on the development project. |

The host comes from each project's own configuration, not from the code. Until a project's host
is configured, `url` is `null`: no address is guessed, and a development link never gets a
production URL, where its token would not resolve.

Payer e-mails (invoice, reminder, payment nudge) do not read this setting yet: their `/pay` links
still follow the project's configured origin and can differ from `url`. Share `url` itself when the two
must match.

## The `/v1` path convention

The base URL already carries the version segment: production's base URL is
`https://api.swaps.app/v1`, so every path in the [reference](/reference) — `/payment_links`,
`/capabilities`, `/account` — is relative to it, never repeated. A full request URL is the base
URL followed directly by the path, e.g. `https://api.swaps.app/v1/payment_links` (see the
[Get started](/get-started) quickstart for a runnable example).

## Key prefix and environment

`sk_live_…` and `sk_test_…` are not something you choose per request — the prefix is fixed for a
given key at the moment it is issued, because it is fixed for the *account* the key belongs to:
every account carries its own `livemode` (set once, at creation), and a key can only be minted in
the mode that matches its account. This is enforced twice, not just documented: a database
trigger refuses to write an `api_keys` row whose `mode` disagrees with its account's `livemode`,
and the request-time auth check re-derives the expected mode from the account and compares it
again before honoring the key. A mismatch — a key that somehow disagrees with its own account — is
`401 invalid_api_key`, never a silent downgrade to the other mode and never a partial success.

Asking for a key in the other mode is resolved at issuance, not at use: from a live account,
`POST /v1/api_keys` with `livemode: false` mints the key on the account's test twin, creating the
twin the first time (see [Test mode](/test-mode)); from a test twin, `livemode: true` answers
`400 mode_mismatch` (`param: mode`).

Once the key authenticates, its account's mode — which the prefix always matches — decides how
the request runs. `sk_live_…` runs in production against live providers. `sk_test_…` runs as the
account's test twin, on the same base URL: API-key, request-log and account reads run in
production; webhook endpoints, webhook deliveries and the per-object event lists are forwarded to
the development project over a signed hop; and every other operation a key may call answers
`503 temporarily_unavailable`.

What the base URL does decide is where the key is looked up at all: a key is a row in one
project's database, so it authenticates only against the base URL of the project that issued it —
a development key sent to `https://api.swaps.app/v1` fails authentication like any unknown key,
and a production key sent to a development base URL does the same. See
[Test mode](/test-mode) for exactly what is and is not implemented.

Next: [Test mode](/test-mode) for the key-prefix contract in full · [Get started](/get-started)
for a runnable request against the published base URL.
