# Authenticate with API keys and OAuth

Authenticate with an API key or OAuth, and learn what each credential expires after.

Source: https://getolet.com/resources/developers/authentication/
Updated: 2026-10-15

Every request to the OLET public API needs either an API key or an OAuth access token in the `Authorization` header. Use an API key for a script or an internal integration you control; use OAuth when you are building something a merchant installs and approves themselves.

## How do I authenticate a request

Send the credential as a bearer token:

```bash
curl https://api.oletapp.com/your-store/v1/me \
  -H "Authorization: Bearer olet_live_YOUR_API_KEY"
```

```js
const response = await fetch('https://api.oletapp.com/your-store/v1/me', {
  headers: { Authorization: 'Bearer olet_live_YOUR_API_KEY' },
})
```

An OAuth access token goes in the same header, in place of the API key.

## Where do I create an API key

In the back office, go to Integrations, then **API & apps**, and click **Create API key** under **API keys**. Give the key a name and tick the scopes it needs, grouped as Catalog, Inventory, Customers and Sales. Only the Catalog read scope is ticked to start with, and ticking a write scope also ticks its read scope. Click **Create key**, then copy the key from the **API key created** window. It reads "Copy this key now. You won't be able to see it again."

Only store admins can open this page, and creating a key needs the Growth plan. Your API base URL is shown above the key list, ready to copy.

To stop a key, click the revoke button on its row and confirm with **Revoke key**. Apps using it stop working immediately.

## What does OAuth look like

OLET supports the standard authorization code flow, with PKCE (`S256`) supported for public clients.

1. Send the merchant to authorize:

   ```
   https://admin.oletapp.com/oauth/authorize?response_type=code&client_id=YOUR_CLIENT_ID&redirect_uri=YOUR_REDIRECT_URI&scope=catalog:read+sales:read&state=YOUR_STATE
   ```

   Only `response_type=code` is supported. To use PKCE, also send `code_challenge` and `code_challenge_method=S256`.

2. The merchant signs in and sees **Connect YOUR_APP to YOUR_STORE**, with the access your app asked for. They click **Allow access** or **Cancel**. OLET then redirects back to your `redirect_uri` with a `code` (or `error=access_denied` on Cancel), and your `state` echoed back.
3. Exchange the code within 10 minutes. A code works once.

   ```bash
   curl https://api.oletapp.com/oauth/token \
     -u "YOUR_CLIENT_ID:YOUR_CLIENT_SECRET" \
     -d grant_type=authorization_code \
     -d code=THE_CODE \
     -d redirect_uri=YOUR_REDIRECT_URI
   ```

   Send the client credentials either as HTTP Basic auth, as above, or as `client_id` and `client_secret` in the body, but not both. If you used PKCE, add `code_verifier`.

The response carries `access_token`, `refresh_token`, `token_type` (`Bearer`), `expires_in` (`3600`), `scope` (the granted scopes, separated by spaces), `store_name`, and `api_base_url`, the exact base URL to call for this store.

An access token lasts 1 hour. When it expires, exchange the refresh token for a new access token with `grant_type=refresh_token` and `refresh_token=THE_REFRESH_TOKEN`. The refresh token itself does not rotate: you get the same refresh token back every time, so keep using it.

`POST /oauth/token` errors use the OAuth shape, `{"error": "invalid_grant", "error_description": "..."}`, not the usual [error envelope](/resources/developers/errors/). A code or token that is wrong, expired or already used answers `invalid_grant`. Bad client credentials answer `401 invalid_client`.

To register an OAuth app, email support@getolet.com. Client creation is not yet self-serve.

## What does the merchant see

Connected apps appear under **Connected apps** on the same **API & apps** page, with the app name, its access, who approved it, and when it was last used. The merchant can click the disconnect button and confirm with **Disconnect** to cut the connection. Automations using it stop right away, and the app can ask again.

If the signed-in user is not an admin, the consent screen reads "Only store admins can connect apps" and asks them to get an admin to connect your app. If the store is not on the Growth plan, it reads "Connecting apps needs the Growth plan." and offers **See plans**.

## What can a credential do

Both API keys and OAuth connections carry a set of scopes that limit what they can read or write.

| Scope | Grants |
| --- | --- |
| `catalog:read` | View products, prices, categories, brands, tags and suppliers |
| `catalog:write` | Create, update and delete products, brands, categories, tags and suppliers |
| `inventory:read` | View stock levels across outlets |
| `inventory:write` | Adjust stock levels across outlets |
| `customers:read` | View customers, including names, emails and phone numbers |
| `customers:write` | Create and update customers, and permanently erase a customer's personal details. Erasure cannot be undone. |
| `sales:read` | View completed sales and refunds |

A `:write` scope also satisfies the matching `:read` requirement, so a key with `catalog:write` can call a `catalog:read` endpoint too. There is no `sales:write` scope, because the API only reads sales.

A few reference endpoints need no particular scope, so any valid credential can call them: `GET /me`, `GET /store`, `GET /outlets`, `GET /payment-types` and `GET /taxes`. They explain the ids and prices that the scoped endpoints return.

A call without the right scope answers `403 INSUFFICIENT_SCOPE`.

## What happens if a credential goes idle

An API key or an OAuth connection that goes 90 days without a request stops working. A key that was never used counts from the day it was created. There is no separate warning; the next request answers `UNAUTHORIZED`. The back office shows it as **Expired**. Create a new key or reconnect to keep going.

## Finding your store prefix

Every path starts with your store's prefix, for example `your-store` in `https://api.oletapp.com/your-store/v1/products`. You can find it two ways: call `GET /me` with any valid credential, or read `api_base_url` from an OAuth token response.

## Where to go next

See the [error codes](/resources/developers/errors/) you might get back, or browse the full [API reference](/resources/developers/reference/).
