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

# Authentication

> Two flows: one for a product several sellers connect to, one for a seller's own script.

## A product used by several sellers

The authorization code flow with PKCE. You send the seller to Wristler, they sign
in, tick which of their businesses you may act for and see exactly which scopes you
are asking for, and you get a code back to exchange for a token.

**1. Send them to the consent screen**

```
https://wristler.eu/oauth/authorize
  ?client_id=YOUR_CLIENT_ID
  &redirect_uri=https://your-app.example/oauth/callback
  &response_type=code
  &scope=watches:read%20watches:write%20orders:read
  &state=RANDOM
  &code_challenge=BASE64URL_SHA256_OF_VERIFIER
  &code_challenge_method=S256
```

Your redirect URI has to be registered with us beforehand and match exactly,
including the port.

**2. Exchange the code**

```bash theme={null}
curl -X POST https://api.wristler.eu/oauth/token \
  -d grant_type=authorization_code \
  -d client_id=$CLIENT_ID \
  -d client_secret=$CLIENT_SECRET \
  -d redirect_uri=https://your-app.example/oauth/callback \
  -d code=$CODE \
  -d code_verifier=$VERIFIER
```

**3. Keep it alive**

Access tokens last **one hour**, refresh tokens **90 days**, and a refresh is
renewed on every use — so an integration that runs at least once a quarter
never needs the seller again.

Refresh on the clock, before a call fails, rather than on a `401`. A refresh
that comes back `invalid_grant` means the link is broken: re-authorize rather
than retrying.

> [!TIP]
>
> **Testing this by hand is awkward**, because the playground in the reference can
> exchange a code but cannot get one: that half is a redirect, an approval and a
> redirect back. On staging there is a page that does it for you —
> `/oauth/playground` on the website — which starts the flow, keeps the PKCE
> verifier, and hands back the code and the verifier side by side, ready to paste
> into the token form. It does not exist in production.

> [!NOTE]
>
> The two halves live on different hosts, which most OAuth clients ask for
> separately. The authorization URL is on the website,
> `https://wristler.eu/oauth/authorize`, because it is a page a person looks at.
> The token URL is on the API, `https://api.wristler.eu/oauth/token`.

## A seller's own script or cron job

Client credentials. No browser and no redirect: the seller creates the client
themselves in **Trader → Settings → Integrations**, and owning it is the
authorization.

```bash theme={null}
curl -X POST https://api.wristler.eu/oauth/token \
  -d grant_type=client_credentials \
  -d client_id=$CLIENT_ID \
  -d client_secret=$CLIENT_SECRET \
  -d 'scope=watches:read watches:write orders:read'
```

The secret is shown once, when the key is made. We store only a hash of it, so the
only way back from losing it is a new key.

The key may do only what the seller ticked when they made it: a token that asks for
more is still answered `403` on the rest. A token lasts
**one hour** and comes without a refresh token: when it runs out, ask for a new one
the same way.

Nobody is signed in behind such a token: the business is. `GET /v1/me` answers with
the key itself — the client id as `id`, the key's name as `name`, every personal field
`null` — and its `sellers` is the one business that made it, which is also all
`/v1/sellers` lists.

> [!NOTE]
>
> A client with a secret has to authenticate at the token endpoint even when it uses
> PKCE. A client without one — an app, anything that would have to ship its secret —
> proves itself with the verifier alone, which is what PKCE is for.

## What an authorization actually is

**It belongs to the business, not to the person who approved it.** Any of their
colleagues can withdraw it later, and it survives that person leaving. You reach
exactly the businesses that ticked you through — never the other businesses the
approver happens to work at.

**What each business allowed is checked per request.** Your token carries the union
of what every business that connected you agreed to, so the token alone cannot
answer "may I do this here". [The two checks →](https://docs.wristler.eu/concepts/scopes.md)

**A seller can withdraw you at any time**, from the same screen. Every token your
client issued stops reaching that business immediately; there is nothing to expire
first. The business drops out of `/v1/sellers` and its addresses answer `404`, as
if it were never there. A seller who narrows what you may do instead makes the
withdrawn half answer `403`. Treat either as the seller's decision, and tell them
what reconnecting would restore rather than retrying in a loop. Withdrawing a
seller's own key revokes the key itself, so its next token request fails.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.