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

# Authentication

> How the Connecta DESK API token works: sk_live_ format, Bearer header, expiry date, allowed IPs and rotation.

Every request to the v1 API carries the company token in the `Authorization` header, using the
`Bearer` scheme:

```bash theme={null}
Authorization: Bearer sk_live_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6
```

The API does not use logins, sessions or refresh tokens. The token is the whole credential.

## What the token is

<Columns cols={2}>
  <Card title="It belongs to the company" icon="building">
    It is not tied to any user. It keeps working even if the person who created it leaves the
    company, and the actions taken with it show up under the integration's name.
  </Card>

  <Card title="It carries its own rules" icon="shield-check">
    Permissions per module and method, expiry date and allowed IPs live in the token itself.
    Whoever creates the token decides how far it can go.
  </Card>
</Columns>

The value has the `sk_live_` format followed by 32 characters. The first characters form the
**prefix** (`sk_live_ab12cd34`), which the panel shows in the list so you can identify each
token.

<Note>
  For security reasons, the full key appears only once, at creation, and cannot be recovered
  afterwards. If you lose the key, rotate the token.
</Note>

## Using it on every request

<CodeGroup>
  ```bash cURL theme={null}
  curl --request GET \
    --url "https://api.connectadesk.com/api/v1/tickets?status=open" \
    --header "Authorization: Bearer $CONNECTA_TOKEN"
  ```

  ```javascript Node.js theme={null}
  const api = (path, init = {}) =>
    fetch(`${process.env.CONNECTA_URL}${path}`, {
      ...init,
      headers: {
        Authorization: `Bearer ${process.env.CONNECTA_TOKEN}`,
        'Content-Type': 'application/json',
        ...init.headers
      }
    });

  const res = await api('/tickets?status=open');
  ```

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

  session = requests.Session()
  session.headers.update({
      "Authorization": f"Bearer {os.environ['CONNECTA_TOKEN']}",
      "Content-Type": "application/json",
  })

  res = session.get(f"{os.environ['CONNECTA_URL']}/tickets", params={"status": "open"})
  ```

  ```php PHP theme={null}
  $ch = curl_init(getenv('CONNECTA_URL') . '/tickets?status=open');
  curl_setopt_array($ch, [
      CURLOPT_RETURNTRANSFER => true,
      CURLOPT_HTTPHEADER => [
          'Authorization: Bearer ' . getenv('CONNECTA_TOKEN'),
          'Content-Type: application/json',
      ],
  ]);

  $response = curl_exec($ch);
  ```
</CodeGroup>

## When the token is rejected

A token that is missing, badly formatted, nonexistent, revoked, expired or used from an
unauthorized IP gets **exactly the same response**:

```json theme={null}
{
  "error": {
    "code": "ERR_API_UNAUTHORIZED",
    "message": "Token de API ausente ou inválido."
  }
}
```

For security reasons, the API does not say why the token was rejected.

<Tip>
  To find out the reason, check the panel. It shows the token's status, the expiry date, the
  allowed IPs and the last use (`lastUsedAt`). If `lastUsedAt` has not changed, the API never
  recognized your request.
</Tip>

## Expiry date

The expiry date is optional and is set when the token is created. Once it passes, the token stops
working immediately, with no prior warning and no grace period.

<Warning>
  Write down the expiry date along with the integration's details. Since the response is the same
  generic 401, an expired token looks like a wrong token, and finding the cause takes time.
</Warning>

To swap the key without recreating the integration, use **Rotate** in the panel. The token keeps
its name, permissions and allowed IPs and gets a new value, which also appears only once. The old
value stops working immediately.

## Allowed IPs

If the integration runs from a fixed IP, restrict access. Once the list is filled in, calls from
any other origin get the same 401.

<ParamField path="Single address" type="IPv4">
  Allows only that address, for example `200.150.10.20`.
</ParamField>

<ParamField path="Range" type="CIDR">
  Allows any address within the range, for example `191.30.0.0/16`.
</ParamField>

List rules:

* **Blank means any origin is accepted.** This is the default when the field has never been filled in.
* **Up to 50 entries** per token.
* **IPv4 only.** IPv6 addresses are not accepted when registering.
* The API uses the real IP of the connection. For security reasons, an `X-Forwarded-For` header
  sent in the request does not change the origin that is considered.

<Warning>
  Before restricting an integration that is already in production, confirm its real outbound IP.
  Cloud services often go out through an address different from the one shown in the provider's
  panel, and a wrong list takes the integration down immediately.
</Warning>

## Managing tokens

Creating, editing, rotating and revoking tokens are done in the **panel**, signed in as an
administrator. For security reasons, an API token cannot manage tokens, and there is no public
endpoint for that.

<CardGroup cols={2}>
  <Card title="Permissions" icon="shield-check" href="/en/essentials/permissoes">
    What each module and method unlocks.
  </Card>

  <Card title="Token security" icon="lock" href="/en/guides/seguranca-do-token">
    How to choose scope and expiry, and what to do if a key leaks.
  </Card>
</CardGroup>


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