API de Harmony
Lee los datos de tu organización desde tus propios sistemas, sin pasar por la interfaz. Es una API de sólo lectura: ningún endpoint escribe, y una clave filtrada expone información pero nunca altera tu operación.
https://harmony.report/api/v1 · openapi.json — impórtalo en Postman, Insomnia o un generador de clientes.
Autenticación
Un super administrador emite las claves desde Ajustes → API. La clave se muestra una sola vez: Harmony guarda su huella, no la clave. Si se pierde, se revoca y se emite otra.
curl https://harmony.report/api/v1/me \
-H "Authorization: Bearer hk_live_tu_clave"
La API es de servidor a servidor: no responde a peticiones desde un navegador. Una clave en el código de un frontend es una clave pública.
El mes es la unidad de consulta
Turnos, novedades, asignaciones y registros financieros se piden por mes, con period=YYYY-MM, y el parámetro es obligatorio. No hay rango de fechas: Harmony organiza la operación por período mensual, que es también la unidad con la que se cierra el mes. Cada turno vuelve con su fecha completa en date.
Paginación
limit va de 1 a 5.000 (50 por defecto). Para volumen conviene subirlo: las novedades de un mes son miles, y a 5.000 por página son unas pocas peticiones contra el mismo límite por minuto. La respuesta trae nextCursor: mientras no sea null, hay más páginas. Es la única señal de fin — nunca el tamaño de data.
Expansiones
Las cuatro rutas por identificador —cliente, sede, puesto y persona— aceptan include: una lista de secciones separadas por comas que llegan en la misma respuesta. Sirve para no encadenar una petición por cada pieza de la programación.
curl "https://harmony.report/api/v1/points/{id}?period=2026-08&include=area,customer,assignments,workers,shifts,incidents,elsewhere" \
-H "Authorization: Bearer hk_live_tu_clave"
Esa sola petición trae el puesto, su sede, su cliente, quién está asignado, quién está programado —con assigned para distinguirlos—, los turnos y las novedades del mes, y dónde más está esa gente.
Tres reglas que conviene saber antes de escribir la primera llamada. Sin include la respuesta no cambia; con él, el recurso viene dentro de un sobre —{ point: …, shifts: [] }— junto a las secciones pedidas. Las secciones que viven en un mes exigen period, y pedirlas sin él es un 400, no un mes por defecto. Y cada sección tiene un tope de 3.000 elementos —y elsewhere consulta como mucho 150 personas—: si se alcanza, su nombre aparece en truncated. Para volumen la vía no es ésta sino la ruta plana con cursor, que pagina hasta 5.000 por página; las expansiones componen una vista, no exportan.
Endpoints
GET/meLa clave que estás usando
Devuelve la organización de la clave, su alcance y el límite de peticiones. Útil como primera llamada para comprobar que la clave funciona.
Petición
curl "https://harmony.report/api/v1/me" \
-H "Authorization: Bearer hk_live_tu_clave"
Respuesta · 200
{
"key": {
"name": "Integración de nómina",
"prefix": "hk_live_a1b2c3",
"createdAt": "2026-09-07T15:04:00.000Z",
"lastUsedAt": "2026-09-07T15:41:12.000Z",
"expiresAt": null
},
"organization": {
"id": "p57fazjfjmde4egmkz0xh6b51982enz0",
"name": "Seguridad Andina"
},
"scope": {
"type": "organization",
"workspaceIds": null,
"unreachableWorkspaceIds": []
},
"rateLimit": {
"limit": 60,
"windowSeconds": 60
}
}
GET/workspacesEspacios de trabajo
Los espacios que la clave alcanza. Una clave de organización los devuelve todos; una acotada, sólo los suyos.
Petición
curl "https://harmony.report/api/v1/workspaces" \
-H "Authorization: Bearer hk_live_tu_clave"
Respuesta · 200
{
"data": [
{
"id": "k17fgbsj1hyw66bq5gw5sz2g8s82fs5j",
"name": "Operación Bogotá",
"active": true,
"createdAt": "2026-01-15T14:02:11.000Z"
}
],
"nextCursor": null
}
Campos de Workspace
| id | string | Identificador de Convex. |
| name | string | Nombre del espacio. |
| active | boolean | Si está activo. |
| createdAt | string | Creación, ISO 8601. |
GET/customersClientes
Los clientes de la organización. Con una clave acotada a espacios, workspaceId es obligatorio: no hay índice que recorra varios espacios en una sola página.
Parámetros
| workspaceId | string | según alcance | Espacio del que listar los clientes. |
| limit | integer | opcional | Tamaño de página, de 1 a 5.000. Por defecto 50. Para volumen —todas las novedades de un mes— conviene subirlo: son menos peticiones contra el mismo límite por minuto. |
| cursor | string | opcional | El nextCursor de la página anterior. |
Petición
curl "https://harmony.report/api/v1/customers" \
-H "Authorization: Bearer hk_live_tu_clave"
Respuesta · 200
{
"data": [
{
"id": "j5712xk9qb3v8w1n0m4pz6r2t8s5c7d1",
"name": "Centro Comercial Andino",
"identifier": "900123456-7",
"code": "CC-AND",
"workspaceId": "k17fgbsj1hyw66bq5gw5sz2g8s82fs5j",
"idle": false,
"active": true,
"hasForm": true,
"createdAt": "2026-02-03T09:11:00.000Z"
}
],
"nextCursor": null
}
Campos de Customer
| id | string | Identificador de Convex. |
| name | string | Razón social del cliente. |
| identifier | string | NIT o documento. |
| workspaceId | string | Espacio al que pertenece. |
| idle | boolean | Suspendido temporalmente. |
| active | boolean | Si está activo. |
| hasForm | boolean | Tiene ficha de formulario. |
| createdAt | string | Creación, ISO 8601. |
GET/customers/{id}Un cliente
Un cliente por su identificador. Responde 404 si no existe o si está fuera del alcance de la clave: no se distingue, para no confirmar la existencia de datos ajenos. Con include devuelve la programación del cliente completa —sedes, puestos, personal, turnos y novedades del mes— en una sola petición.
Parámetros
| id | string · ruta | obligatorio | Identificador del cliente. |
| include | string | opcional | Secciones que añadir a la respuesta, separadas por comas. Sin este parámetro la respuesta no cambia; con él, el recurso pasa a venir dentro de un sobre junto a las secciones pedidas. |
| period | string | opcional | Mes a consultar, YYYY-MM. Obligatorio en cuanto se expande algo que vive en un mes; sin él esas expansiones responden 400. |
Expansiones · include
| workspace | | El espacio de trabajo al que pertenece. |
| areas | | Las sedes del cliente. |
| points | | Los puestos que cuelgan. |
| workers | pide period | Las personas asignadas y las programadas, cada una con assigned: true | false. Alguien puede cubrir un puesto sin estar asignado, y alguien asignado puede no tener ni un turno. |
| assignments | pide period | Las asignaciones del período. |
| shifts | pide period | Los turnos del período. |
| incidents | pide period | Las novedades del período. Cada una lleva además fields: sus detalles, las respuestas al formulario de novedades de la organización, como [{ fieldId, label, value }] en el orden del formulario, sólo con lo respondido y visible, y con value ya legible (la etiqueta de una lista, fechas DD/MM/AAAA, importes en pesos). Sin formulario, fields es []. |
| positions | | Los cargos referidos por los registros, sin repetir. |
| categories | | Las categorías de novedad referidas, sin repetir. |
Petición
curl "https://harmony.report/api/v1/customers/j5712xk9qb3v8w1n0m4pz6r2t8s5c7d1" \
-H "Authorization: Bearer hk_live_tu_clave"
Respuesta · 200
{
"id": "j5712xk9qb3v8w1n0m4pz6r2t8s5c7d1",
"name": "Centro Comercial Andino",
"identifier": "900123456-7",
"code": "CC-AND",
"workspaceId": "k17fgbsj1hyw66bq5gw5sz2g8s82fs5j",
"idle": false,
"active": true,
"hasForm": true,
"createdAt": "2026-02-03T09:11:00.000Z"
}
Campos de Customer
| id | string | Identificador de Convex. |
| name | string | Razón social del cliente. |
| identifier | string | NIT o documento. |
| workspaceId | string | Espacio al que pertenece. |
| idle | boolean | Suspendido temporalmente. |
| active | boolean | Si está activo. |
| hasForm | boolean | Tiene ficha de formulario. |
| createdAt | string | Creación, ISO 8601. |
GET/areas/{id}Una sede
Una sede por su identificador, con lo que cuelga de sus puestos. Los turnos y las novedades cuelgan del puesto y no de la sede, así que expandirlos cuesta una consulta por puesto: una sede de más de 50 responde 400 y hay que pedir sus puestos uno a uno.
Parámetros
| id | string · ruta | obligatorio | Identificador de la sede. |
| include | string | opcional | Secciones que añadir a la respuesta, separadas por comas. Sin este parámetro la respuesta no cambia; con él, el recurso pasa a venir dentro de un sobre junto a las secciones pedidas. |
| period | string | opcional | Mes a consultar, YYYY-MM. Obligatorio en cuanto se expande algo que vive en un mes; sin él esas expansiones responden 400. |
Expansiones · include
| customer | | El cliente al que pertenece. |
| points | | Los puestos que cuelgan. |
| workers | pide period | Las personas asignadas y las programadas, cada una con assigned: true | false. Alguien puede cubrir un puesto sin estar asignado, y alguien asignado puede no tener ni un turno. |
| assignments | pide period | Las asignaciones del período. |
| shifts | pide period | Los turnos del período. |
| incidents | pide period | Las novedades del período. Cada una lleva además fields: sus detalles, las respuestas al formulario de novedades de la organización, como [{ fieldId, label, value }] en el orden del formulario, sólo con lo respondido y visible, y con value ya legible (la etiqueta de una lista, fechas DD/MM/AAAA, importes en pesos). Sin formulario, fields es []. |
| elsewhere | pide period | Los turnos y novedades de esas personas fuera de aquí, en el mismo mes: lo que responde si alguien está libre. Sólo llega lo que la clave alcanza. Hay que pedir también workers. |
| positions | | Los cargos referidos por los registros, sin repetir. |
| categories | | Las categorías de novedad referidas, sin repetir. |
Petición
curl "https://harmony.report/api/v1/areas/j5712xk9qb3v8w1n0m4pz6r2t8s5c7d1" \
-H "Authorization: Bearer hk_live_tu_clave"
Respuesta · 200
{
"id": "m9231bc4de5f6g7h8i9j0k1l2m3n4o5p",
"name": "Torre Norte",
"description": "Acceso principal y parqueaderos",
"active": true,
"customerId": "j5712xk9qb3v8w1n0m4pz6r2t8s5c7d1"
}
Campos de Area
| id | string | Identificador de Convex. |
| name | string | Nombre de la sede. |
| description | string | Descripción libre. |
| active | boolean | Si está activa. |
| customerId | string | Cliente al que pertenece. |
GET/points/{id}Un puesto
Un puesto por su identificador. Es la ruta que resuelve el caso completo: su sede, su cliente, quién está asignado, quién está programado, los turnos y las novedades del mes, y dónde más está esa gente — todo en una petición.
Parámetros
| id | string · ruta | obligatorio | Identificador del puesto. |
| include | string | opcional | Secciones que añadir a la respuesta, separadas por comas. Sin este parámetro la respuesta no cambia; con él, el recurso pasa a venir dentro de un sobre junto a las secciones pedidas. |
| period | string | opcional | Mes a consultar, YYYY-MM. Obligatorio en cuanto se expande algo que vive en un mes; sin él esas expansiones responden 400. |
Expansiones · include
| area | | La sede del puesto. Es null cuando el puesto cuelga del cliente sin sede. |
| customer | | El cliente al que pertenece. |
| workers | pide period | Las personas asignadas y las programadas, cada una con assigned: true | false. Alguien puede cubrir un puesto sin estar asignado, y alguien asignado puede no tener ni un turno. |
| assignments | pide period | Las asignaciones del período. |
| shifts | pide period | Los turnos del período. |
| incidents | pide period | Las novedades del período. Cada una lleva además fields: sus detalles, las respuestas al formulario de novedades de la organización, como [{ fieldId, label, value }] en el orden del formulario, sólo con lo respondido y visible, y con value ya legible (la etiqueta de una lista, fechas DD/MM/AAAA, importes en pesos). Sin formulario, fields es []. |
| elsewhere | pide period | Los turnos y novedades de esas personas fuera de aquí, en el mismo mes: lo que responde si alguien está libre. Sólo llega lo que la clave alcanza. Hay que pedir también workers. |
| positions | | Los cargos referidos por los registros, sin repetir. |
| categories | | Las categorías de novedad referidas, sin repetir. |
Petición
curl "https://harmony.report/api/v1/points/j5712xk9qb3v8w1n0m4pz6r2t8s5c7d1" \
-H "Authorization: Bearer hk_live_tu_clave"
Respuesta · 200
{
"id": "n8321cd5ef6g7h8i9j0k1l2m3n4o5p6q",
"name": "Portería Norte",
"description": "24 horas",
"areaId": "m9231bc4de5f6g7h8i9j0k1l2m3n4o5p",
"customerId": "j5712xk9qb3v8w1n0m4pz6r2t8s5c7d1",
"idle": false,
"active": true
}
Campos de Point
| id | string | Identificador de Convex. |
| name | string | Nombre del puesto. |
| description | string | Descripción libre. |
| areaId | string | null | Sede en la que está, si tiene. |
| customerId | string | Cliente al que pertenece. |
| idle | boolean | Puesto inactivo. |
| active | boolean | Si está activo. |
GET/areasSedes de un cliente
Las sedes cuelgan del cliente y ése es su único índice, así que customerId es obligatorio.
Parámetros
| customerId | string | obligatorio | Cliente del que listar las sedes. |
| limit | integer | opcional | Tamaño de página, de 1 a 5.000. Por defecto 50. Para volumen —todas las novedades de un mes— conviene subirlo: son menos peticiones contra el mismo límite por minuto. |
| cursor | string | opcional | El nextCursor de la página anterior. |
Petición
curl "https://harmony.report/api/v1/areas?customerId=j5712xk9qb3v8w1n0m4pz6r2t8s5c7d1" \
-H "Authorization: Bearer hk_live_tu_clave"
Respuesta · 200
{
"data": [
{
"id": "m9231bc4de5f6g7h8i9j0k1l2m3n4o5p",
"name": "Torre Norte",
"description": "Acceso principal y parqueaderos",
"active": true,
"customerId": "j5712xk9qb3v8w1n0m4pz6r2t8s5c7d1"
}
],
"nextCursor": null
}
Campos de Area
| id | string | Identificador de Convex. |
| name | string | Nombre de la sede. |
| description | string | Descripción libre. |
| active | boolean | Si está activa. |
| customerId | string | Cliente al que pertenece. |
GET/pointsPuestos de servicio
Se piden por cliente o por sede — uno de los dos, no los dos a la vez.
Parámetros
| customerId | string | opcional | Cliente del que listar los puestos. |
| areaId | string | opcional | Sede de la que listar los puestos. |
| limit | integer | opcional | Tamaño de página, de 1 a 5.000. Por defecto 50. Para volumen —todas las novedades de un mes— conviene subirlo: son menos peticiones contra el mismo límite por minuto. |
| cursor | string | opcional | El nextCursor de la página anterior. |
Petición
curl "https://harmony.report/api/v1/points" \
-H "Authorization: Bearer hk_live_tu_clave"
Respuesta · 200
{
"data": [
{
"id": "n8321cd5ef6g7h8i9j0k1l2m3n4o5p6q",
"name": "Portería Norte",
"description": "24 horas",
"areaId": "m9231bc4de5f6g7h8i9j0k1l2m3n4o5p",
"customerId": "j5712xk9qb3v8w1n0m4pz6r2t8s5c7d1",
"idle": false,
"active": true
}
],
"nextCursor": null
}
Campos de Point
| id | string | Identificador de Convex. |
| name | string | Nombre del puesto. |
| description | string | Descripción libre. |
| areaId | string | null | Sede en la que está, si tiene. |
| customerId | string | Cliente al que pertenece. |
| idle | boolean | Puesto inactivo. |
| active | boolean | Si está activo. |
GET/workersPersonal
workspaceId es obligatorio: el personal no tiene índice por organización, sólo por espacio y por cliente.
Parámetros
| workspaceId | string | obligatorio | Espacio del que listar el personal. |
| customerId | string | opcional | Acota al personal de un cliente de ese espacio. |
| search | string | opcional | Búsqueda difusa por nombre. Devuelve los mejores resultados de una vez, con nextCursor siempre en null. |
| limit | integer | opcional | Tamaño de página, de 1 a 5.000. Por defecto 50. Para volumen —todas las novedades de un mes— conviene subirlo: son menos peticiones contra el mismo límite por minuto. |
| cursor | string | opcional | El nextCursor de la página anterior. |
Petición
curl "https://harmony.report/api/v1/workers?workspaceId=k17fgbsj1hyw66bq5gw5sz2g8s82fs5j" \
-H "Authorization: Bearer hk_live_tu_clave"
Respuesta · 200
{
"data": [
{
"id": "w4412ef6gh7i8j9k0l1m2n3o4p5q6r7s",
"name": "Ana María Pérez",
"identifier": "1023456789",
"group": "Grupo A",
"customerId": "j5712xk9qb3v8w1n0m4pz6r2t8s5c7d1",
"workspaceId": "k17fgbsj1hyw66bq5gw5sz2g8s82fs5j",
"idle": false,
"active": true,
"hasForm": true,
"createdAt": "2026-02-10T16:40:00.000Z"
}
],
"nextCursor": null
}
Campos de Worker
| id | string | Identificador de Convex. |
| name | string | Nombre de la persona. |
| identifier | string | Documento de identidad. |
| group | string | null | Grupo o cuadrilla. |
| customerId | string | Cliente al que está adscrita. |
| workspaceId | string | Espacio al que pertenece. |
| idle | boolean | Inactiva temporalmente. |
| active | boolean | Si está activa. |
| hasForm | boolean | Tiene ficha de formulario. |
| createdAt | string | Creación, ISO 8601. |
GET/workers/{id}Una persona
Una persona por su identificador, si la clave la alcanza. Con include responde a la pregunta girada: no quién cubre un puesto, sino dónde está esta persona este mes.
Parámetros
| id | string · ruta | obligatorio | Identificador de la persona. |
| include | string | opcional | Secciones que añadir a la respuesta, separadas por comas. Sin este parámetro la respuesta no cambia; con él, el recurso pasa a venir dentro de un sobre junto a las secciones pedidas. |
| period | string | opcional | Mes a consultar, YYYY-MM. Obligatorio en cuanto se expande algo que vive en un mes; sin él esas expansiones responden 400. |
Expansiones · include
| workspace | | El espacio de trabajo al que pertenece. |
| customer | | El cliente al que pertenece. |
| assignments | pide period | Las asignaciones del período. |
| shifts | pide period | Los turnos del período. |
| incidents | pide period | Las novedades del período. Cada una lleva además fields: sus detalles, las respuestas al formulario de novedades de la organización, como [{ fieldId, label, value }] en el orden del formulario, sólo con lo respondido y visible, y con value ya legible (la etiqueta de una lista, fechas DD/MM/AAAA, importes en pesos). Sin formulario, fields es []. |
| positions | | Los cargos referidos por los registros, sin repetir. |
| categories | | Las categorías de novedad referidas, sin repetir. |
Petición
curl "https://harmony.report/api/v1/workers/j5712xk9qb3v8w1n0m4pz6r2t8s5c7d1" \
-H "Authorization: Bearer hk_live_tu_clave"
Respuesta · 200
{
"id": "w4412ef6gh7i8j9k0l1m2n3o4p5q6r7s",
"name": "Ana María Pérez",
"identifier": "1023456789",
"group": "Grupo A",
"customerId": "j5712xk9qb3v8w1n0m4pz6r2t8s5c7d1",
"workspaceId": "k17fgbsj1hyw66bq5gw5sz2g8s82fs5j",
"idle": false,
"active": true,
"hasForm": true,
"createdAt": "2026-02-10T16:40:00.000Z"
}
Campos de Worker
| id | string | Identificador de Convex. |
| name | string | Nombre de la persona. |
| identifier | string | Documento de identidad. |
| group | string | null | Grupo o cuadrilla. |
| customerId | string | Cliente al que está adscrita. |
| workspaceId | string | Espacio al que pertenece. |
| idle | boolean | Inactiva temporalmente. |
| active | boolean | Si está activa. |
| hasForm | boolean | Tiene ficha de formulario. |
| createdAt | string | Creación, ISO 8601. |
GET/assignmentsAsignaciones del mes
Qué persona cubre qué puesto en un mes. Las asignaciones cuelgan del cliente —no del espacio—, así que se piden con customerId.
Parámetros
| period | string | obligatorio | Mes a consultar, YYYY-MM. Obligatorio: la operación se guarda por período mensual y no hay rango de fechas. |
| customerId | string | obligatorio | Cliente cuyas asignaciones se listan. |
| limit | integer | opcional | Tamaño de página, de 1 a 5.000. Por defecto 50. Para volumen —todas las novedades de un mes— conviene subirlo: son menos peticiones contra el mismo límite por minuto. |
| cursor | string | opcional | El nextCursor de la página anterior. |
Petición
curl "https://harmony.report/api/v1/assignments?period=2026-08&customerId=j5712xk9qb3v8w1n0m4pz6r2t8s5c7d1" \
-H "Authorization: Bearer hk_live_tu_clave"
Respuesta · 200
{
"data": [
{
"id": "a1122gh8ij9k0l1m2n3o4p5q6r7s8t9u",
"period": "2026-08",
"pointId": "n8321cd5ef6g7h8i9j0k1l2m3n4o5p6q",
"pointName": "Portería Norte",
"workerId": "w4412ef6gh7i8j9k0l1m2n3o4p5q6r7s",
"workerName": "Ana María Pérez",
"workerIdentifier": "1023456789",
"customerId": "j5712xk9qb3v8w1n0m4pz6r2t8s5c7d1",
"skipExtraDays": false
}
],
"nextCursor": null
}
Campos de Assignment
| id | string | Identificador de Convex. |
| period | string | null | Mes, YYYY-MM. |
| pointId | string | Puesto cubierto. |
| pointName | string | Nombre del puesto al asignar. |
| workerId | string | Persona asignada. |
| workerName | string | Nombre al asignar. |
| workerIdentifier | string | Documento al asignar. |
| customerId | string | Cliente del puesto. |
| skipExtraDays | boolean | Excluye los días adicionales del mes. |
GET/shiftsTurnos del mes
Los turnos programados de un espacio en un mes, descansos incluidos (color: "gray").
Parámetros
| period | string | obligatorio | Mes a consultar, YYYY-MM. Obligatorio: la operación se guarda por período mensual y no hay rango de fechas. |
| workspaceId | string | obligatorio | Espacio cuyos turnos se listan. |
| limit | integer | opcional | Tamaño de página, de 1 a 5.000. Por defecto 50. Para volumen —todas las novedades de un mes— conviene subirlo: son menos peticiones contra el mismo límite por minuto. |
| cursor | string | opcional | El nextCursor de la página anterior. |
Petición
curl "https://harmony.report/api/v1/shifts?period=2026-08&workspaceId=k17fgbsj1hyw66bq5gw5sz2g8s82fs5j" \
-H "Authorization: Bearer hk_live_tu_clave"
Respuesta · 200
{
"data": [
{
"id": "s7712ij0kl1m2n3o4p5q6r7s8t9u0v1w",
"date": "2026-08-12",
"period": "2026-08",
"day": 12,
"description": "06:00 a 18:00",
"color": "green",
"startsAt": "2026-08-12T06:00:00-05:00",
"endsAt": "2026-08-12T18:00:00-05:00",
"workerId": "w4412ef6gh7i8j9k0l1m2n3o4p5q6r7s",
"workerName": "Ana María Pérez",
"workerIdentifier": "1023456789",
"pointId": "n8321cd5ef6g7h8i9j0k1l2m3n4o5p6q",
"positionId": "p1122kl2m3n4o5p6q7r8s9t0u1v2w3x",
"customerId": "j5712xk9qb3v8w1n0m4pz6r2t8s5c7d1",
"workspaceId": "k17fgbsj1hyw66bq5gw5sz2g8s82fs5j",
"sequenceId": null,
"sequenceName": "Rotativa 2x2"
}
],
"nextCursor": null
}
Campos de Shift
| id | string | Identificador de Convex. |
| date | string | null | Fecha completa del turno, YYYY-MM-DD. |
| period | string | null | Mes, YYYY-MM. |
| day | number | Día del mes. |
| description | string | «06:00 a 18:00» o «Descanso». |
| color | string | green turno · gray descanso. |
| startsAt | string | null | Inicio con huso, ISO 8601. Nulo en un descanso. |
| endsAt | string | null | Fin con huso, ISO 8601. |
| workerId | string | Persona que cubre el turno. |
| workerName | string | Nombre al programar. |
| workerIdentifier | string | Documento al programar. |
| pointId | string | null | Puesto cubierto. |
| positionId | string | null | Cargo aplicado. |
| customerId | string | Cliente del turno. |
| workspaceId | string | Espacio del turno. |
| sequenceId | string | null | Secuencia que lo generó. |
| sequenceName | string | Nombre de la secuencia. |
GET/incidentsNovedades del mes
Faltas, adicionales y seguimientos de un espacio en un mes, con su categoría.
Parámetros
| period | string | obligatorio | Mes a consultar, YYYY-MM. Obligatorio: la operación se guarda por período mensual y no hay rango de fechas. |
| workspaceId | string | obligatorio | Espacio cuyas novedades se listan. |
| limit | integer | opcional | Tamaño de página, de 1 a 5.000. Por defecto 50. Para volumen —todas las novedades de un mes— conviene subirlo: son menos peticiones contra el mismo límite por minuto. |
| cursor | string | opcional | El nextCursor de la página anterior. |
Petición
curl "https://harmony.report/api/v1/incidents?period=2026-08&workspaceId=k17fgbsj1hyw66bq5gw5sz2g8s82fs5j" \
-H "Authorization: Bearer hk_live_tu_clave"
Respuesta · 200
{
"data": [
{
"id": "i9912kl1m2n3o4p5q6r7s8t9u0v1w2x3",
"date": "2026-08-14",
"period": "2026-08",
"day": 14,
"abbreviation": "FI",
"description": "Falta injustificada",
"color": "red",
"keep": false,
"categoryId": "c2231lm2n3o4p5q6r7s8t9u0v1w2x3y4",
"categoryName": "Falta",
"startsAt": null,
"endsAt": null,
"workerId": "w4412ef6gh7i8j9k0l1m2n3o4p5q6r7s",
"workerName": "Ana María Pérez",
"workerIdentifier": "1023456789",
"pointId": "n8321cd5ef6g7h8i9j0k1l2m3n4o5p6q",
"positionId": null,
"customerId": "j5712xk9qb3v8w1n0m4pz6r2t8s5c7d1",
"workspaceId": "k17fgbsj1hyw66bq5gw5sz2g8s82fs5j"
}
],
"nextCursor": null
}
Campos de Incident
| id | string | Identificador de Convex. |
| date | string | null | Fecha completa, YYYY-MM-DD. |
| period | string | null | Mes, YYYY-MM. |
| day | number | Día del mes. |
| abbreviation | string | Sigla con la que se pinta. |
| description | string | Descripción de la novedad. |
| color | string | sky · red · yellow. |
| keep | boolean | Si cuenta como día trabajado. |
| categoryId | string | null | Categoría aplicada. |
| categoryName | string | null | Nombre al registrar. |
| startsAt | string | null | Inicio con huso, ISO 8601. |
| endsAt | string | null | Fin con huso, ISO 8601. |
| workerId | string | Persona implicada. |
| workerName | string | Nombre al registrar. |
| workerIdentifier | string | Documento al registrar. |
| pointId | string | null | Puesto donde ocurrió. |
| positionId | string | null | Cargo aplicado. |
| customerId | string | Cliente de la novedad. |
| workspaceId | string | Espacio de la novedad. |
GET/financial-recordsIngresos y egresos del mes
Con una clave de organización se leen todos los del mes; con una acotada, customerId es obligatorio.
Parámetros
| period | string | obligatorio | Mes a consultar, YYYY-MM. Obligatorio: la operación se guarda por período mensual y no hay rango de fechas. |
| customerId | string | según alcance | Cliente cuyos registros se listan. |
| limit | integer | opcional | Tamaño de página, de 1 a 5.000. Por defecto 50. Para volumen —todas las novedades de un mes— conviene subirlo: son menos peticiones contra el mismo límite por minuto. |
| cursor | string | opcional | El nextCursor de la página anterior. |
Petición
curl "https://harmony.report/api/v1/financial-records?period=2026-08" \
-H "Authorization: Bearer hk_live_tu_clave"
Respuesta · 200
{
"data": [
{
"id": "f3312mn3o4p5q6r7s8t9u0v1w2x3y4z5",
"period": "2026-08",
"type": "income",
"concept": "Servicio de vigilancia agosto",
"amount": 18500000,
"customerId": "j5712xk9qb3v8w1n0m4pz6r2t8s5c7d1",
"costCenterId": "k9912ab3c4d5e6f7g8h9i0j1k2l3m4n5",
"costCenterCode": "00201",
"costCenterName": "CAJAMAG"
}
],
"nextCursor": null
}
Campos de FinancialRecord
| id | string | Identificador de Convex. |
| period | string | null | Mes, YYYY-MM. |
| type | string | income ingreso · expense egreso. |
| concept | string | Concepto del registro. |
| amount | number | Monto, en pesos. |
| customerId | string | null | Cliente al que se imputa. Nulo en un centro indirecto, cuyo devengo no se factura a ningún cliente. |
| costCenterId | string | null | Centro de costo del registro. Nulo cuando la facturación llega agregada sobre varios centros. |
| costCenterCode | string | null | Código externo del centro. |
| costCenterName | string | null | Nombre del centro. |
GET/positionsCargos
Catálogo de cargos de la organización. Es una entidad de organización, así que una clave acotada a espacios lo ve igual.
Parámetros
| limit | integer | opcional | Tamaño de página, de 1 a 5.000. Por defecto 50. Para volumen —todas las novedades de un mes— conviene subirlo: son menos peticiones contra el mismo límite por minuto. |
| cursor | string | opcional | El nextCursor de la página anterior. |
Petición
curl "https://harmony.report/api/v1/positions" \
-H "Authorization: Bearer hk_live_tu_clave"
Respuesta · 200
{
"data": [
{
"id": "p1122kl2m3n4o5p6q7r8s9t0u1v2w3x",
"name": "Guarda de seguridad",
"value": 1423500,
"year": 2026,
"active": true
}
],
"nextCursor": null
}
Campos de Position
| id | string | Identificador de Convex. |
| name | string | Nombre del cargo. |
| value | number | Valor asociado al cargo. |
| year | number | Año de vigencia. |
| active | boolean | Si está activo. |
GET/categoriesCategorías de novedad
Catálogo de categorías de la organización, con su sigla y si cuentan como día trabajado.
Parámetros
| limit | integer | opcional | Tamaño de página, de 1 a 5.000. Por defecto 50. Para volumen —todas las novedades de un mes— conviene subirlo: son menos peticiones contra el mismo límite por minuto. |
| cursor | string | opcional | El nextCursor de la página anterior. |
Petición
curl "https://harmony.report/api/v1/categories" \
-H "Authorization: Bearer hk_live_tu_clave"
Respuesta · 200
{
"data": [
{
"id": "c2231lm2n3o4p5q6r7s8t9u0v1w2x3y4",
"name": "Falta",
"abbreviation": "FI",
"color": "red",
"keep": false,
"shouldCount": true
}
],
"nextCursor": null
}
Campos de Category
| id | string | Identificador de Convex. |
| name | string | Nombre de la categoría. |
| abbreviation | string | Sigla con la que se pinta. |
| color | string | sky · red. |
| keep | boolean | Si cuenta como día trabajado. |
| shouldCount | boolean | Si entra en los conteos. |
Errores
{
"error": {
"code": "missing_parameter",
"message": "Falta un parámetro obligatorio: period"
}
}
| code | HTTP | Cuándo |
|---|
| missing_authorization | 401 | No llegó la cabecera `Authorization`. |
| invalid_api_key | 401 | La clave no existe o está mal escrita. |
| revoked_api_key | 401 | La clave fue revocada. |
| expired_api_key | 401 | La clave caducó. |
| missing_parameter | 400 | Falta un parámetro obligatorio; el mensaje lo nombra. |
| invalid_period | 400 | El período no tiene la forma `YYYY-MM`. |
| invalid_limit | 400 | `limit` fuera de 1–5.000. |
| response_too_large | 413 | La respuesta no cabe. Baja el `limit` o pide menos expansiones a la vez. |
| not_found | 404 | El recurso no existe o está fuera del alcance de la clave. |
| rate_limited | 429 | Más de 60 peticiones en un minuto. Trae `Retry-After`. |
| method_not_allowed | 405 | La API es de sólo lectura: sólo admite `GET`. |
| internal_error | 500 | Error inesperado al atender la petición. |
Un recurso fuera del alcance de la clave responde 404, no 403: distinguirlos confirmaría la existencia de datos ajenos.
Límite de peticiones
60 peticiones por minuto y por clave. Al superarlo, la respuesta es 429 con Retry-After en segundos. Si necesitas más volumen, pide menos páginas más grandes: limit=100.