Límites y errores
Errores
Todos los errores tienen la misma forma, y code es estable (el message es para personas y puede cambiar):
{"error": {"code": "missing_scope", "message": "Esta clave no tiene el alcance «ptz».", "status": 403,
"request_id": "9b1c…", "required_scope": "ptz"}}
Una validación trae fields ({"name": ["El campo nombre es obligatorio."]}), un 429 trae retry_after (y la cabecera Retry-After), y un choque con el préstamo de control trae lease (quién lo tiene y hasta cuándo). Cada respuesta, buena o mala, trae X-Request-Id.
| Estado | code |
Qué hacer |
|---|---|---|
| 400 | key_in_query |
La clave va en la cabecera. Rotala: quedó en logs |
| 401 | invalid_key, key_revoked, key_expired, creator_suspended, key_inactive |
Crear otra clave, o reactivar a quien la creó |
| 403 | missing_scope |
La clave necesita el alcance de required_scope |
| 403 | forbidden, space_suspended |
El rol no alcanza, o el espacio está suspendido |
| 404 | not_found |
No existe o esta clave no puede verlo: es lo mismo a propósito |
| 409 | conflict, camera_refused |
La cámara no puede ahora (sin conexión, otra función lo impide) |
| 409 | camera_leased |
Otra clave tiene el control de la cámara |
| 409 | lease_held |
Pediste o soltaste un control que es de otra clave |
| 409 | lease_hold_limit |
Tu clave llegó a los 30 minutos seguidos con esa cámara: esperá retry_after segundos |
| 409 | lease_revoked_by_user |
Una persona liberó la cámara desde el panel: tu clave no la puede tomar por retry_after segundos |
| 409 | kill_switch, automation_inactive, not_started, automation_refused |
La automatización no puede correr o cambiar ahora |
| 410 | gone |
La cámara se está dando de baja |
| 422 | validation_failed, no_manual_trigger |
Datos inválidos (fields), o la automatización no tiene disparador Manual |
| 429 | rate_limited, automation_capped |
Esperá Retry-After |
| 5xx | internal_error, upstream_error, unavailable |
Reintentá solo lo que no duplica (ver abajo) |
Topes
| Qué | Tope |
|---|---|
| Pedidos de una clave | Lo que se eligió al crearla: de 1 a 600 por minuto (120 de fábrica) |
| Pedidos por dirección IP, antes de mirar la clave | 1200 por minuto |
PTZ (move y stop) |
400 por minuto por persona, como el pad del panel |
| Video en vivo y hablar | 30 por minuto por persona |
| Enlaces a archivos (fotos y grabaciones) | 240 por minuto |
| Comandos a la cámara | 60 por minuto por persona |
| Altas de cámaras | 10 por hora por espacio; rotar credenciales, 6 |
| Préstamo de control (tomar, renovar, soltar, mirar) | 60 por minuto por clave |
| Ejecutar automatizaciones | 30 por minuto por clave, y además los del motor: 12 ejecuciones por minuto y 3 abiertas a la vez por automatización (automation_capped) |
| Probar el bucket y hacer ping a un webhook | 10 por minuto por clave |
Las claves y las sesiones del panel de una misma persona comparten los topes por persona.
Reintentos
- Ante 429, esperá lo que dice
Retry-Aftery probá de nuevo: el servidor no hizo nada. - Ante 5xx o un corte, reintentá solo lo que no duplica:
GET,PUT,DELETE, unPOSTconIdempotency-Key, renovar el préstamo, prender o apagar una automatización. No reintentes a ciegasPOST /automations/{id}/run: podría correr dos veces. - Los SDK ya lo hacen así, con espera creciente (0,5 s, 1 s, 2 s… hasta 8 s) y un poco de azar.
Idempotencia
POST /cameras y POST /webhooks aceptan Idempotency-Key. Repetir con la misma (durante 24 h, con la misma clave de API) devuelve la misma respuesta y la cabecera Idempotent-Replayed: true, sin crear otro. Con otro cuerpo, 422 idempotency_mismatch; si el primero sigue corriendo, 409 idempotency_in_progress. Los SDK ponen una sola en cada alta.
Páginas
Las listas aceptan ?page= y ?per_page= (hasta 100) y contestan {"data": [...], "meta": {"page": 1, "per_page": 25, "has_more": true}}. cameras.all(), automations.all() y paginate() recorren todas.