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

# Autenticación

> Cómo obtener y renovar un token de acceso para usar el API de Fegora.

El API de Fegora usa **OAuth 2.0** con el flujo *password grant*. Antes de llamar a cualquier endpoint de negocio (DTE, cuenta, etc.) debe solicitar un token de acceso con su usuario y contraseña. Ese token se envía luego como `Bearer` en cada llamada subsiguiente.

## Solicitar un token

Haga una solicitud `POST` con el usuario y la contraseña, codificados como formulario, al endpoint de token:

```
POST https://api.fegora.com/token
```

### Headers

| Header         | Valor                               |
| -------------- | ----------------------------------- |
| `Content-Type` | `application/x-www-form-urlencoded` |
| `Accept`       | `application/json`                  |

### Body (form-urlencoded)

| Campo        | Tipo   | Obligatorio | Descripción                                                                |
| ------------ | ------ | ----------- | -------------------------------------------------------------------------- |
| `grant_type` | string | **SÍ**      | Siempre `password` para esta solicitud.                                    |
| `client_id`  | string | **SÍ**      | Siempre `apiApp`.                                                          |
| `username`   | string | **SÍ**      | El usuario asignado a su cuenta Fegora. Por lo regular corresponde al NIT. |
| `password`   | string | **SÍ**      | La contraseña asignada.                                                    |

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST https://api.fegora.com/token \
    -H "Content-Type: application/x-www-form-urlencoded" \
    -H "Accept: application/json" \
    -d "grant_type=password&client_id=apiApp&username=12345678&password=contrasenia-ejemplo"
  ```
</CodeGroup>

### Respuesta `200 OK`

```json theme={null}
{
  "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...ejemplo",
  "token_type": "bearer",
  "expires_in": 86399,
  "refresh_token": "9f2c1a7b8e4d4c2a9b0e1f3a5c6d7e8f"
}
```

| Campo           | Descripción                                                                                     |
| --------------- | ----------------------------------------------------------------------------------------------- |
| `access_token`  | Token JWT que debe enviarse como `Bearer` en cada llamada al API.                               |
| `token_type`    | Siempre `bearer`.                                                                               |
| `expires_in`    | Vigencia del token en segundos. El API emite tokens válidos por 24 horas.                       |
| `refresh_token` | Token opaco que permite obtener un nuevo `access_token` sin volver a enviar usuario/contraseña. |

<Note>
  El `access_token` es un JWT firmado. Dentro de sus claims viaja, entre otros datos, una o más entradas `cuenta` — vea [Cuentas múltiples en un mismo token](#cuentas-múltiples-en-un-mismo-token) más abajo.
</Note>

## Renovar el token (refresh)

Cuando el `access_token` expira, use el `refresh_token` obtenido al solicitar el token original — no es necesario reenviar usuario y contraseña.

```
POST https://api.fegora.com/token
```

### Body (form-urlencoded)

| Campo           | Tipo   | Obligatorio | Descripción                                           |
| --------------- | ------ | ----------- | ----------------------------------------------------- |
| `grant_type`    | string | **SÍ**      | Siempre `refresh_token`.                              |
| `refresh_token` | string | **SÍ**      | El `refresh_token` recibido en la respuesta anterior. |
| `client_id`     | string | **SÍ**      | Siempre `apiApp`.                                     |

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST https://api.fegora.com/token \
    -H "Content-Type: application/x-www-form-urlencoded" \
    -H "Accept: application/json" \
    -d "grant_type=refresh_token&refresh_token=9f2c1a7b8e4d4c2a9b0e1f3a5c6d7e8f&client_id=apiApp"
  ```
</CodeGroup>

La respuesta `200 OK` tiene la misma forma que la de la solicitud original: un nuevo `access_token` junto con un nuevo `refresh_token`.

<Warning>
  Cada vez que se usa un `refresh_token`, éste se invalida y se reemplaza por uno nuevo en la respuesta. Un mismo `refresh_token` no puede reutilizarse dos veces.
</Warning>

## Usar el token en las llamadas al API

Todas las llamadas al API (salvo `POST /token`) requieren el header `Authorization` con el `access_token` como `Bearer`:

```bash theme={null}
curl -X GET https://api.fegora.com/dte \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...ejemplo"
```

## Errores de autenticación

Si la solicitud de token falla, el endpoint responde `HTTP 400 Bad Request` con un cuerpo en el formato estándar de OAuth2 — **distinto** al formato `{ codigo, mensaje, ... }` que usa el resto del API (vea [Manejo de errores](/primeros-pasos/errores)):

```json theme={null}
{
  "error": "invalid_grant",
  "error_description": "Credenciales inválidas"
}
```

| `error`                  | Cuándo ocurre                                                          |
| ------------------------ | ---------------------------------------------------------------------- |
| `invalid_grant`          | Usuario o contraseña incorrectos, o `refresh_token` inválido/expirado. |
| `incorrect_status`       | El usuario está bloqueado por intentos fallidos repetidos.             |
| `unsupported_grant_type` | El `grant_type` enviado no es `password` ni `refresh_token`.           |

## Cuentas múltiples en un mismo token

Un usuario puede tener acceso a más de una cuenta (empresa emisora) en Fegora. En ese caso, el `access_token` incluye una entrada `cuenta` por cada cuenta asociada al usuario.

Al crear un DTE (`POST /dte`), el campo `idCuenta` del body indica sobre qué cuenta se emite el documento:

* Si el usuario tiene **una sola cuenta**, `idCuenta` puede omitirse — el API la infiere automáticamente.
* Si el usuario tiene **varias cuentas**, se recomienda enviar `idCuenta` explícitamente. Si se omite, el API usa la primera cuenta asociada al usuario, lo cual puede no ser la cuenta esperada.
