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

# Authentication

> How to generate an API token from your PublishBuddy account and use it to authenticate every request.

The PublishBuddy API uses **personal access tokens** sent as Bearer tokens in the `Authorization` header. Every endpoint requires authentication — there is no anonymous access.

## Generate a token

Tokens are created and managed inside the dashboard, scoped to your user account.

<Steps>
  <Step title="Sign in to the dashboard">
    Go to [app.publishbuddy.com](https://app.publishbuddy.com) and sign in with the account you want the token to act on behalf of. The token will be able to access every workspace that account is a member of, with the same permissions your user has in each workspace.
  </Step>

  <Step title="Open User Settings → Login & Security">
    Click your avatar (top-right), choose **User Settings**, then open the **Login & Security** tab. You'll see a section labelled **API tokens**.
  </Step>

  <Step title="Create a new token">
    Click **Generate API token**, give it a descriptive name (for example `Zapier integration` or `Internal CMS sync`), select the abilities you want the token to grant, and confirm.
  </Step>

  <Step title="Copy the token immediately">
    The token value is shown **once**, when it's created. Copy it into your secret manager, `.env` file, or wherever your application reads secrets from. You will not be able to view it again — if you lose it, revoke it and create a new one.
  </Step>
</Steps>

<Warning>
  Treat tokens like passwords. Anyone with your token can read and modify your workspaces. Never commit them to version control, never paste them in browser screenshots, and rotate them if you suspect they've leaked.
</Warning>

## Token abilities (scopes)

When you create a token you select one or more **abilities**. The API rejects any request whose token does not include the ability required by that endpoint. You can think of abilities as a least-privilege control: if a token is only going to publish posts, don't grant it analytics access.

| Ability          | What it unlocks                                             |
| ---------------- | ----------------------------------------------------------- |
| `workspaces`     | List workspaces the user belongs to                         |
| `profiles`       | List social profiles inside a workspace                     |
| `posts`          | Create, read, update, and delete posts                      |
| `media_contents` | Upload, list, and manage media library items and folders    |
| `analytics`      | Read analytics for profiles, workspaces, and best-time data |

Each endpoint in the [API reference](/api-reference/introduction) documents the ability it requires. A token can hold any combination of abilities.

## Use the token

Send it as a Bearer token on every request:

```bash theme={null}
curl https://api.publishbuddy.com/v1/workspaces \
  -H "Authorization: Bearer 1|2f9c8b1a4d6e7..."
```

<CodeGroup>
  ```javascript Node.js (fetch) theme={null}
  const res = await fetch('https://api.publishbuddy.com/v1/workspaces', {
    headers: {
      Authorization: `Bearer ${process.env.PUBLISHBUDDY_TOKEN}`,
      Accept: 'application/json',
    },
  });
  const { data } = await res.json();
  ```

  ```python Python (requests) theme={null}
  import os, requests

  resp = requests.get(
      'https://api.publishbuddy.com/v1/workspaces',
      headers={
          'Authorization': f'Bearer {os.environ["PUBLISHBUDDY_TOKEN"]}',
          'Accept': 'application/json',
      },
  )
  resp.raise_for_status()
  workspaces = resp.json()['data']
  ```

  ```php PHP (Guzzle) theme={null}
  $client = new GuzzleHttp\Client(['base_uri' => 'https://api.publishbuddy.com/v1/']);
  $res = $client->get('workspaces', [
      'headers' => [
          'Authorization' => 'Bearer ' . getenv('PUBLISHBUDDY_TOKEN'),
          'Accept' => 'application/json',
      ],
  ]);
  $workspaces = json_decode($res->getBody(), true)['data'];
  ```

  ```ruby Ruby (Net::HTTP) theme={null}
  require 'net/http'
  require 'json'

  uri = URI('https://api.publishbuddy.com/v1/workspaces')
  req = Net::HTTP::Get.new(uri)
  req['Authorization'] = "Bearer #{ENV['PUBLISHBUDDY_TOKEN']}"
  req['Accept'] = 'application/json'

  res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) { |http| http.request(req) }
  workspaces = JSON.parse(res.body)['data']
  ```
</CodeGroup>

## Revoke a token

Revoking is immediate — any in-flight requests with the token will start returning `401 Unauthenticated` within seconds.

<Steps>
  <Step title="Open Login & Security again">
    Same place you created the token: **User Settings → Login & Security**.
  </Step>

  <Step title="Click Revoke next to the token">
    Tokens are listed by name, last-used timestamp, and creation date. Find the one you want to invalidate.
  </Step>

  <Step title="Replace it in your applications">
    Generate a new token (with the same abilities, if you're just rotating) and redeploy any application that depends on it.
  </Step>
</Steps>

## Common authentication errors

| Status | Code                  | Meaning                                        | Fix                                                                                    |
| ------ | --------------------- | ---------------------------------------------- | -------------------------------------------------------------------------------------- |
| `401`  | `unauthenticated`     | Missing, malformed, or revoked token           | Check the `Authorization` header is exactly `Bearer <token>` with no extra whitespace. |
| `403`  | `forbidden_ability`   | Token doesn't include the required ability     | Recreate the token in the dashboard with the missing ability ticked.                   |
| `403`  | `forbidden_workspace` | Token's owner is not a member of the workspace | Add the user to the workspace, or use a token from a member account.                   |

See [Errors](/api-essentials/errors) for the full reference.

## Best practices

<AccordionGroup>
  <Accordion title="Use one token per integration" icon="layer-group">
    Don't share one token across Zapier, your internal CMS, and ad-hoc scripts. Issuing a separate token per integration lets you revoke one without breaking the others, and the `Last used` column in the dashboard tells you which is which.
  </Accordion>

  <Accordion title="Grant only the abilities you need" icon="shield-halved">
    A token used only for analytics ingestion shouldn't carry `posts` or `media_contents` abilities. Reducing scope reduces blast radius if the token leaks.
  </Accordion>

  <Accordion title="Rotate regularly" icon="rotate">
    Rotate tokens on a schedule (e.g. every 90 days) and immediately whenever a team member who knew the token leaves the organisation.
  </Accordion>

  <Accordion title="Store tokens server-side only" icon="lock">
    Never embed a PublishBuddy token in a browser bundle, mobile app, or any client-controlled environment. Tokens are designed for server-to-server use.
  </Accordion>
</AccordionGroup>
