# Authentication

> Secret and publishable keys, user sessions, and why everything goes through your server.

Every request carries an API key. Requests for a signed-in user also carry that user's session. Both are sent by **your server**.

## Keys

```http
Authorization: Bearer sk_test_51Hx…
```

| Key | Prefix | Where it lives | What it can do |
|---|---|---|---|
| Secret | `sk_live_…`, `sk_test_…` | Your server's environment | Everything a storefront does, including acting for a signed-in user. |
| Publishable | `pk_live_…`, `pk_test_…` | Native apps | Public data only: the marketplace, listings, search, categories, catalogue terms, locations. |

- **Live** keys work on `https://api.mongrl.com`, **test** keys on `https://api.staging.mongrl.com`.
- A key belongs to one marketplace, so it also says which marketplace you mean. There's no marketplace id to send.
- The marketplace's team creates and revokes keys in the console. A key is shown once.
- A key that doesn't work (unknown, revoked, malformed) gets `401`, never anonymous access.
- `GET /v1/marketplace` is the quickest way to check a key.

## Why your server makes the calls

The API doesn't accept requests from web pages on other sites, and the secret key must never reach a browser. So your storefront's server calls mongrl, and your pages call your server. That's also where sessions stay safe: in an HttpOnly cookie that scripts can't read.

```text
browser ──your cookie──▶ your server ──sk_… + X-User-Session──▶ mongrl API
```

Frameworks with server functions or route handlers (TanStack Start, Next.js, Remix, SvelteKit, Nuxt) make this short: one server-only module builds the client per request.

## User sessions

Signing in returns a session:

```json
{
  "user": { "id": "4c1e…", "email": "mia@example.com", "first_name": "Mia" },
  "session": { "token": "q8Jx2mR…", "expires_at": "2026-11-18T10:00:00Z" }
}
```

1. Store `session.token` in a cookie: `HttpOnly; Secure; SameSite=Lax; Path=/`. Give it a long life (90 days); the API decides when the session ends.
2. On requests for that user, send it as `X-User-Session: <token>` together with your secret key.
3. A session ends 30 days after it was last used, and 90 days after sign-in at the latest.
4. Signing out is `DELETE /v1/sessions/current`. Then clear the cookie.
5. A `401` on a user request means the session has ended: clear the cookie and send them to sign in.

The token is shown only when the session is created. mongrl stores just a hash of it.

## Which endpoints need what

| Endpoints | Key | Session |
|---|---|---|
| `/v1/marketplace`, `/v1/listings`, `/v1/categories`, `/v1/catalog-terms`, `/v1/locations` | secret or publishable | no |
| `/v1/users`, `/v1/sessions…`, `/v1/password-resets…`, `/v1/email-verifications` | secret | no (they create one) |
| `/v1/me…` (profile, listings, orders, favourites, messages, payouts) | secret | yes |
| `/v1/images/{id}/{file}` | none | no |

Listing responses include `favorited` (has this user saved it?) only when you send a session.

## Common mistakes

- **Calling the API from client-side code.** Move the call into a server function.
- **Putting the token in localStorage.** Use an HttpOnly cookie.
- **Sharing one client between requests with a session in it.** Build the client per request, so one user's session never leaks into another's request.
