Ir al contenido

Errores y códigos de estado

Cómo informa errores la API REST de Rhombus: códigos de estado HTTP, el cuerpo de error y su errorCode, errores de autenticación y de permisos, el campo error en las respuestas 200, errores de validación y de JSON, errores del servidor y qué fallos reintentar.

En esta página

La API REST informa una solicitud fallida de una de dos formas, así que revisa ambas:

  1. Un código de estado HTTP de error cuando la solicitud no se puede procesar: JSON mal formado, un campo que falta, un problema con la credencial, un permiso denegado o un límite de tasa. El cuerpo tiene "error": true y un errorCode legible por máquina.
  2. HTTP 200 con "error": true cuando la solicitud era válida pero la acción falló o se rechazó, por ejemplo un rol que no puedes asignar. El motivo está en errorMsg:
json
{
  "error": true,
  "errorMsg": "User not permitted to generate an api token for the specified role"
}

Una solicitud tuvo éxito solo cuando el estado es 2xx y error es false.

El sobre de la respuesta

Cada respuesta JSON de la API REST incluye los mismos tres campos junto a sus datos:

CampoPresenteSignificado
errorSiemprefalse si tuvo éxito, true si la acción falló
errorMsgCuando error es trueUn motivo legible. Regístralo y muéstralo a un operador; no lo analices, porque su texto puede cambiar
warningMsgRara vezLa solicitud tuvo éxito, pero algo requiere atención, como un valor predeterminado que el servidor aplicó por ti

Las respuestas de error tienen el mismo cuerpo en todos los hosts: la API REST, los hosts de medios, los handshakes de WebSocket y el servidor MCP.

Cada respuesta de error incluye errorCode, un código estable para decidir qué hacer, junto a errorMsg, un mensaje para mostrar o registrar. Los códigos nunca se renombran ni se eliminan, pero se agregan nuevos: maneja un código que no reconozcas según su estado HTTP.

Toda respuesta con un estado de error tiene el mismo cuerpo. msg es la frase de motivo HTTP, que se conserva para clientes antiguos; status repite el estado HTTP; timestamp es el momento del error, en milisegundos de época Unix; txid es el ID de la solicitud que debes indicar al soporte, también enviado como encabezado x-rhombus-txid:

json
{
  "msg": "Forbidden",
  "error": true,
  "errorCode": "FUNCTIONALITY_REQUIRED",
  "errorMsg": "Your role is missing a functionality this action requires.",
  "status": 403,
  "timestamp": 1791367200000,
  "txid": "yJ2kQw8LTaO1xX4hVd7mPA"
}

Los códigos que devuelve la API hoy (los significados están en inglés):

