Skip to content

Convenciones ​

Lo que se describe en esta página vale para todos los recursos.

Dirección ​

La API responde en el subdominio de la cuenta, bajo /api/v1/:

https://<subdominio>.safetypanel.com.ar/api/v1/

El subdominio es la parte inicial de la dirección con la que se ingresa al panel (por ejemplo, para tuempresa.safetypanel.com.ar es tuempresa). La dirección exacta de la cuenta aparece en Configuración → API.

Todas las respuestas son JSON. Los recursos se leen con GET, y con PATCH se modifican sus campos externos, siempre que la key tenga el permiso del recurso. Cualquier otro método, o una ruta que no existe, responde 404 con el código not_found.

Autenticación ​

Cada llamada lleva la key en el encabezado Authorization, con el esquema Bearer:

Authorization: Bearer spk_…

No se acepta la key en la dirección ni en otro esquema. Una llamada sin key, con una key revocada, con una key de otra cuenta o con una key que nunca existió responde lo mismo: 401 con el código unauthorized. La respuesta no distingue entre esos casos a propósito.

Una cuenta desactivada también responde 401.

Permisos ​

Cada key tiene permisos por recurso, que se eligen al emitirla y no cambian después. Para cada recurso, la key tiene uno de tres niveles:

NivelPermisoQué deja hacer
Sin acceso—Nada: toda llamada al recurso responde 403.
Lectura<recurso>:read, por ejemplo sites:readLeerlo con GET.
Lectura y escritura<recurso>:write, por ejemplo sites:writeLeerlo y modificarlo. La escritura incluye la lectura.

Cada recurso indica en Recursos qué permiso lo lee y cuál lo modifica. Una llamada sin el permiso que necesita responde 403 con el código insufficient_scope, y el mensaje dice qué permiso falta:

json
{
  "error": {
    "code": "insufficient_scope",
    "message": "Esta key no tiene el permiso sites:write."
  }
}

Ningún permiso se da solo: una key nueva tiene únicamente lo que se le eligió, y un recurso que se agregue a la API más adelante no le llega a ninguna key existente.

Keys anteriores a los permisos ​

Una key emitida antes de que existieran los permisos conserva exactamente el acceso que tenía, nunca más: lee account, sites, site_groups y professionals, y con PATCH solo modifica sus campos externos. Cualquier otra escritura, aunque el recurso la admita, responde 403 con el código insufficient_scope. Para elegirle otros permisos, emití una key nueva y revocá la anterior.

Fechas y horas ​

Todos los instantes van en UTC y en formato ISO 8601, por ejemplo 2026-09-23T17:41:48Z, aunque la cuenta opere en otra zona horaria.

Errores ​

Todo error responde con el mismo formato: un objeto error con un code en inglés, pensado para que lo lea el sistema, y un message en español, pensado para que lo lea una persona.

json
{
  "error": {
    "code": "invalid_parameter",
    "message": "per_page debe ser un entero entre 1 y 100."
  }
}
Estado HTTPcodeCuándo
400idempotency_key_missingLa escritura exige el encabezado Idempotency-Key y no llegó.
400idempotency_key_invalidEl encabezado Idempotency-Key está vacío, supera los 255 caracteres o tiene caracteres no admitidos.
401unauthorizedFalta la key, no es válida para esta cuenta o la cuenta está desactivada.
403module_disabledLa key es válida, pero la cuenta no tiene la API habilitada.
403insufficient_scopeLa key es válida, pero no tiene el permiso que la llamada necesita. El mensaje dice cuál.
404not_foundEl registro no existe en esta cuenta, o la ruta no existe.
409idempotency_key_in_progressOtro pedido con la misma Idempotency-Key todavía está en curso.
422invalid_parameterUn parámetro o un campo enviado con PATCH tiene un valor inválido. El mensaje dice cuál y qué valores acepta.
422idempotency_key_reusedLa Idempotency-Key ya se usó con un pedido distinto.
429rate_limitedLa key superó su límite de uso.

La key se valida antes que todo lo demás: una llamada sin key válida recibe 401 aunque la ruta no exista o la cuenta no tenga el módulo habilitado. Después se revisa el módulo (module_disabled) y luego el permiso (insufficient_scope): una key sin permiso sobre un recurso recibe 403 aunque el registro pedido no exista.

Un registro de otra cuenta responde 404, igual que uno que no existe.

Paginación ​

Los listados se entregan por páginas, ordenados por id de menor a mayor. Así una página no cambia entre dos llamadas seguidas.

ParámetroValor por defectoValores válidos
page1Un entero desde 1.
per_page25Un entero entre 1 y 100.

La respuesta trae los registros en data y la información para recorrer el resto en meta:

json
{
  "data": [ … ],
  "meta": {
    "page": 1,
    "per_page": 25,
    "total": 42,
    "next_page": 2
  }
}
  • total es la cantidad de registros que cumplen los filtros, sumando todas las páginas.
  • next_page es el número de la página siguiente, o null en la última. Para recorrer un listado completo, pedí páginas hasta que next_page sea null.
  • Una página más allá de la última responde con data vacío, no con un error.

Filtros ​

Los filtros se pasan como parámetros en la dirección y se combinan con la paginación. Cada recurso indica cuáles acepta.

updated_since ​

Devuelve solo los registros que cambiaron en o después del instante indicado. Es la forma de sincronizar sin volver a leer todo: guardá el momento de la última lectura y pedí lo que cambió desde entonces.

  • Acepta un instante ISO 8601 (2026-09-01T12:00:00Z) o una fecha sola (2026-09-01), que se toma como la medianoche UTC de ese día.
  • Un instante sin zona horaria se interpreta en UTC.
  • Cualquier otro valor responde 422.

