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

# OAuth 2.0

> Authenticate on behalf of Penbox workspaces with OAuth 2.0, using a client ID and client secret

<Warning>
  **This is not the recommended way to authenticate.** For most integrations, use a [personal API token](/api-reference/authentication): it is simpler and needs no setup on our side. Choose OAuth 2.0 only if you need each workspace to authorize your application itself.
</Warning>

<Info>
  **Enterprise feature.** OAuth 2.0 is available on the Enterprise plan. Contact your Penbox account manager to enable it.
</Info>

Use OAuth 2.0 when you build an **integration used by several Penbox workspaces** (a partner platform, a marketplace app). Each workspace admin authorizes your application once, and you receive a Bearer token for that workspace, without ever handling a personal API token.

<Note>
  OAuth 2.0 clients are provisioned by Penbox. There is no self-service screen to create one yet. Contact [support@penbox.io](mailto:support@penbox.io) with your application name, logo, website and the HTTPS redirect URI(s) you will use. You receive a `client_id` and a `client_secret` in return.
</Note>

## How it works

Penbox supports the **Authorization Code** flow, with refresh tokens.

```mermaid theme={null}
sequenceDiagram
    participant U as Workspace admin
    participant A as Your application
    participant P as Penbox (connect.penbox.io)
    A->>U: Redirect to /authorize
    U->>P: Sign in and pick a workspace
    P->>A: Redirect to your redirect_uri with ?code=...
    A->>P: POST /token (code + client_id + client_secret)
    P->>A: access_token + refresh_token
    A->>P: API calls with Authorization: Bearer access_token
```

<Warning>
  The `client_secret` must stay on your server. Never ship it in a browser app or a mobile app.
</Warning>

## Endpoints

| Environment | Base URL |
| - | - |
| Production | `https://connect.penbox.io` |
| Staging | `https://connect.aiboov.com` |

The authorization and token endpoints live at the root of the host, not under `/v1`. The server metadata is published at `/.well-known/openid-configuration`.

## Step 1: Send the user to Penbox

Redirect the user's browser to `/authorize`:

```text theme={null}
https://connect.penbox.io/authorize
  ?client_id=YOUR_CLIENT_ID
  &redirect_uri=https%3A%2F%2Fapp.example.com%2Fpenbox%2Fcallback
  &response_type=code
  &state=RANDOM_VALUE
```

| Parameter | Required | Description |
| - | - | - |
| `client_id` | Yes | The client ID Penbox gave you. |
| `redirect_uri` | Yes | Must use `https://` and match one of the URIs registered for your client. It cannot contain `code`, `state`, `error` or `error_description` query parameters. |
| `response_type` | Yes | Use `code`. |
| `state` | Recommended | An opaque value returned unchanged to your redirect URI. Use it to prevent CSRF. |
| `scope` | No | Defaults to `offline_access`, which returns a refresh token. |

The user signs in, selects the workspace to authorize, and approves your application.

## Step 2: Receive the authorization code

Penbox redirects the user back to your `redirect_uri`:

```text theme={null}
https://app.example.com/penbox/callback?code=AUTHORIZATION_CODE&state=RANDOM_VALUE
```

Check that `state` matches what you sent. If something went wrong, the redirect carries `error` and `error_description` instead of `code`.

## Step 3: Exchange the code for tokens

Call `POST /token` from your server with your credentials:

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST 'https://connect.penbox.io/token' \
    -H 'Content-Type: application/json' \
    -d '{
      "grant_type": "authorization_code",
      "code": "AUTHORIZATION_CODE",
      "redirect_uri": "https://app.example.com/penbox/callback",
      "client_id": "YOUR_CLIENT_ID",
      "client_secret": "YOUR_CLIENT_SECRET"
    }'
  ```

  ```javascript JavaScript theme={null}
  const response = await fetch('https://connect.penbox.io/token', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      grant_type: 'authorization_code',
      code,
      redirect_uri: 'https://app.example.com/penbox/callback',
      client_id: process.env.PENBOX_CLIENT_ID,
      client_secret: process.env.PENBOX_CLIENT_SECRET
    })
  });

  const tokens = await response.json();
  ```
</CodeGroup>

The body can be JSON or `application/x-www-form-urlencoded`. Instead of the body, you can also send the credentials with HTTP Basic authentication (`Authorization: Basic base64(client_id:client_secret)`).

If you sent a `redirect_uri` in step 1, send the same value here.

**Response:**

```json theme={null}
{
  "access_token": "eyJhbGciOi...",
  "id_token": "eyJhbGciOi...",
  "refresh_token": "d41f...",
  "token_type": "Bearer",
  "expiresIn": 86400
}
```

| Field | Description |
| - | - |
| `access_token` | The Bearer token for API calls. Valid for 24 hours (`expiresIn`, in seconds). It is scoped to the workspace the user selected. |
| `refresh_token` | Returned when the `offline_access` scope was granted (the default). Use it to get a new access token. |
| `id_token` | Basic profile of the user who authorized (name, email, locale). |

## Step 4: Call the API

Use the access token exactly like a personal API token:

```bash theme={null}
curl -X GET 'https://connect.penbox.io/v1/cases' \
  -H 'Authorization: Bearer ACCESS_TOKEN'
```

## Refreshing the access token

Before the access token expires, request a new one:

```bash theme={null}
curl -X POST 'https://connect.penbox.io/token' \
  -H 'Content-Type: application/json' \
  -d '{
    "grant_type": "refresh_token",
    "refresh_token": "REFRESH_TOKEN",
    "client_id": "YOUR_CLIENT_ID",
    "client_secret": "YOUR_CLIENT_SECRET"
  }'
```

The response contains a new `access_token`. The refresh token stays the same, so keep storing it.

<Note>
  If the refresh token is rejected (`400`), send the workspace admin through the authorization flow again.
</Note>

## Errors

| Status | Cause |
| - | - |
| `401` | Missing or invalid `client_id` / `client_secret`, unknown or invalid `code`, `redirect_uri` that differs from the one used in step 1, or unsupported `grant_type`. |
| `400` | Malformed request, or invalidated refresh token. |

At the `/authorize` step, errors are sent back to your `redirect_uri` as `error` and `error_description`. If the `redirect_uri` itself is invalid, the error is shown to the user instead of redirecting.

## Security checklist

* Store `client_secret` and refresh tokens in a secrets manager, never in source control or client-side code.
* Always send and verify `state`.
* Register exact HTTPS redirect URIs.
* Keep one access token and one refresh token per authorized workspace.
* Ask Penbox to rotate your `client_secret` if it may have leaked.

## Next steps

<CardGroup cols={2}>
  <Card title="Bearer token authentication" icon="key" href="/api-reference/authentication">
    Use a personal API token for your own workspace
  </Card>

  <Card title="Error Codes" icon="triangle-exclamation" href="/api-reference/error-codes">
    HTTP status codes returned by the API
  </Card>
</CardGroup>


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