CódigoEstadoSignificado
BAD_REQUEST400The request is invalid.
VALIDATION_FAILED400The request failed validation.
MISSING_REQUIRED_FIELD400The request is missing a required field.
INVALID_JSON400The request body is not valid JSON.
INVALID_REQUEST_BODY400The request body does not match the expected object structure.
METHOD_NOT_ALLOWED405This endpoint does not accept that HTTP method; the Allow header lists the ones it does.
UNSUPPORTED_MEDIA_TYPE415This endpoint does not accept that Content-Type. Send application/json.
FEATURE_NOT_ENABLED400This feature is not enabled for the organization.
UNAUTHENTICATED401Authentication is required. Supply valid credentials.
FORBIDDEN403You do not have permission to perform this action.
NOT_FOUND404The requested resource was not found.
CONFLICT409The request conflicts with the current state of the resource.
RATE_LIMITED429Too many requests; try again later.
INTERNAL_ERROR500An unexpected error occurred.
SERVICE_UNAVAILABLE503The service is temporarily unavailable; try again shortly.
REQUEST_FAILED200 con error: trueThe request could not be completed.
INVALID_CREDENTIAL401The supplied credential is malformed or not recognized.
CREDENTIAL_EXPIRED401The supplied credential has expired.
CREDENTIAL_REVOKED401The supplied credential has been revoked.
CERTIFICATE_MISMATCH401The client certificate does not match the supplied credential.
INVALID_AUTH_ORG400The x-auth-org header is not a valid organization UUID.
DOMAIN_NOT_ALLOWED403Requests from this domain are not allowed for this credential.
DEVICE_SCOPE_MISMATCH403This token is scoped to a different device.
ROLE_NOT_PERMITTED403Your role does not permit access to this organization.
FUNCTIONALITY_REQUIRED403Your role is missing a functionality this action requires.
PARTNER_FUNCTIONALITY_REQUIRED403Your partner role is missing a functionality this action requires.
PERMISSION_REQUIRED403Your role does not have the required permission on the requested resource.
PARTNER_CLIENT_ACCESS_DENIED403Your partner organization does not have access to this client organization.
HARDWARE_NOT_AVAILABLE403No hardware with this serial number is available for your organization to register.
HARDWARE_ALREADY_REGISTERED403This hardware is already registered to an organization. Remove it from that organization before registering it again.
LOCATION_NOT_FOUND400The location was not found in the target organization.
PARTNER_PORTAL_NOT_ENABLED403The partner portal is not enabled for your partner organization. Contact Rhombus support.
MANAGED_BY_MSP403This organization is managed by a Managed Service Provider. Contact the provider or Rhombus support.
SUPER_ADMIN_RESTRICTED_BY_MSP403The Super Admin role in this organization is managed by a Managed Service Provider. Contact the provider or Rhombus support.
MSP_DESIGNATION_INVALID403This operation requires an active, signed Managed Service Provider designation.
NOT_MANAGED_CLIENT403This organization is not a managed client of your Managed Service Provider account.
DEVICES_STILL_REGISTERED200 con error: trueDevices are still registered in this client organization.
LICENSES_STILL_ASSIGNED200 con error: trueLicenses you own are still assigned to devices in this client organization.
DOOR_BOUND_HARDWARE200 con error: trueThis device is bound to one or more access-controlled doors. Remove the bindings in the source organization before moving it.
CONFIRM_NAME_MISMATCH409The confirmation text does not match the client organization name.
DUPLICATE_CLIENT_NAME409You already manage a client organization with this name. Set allowDuplicateName to create it anyway.
LICENSE_V2_DISABLED409Pooled licenses cannot be assigned to this client organization because it is not on license entitlements. Contact Rhombus support.
INSUFFICIENT_POOL_LICENSES409Not enough unassigned licenses of this product in the pool.

Los campos con valor nulo se omiten en las respuestas, así que trata un campo ausente como nulo. Las listas vacías sí se envían.

Códigos de estado

EstadoCuerpoSignificado¿Reintentar?
200 con "error": true{"error":true,"errorMsg":"…", …}La acción falló o se rechazóNo; actúa según errorMsg
400{"msg":"Invalid Json", …}El cuerpo no es JSON válidoNo
400{"msg":"JSON doesn't match expected object structure", …}Un campo tiene el tipo equivocado, un valor de enum no se reconoce o (en algunas operaciones) el cuerpo trae un campo que la operación no aceptaNo
400{"msg":"Request missing required field", …} o {"error":true,"errorMsg":"[Error: [campo]: …]", …}Falta un campo obligatorio o un valor está fuera de rango. El mensaje nombra el campoNo
400{"errorCode":"INVALID_AUTH_ORG", …}El encabezado x-auth-org está mal formadoNo
401{"errorCode":"UNAUTHENTICATED", …} u otro código de credencialLa credencial falta, está mal formada, es desconocida, venció o fue revocadaNo; consulta Errores de autenticación y de permisos
403{"errorCode":"PERMISSION_REQUIRED", …} u otro código de permisoLa credencial es válida, pero no tiene permiso para hacer estoNo; consulta Errores de autenticación y de permisos
404{"msg":"Not Found", …}La ruta /api no existe (NOT_FOUND; las rutas distinguen mayúsculas y minúsculas, así que una ruta con mayúsculas incorrectas también recibe 404), o algo que la operación busca no existeNo
405{"errorCode":"METHOD_NOT_ALLOWED", …}La ruta no acepta este método HTTPNo
415{"errorCode":"UNSUPPORTED_MEDIA_TYPE", …}El Content-Type de la solicitud no es uno que la operación acepteNo
429{"errorCode":"RATE_LIMITED", …} o un errorMsg que nombra las cámarasEl límite de tasa de tu organización o un límite por cámara para clips y fotogramasSí, después de Retry-After; consulta Límites de tasa
500{"msg":"…", …}Un error inesperado del servidorSí, unas pocas veces con espera exponencial
503{"errorCode":"SERVICE_UNAVAILABLE", …}Rhombus no pudo comprobar la credencial en ese momentoSí, con espera exponencial
502, 503, 504VaríaUn problema temporal de la puerta de enlace o de disponibilidadSí, con espera exponencial