Un registro también cuenta como cambiado cuando cambia algo que la API muestra dentro de él, por ejemplo, un profesional que se asigna a un sitio. Cada recurso detalla qué cambios lo mueven.

status ​

Filtra por estado. Si no se indica, devuelve solo los registros activos, tal como los muestra el panel. Un valor que el recurso no admite responde 422 y el mensaje lista los válidos.

Una búsqueda por external_reference, o por dni en los empleados, devuelve todos los estados salvo que se indique status.

El filtro solo afecta a los listados: un registro pedido por su id se devuelve en cualquier estado, y el campo status indica en cuál está.

external_reference ​

Devuelve los registros cuya referencia externa es exactamente el valor indicado. Puede haber cero, uno o varios: Safety Panel no exige que la referencia sea única.

Cuando se busca por referencia, el listado incluye los registros en cualquier estado, no solo los activos, para que un sistema encuentre también a quien está dado de baja. Si se indica status además, se respeta.

Campos externos ​

Todos los recursos tienen dos campos que pertenecen al sistema que se integra, no a Safety Panel. Safety Panel los guarda y los devuelve, pero nunca los interpreta ni los muestra, salvo la referencia externa, que se ve en el panel en solo lectura.

CampoTipoQué contiene
external_referencetexto o nullEl identificador que tu sistema le da al registro. Hasta 255 caracteres. No tiene que ser único.
external_metadataobjetoPares clave–valor de tu sistema. Por defecto, {}.

Se modifican con PATCH sobre el registro, enviando solo estos campos en JSON. Cualquier otro campo responde 422, y un campo que no se envía no cambia.

sh
curl -X PATCH "https://tuempresa.safetypanel.com.ar/api/v1/professionals/45" \
  -H "Authorization: Bearer spk_…" \
  -H "Content-Type: application/json" \
  -d '{"external_reference": "E-123", "external_metadata": {"cost_center": "4410"}}'

La respuesta es el registro completo, como lo devuelve GET. Un cambio en estos campos mueve el updated_at del registro.

  • Referencia externa: un texto la reemplaza, y "" o null la quitan.
  • Metadatos: se actualizan por clave. Una clave enviada reemplaza su valor y las demás no cambian. Una clave con "" o null se borra, y "external_metadata": "" los borra todos.

Los metadatos admiten hasta 50 claves. Cada clave tiene hasta 40 caracteres y no lleva [ ni ], y cada valor es un texto de hasta 500 caracteres. Un número, un booleano o un objeto como valor responde 422: guardalo como texto.

No guardes datos sensibles en los metadatos

Los metadatos no se cifran y los lee cualquier key de la cuenta con permiso sobre ese recurso. No guardes contraseñas, tokens ni datos personales sensibles.

Idempotencia ​

Una escritura puede llevar el encabezado Idempotency-Key con una clave que tu sistema genera para esa operación. Si la llamada se corta —un timeout, una caída de red— y no sabés si llegó, reintentala con la misma clave: Safety Panel no la ejecuta dos veces.

sh
curl -X PATCH "https://tuempresa.safetypanel.com.ar/api/v1/professionals/45" \
  -H "Authorization: Bearer spk_…" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 6f1c2a4e-8b0d-4d5e-9a3f-2c7b1e9d0a64" \
  -d '{"external_reference": "E-123"}'
  • Usá una clave nueva por operación, por ejemplo un UUID. La clave tiene entre 1 y 255 caracteres ASCII visibles, sin espacios.
  • Un reintento con la misma clave y el mismo pedido (mismo método, misma ruta con los mismos parámetros y mismo cuerpo) no se vuelve a ejecutar: responde el mismo estado y el mismo cuerpo que la primera vez, con el encabezado Idempotent-Replayed: true. El orden de los campos en el JSON no importa.
  • La misma clave con otro pedido responde 422 con el código idempotency_key_reused.
  • Si el primer pedido todavía está en curso, el reintento responde 409 con el código idempotency_key_in_progress. Esperá unos segundos y reintentá con la misma clave.
  • Solo se recuerdan las respuestas exitosas (2xx). Si la llamada responde un error, no se escribió nada: corregí el pedido y reintentá, con la misma clave o con otra.
  • Cada clave vale para la key de API que la usó y se recuerda durante 24 horas. Después, la misma clave se toma como una operación nueva. Dos keys de la misma cuenta no comparten claves.

La autenticación, la habilitación del módulo y los permisos de la key se verifican antes que la clave: una llamada rechazada por cualquiera de ellos no consume la clave ni recibe una respuesta de idempotencia.

Los PATCH de campos externos aceptan el encabezado pero no lo exigen. Las escrituras que lo exigen lo indican en Recursos, y sin él responden 400 con el código idempotency_key_missing.

Límites de uso ​

Cada key tiene un cupo de 300 llamadas por minuto. El cupo es por key, no por dirección IP, así que dos sistemas con keys distintas no se afectan entre sí.

Cada respuesta a una llamada con key informa el cupo en tres encabezados:

EncabezadoQué indica
x-ratelimit-limitLas llamadas permitidas por minuto.
x-ratelimit-remainingLas que quedan en el minuto en curso.
x-ratelimit-resetEl instante en que se renueva el cupo, en segundos desde el 1 de enero de 1970 (UTC).

El cupo se renueva al comienzo de cada minuto, no un minuto después de la primera llamada. Una llamada por encima del cupo responde 429 con el código rate_limited y el encabezado retry-after, que indica cuántos segundos faltan para que se renueve. Esperá ese tiempo antes de reintentar.

Documentación de Safety Panel, software de gestión de seguridad e higiene.