Apariencia
Recursos
Todas las rutas son relativas a la dirección de la cuenta, https://<subdominio>.safetypanel.com.ar/api/v1/, y todas requieren una key con el permiso del recurso. Con PATCH solo se modifican los campos externos, y toda escritura acepta el encabezado Idempotency-Key para reintentar sin repetirla.
Cuenta
GET /api/v1/account
PATCH /api/v1/accountPermisos: account:read para leer, account:write para leer y modificar.
La cuenta a la que pertenece la key. Es la primera llamada para comprobar que una credencial funciona.
| Campo | Tipo | Qué contiene |
|---|---|---|
id | entero | Identificador de la cuenta. |
name | texto | Nombre de la cuenta. |
subdomain | texto | Subdominio de la cuenta. |
account_type | texto o null | agency (consultora) o company (empresa). |
country | texto | País de la cuenta, en código de dos letras (por ejemplo, AR). |
time_zone | texto | Zona horaria en la que opera la cuenta (por ejemplo, Buenos Aires). Los instantes de la API van igual en UTC. |
external_reference, external_metadata | texto o null, objeto | Los campos externos de tu sistema. |
Sitios
GET /api/v1/sites
GET /api/v1/sites/:id
PATCH /api/v1/sites/:idPermisos: sites:read para leer, sites:write para leer y modificar.
Los sitios de la cuenta. El listado se pagina y acepta los filtros status, updated_since y external_reference. Un sitio pedido por su id se devuelve en cualquier estado.
json
{
"id": 12,
"name": "Planta Norte",
"cuit": "30-71234567-8 / 30-70123456-1",
"responsible_entities": [
{
"id": 7,
"name": "Metalúrgica Norte SA",
"tax_id": "30-71234567-8",
"economic_activities": [
{ "code": "251101", "name": "Fabricación de carpintería metálica" }
]
},
{
"id": 9,
"name": "Norte Logística SRL",
"tax_id": "30-70123456-1",
"economic_activities": []
}
],
"street": "Av. Siempreviva 742",
"city": "Springfield",
"province": "Buenos Aires",
"postal_code": "1414",
"status": "active",
"slug": "planta-norte",
"site_group": { "id": 3, "name": "Grupo Taller" },
"responsible_professional_id": 45,
"professional_ids": [45, 51],
"external_reference": null,
"external_metadata": {},
"created_at": "2026-03-02T14:05:11Z",
"updated_at": "2026-09-20T18:30:00Z"
}| Campo | Tipo | Qué contiene |
|---|---|---|
id | entero | Identificador del sitio. Es el único identificador que acepta /sites/:id. |
name | texto | Nombre del sitio. |
cuit | texto o null | Los CUIT de las razones sociales responsables del sitio, separados por " / " (ver abajo). |
responsible_entities | lista de objetos | Las razones sociales responsables del sitio, cada una con su id, su name, su tax_id (el CUIT, o null si no lo tiene) y sus economic_activities (ver abajo). Siempre tiene al menos una. |
street, city, province, postal_code | texto o null | Dirección del sitio. |
status | texto | active, inactive o deleted. |
slug | texto | Identificador legible que el panel usa en sus direcciones. |
site_group | objeto o null | El grupo de sitios al que pertenece, con su id y su name, o null si no pertenece a ninguno. |
responsible_professional_id | entero o null | El profesional responsable del sitio. |
professional_ids | lista de enteros | Los profesionales asignados al sitio. Para saber qué profesionales acceden a un sitio, ver a qué sitios accede un profesional. |
external_reference, external_metadata | texto o null, objeto | Los campos externos de tu sistema. |
created_at, updated_at | instante | Cuándo se creó y cuándo cambió por última vez. |
En las cuentas de empresa, el panel no carga grupo ni profesional responsable por sitio, así que site_group y responsible_professional_id vienen en null. Si la cuenta antes fue consultora, el grupo que cargó entonces se conserva pero la API no lo muestra, y el responsable que haya quedado se sigue mostrando porque todavía le da acceso al sitio. cuit y responsible_entities vienen en los dos tipos de cuenta. Un cambio de tipo de cuenta no cambia el updated_at de los sitios ni de los grupos: si account_type cambia en /account, hacé una lectura completa.
El CUIT viene de las razones sociales responsables
Desde octubre de 2026, el sitio no tiene CUIT propio y el grupo tampoco: el CUIT es de cada razón social responsable del sitio. responsible_entities las lista en el orden en que se vincularon, y cuit une sus CUIT con " / ", como se veían antes los sitios con varios CUIT. Es null cuando ninguna de sus responsables tiene CUIT.
Dos cambios en el formato de cuit respecto de antes:
- Siempre con guiones. Un CUIT cargado sin guiones (
30712345678) ahora llega como30-71234567-8. - Ya no se hereda del grupo. Los sitios que tomaban el CUIT de su grupo tienen ahora como responsable una razón social con ese CUIT, así que el valor no cambia.
Para leer cada razón social por separado, usá responsible_entities en lugar de partir cuit.
Las actividades económicas de cada responsable
Desde octubre de 2026, cada objeto de responsible_entities trae economic_activities: la lista de actividades económicas de esa razón social, ordenada por código, cada una con su code (texto, en Argentina los seis dígitos del CLAE) y su name. Es una lista vacía cuando no tiene ninguna cargada.
El sitio no tiene un campo economic_activities propio. Las actividades son de cada razón social, y un sitio con varias responsables tiene las de cada una: una sola lista las mezclaría sin decir de quién es cada actividad. Para obtener todas las del sitio, recorré responsible_entities.
Estados
status | Qué significa |
|---|---|
active | Activo en el panel. Es el único estado que devuelve el listado por defecto. |
inactive | Inactivo en el panel. |
deleted | Archivado en el panel. El sitio se conserva con toda su información. |
Para enterarte de los cambios de estado al sincronizar, pedí también status=inactive y status=deleted junto con updated_since.
Qué mueve updated_at
Además de cualquier cambio en los datos del propio sitio, el updated_at de un sitio cambia cuando:
- se asigna o se quita un profesional;
- el sitio entra o sale de un grupo, o su grupo cambia de nombre o se elimina;
- se agrega o se quita una de sus razones sociales responsables, o una de ellas cambia de nombre, de CUIT o de actividades económicas.
Así, updated_since alcanza para enterarse de todo lo que cambió en un sitio.
Grupos de sitios
GET /api/v1/site_groups
GET /api/v1/site_groups/:id
PATCH /api/v1/site_groups/:idPermisos: site_groups:read para leer, site_groups:write para leer y modificar.
Los grupos de sitios de la cuenta: los sitios que una consultora atiende bajo un mismo contrato. Las empresas no tienen grupos, así que en sus cuentas el listado viene vacío y pedir un grupo por su id responde 404. Vale también para una cuenta que antes fue consultora: sus grupos se conservan y vuelven a aparecer si la cuenta vuelve a ser consultora.
El listado se pagina y acepta los filtros updated_since y external_reference. Los grupos no tienen estado, así que no aceptan status.
json
{
"id": 3,
"name": "Grupo Taller",
"site_ids": [12, 14, 20],
"external_reference": null,
"external_metadata": {},
"created_at": "2026-02-10T09:00:00Z",
"updated_at": "2026-09-18T11:22:03Z"
}| Campo | Tipo | Qué contiene |
|---|---|---|
id | entero | Identificador del grupo. |
name | texto | Nombre del grupo. |
site_ids | lista de enteros | Todos los sitios del grupo, en cualquier estado. |
external_reference, external_metadata | texto o null, objeto | Los campos externos de tu sistema. |
created_at, updated_at | instante | Cuándo se creó y cuándo cambió por última vez. |
El updated_at de un grupo también cambia cuando un sitio entra o sale de él.
Desde octubre de 2026, el grupo ya no trae la clave cuit, que venía siempre en null porque el grupo no tiene CUIT. El CUIT de cada sitio está en sus responsible_entities.
Un grupo eliminado deja de aparecer en la API. Sus sitios siguen existiendo: su updated_at cambia y su site_group pasa a ser null, así que una sincronización con updated_since sobre los sitios se entera.
Profesionales
GET /api/v1/professionals
GET /api/v1/professionals/:id
PATCH /api/v1/professionals/:idPermisos: professionals:read para leer, professionals:write para leer y modificar.
Los profesionales de la cuenta. Sirve, por ejemplo, para que otra plataforma deje entrar a un profesional si su email está en esta lista.
El listado se pagina y acepta los filtros status, updated_since y external_reference. Un profesional pedido por su id se devuelve en cualquier estado.
json
{
"id": 45,
"dni": "30123456",
"first_name": "Ana",
"last_name": "Pérez",
"email": "[email protected]",
"phone": "+54 11 5555-0000",
"status": "active",
"full_client_access": false,
"external_reference": "E-123",
"external_metadata": { "cost_center": "4410" },
"created_at": "2026-01-15T13:00:00Z",
"updated_at": "2026-09-19T20:10:42Z"
}| Campo | Tipo | Qué contiene |
|---|---|---|
id | entero | Identificador del profesional. Es el que aparece en responsible_professional_id y professional_ids de los sitios. |
dni | texto | Documento del profesional. |
first_name, last_name | texto | Nombre y apellido. |
email | texto | Email del profesional. |
phone | texto o null | Teléfono. |
status | texto | active, inactive o deleted. |
full_client_access | booleano | Si el profesional tiene acceso a todos los sitios de la cuenta (ver abajo). |
external_reference, external_metadata | texto o null, objeto | Los campos externos de tu sistema. |
created_at, updated_at | instante | Cuándo se creó y cuándo cambió por última vez. |
Estados
status | Qué significa |
|---|---|
active | Activo en el panel. Es el único estado que devuelve el listado por defecto. |
inactive | Inactivo en el panel. |
deleted | Eliminado en el panel: dado de baja. Se conserva con todo su historial y puede restaurarse. |
Un profesional dado de baja no aparece en el listado por defecto. Para enterarte de las bajas al sincronizar, pedí también status=deleted junto con updated_since.
A qué sitios accede un profesional
La API no trae una lista de sitios por profesional. Se arma con dos recursos:
- Si
full_client_accessestrue, el profesional accede a todos los sitios de la cuenta, incluidos los que se creen después. - Si es
false, accede a los sitios que lo nombran como responsable (responsible_professional_id) o que lo incluyen entre los asignados (professional_ids).
Asignar o quitar un profesional cambia el updated_at del sitio, no el del profesional. Para mantener sincronizado el acceso, seguí los cambios de los dos recursos: full_client_access con updated_since sobre los profesionales, y las asignaciones con updated_since sobre los sitios.
Empleados
GET /api/v1/employees
GET /api/v1/employees/:id
PATCH /api/v1/employees/:idPermisos: employees:read para leer, employees:write para leer y modificar.
Los empleados de la cuenta, con su empleador y los sitios en los que trabajan. Es el recurso que un sistema de RRHH o un ERP sincroniza con su padrón de personal.
El listado se pagina y acepta los filtros status, updated_since y external_reference, más estos:
| Filtro | Devuelve |
|---|---|
dni | Los empleados con ese documento. Acepta el número con o sin puntos ni guiones (30.123.456 es lo mismo que 30123456). |
personnel_number | Los empleados con ese número de legajo, exactamente. |
site_id | Los empleados asignados a ese sitio. Un sitio que no existe en la cuenta devuelve una lista vacía, y un valor que no es un número entero responde 422. |
Una búsqueda por dni o por external_reference devuelve al empleado en cualquier estado, igual que la búsqueda por referencia. Así un sistema encuentra a quien está dado de baja y lo reactiva en lugar de cargarlo dos veces. Si se indica status además, se respeta. Un empleado pedido por su id se devuelve en cualquier estado.
json
{
"id": 81,
"dni": "30123456",
"first_name": "Juan",
"last_name": "Gómez",
"personnel_number": "L-0042",
"email": "[email protected]",
"status": "active",
"legal_entity_id": 7,
"sites": [
{ "site_id": 12, "position_id": 30 },
{ "site_id": 14, "position_id": null }
],
"external_reference": "E-123",
"external_metadata": { "cost_center": "4410" },
"created_at": "2026-04-02T12:00:00Z",
"updated_at": "2026-10-20T15:42:10Z"
}| Campo | Tipo | Qué contiene |
|---|---|---|
id | entero | Identificador del empleado. Es el único identificador que acepta /employees/:id. |
dni | texto | Documento del empleado, sin puntos ni guiones. |
first_name, last_name | texto | Nombre y apellido. |
personnel_number | texto o null | Número de legajo. |
email | texto o null | Email del empleado, o null si no tiene uno (ver abajo). |
status | texto | active, inactive o deleted. |
legal_entity_id | entero | La razón social que lo emplea. Siempre tiene una. |
sites | lista de objetos | Los sitios a los que está asignado, ordenados por site_id. Cada uno trae el site_id y el position_id del puesto que ocupa en ese sitio, o null si no tiene puesto ahí. Es una lista vacía si no está asignado a ningún sitio. |
external_reference, external_metadata | texto o null, objeto | Los campos externos de tu sistema. |
created_at, updated_at | instante | Cuándo se creó y cuándo cambió por última vez. |
sites lista los sitios en cualquier estado: el estado de cada uno está en los sitios. Funciona igual en las cuentas de consultora y de empresa. En una empresa con un solo sitio, cada empleado nuevo queda asignado a ese sitio automáticamente.
El email puede venir vacío
Muchos empleados no tienen email: firman con su DNI. Cuando no se le carga uno, el panel le asigna una dirección de relleno que no recibe correo. La API no la muestra: en ese caso email viene en null, para que tu sistema no la guarde como un dato real.
Estados
status | Qué significa |
|---|---|
active | Activo en el panel. Es el único estado que devuelve el listado por defecto. |
inactive | Inactivo en el panel: sigue visible y puede iniciar sesión, pero deja de contarse en las tasas de capacitación. |
deleted | Eliminado en el panel: dado de baja. Se conserva con todo su historial (capacitaciones, entregas de EPP firmadas) y puede reactivarse. |
Un empleado nunca se borra. Para enterarte de las bajas al sincronizar, pedí también status=deleted junto con updated_since.
Qué mueve updated_at
Además de cualquier cambio en los datos del propio empleado, su updated_at cambia cuando:
- se lo asigna a un sitio o se lo quita de uno;
- cambia su puesto en un sitio, o se elimina el puesto que ocupaba;
- su razón social empleadora se fusiona con otra.
Así, updated_since alcanza para enterarse de todo lo que cambió en sites y en legal_entity_id. Un cambio de nombre del sitio, del puesto o de la razón social no lo mueve, porque el empleado solo trae sus identificadores.