La mayoría de los rechazos llegan como 200 con "error": true; solo algunas operaciones responden 404. Maneja ambos casos.

Errores de autenticación y de permisos

401 significa que Rhombus no pudo establecer quién llama. 403 significa que sí pudo, y que ese llamador no tiene permiso para hacer esto. Ninguno tiene éxito si lo repites sin cambios: corrige la credencial, el rol o la solicitud. El único caso que se reintenta es una credencial vencida, después de renovarla.

EstadoerrorCodeCausa
401UNAUTHENTICATEDNo se presentó ninguna credencial
401INVALID_CREDENTIALLa credencial está mal formada o no se reconoce, incluida una clave de API eliminada o un token federado revocado
401CREDENTIAL_EXPIREDLa credencial venció. Renueva el token de acceso OAuth, emite un nuevo token federado o reemplaza la clave de API, y reintenta
401CREDENTIAL_REVOKEDSe revocó la autorización de OAuth detrás del access token
401CERTIFICATE_MISMATCHEl certificado de cliente no pertenece a la clave de API (Client certificate does not match the api key)
403PERMISSION_REQUIREDEl rol de la credencial, o los scopes de un token de acceso OAuth, no permiten esta operación o este dispositivo
403PARTNER_CLIENT_ACCESS_DENIEDLa credencial de partner no puede actuar en la organización indicada en x-auth-org
403DOMAIN_NOT_ALLOWEDUn token federado emitido con un domain llegó desde otro sitio: el Origin de la solicitud, o su Referer cuando no hay Origin, no coincide
403DEVICE_SCOPE_MISMATCHUn token federado limitado a un dispositivo se usó para otro dispositivo o para una solicitud que no está ligada a su dispositivo

Scope de OAuth faltante. Cuando a un token de acceso OAuth le falta el scope que necesita una operación, el 403 nombra el scope en un encabezado WWW-Authenticate. Pide ese scope cuando el usuario inicia sesión (Iniciar sesión con Rhombus):

Scope faltante
HTTP/1.1 403 Forbidden
WWW-Authenticate: Bearer error="insufficient_scope", scope="physical"

Enlaces compartidos protegidos con contraseña. Una solicitud a un enlace compartido protegido con contraseña sin la contraseña correcta recibe 401 con WWW-Authenticate: Basic.

Autenticación indica qué revisar para cada tipo de credencial.

Cuerpos de solicitud

  • Envía POST con Content-Type: application/json y un objeto JSON, {} cuando la operación no recibe parámetros.
  • Los nombres de campo distinguen mayúsculas y minúsculas, y la mayoría de las operaciones ignoran los campos que no reconocen. Un campo mal escrito o con mayúsculas incorrectas se descarta sin error, así que un filtro que parece no tener efecto suele ser un error de escritura. Copia los nombres de campo de la referencia de la API (en inglés).
  • Los valores de enum distinguen mayúsculas y minúsculas. Un valor no reconocido normalmente se rechaza con 400, pero algunos campos lo sustituyen por su valor predeterminado, así que un error de escritura puede cambiar una configuración. Envía los valores exactamente como los lista la referencia.
  • Las marcas de tiempo son milisegundos de época Unix en los campos que terminan en Ms. Los campos que terminan en Sec, Secs o Seconds están en segundos.
  • Los mensajes de validación usan los nombres de campo del servidor, que pueden llevar un guion bajo inicial: [Error: [_targetUuid]: unexpected null value for required field] se refiere a targetUuid.

