---
title: Tokens and expiry
description: How long access and refresh tokens last, how to refresh them, and how to store them safely.
---

A partner API token proves that a shop member approved your app. If it leaks, someone else can read that shop's data. Follow these rules.

## Lifetimes

| Token         | Lifetime              | Use                                                      |
| ------------- | --------------------- | -------------------------------------------------------- |
| Access token  | 15 minutes from issue | Send it as `Authorization: Bearer` on API calls          |
| Refresh token | 90 days from issue    | Exchange it for new tokens when the access token expires |

Refresh tokens rotate: every refresh returns a new one, and the one you used stops working. As long as you refresh regularly, the connection keeps working past 90 days.

## Refreshing

```ts
const response = await fetch(
  "https://api.toreca-cloud.com/api/auth/oauth2/token",
  {
    method: "POST",
    headers: {
      Authorization: "Basic " + basicCredentials,
      "Content-Type": "application/x-www-form-urlencoded",
    },
    body: new URLSearchParams({
      grant_type: "refresh_token",
      refresh_token: storedRefreshToken,
      resource: "https://api.toreca-cloud.com/partner/v1",
    }),
  },
);
const next = await response.json();
// Save next.refresh_token (the old one no longer works)
```

- When the API answers `401`, refresh and retry once.
- Refresh at most once at a time per connection. Two parallel refreshes make one of them use a stale refresh token and fail.
- Save the new refresh token as soon as you receive it. If saving fails, you can't refresh again.
- If refreshing fails with `invalid_grant`, ask a shop member to approve again.

## Storing tokens

- Keep tokens and the client secret on your server only. Never put them in a browser, a mobile app or a static site's JavaScript.
- Encrypt them at rest and limit who and what can read them.
- Keep tokens out of logs, error reports and analytics.
- If the client secret may have leaked, ask the shop admin to regenerate it ("Regenerate secret"). The old secret stops working at once.

## When the connection stops

The API refuses calls in these cases even with an unexpired token.

| Situation                                             | Response                 |
| ----------------------------------------------------- | ------------------------ |
| The shop revoked the connection                       | `403 connection_revoked` |
| The shop or Toreca Cloud paused the app               | `403 client_disabled`    |
| The approving member left or lost a needed permission | `403 forbidden`          |
| The token lacks the needed scope                      | `403 insufficient_scope` |

To disconnect from your side, call `DELETE /partner/v1/connection`. It also revokes every refresh token your app holds for that shop.
