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

# Errores y límites de una app

> Los errores de la conexión y de los tokens, qué hacer con cada uno, y cuántos pedidos acepta una app por minuto.

Una app recibe errores de dos formas:

* **`/oauth/token` y `/oauth/revoke`** responden como OAuth: un JSON con `error` y `error_description`.
* **La API (`/v1`)** responde como siempre: `application/problem+json` con su `code`. Lee [Errores](https://docs.craqui.com/errors.md).

## La conexión: lo que vuelve a tu dirección de retorno

| `error` | Qué pasó | Qué hacer |
| - | - | - |
| `access_denied` | El propietario canceló, o no tiene un negocio donde pueda conectar tu app | Muéstrale que no se conectó y deja el botón para intentar otra vez |
| `invalid_scope` | `scope` nombra un permiso que tu app no declaró, o uno que ninguna app puede tener | Pide solo permisos de tu app. Lee [Permisos](https://docs.craqui.com/apps/permissions.md) |
| `invalid_target` | `resource` no es `https://api.craqui.com/v1` | Manda ese valor o no mandes `resource` |

`invalid_scope` e `invalid_target` vuelven a tu web solo si quien conecta puede conectar tu app: una app en
desarrollo no le devuelve nada a alguien que no es propietario del negocio que la registró.

Si el `client_id` o la `redirect_uri` no son válidos, o falta el PKCE, Craqui no vuelve a tu web: el propietario
ve en la pantalla de conexión que la app no está bien configurada. Revisa la URL contra
[Conectar un negocio](https://docs.craqui.com/apps/authorization-code.md).

## `/oauth/token` y `/oauth/revoke`

| Estado | `error` | Qué pasó | Qué hacer |
| - | - | - | - |
| `400` | `invalid_request` | Falta un parámetro, el `code_verifier` no tiene el formato de PKCE, el cuerpo no es un formulario (o JSON), usaste dos formas de autenticar tu app, o el pedido vino de un navegador | Corrige el pedido. Estos dos endpoints se llaman solo desde tu servidor: un pedido con el header `Origin` se rechaza |
| `401` | `invalid_client` | El identificador o el secreto no son válidos, el secreto fue revocado o venció, o Craqui suspendió tu app | Revisa el secreto que usa tu servidor. Si lo revocaste, usa el otro o crea uno nuevo |
| `400` | `invalid_grant` | El código o el refresh token no sirven: venció, ya se usó, se revocó, la conexión se desconectó, o es de otra app | No reintentes. Lee [Tokens](https://docs.craqui.com/apps/tokens.md) |
| `400` | `unsupported_grant_type` | `grant_type` no es `authorization_code` ni `refresh_token` | Usa uno de esos dos |
| `429` | `slow_down` | Tu app pasó los 60 pedidos por minuto en estos endpoints | Espera los segundos de `Retry-After` |
| `503` | `temporarily_unavailable` | No pudimos atender el pedido en este momento | Reintenta en unos segundos |
| `500` | `server_error` | Algo falló de nuestro lado | Reintenta en unos segundos; si sigue, escríbenos |

Una respuesta `401` trae el header `WWW-Authenticate: Basic`.

## La API con el token de una app

Además de los errores de siempre, el token de una app puede recibir estos:

| Estado | `code` | Qué pasó | Qué hacer |
| - | - | - | - |
| `401` | [`invalid_access_token`](https://docs.craqui.com/errors/invalid_access_token) | El access token venció, se revocó, está incompleto, o mandaste el refresh token o el secreto | Renueva el access token con el refresh token y repite el pedido |
| `401` | [`app_connection_revoked`](https://docs.craqui.com/errors/app_connection_revoked) | La conexión con ese negocio se desconectó: el propietario, tu app o Craqui por seguridad | Marca el negocio como desconectado y ofrece conectar de nuevo |
| `403` | [`app_connection_suspended`](https://docs.craqui.com/errors/app_connection_suspended) | Quien conectó tu app ya no es propietario del negocio | Pídele a otro propietario del negocio que conecte tu app de nuevo |
| `403` | [`app_suspended`](https://docs.craqui.com/errors/app_suspended) | Craqui suspendió tu app | Escríbenos a soporte |
| `403` | [`plan_required`](https://docs.craqui.com/errors/plan_required) | El plan del negocio dejó de incluir la API | Espera: cuando vuelva a un plan con API, los mismos tokens funcionan |
| `403` | [`scope_required`](https://docs.craqui.com/errors/scope_required) | La conexión no tiene el permiso de esa ruta (`required_scope`) | Pídele al propietario que vuelva a conectar tu app aceptándolo |
| `400` | [`invalid_request`](https://docs.craqui.com/errors/invalid_request) | El token vino en la URL o en un cuerpo de formulario | Mándalo solo en `Authorization: Bearer` |
| `403` | [`browser_not_allowed`](https://docs.craqui.com/errors/browser_not_allowed) | El pedido vino de un navegador | Llama a la API desde tu servidor |

Los `401` traen el header
`WWW-Authenticate: Bearer error="invalid_token", resource_metadata="https://api.craqui.com/.well-known/oauth-protected-resource/v1"`.

### Las pausas no desconectan

Dos situaciones dejan la conexión **en pausa**, sin cortarla:

* **El plan del negocio deja de incluir la API.** La API responde `plan_required`, pero el refresh token sigue
  renovándose: no lo descartes. Cuando el negocio vuelve a un plan con API, todo funciona sin conectar de nuevo.
* **Quien conectó tu app deja de ser propietario.** La API responde `app_connection_suspended`, y el refresh
  token también sigue renovándose. Si esa persona vuelve a ser propietaria, todo funciona sin conectar de nuevo.
  Si no, otro propietario del negocio conecta tu app desde tu web, y esa conexión reemplaza a la anterior.

En las dos, el propietario ve la conexión **En pausa** en sus aplicaciones conectadas, con el motivo. Una
suspensión de tu app tampoco desconecta: si la reactivamos, los mismos tokens vuelven a funcionar.

### Cuánto tarda un corte

Desconectar, suspender tu app o perder el rol de propietario se nota en la API en **menos de un minuto**: puede
que algunos pedidos de los segundos siguientes todavía funcionen. La renovación de tokens, en cambio, se corta en
el primer intento cuando la conexión se desconectó (`invalid_grant`) o tu app está suspendida
(`invalid_client`).

## Límites

Una app tiene dos límites por minuto, y un pedido pasa solo si los dos tienen lugar:

| | Lecturas | Escrituras | Escrituras de plata |
| - | - | - | - |
| **Por conexión** (tu app en un negocio) | 100 | 30 | 10 |
| **Por app**, sumando todos los negocios | 1.000 | 300 | 100 |

* Por conexión son los mismos números que una clave: un negocio no le da a tu app más que a cualquier otro
  sistema.
* El límite por app protege a todos los negocios de un error en tu código. Si tu app crece y lo necesitas más
  alto, escríbenos: lo subimos a 5.000 lecturas, 1.500 escrituras y 500 de plata por minuto.
* Un pedido que la conexión ya rechazó no cuenta para el total de la app: un negocio con mucho movimiento no le
  gasta el límite a los demás.

Los headers `RateLimit-Policy` y `RateLimit` de cada respuesta dicen el límite que está **más cerca de cortar**,
el de la conexión o el de la app. Cuando se acaba, la respuesta es `429` [`rate_limited`](https://docs.craqui.com/errors/rate_limited)
con `Retry-After`. Lee [Límites](https://docs.craqui.com/rate-limits.md).

**`/oauth/token` y `/oauth/revoke`** tienen su propio límite: **60 pedidos por minuto por app**, sumando los dos y
todos los negocios. Renueva cada access token solo cuando está por vencer, no en cada pedido, y reparte las
renovaciones en el tiempo en vez de renovar todos los negocios a la misma hora.


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