Reintentos y espera exponencial

Reintenta solo lo que puede tener éxito sin cambios:

RespuestaAcción
2xx con "error": falseÉxito
2xx con "error": trueNo reintentes sin cambios; corrige la solicitud o los datos que describe errorMsg
400, 401, 403, 404, 405, 415No reintentes; corrige la solicitud, la credencial o el rol
401 con CREDENTIAL_EXPIREDRenueva la credencial y reintenta
429Espera los segundos de Retry-After y reintenta
500, 502, 503, 504, un tiempo de espera agotado o una conexión cortadaReintenta con espera exponencial y jitter

Cómo esperar:

  • Limitación (429): el servidor indica cuánto esperar en Retry-After, tanto para el límite de tu organización como para los límites por cámara de clips y fotogramas. Espera al menos ese tiempo, más un poco de jitter aleatorio para que muchos procesos no reintenten en el mismo instante. Reintentar antes de tiempo con una cámara limitada alarga su siguiente espera.
  • Fallos temporales (5xx, errores de red): espera de forma exponencial con jitter completo: un tiempo aleatorio entre 0 y min(60 s, 1 s × 2^intento).
  • Limita cada ciclo de reintentos con un número máximo de intentos (unos 5) y un presupuesto de tiempo, y después informa el fallo con su estado, errorCode, errorMsg y txid.
  • Comprueba antes de repetir una escritura. Si una solicitud que crea algo (un clip, una clave, un usuario) agotó el tiempo o falló con un 5xx, puede haber tenido éxito igualmente; busca el resultado antes de enviarla de nuevo para no crear un duplicado.
  • Comparte el presupuesto. Todas las claves y tokens de tu organización consumen el mismo límite de tasa, así que muchos procesos reintentando a la vez cuentan juntos; reparte las tareas programadas en lugar de iniciarlas todas en el mismo minuto.

Un cliente que sigue estas reglas:

import random
import time

import requests

RETRYABLE = {429, 500, 502, 503, 504}

session = requests.Session()
session.headers.update({"x-auth-scheme": "api-token", "x-auth-apikey": "YOUR_API_KEY"})

def call(url, body, attempts=5, base=1.0, cap=60.0, budget=120.0):
    """POST to the Rhombus API, retrying throttling and temporary failures."""
    deadline = time.monotonic() + budget
    for attempt in range(attempts):
        try:
            response = session.post(url, json=body, timeout=30)
        except (requests.ConnectionError, requests.Timeout):
            response = None  # network trouble: retry like a 5xx
        if response is not None and response.status_code not in RETRYABLE:
            response.raise_for_status()  # other 4xx: fix the request, don't retry
            data = response.json()
            if data.get("error"):
                raise RuntimeError(data.get("errorMsg"))  # refused: don't retry unchanged
            return data
        if response is not None and response.status_code == 429:
            delay = float(response.headers.get("Retry-After", "1")) + random.uniform(0, 1)
        else:
            delay = random.uniform(0, min(cap, base * 2 ** attempt))
        if attempt == attempts - 1 or time.monotonic() + delay > deadline:
            break
        time.sleep(delay)
    raise RuntimeError(f"gave up after {attempt + 1} attempts: {url}")

Contactar al soporte

Cuando un error persiste, escribe a support@rhombus.com con:

  • La ruta de la operación y la hora de la solicitud, con su zona horaria.
  • Tu región (EE. UU. o UE).
  • El código de estado y el cuerpo, incluidos errorCode y errorMsg.
  • El ID de la solicitud: txid en el cuerpo del error, que también se envía como encabezado de respuesta x-rhombus-txid.

Nunca envíes tu clave de API, tus tokens OAuth ni tus tokens federados.