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

# API credentials

> Choose Access Tokens for one-time tasks or OAuth Clients for ongoing integrations, and use OAuth 2.0 Client Credentials.

Use this guide when you build an integration for a specific Stora account and need to manage its API credentials in the [Developer Portal](https://developer.stora.co).

You have two options: **Access Tokens**, which you use directly in API requests, and **OAuth Clients**, which exchange a client ID and secret for short-lived access tokens.

<Note>
  Managed-account OAuth Clients use the **OAuth 2.0 Client Credentials** flow. They are different from partner apps. If you build a partner app that operators authorise themselves, or prepare an app for marketplace distribution, follow [Building a partner integration](/2025-09/guides/partner-integrations) and use Authorization Code instead.
</Note>

## Connect a Stora account

Before creating credentials for a real account, follow [Connect an account](/2025-09/guides/connect-account) to generate an invitation and get staff approval. Then open the active account under **Connected accounts** in the Developer Portal.

To explore without real account data, [create a test operator](/2025-09/guides/developer-portal#testing--looking-around) first.

## Choose Access Tokens or OAuth Clients

Both options authenticate requests with `Authorization: Bearer <token>`. The difference is how you obtain and renew the token.

| | Access Tokens | OAuth Clients |
| - | - | - |
| Credentials you store | An access token created in the portal | A client ID and client secret |
| Before calling the API | Use the token directly | Exchange the client credentials for an access token |
| Token expiry | Tokens expire after the lifetime you choose at creation | Issued tokens expire after 2 hours |
| Continued access | [Extend the token](/2025-09/guides/manage-account-access#extend-an-access-token) within the allowed limit, or create a replacement | Request another token using the same client credentials |
| Best fit | One-time scripts and short-lived AI-agent tasks | Ongoing server-to-server integrations and scheduled jobs |

**Access Tokens expire.** Use them for one-time scripts or short-lived tasks performed by an AI agent, where you need to make requests without implementing a token exchange. Once a token expires, it no longer authenticates requests. Use **OAuth Clients** instead for ongoing integrations so your backend can obtain new tokens automatically.

For AI-agent tasks, select only the scopes the task needs and the shortest suitable token lifetime. Supply the token through protected runtime configuration, not in a prompt, and revoke it when the task is finished.

Use **OAuth Clients** for an ongoing integration where your backend can obtain and renew tokens automatically. Short-lived tokens limit how long a leaked access token remains usable, but you must still protect the client secret: anyone with it can request new tokens.

### Use an Access Token

1. Open the connected account and click **New access token** under **Access tokens**.
2. Enter a **Name**, select the scopes you need, and choose an **Expires** value.
3. Click **Create access token** and copy the token immediately. You cannot view it again.
4. Use it directly as the bearer token in your [first API request](/2025-09/guides/first-request).

## Use an OAuth Client

Client Credentials is a server-to-server flow. After creating the client in the portal, your backend can request tokens without a browser redirect, callback URL, or interactive Stora login.

### 1. Create the OAuth Client

1. Open the account under **Connected accounts**.
2. Under **OAuth clients**, click **New OAuth client**.
3. Enter a **Name** that identifies your integration, such as `Reporting sync`.
4. Select only the scopes your integration needs. For the example below, select `public.site:read`.
5. Click **Create OAuth client**.
6. Copy the **Client ID** and **Client secret**, then click **Done**.

<Warning>
  The client secret is shown only once. Store it in a server-side secret manager or protected configuration. Never include it in browser JavaScript, mobile apps, distributed plugins, source control, or logs. Keep access tokens private too.
</Warning>

### 2. Exchange the credentials for an access token

Send a JSON request to `POST https://public-api.stora.co/oauth2/token`. Replace `YOUR_CLIENT_ID` and `YOUR_CLIENT_SECRET` with the credentials you copied.

```bash theme={null}
curl --fail-with-body -X POST "https://public-api.stora.co/oauth2/token" \
  -H "content-type: application/json" \
  -d '{
    "grant_type": "client_credentials",
    "client_id": "YOUR_CLIENT_ID",
    "client_secret": "YOUR_CLIENT_SECRET",
    "scope": "public.site:read"
  }'
```

* `grant_type` must be `client_credentials`.
* `client_id` and `client_secret` identify and authenticate the OAuth Client.
* `scope` is required. Use a space-separated list of scopes enabled for the client, such as `public.site:read public.contact:read`.

The token endpoint is not versioned. Do not add `/2025-09` to its URL.

An example successful response:

```json theme={null}
{
  "access_token": "YOUR_ISSUED_ACCESS_TOKEN",
  "token_type": "Bearer",
  "expires_in": 7200,
  "scope": "public.site:read",
  "created_at": 1740235260
}
```

Use `access_token` for API requests. `expires_in` is the token lifetime in seconds: `7200` means 2 hours. The response's `scope` tells you which permissions the token has.

### 3. Call the API with the issued token

Replace `YOUR_ISSUED_ACCESS_TOKEN` with the `access_token` from the response:

```bash theme={null}
curl --fail-with-body "https://public-api.stora.co/2025-09/sites" \
  -H "accept: application/json" \
  -H "authorization: Bearer YOUR_ISSUED_ACCESS_TOKEN"
```

This returns the sites for the account associated with the OAuth Client. Unlike the token endpoint, resource endpoints include the API version in the path.

<Note>
  The bearer token is the issued `access_token`, not the client ID or client secret. Both portal-created Access Tokens and OAuth-issued access tokens use the same bearer header.
</Note>

### 4. Obtain a new token before expiry

Store the token and its expiry securely on your backend. Reuse the token for API calls rather than requesting a new one for every call.

Before the token expires, repeat the request in step 2 with the same client ID, client secret, and required scopes. For example, request a replacement 5 minutes before expiry. Use the new `access_token` in subsequent API requests.

**Client Credentials does not issue a refresh token.** Do not use `grant_type: refresh_token` for these OAuth Clients. Renew access by repeating the `client_credentials` exchange.

## Troubleshoot authentication

| Problem | What to check |
| - | - |
| The token exchange fails | Check the client ID and secret, the `client_credentials` grant type, and that the client is active. Inspect the response's `error` and `error_description`. |
| A requested scope is rejected | Check that the scope is available to the account and enabled for the OAuth Client. Use spaces, not commas, between scopes. |
| An API request is unauthorised | Check that the bearer value is an access token, that it has not expired or been revoked, that the client is active, and that the account still has Public API access. |
| A request lacks permission | Check the scopes required by the endpoint against the issued token's scopes. Manage the client's scopes in the portal and request a new token with the required scopes. |

See [Authorization](/2025-09/guides/authorization) for available scopes and [Errors](/2025-09/guides/errors) for API error responses.

## Manage existing credentials

See [Manage access](/2025-09/guides/manage-account-access) to change scopes, extend Access Tokens, disable or revoke credentials, and terminate an account connection.


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