# Authentication & Tokens

Every request to `/payments-api/v1` authenticates with a **seller bearer token**. Tokens are scoped to a single seller account and grant full API access — treat them like passwords.

## The auth header

Include the token in every request as an HTTP Bearer token:

```http
Authorization: Bearer <your_token>
```

Anything else — missing header, malformed value, or an unrecognised token — returns `401 Unauthorized`.

## How to get a token

You obtain your token yourself — there is no need to wait on the Fynex team.

**From the dashboard (easiest):**

1. Log in to the dashboard with your username and password — staging: `https://staging-dashboard.fynex.ai`, production: `https://dashboard.fynex.ai`.
2. Select your seller account.
3. Open the **Integration** page. It shows the API token masked, with a reveal (eye) toggle and a copy button.
4. Click the reveal icon, then copy the token. This value is exactly what you pass as `Authorization: Bearer <token>`.

If you don't have a dashboard login yet, ask your Fynex contact to set you up.

**Programmatically:** `POST /api/v1/onboarding/start` (sign-up; sets the session cookie itself) — or, for an existing account, `POST /api/v1/login/dashboard` (which sets the
session cookie) → the `createSellerAccount` GraphQL mutation, which returns the new seller
account's `authorizationToken`. The first user of a new organization is automatically granted the
`selleraccounts:create` / `selleraccounts:update` permissions this requires. See the
[Account setup & onboarding](https://api.fynex.ai/payments-api/v2/docs#tag/onboarding) guide for the full walkthrough.

> [!IMPORTANT]
> A new seller account is created in `Demo` mode and **activated automatically**, so your token
> works immediately for **sandbox testing** — you can take test payments straight away (see the
> [Quickstart](https://api.fynex.ai/payments-api/v2/docs#tag/quickstart)). **Going live** (real-money processing) is separate: it requires
> KYB approval and a Fynex-assigned live acquirer terminal. If a sandbox call ever returns
> `403 seller account is not active`, the token is still valid — the account just isn't active
> yet; contact Fynex with your seller account ID.

> [!NOTE]
> A separate token is issued per seller account, and staging and production are independent — get the staging token from `https://staging-dashboard.fynex.ai` and the production token from `https://dashboard.fynex.ai`.

> [!CAUTION]
> The Integration page also has a **Regenerate** button. Regenerating issues a new token and **immediately invalidates the old one** — there is no overlap window. Only use it when you intend to rotate (see [Rotating a token](#rotating-a-token)).

## What a token looks like

Tokens are opaque database strings. There are **no prefix conventions** such as `sk_test_` or `sk_live_` — the string you receive is the full token value.

## Verifying a token

The quickest "is this token alive?" check is `GET /payment-methods`. It requires only a valid seller token and returns the payment methods enabled on your account.

#### curl

```bash
curl -sS "$FYNEX_API/payment-methods" \
  -H "Authorization: Bearer $FYNEX_TOKEN"
```

#### JavaScript

```js
const res = await fetch(`${process.env.FYNEX_API}/payment-methods`, {
  headers: { Authorization: `Bearer ${process.env.FYNEX_TOKEN}` },
});
const data = await res.json();
console.log(data); // { sellerAccountId, allowedPaymentMethods, allowedCurrencies, ... }
```

#### Python

```python
import os, requests

res = requests.get(
    f"{os.environ['FYNEX_API']}/payment-methods",
    headers={"Authorization": f"Bearer {os.environ['FYNEX_TOKEN']}"},
)
print(res.json())  # { "sellerAccountId": ..., "allowedPaymentMethods": [...], ... }
```

A `200 OK` JSON response confirms the token works and the account is active. A `401` means the token is missing, malformed, or invalid. A `403 seller account is not active` means the token is **valid and recognised** but the account isn't active — contact Fynex with your seller account ID (see [How to get a token](#how-to-get-a-token)).

## Storing tokens securely

- **Never commit tokens to source control.** Use a secret manager (HashiCorp Vault, AWS Secrets Manager, GitHub Encrypted Secrets) or environment variables loaded at runtime.
- **One token per environment.** Keep staging and production tokens separate.
- **One token per seller account.** There is no cross-account access; operate multiple sellers with one token each.

```bash
# .env — never commit this file
FYNEX_TOKEN=<your_token_here>
FYNEX_API=https://api.fynex.ai/payments-api/v1
```

## Rotating a token

The simplest way to rotate is the **Regenerate** button on the dashboard Integration page (the same page you got the token from). It issues a new token and reveals it for copying.

> [!CAUTION]
> Rotation **atomically replaces** the existing token. The old token becomes invalid **immediately** — there is no two-token overlap window. Plan a brief service restart or deploy when rotating.

If you prefer to automate it, the same operation is exposed as the GraphQL mutation `regenerateSellerAccountToken` on the `/dashboard/graphql` endpoint (cookie-session authenticated).

### Rotation steps (GraphQL)

1. **Authenticate with the dashboard** to obtain a `dashboard_session` cookie:

   ```bash
   curl -c cookies.txt -X POST https://api.fynex.ai/api/v1/login/dashboard \
     -H "Content-Type: application/json" \
     -d '{"email": "you@example.com", "password": "..."}'
   ```

2. **Call `regenerateSellerAccountToken`** with your seller account ID as `merchantId`:

   ```bash
   curl -b cookies.txt -X POST https://api.fynex.ai/dashboard/graphql \
     -H "Content-Type: application/json" \
     -d '{
       "query": "mutation Rotate($merchantId: ID!) { regenerateSellerAccountToken(merchantId: $merchantId) { authorizationToken } }",
       "variables": { "merchantId": "42" }
     }'
   ```

   The response contains the new `authorizationToken` value.

3. **Update your services** — replace the token in your secret manager / environment and restart affected services before the old token is invalidated (which happened in step 2).

### When to rotate

- A developer with access leaves the team.
- You suspect or confirm a token leak.
- As a precautionary measure on a regular schedule (quarterly is a common default).

## Token properties

| Property | Value |
|----------|-------|
| Scope | Single seller account |
| Expiry | None — tokens do not expire |
| Revoke operation | None — rotate to invalidate |
| Multiple tokens per account | Not supported — one bearer token per seller account |
| Rate limiting | Per-seller token-bucket rate limit; the budget differs by environment — read `X-RateLimit-Limit` / `X-RateLimit-Remaining` rather than assuming a rate. Returns `429` with a `Retry-After` header when exceeded |

## Common errors

| Status | Body | Cause |
|--------|------|-------|
| `401` | `authorization token is required` (plaintext) | Header absent or malformed at the middleware level |
| `401` | `invalid authorization token` (plaintext) | Token value is not recognised |
| `401` | `{"error": "seller auth is required"}` | Missing seller context inside a handler |
| `403` | `seller account is not active` (plaintext) | Token is valid but the seller account isn't active — new demo accounts activate automatically; if you see this, contact Fynex with your seller account ID |
| `403` | `{"error": "resource does not belong to this seller"}` | Token is valid but the resource belongs to a different seller |

> [!NOTE]
> The `401` response body from the auth middleware is plain text, not JSON. Once inside a handler, all error responses are JSON `{"error": "..."}`.

## See also

- **[Quickstart](https://api.fynex.ai/payments-api/v2/docs#tag/quickstart)** — Take your first test payment in 5 minutes.
- **[Errors](https://api.fynex.ai/payments-api/v2/docs#tag/errors)** — Full status code and error body reference.
- **[Request headers](https://api.fynex.ai/payments-api/v2/docs#tag/headers)** — All request headers reference — required, conditional, and optional.
