Desarrolladores
Diecisiete extremos en diez direcciones, cinco sucesos salientes y una especificación OpenAPI 3.1 pública que Make y n8n importan solos. Aquí está entera: los parámetros uno a uno, ejemplos que funcionan tal cual y la lista de lo que no hace.
Especificación OpenAPI 3.1, sin clave
https://app.blastack.com/api/openapi.json
Raíz de la API
https://app.blastack.com/api/v1
Pegad la primera dirección en el importador de n8n o de Make y los nodos salen solos. Zapier no lee OpenAPI: sus pasos están más abajo, escritos aparte por eso mismo.
De un vistazo
Ocho datos. Con estos ya se escribe la petición sin bajar más.
| Qué | Valor | Nota |
|---|---|---|
| Raíz de la API | https://app.blastack.com/api/v1 | Vive en la aplicación y no en esta web. Una sola dirección, y no cambia. |
| Especificación | https://app.blastack.com/api/openapi.json | Pública y sin clave, para que un importador la pueda leer antes de tener credenciales. |
| Autenticación | Authorization: Bearer blsk_<prefix>_<secret> | En la cabecera, nunca en la dirección: las direcciones acaban en los registros del servidor y del proxy de delante. |
| Formato | application/json; charset=utf-8 | JSON entrando y saliendo. No hay XML ni formularios. |
| Límite | 120 req/min · RateLimit-Remaining | Por clave y por minuto. Cada respuesta lleva cuántas quedan y cuándo se renueva la cuenta. |
| Paginación | page=1 · per_page=50 · ≤ 200 | La primera página es la uno, no la cero. Pedir más de doscientas se recorta en vez de fallar. |
| Errores | { error: { status, code, message } } | Siempre el mismo objeto, con un código estable pensado para escribirle un condicional encima. |
| Entorno de pruebas | — | No hay. Se trabaja contra producción, y por eso conviene empezar con una clave de solo lectura. |
curl https://app.blastack.com/api/v1/people?per_page=1 \
-H "Authorization: Bearer blsk_a1b2c3d4e5f6_YOUR_SECRET"Lo que hay
Están las tres funcionando. Lo que falta está más abajo, en su propio apartado y dicho por su nombre.
Que vuestro sistema de personal dé de alta, cambie de departamento y desactive a la gente sin que nadie lo copie a mano. Es la mitad que de verdad importa a informática: el día que despedís a alguien, la cuenta se desactiva sola.
Filtro por userName y externalId, paginación y modificación parcial. Se leen el departamento y la dirección de trabajo, así que quien entra por aquí nace con su departamento y su centro puestos. Dar de baja desactiva, no borra.
Entrar con la cuenta de la empresa desde Entra ID, Google Workspace u Okta. Convive con el código de acceso, que es como entra la plantilla que no tiene correo corporativo.
Se configura pegando la dirección de descubrimiento del proveedor y se comprueba contra él antes de guardar. Los dominios de correo se verifican por DNS. No hay SAML ni LDAP.
Publicar desde el programa que ya sabe que la línea 3 para el jueves, dar de baja a quien sale del sistema de personal, y que os llamemos nosotros cuando pasa algo en vez de tener que preguntar cada cinco minutos.
JSON sobre HTTPS, clave por cabecera, ámbitos por clave, errores con código estable y avisos salientes firmados. Es lo que se documenta en el resto de esta página.
Automatización
Las tres no se montan igual, y decirlo ahorra la tarde que se pierde descubriéndolo. Make y n8n leen la especificación y se configuran casi solos; Zapier no la lee, así que allí cada llamada se escribe a mano. Las tres funcionan; el trabajo no es el mismo.
| Herramienta | ¿Lee la especificación? | Disparador recomendado |
|---|---|---|
| n8n | Sí, entera | Nodo «Webhook» + POST /webhooks |
| Make | Sí, en el editor de aplicaciones propias | «Custom webhook» + POST /webhooks |
| Zapier | No | «Catch Hook» + POST /webhooks |
El camino más corto de los tres. Se da de alta una credencial de cabecera, se pega la dirección de la especificación en el importador y queda un nodo por operación con sus parámetros ya puestos.
Pasos
A tener en cuenta
El nodo de webhook responde 200 antes de ejecutar el resto del flujo, que es justo lo que hace falta: abandonamos a los diez segundos y eso cuenta como fallo.
El módulo genérico de HTTP basta para todo, y quien vaya a montar muchos escenarios puede subir la especificación al editor de aplicaciones propias y quedarse con un módulo por operación.
Pasos
A tener en cuenta
Make cobra por operación: un escenario que pregunte cada cinco minutos gasta ocho mil operaciones al mes para no encontrar casi nada. Con el webhook se gasta una por suceso.
Zapier no importa OpenAPI. Se monta con los pasos genéricos de webhooks, o construyendo una integración privada en su plataforma de desarrollo, donde cada extremo se declara a mano.
Pasos
A tener en cuenta
Sus disparadores por sondeo gastan una tarea en cada pasada, encuentren algo o no. El «Catch Hook» solo gasta cuando de verdad ha pasado algo.
Claves
Hoy las emitimos nosotros, a mano, y os decimos con qué ámbitos. No hay pantalla de autoservicio y está dicho abajo, entre lo que falta. Cada clave vive atada a una empresa y ninguna ruta lee de la petición a qué empresa preguntar, así que no hay dónde equivocarse.
Decidnos para qué es y qué tiene que poder hacer, y la emitimos con esos ámbitos y ninguno más. Se enseña una sola vez: de ella guardamos un resumen y nada más, así que si se pierde se revoca y se emite otra.
En la cabecera de autorización, nunca en la dirección. Las direcciones acaban en el registro del servidor, en el del proxy de delante y en el historial de quien la pruebe a mano.
En vuestro gestor de secretos, no en el repositorio. Empieza por un prefijo reconocible justamente para que los detectores de secretos la cacen si se escapa.
La API entra en el módulo de identidad corporativa, el mismo que abre el inicio de sesión único y el aprovisionamiento. Si la empresa no lo tiene contratado, la respuesta lo dice con esas palabras y no con un error genérico.
| Ámbito | Qué abre |
|---|---|
| people:read | Leer la plantilla y los departamentos y centros a los que se puede dirigir un aviso. Es lo único que necesita un cuadro de mando. |
| people:write | Dar de alta, cambiar y desactivar personas. Desactivar cierra sus sesiones, así que no viene de regalo con el de leer. |
| announcements:read | Leer lo publicado y quién ha confirmado una lectura obligatoria. Va aparte del de personas: una lista de nombres y puestos es el directorio, y el texto de los comunicados no. |
| announcements:write | Publicar en el tablón. Respeta la cola de revisión de la empresa, así que una clave no salta el circuito de aprobación de nadie. |
| metrics:read | Los indicadores agregados. Nadie sale con nombre y apellidos. |
| webhooks:read | Ver las direcciones registradas y el registro de entregas. Es lo que contesta «por qué no me llega nada» sin abrir un ticket. |
| webhooks:write | Registrar, cambiar y borrar direcciones de aviso saliente. El más delicado de la lista: decide a qué servidor de internet mandamos el contenido de vuestros comunicados. |
Extremos
Ordenadas por familia. La columna del ámbito es la que hay que mirar al pedir la clave: si falta, la respuesta dice cuál hacía falta y cuáles tiene la vuestra.
| Método | Dirección | Ámbito | Qué hace |
|---|---|---|---|
| GET | /people | people:read | La plantilla, con su departamento, su centro y el identificador que tiene en vuestro sistema de personal. Diecisiete parámetros, entre ellos el filtro por fecha de última modificación, que es el que permite sincronizar cada cuarto de hora sin traérsela entera. |
| GET | /people/{id} | people:read | Una persona concreta, buscada por nuestro identificador o por el vuestro. Sirve para refrescar una ficha sin recorrer la lista. |
| POST | /people | people:write | Da de alta a alguien. Es para cuando vuestro sistema de personal no habla SCIM; si lo habla, mejor SCIM, que además da de baja. El departamento y el centro se dan por nombre y se crean si no existen. |
| PATCH | /people/{id} | people:write | Cambia solo los campos que mandéis. Uno que no vaya no se toca, así que una sincronización que solo conoce el puesto no puede borrarle el centro a nadie. |
| DELETE | /people/{id} | people:write | Desactiva; no borra nunca. Le cierra las sesiones en el acto, así que quien sale un viernes queda fuera ese viernes. Lo que publicó se queda. |
| GET | /audiences | people:read | Los departamentos y los centros que existen, con cuánta gente hay en cada uno. Es de donde salen los nombres a los que se puede dirigir un aviso, y el filtro de grupos vacíos es el que encuentra un mapeo de aprovisionamiento roto. |
| GET | /announcements | announcements:read | Todo lo publicado, con las lecturas y las confirmaciones de cada pieza. Incluye borradores y rechazados para que un cuadro de mando pueda pintar el embudo entero. |
| GET | /announcements/{id} | announcements:read | Una publicación concreta. Es el paso siguiente al identificador que devuelve la publicación o que llega en un aviso saliente. |
| POST | /announcements | announcements:write | Publica en el tablón. Hay que decir quién firma y no hay valor por omisión. Si esa persona no puede publicar sin revisión, entra en la cola y la respuesta lo dice. |
| GET | /announcements/{id}/acknowledgements | announcements:read | Quién ha confirmado una lectura obligatoria y quién no, con nombre y hora. Es la única lista nominal de toda la API, y lo es porque ese es justamente el objeto de la lectura obligatoria: un acto voluntario, firmado y fechado que se le pueda enseñar a una inspección. |
| GET | /metrics | metrics:read | La plantilla de hoy, las altas, las publicaciones y el alcance por publicación y por departamento. Los mismos números que la pantalla de analítica, calculados por el mismo código. |
| GET | /webhooks | webhooks:read | Vuestras direcciones registradas, cada una con su salud: fallos seguidos, qué contestó vuestro servidor la última vez y, si la hemos pausado, por qué. |
| POST | /webhooks | webhooks:write | Registra una dirección y los sucesos que quiere. La respuesta trae el secreto de firma, y es la única vez que se ve. |
| GET | /webhooks/{id} | webhooks:read | Una dirección concreta, con su salud. |
| PATCH | /webhooks/{id} | webhooks:write | Cambia lo que mandéis. Poner la dirección otra vez activa levanta también la pausa que hayamos aplicado tras fallos repetidos y pone el contador a cero. |
| DELETE | /webhooks/{id} | webhooks:write | La borra con su histórico. Para dejar de recibir un tiempo sin perder el histórico, es mejor desactivarla. |
| GET | /webhooks/{id}/deliveries | webhooks:read | Todo lo que hemos intentado mandaros: intentos, qué contestasteis y cuándo toca el siguiente. El identificador es el mismo que viajó en la cabecera, así que se puede cruzar con vuestros registros. |
Parámetros
Ninguna lista está resumida: son las mismas que comprueba el servidor. Un parámetro que no esté aquí no existe, y escribirlo devuelve un error que dice cuáles admite ese extremo en vez de ignorarlo en silencio.
La columna de la derecha va en inglés a propósito. Sale del catálogo del que también se genera la especificación, así que es palabra por palabra lo que n8n y Make enseñan dentro de sus nodos. Traducirla aquí sería tener dos versiones que un día dirían cosas distintas.
people:read
La plantilla, con su departamento, su centro y el identificador que tiene en vuestro sistema de personal. Diecisiete parámetros, entre ellos el filtro por fecha de última modificación, que es el que permite sincronizar cada cuarto de hora sin traérsela entera.
| Parámetro | Tipo | Valores admitidos | Por defecto | Ejemplo | Qué hace |
|---|---|---|---|---|---|
| page | enteromínimo 1 | 1 | 2 | Page to return. The first page is 1, not 0. | |
| per_page | enteromínimo 1 · máximo 200 · fuera de rango se recorta en vez de fallar | 50 | 100 | How many items per page. Anything above 200 is clamped to 200 rather than rejected: asking for more is a reasonable thing to want. | |
| sort | texto | created_at · updated_at · name · email | created_at | Field to sort by. Defaults to created_at. | |
| order | texto | asc · desc | asc | Sort direction. | |
| fields | texto · varios separados por comas | external_id · email · name · job_title · department · center · role · status · language · created_at · updated_at | external_id,department,status | Comma-separated list of fields to return. `id` is always included. Use it to avoid pulling personal data you do not need. | |
| status | texto · varios separados por comas | active · invited · disabled | active | Only people in these states. Several may be given. | |
| role | texto · varios separados por comas | owner · admin · gestor_personas · responsable_local · informatico · editor · revisor · analista · employee | Only people with these roles. Several may be given. | ||
| department | texto · varios separados por comas | Maintenance,Quality | Department name, or several separated by commas. Case-insensitive: your directory writing MANTENIMIENTO is not an error. | ||
| center | texto · varios separados por comas | Site name, or several separated by commas. Case-insensitive. | |||
| no_department | booleano | Only people with no department. These are the ones who never see a targeted announcement, so this is the filter that finds a broken provisioning mapping. | |||
| language | texto · varios separados por comas | es · ca · en · pt · fr · it · de · sv · nl · pl · ro · cs | Only people who picked this reading language. | ||
| texto | Exact email address, case-insensitive. | ||||
| external_id | texto | Exact external_id, as sent by your HR system over SCIM. | |||
| q | texto | garcia | Free text over name, email and job title. Case-insensitive, matches anywhere in the value. | ||
| updated_since | fecha ISO 8601 | 2026-08-01T00:00:00Z | Only people changed at or after this instant. This is the one that lets you sync every fifteen minutes without pulling two thousand rows every time. | ||
| created_since | fecha ISO 8601 | Only people added at or after this instant. | |||
| created_until | fecha ISO 8601 | Only people added before this instant. |
people:read
Una persona concreta, buscada por nuestro identificador o por el vuestro. Sirve para refrescar una ficha sin recorrer la lista.
| Parámetro | Tipo | Valores admitidos | Por defecto | Ejemplo | Qué hace |
|---|---|---|---|---|---|
| idObligatorio | texto | a3f1… | Blastack id (a uuid) or the person's external_id from your HR system. A Blastack id is always a uuid and yours almost never is, so they cannot be confused; if one ever were, ours wins. |
| Parámetro | Tipo | Valores admitidos | Por defecto | Ejemplo | Qué hace |
|---|---|---|---|---|---|
| fields | texto · varios separados por comas | external_id · email · name · job_title · department · center · role · status · language · created_at · updated_at | external_id,department,status | Comma-separated list of fields to return. `id` is always included. Use it to avoid pulling personal data you do not need. |
people:write
Da de alta a alguien. Es para cuando vuestro sistema de personal no habla SCIM; si lo habla, mejor SCIM, que además da de baja. El departamento y el centro se dan por nombre y se crean si no existen.
| Parámetro | Tipo | Valores admitidos | Por defecto | Ejemplo | Qué hace |
|---|---|---|---|---|---|
| nameObligatorio | textolargo máximo 120 | Ana García | Full name, as it should appear on the wall. | ||
| textolargo máximo 200 | Work email. Optional: most of the workforce this product is for has none, and they get in with an invite code instead. Unique within the company. | ||||
| external_id | textolargo máximo 200 | EMP-0042 | The id this person has in your system. Store it and you can cross both lists later without matching names. | ||
| job_title | textolargo máximo 120 | Job title. | |||
| department | textolargo máximo 80 | Department name. Created if it does not exist. Without one, this person only ever sees company-wide announcements. | |||
| center | textolargo máximo 80 | Site name. Created if it does not exist. | |||
| role | texto | admin · gestor_personas · responsable_local · informatico · editor · revisor · analista · employee | employee | What they can do. `owner` cannot be given from the API: a key pasted in a cron job must not be able to mint an account that can cancel the subscription. | |
| status | texto | active · invited · disabled | active | `active` can sign in; `invited` is created but not counted as signed up yet; `disabled` is created already deactivated, which is what a backfill of former staff needs. | |
| language | texto | es · ca · en · pt · fr · it · de · sv · nl · pl · ro · cs | Reading language. Leave it out and they read in the company's, which is what you want unless you actually know. |
people:write
Cambia solo los campos que mandéis. Uno que no vaya no se toca, así que una sincronización que solo conoce el puesto no puede borrarle el centro a nadie.
| Parámetro | Tipo | Valores admitidos | Por defecto | Ejemplo | Qué hace |
|---|---|---|---|---|---|
| idObligatorio | texto | a3f1… | Blastack id (a uuid) or the person's external_id from your HR system. A Blastack id is always a uuid and yours almost never is, so they cannot be confused; if one ever were, ours wins. |
| Parámetro | Tipo | Valores admitidos | Por defecto | Ejemplo | Qué hace |
|---|---|---|---|---|---|
| name | textolargo máximo 120 | Full name. | |||
| textolargo máximo 200 | Work email. | ||||
| external_id | textolargo máximo 200 | The id this person has in your system. | |||
| job_title | textolargo máximo 120 | Job title. | |||
| department | textolargo máximo 80 | Department name. Created if it does not exist. | |||
| center | textolargo máximo 80 | Site name. Created if it does not exist. | |||
| role | texto | admin · gestor_personas · responsable_local · informatico · editor · revisor · analista · employee | What they can do. `owner` cannot be given from the API. | ||
| status | texto | active · invited · disabled | Their state. `disabled` also closes their sessions. | ||
| language | texto | es · ca · en · pt · fr · it · de · sv · nl · pl · ro · cs | Reading language. |
people:write
Desactiva; no borra nunca. Le cierra las sesiones en el acto, así que quien sale un viernes queda fuera ese viernes. Lo que publicó se queda.
| Parámetro | Tipo | Valores admitidos | Por defecto | Ejemplo | Qué hace |
|---|---|---|---|---|---|
| idObligatorio | texto | a3f1… | Blastack id (a uuid) or the person's external_id from your HR system. A Blastack id is always a uuid and yours almost never is, so they cannot be confused; if one ever were, ours wins. |
people:read
Los departamentos y los centros que existen, con cuánta gente hay en cada uno. Es de donde salen los nombres a los que se puede dirigir un aviso, y el filtro de grupos vacíos es el que encuentra un mapeo de aprovisionamiento roto.
| Parámetro | Tipo | Valores admitidos | Por defecto | Ejemplo | Qué hace |
|---|---|---|---|---|---|
| type | texto | all · departments · centers | all | Return only departments, only sites, or both. | |
| q | texto | Free text over the name. Case-insensitive. | |||
| with_people | booleano | Only groups that have at least one active person. A department with nobody in it is almost always an old name left behind, and it is the usual cause of an announcement that reaches no one. | |||
| sort | texto | name · people | name | Field to sort by. Defaults to name. | |
| order | texto | asc · desc | asc | Sort direction. |
announcements:read
Todo lo publicado, con las lecturas y las confirmaciones de cada pieza. Incluye borradores y rechazados para que un cuadro de mando pueda pintar el embudo entero.
| Parámetro | Tipo | Valores admitidos | Por defecto | Ejemplo | Qué hace |
|---|---|---|---|---|---|
| page | enteromínimo 1 | 1 | 2 | Page to return. The first page is 1, not 0. | |
| per_page | enteromínimo 1 · máximo 200 · fuera de rango se recorta en vez de fallar | 50 | 100 | How many items per page. Anything above 200 is clamped to 200 rather than rejected: asking for more is a reasonable thing to want. | |
| sort | texto | published_at · created_at · updated_at · title | published_at | Field to sort by. Defaults to published_at. | |
| order | texto | asc · desc | asc | Sort direction. | |
| fields | texto · varios separados por comas | title · body · status · kind · language · audience · community · section · author · notify · comments_enabled · pinned · acknowledgements · reads · published_at · created_at · updated_at · url | external_id,department,status | Comma-separated list of fields to return. `id` is always included. Use it to avoid pulling personal data you do not need. | |
| status | texto · varios separados por comas | draft · pending_review · published · rejected · scheduled · archived | published,scheduled | Only these states. Several may be given. `scheduled` means it is written and waiting for its hour. | |
| author | texto | Who signs it: our id, their email or their external_id. Any of the three. | |||
| audience_type | texto | all · departments · centers | Only announcements aimed this way. | ||
| department | texto · varios separados por comas | Only announcements aimed at these departments, by name. Several may be given. | |||
| center | texto · varios separados por comas | Only announcements aimed at these sites, by name. | |||
| community | texto | Only announcements in this community, by id or by name. | |||
| requires_acknowledgement | booleano | Only required reading, or only the rest. This is the filter a compliance report is built on. | |||
| pinned | booleano | Only pinned announcements, or only the rest. | |||
| q | texto | Free text over title and body. Case-insensitive. | |||
| published_since | fecha ISO 8601 | Published at or after this instant. | |||
| published_until | fecha ISO 8601 | Published before this instant. | |||
| created_since | fecha ISO 8601 | Written at or after this instant. | |||
| updated_since | fecha ISO 8601 | Changed at or after this instant. |
announcements:read
Una publicación concreta. Es el paso siguiente al identificador que devuelve la publicación o que llega en un aviso saliente.
| Parámetro | Tipo | Valores admitidos | Por defecto | Ejemplo | Qué hace |
|---|---|---|---|---|---|
| idObligatorio | texto | The announcement id. |
| Parámetro | Tipo | Valores admitidos | Por defecto | Ejemplo | Qué hace |
|---|---|---|---|---|---|
| fields | texto · varios separados por comas | title · body · status · kind · language · audience · community · section · author · notify · comments_enabled · pinned · acknowledgements · reads · published_at · created_at · updated_at · url | external_id,department,status | Comma-separated list of fields to return. `id` is always included. Use it to avoid pulling personal data you do not need. |
announcements:write
Publica en el tablón. Hay que decir quién firma y no hay valor por omisión. Si esa persona no puede publicar sin revisión, entra en la cola y la respuesta lo dice.
| Parámetro | Tipo | Valores admitidos | Por defecto | Ejemplo | Qué hace |
|---|---|---|---|---|---|
| bodyObligatorio | textolargo máximo 8000 | The text. Plain text; line breaks are kept. | |||
| authorObligatorio | texto | EMP-0042 | Who signs it: our id, their email or their external_id. Must be an active person in this company. | ||
| title | textolargo máximo 160 | Headline. | |||
| audience | objeto | Who it goes to. Leave it out and it goes to everyone. Names work as well as ids, case-insensitively, because your ERP has 'Maintenance' and not a uuid of ours. Nothing is created here: an unknown name is a typo in a script, and the reply tells you which one it was. | |||
| audience.type | texto | all · departments · centers | all | Aim at everyone, at departments or at sites. | |
| audience.ids | lista | ["Maintenance","Quality"] | Department or site names, or their ids. Required unless type is `all`. | ||
| community | texto | Which community it belongs to, by id or by name. Leave it out and it goes on the main wall, which everybody sees. The author has to belong to the community, or be posting to the default one: an API key is not a way into a community its signer is not in. | |||
| section | texto | Which section it belongs to, by id or by name. It must belong to the same community. | |||
| publish_at | fecha ISO 8601 | 2026-09-01T06:00:00Z | Publish later, at this instant. It must be in the future. The reply comes back with `status: scheduled` and the wall shows it when the hour arrives. Leave it out to publish now. Note this means two clocks are involved — yours and ours — so if your system already schedules, prefer calling us at the right moment. | ||
| requires_acknowledgement | booleano | false | Required reading: everyone it reaches gets a notification with a confirm button, and who confirmed is on record. Only signers who can publish without review may ask for it. | ||
| notify | booleano | true | Whether it makes a sound. Written the right way round on purpose: the screen has a 'publish without notifying' checkbox because a form that loses a field should still notify, but here you write the whole JSON and a field called `silent` reads wrong at three in the morning. Default is true. | ||
| comments_enabled | booleano | true | Whether people can comment on it. | ||
| pinned | booleano | false | Keep it at the top of the wall. | ||
| language | texto | es · ca · en · pt · fr · it · de · sv · nl · pl · ro · cs | What language the text is written in. Leave it out and the company's is used. Getting this right matters: a Catalan original labelled as Spanish is shown to a Spanish reader without the 'not translated' notice that tells them nothing is broken. |
announcements:read
Quién ha confirmado una lectura obligatoria y quién no, con nombre y hora. Es la única lista nominal de toda la API, y lo es porque ese es justamente el objeto de la lectura obligatoria: un acto voluntario, firmado y fechado que se le pueda enseñar a una inspección.
| Parámetro | Tipo | Valores admitidos | Por defecto | Ejemplo | Qué hace |
|---|---|---|---|---|---|
| idObligatorio | texto | The announcement id. |
| Parámetro | Tipo | Valores admitidos | Por defecto | Ejemplo | Qué hace |
|---|---|---|---|---|---|
| page | enteromínimo 1 | 1 | 2 | Page to return. The first page is 1, not 0. | |
| per_page | enteromínimo 1 · máximo 200 · fuera de rango se recorta en vez de fallar | 50 | 100 | How many items per page. Anything above 200 is clamped to 200 rather than rejected: asking for more is a reasonable thing to want. | |
| state | texto | all · acknowledged · pending | all | Everyone, only those who confirmed, or only those still pending. | |
| department | texto · varios separados por comas | Only people in these departments, by name. | |||
| center | texto · varios separados por comas | Only people at these sites, by name. | |||
| sort | texto | name · acknowledged_at | name | Field to sort by. Defaults to name. | |
| order | texto | asc · desc | asc | Sort direction. |
metrics:read
La plantilla de hoy, las altas, las publicaciones y el alcance por publicación y por departamento. Los mismos números que la pantalla de analítica, calculados por el mismo código.
| Parámetro | Tipo | Valores admitidos | Por defecto | Ejemplo | Qué hace |
|---|---|---|---|---|---|
| period_days | enteromínimo 1 · máximo 365 | 30 | 90 | How many days back the period covers. Applies to every figure with a period, including the reach lists. | |
| include | texto · varios separados por comas | workforce · announcements · reach_by_announcement · reach_by_department | workforce,announcements | Which blocks to compute, comma-separated. Leave it out for all of them. `reach_by_announcement` is the expensive one: skip it if your dashboard only draws the headline figures. |
webhooks:read
Vuestras direcciones registradas, cada una con su salud: fallos seguidos, qué contestó vuestro servidor la última vez y, si la hemos pausado, por qué.
| Parámetro | Tipo | Valores admitidos | Por defecto | Ejemplo | Qué hace |
|---|---|---|---|---|---|
| page | enteromínimo 1 | 1 | 2 | Page to return. The first page is 1, not 0. | |
| per_page | enteromínimo 1 · máximo 200 · fuera de rango se recorta en vez de fallar | 50 | 100 | How many items per page. Anything above 200 is clamped to 200 rather than rejected: asking for more is a reasonable thing to want. | |
| event | texto | announcement.published · announcement.pending_review · announcement.acknowledged · person.created · person.deactivated | Only endpoints subscribed to this event. | ||
| active | booleano | Only live endpoints, or only the switched-off and paused. |
webhooks:write
Registra una dirección y los sucesos que quiere. La respuesta trae el secreto de firma, y es la única vez que se ve.
| Parámetro | Tipo | Valores admitidos | Por defecto | Ejemplo | Qué hace |
|---|---|---|---|---|---|
| urlObligatorio | textolargo máximo 500 | https://hooks.example.com/blastack | Where we POST. https only, no credentials, no fragment. | ||
| eventsObligatorio | lista | announcement.published · announcement.pending_review · announcement.acknowledged · person.created · person.deactivated | ["announcement.published"] | Which events this endpoint wants. At least one. | |
| name | textolargo máximo 120 | Zapier · switchboard | What it is for, in your words. It is the only thing that lets you delete the right one six months from now. | ||
| secret | textolargo máximo 200 | Your own signing secret, if you would rather set it than have one generated. At least 32 characters. | |||
| active | booleano | true | Start delivering straight away. |
webhooks:read
Una dirección concreta, con su salud.
| Parámetro | Tipo | Valores admitidos | Por defecto | Ejemplo | Qué hace |
|---|---|---|---|---|---|
| idObligatorio | texto | The webhook endpoint id. |
webhooks:write
Cambia lo que mandéis. Poner la dirección otra vez activa levanta también la pausa que hayamos aplicado tras fallos repetidos y pone el contador a cero.
| Parámetro | Tipo | Valores admitidos | Por defecto | Ejemplo | Qué hace |
|---|---|---|---|---|---|
| idObligatorio | texto | The webhook endpoint id. |
| Parámetro | Tipo | Valores admitidos | Por defecto | Ejemplo | Qué hace |
|---|---|---|---|---|---|
| url | textolargo máximo 500 | Where we POST. https only. | |||
| events | lista | announcement.published · announcement.pending_review · announcement.acknowledged · person.created · person.deactivated | Which events this endpoint wants. At least one. | ||
| name | textolargo máximo 120 | What it is for. | |||
| active | booleano | Switch delivery on or off. Turning it on clears a pause. | |||
| rotate_secret | booleano | Generate a new signing secret and return it once in this reply. |
webhooks:write
La borra con su histórico. Para dejar de recibir un tiempo sin perder el histórico, es mejor desactivarla.
| Parámetro | Tipo | Valores admitidos | Por defecto | Ejemplo | Qué hace |
|---|---|---|---|---|---|
| idObligatorio | texto | The webhook endpoint id. |
webhooks:read
Todo lo que hemos intentado mandaros: intentos, qué contestasteis y cuándo toca el siguiente. El identificador es el mismo que viajó en la cabecera, así que se puede cruzar con vuestros registros.
| Parámetro | Tipo | Valores admitidos | Por defecto | Ejemplo | Qué hace |
|---|---|---|---|---|---|
| idObligatorio | texto | The webhook endpoint id. |
| Parámetro | Tipo | Valores admitidos | Por defecto | Ejemplo | Qué hace |
|---|---|---|---|---|---|
| page | enteromínimo 1 | 1 | 2 | Page to return. The first page is 1, not 0. | |
| per_page | enteromínimo 1 · máximo 200 · fuera de rango se recorta en vez de fallar | 50 | 100 | How many items per page. Anything above 200 is clamped to 200 rather than rejected: asking for more is a reasonable thing to want. | |
| status | texto | pending · delivered · given_up | Only deliveries in this state. | ||
| event | texto | announcement.published · announcement.pending_review · announcement.acknowledged · person.created · person.deactivated | Only deliveries of this event type. | ||
| since | fecha ISO 8601 | Only events that happened at or after this instant. |
Ejemplos
Con su petición entera y su respuesta entera. Cambiad la clave y los nombres; lo demás funciona tal cual.
Cuando el sistema de mantenimiento cierra la orden de parada
Es el caso que se pide siempre. Vuestro sistema ya sabe que la línea 3 para el jueves; con esta llamada lo publica él, dirigido a los departamentos que toca y pidiendo confirmación de lectura, sin que nadie escriba nada dos veces.
Petición
curl -X POST https://app.blastack.com/api/v1/announcements \
-H "Authorization: Bearer blsk_a1b2c3d4e5f6_YOUR_SECRET" \
-H "Content-Type: application/json" \
-d '{
"author": "EMP-0042",
"title": "Parada de la línea 3 el jueves",
"body": "El jueves 4 a las 06:00 para la línea 3 por mantenimiento programado. El turno de mañana entra a la hora de siempre y pasa a la línea 2.",
"audience": {
"type": "departments",
"ids": [
"Mantenimiento",
"Calidad"
]
},
"requires_acknowledgement": true,
"notify": true,
"language": "es"
}'Respuesta · 201
{
"id": "6b1f0f1e-0c2a-4c5e-9a3d-2f5b8c7d1e40",
"title": "Parada de la línea 3 el jueves",
"body": "El jueves 4 a las 06:00 para la línea 3 por mantenimiento programado. El turno de mañana entra a la hora de siempre y pasa a la línea 2.",
"status": "published",
"kind": "news",
"language": "es",
"audience": {
"type": "departments",
"ids": [
"9c1d7e5f-2a3b-4c5d-8e9f-0a1b2c3d4e5f",
"44ab7e5f-2a3b-4c5d-8e9f-0a1b2c3d4e5f"
],
"names": [
"Mantenimiento",
"Calidad"
]
},
"community": null,
"section": null,
"author": {
"id": "3f2a1b0c-9d8e-4f7a-8b6c-5d4e3f2a1b0c",
"name": "Ana García",
"email": "ana.garcia@empresa.com",
"external_id": "EMP-0042"
},
"notify": true,
"comments_enabled": true,
"pinned": false,
"acknowledgements": {
"required": true,
"confirmed": 0
},
"reads": 0,
"published_at": "2026-09-01T06:00:00.000Z",
"created_at": "2026-09-01T06:00:00.000Z",
"updated_at": "2026-09-01T06:00:00.000Z",
"url": "https://app.blastack.com/inicio#post-6b1f0f1e-0c2a-4c5e-9a3d-2f5b8c7d1e40"
}El destino va por nombre y no por identificador, porque es lo que vuestro sistema tiene a mano. Si el nombre no existe, la respuesta dice cuál era y no publica nada: media publicación sería peor.
Cuando el sistema de personal marca la baja
Si vuestro sistema habla SCIM, esto ya lo hace solo y no hace falta esta llamada. Si no lo habla —que es lo normal en una fábrica—, con una línea queda desactivado y con las sesiones cerradas en el acto. La dirección admite vuestro propio identificador, así que no hay que mantener una tabla de equivalencias.
Petición
curl -X DELETE https://app.blastack.com/api/v1/people/EMP-0042 \
-H "Authorization: Bearer blsk_a1b2c3d4e5f6_YOUR_SECRET"Respuesta · 200
{
"id": "3f2a1b0c-9d8e-4f7a-8b6c-5d4e3f2a1b0c",
"external_id": "EMP-0042",
"email": "ana.garcia@empresa.com",
"name": "Ana García",
"job_title": "Jefa de turno",
"department": "Mantenimiento",
"center": "Nave 2",
"role": "editor",
"status": "disabled",
"language": null,
"created_at": "2026-02-11T08:14:00.000Z",
"updated_at": "2026-09-01T07:40:22.000Z"
}Desactiva; no borra. Lo que publicó se queda, porque un tablón lleno de huecos no ayuda a nadie. Llamar dos veces no es un error.
Cada mañana, o el día antes de una auditoría
Un escenario de Make o un flujo de n8n que pida los pendientes y los escriba en una hoja: nombre, departamento y si ha confirmado. Es el informe que se pide en una auditoría y el que evita perseguir a nadie por el pasillo.
Petición
curl -G https://app.blastack.com/api/v1/announcements/6b1f0f1e-0c2a-4c5e-9a3d-2f5b8c7d1e40/acknowledgements \
--data-urlencode "state=pending" \
--data-urlencode "department=Mantenimiento" \
--data-urlencode "per_page=200" \
-H "Authorization: Bearer blsk_a1b2c3d4e5f6_YOUR_SECRET"Respuesta · 200
{
"announcement": {
"id": "6b1f0f1e-0c2a-4c5e-9a3d-2f5b8c7d1e40",
"title": "Parada de la línea 3 el jueves"
},
"summary": {
"recipients": 34,
"acknowledged": 27,
"pending": 7
},
"data": [
{
"person": {
"id": "3f2a1b0c-9d8e-4f7a-8b6c-5d4e3f2a1b0c",
"name": "Ana García",
"email": "ana.garcia@empresa.com",
"external_id": "EMP-0042",
"department": "Mantenimiento",
"center": "Nave 2"
},
"acknowledged": false,
"acknowledged_at": null
}
],
"page": 1,
"per_page": 200,
"total": 7
}El resumen se cuenta sobre la población entera y no sobre la página ni sobre el filtro, para que el «12 de 30» siga significando algo. La hora de la confirmación es la de la primera vez y no se toca nunca más.
Avisos salientes
Es lo que convierte esto en algo conectable de verdad. Sin avisos salientes, el disparador de vuestro escenario tiene que ser «cada cinco minutos, pregunta», y eso es lento, se paga por pasada y casi nunca encuentra nada.
| Suceso | Cuándo salta |
|---|---|
| announcement.published | Algo ha salido al muro, venga por donde venga: el compositor, el editor, esta API, la cola de revisión o una publicación programada que llega a su hora. El tipo va dentro, por si solo os interesan los avisos y no las encuestas o los eventos. |
| announcement.pending_review | Una publicación espera aprobación. Solo salta en empresas que tienen encendida la cola de revisión. |
| announcement.acknowledged | Alguien ha confirmado una lectura obligatoria. Un suceso por persona, con quién y cuándo. |
| person.created | Se ha dado de alta a alguien, da igual por dónde: aprovisionamiento, código de acceso, inicio de sesión único, la pantalla de administración o esta API. |
| person.deactivated | Se ha desactivado a alguien. Como aquí no se borra a nadie, esta es la señal de salida. |
{
"id": "a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d",
"type": "announcement.published",
"occurred_at": "2026-09-01T06:00:00.000Z",
"organization_id": "0a9b8c7d-6e5f-4a3b-8c9d-0e1f2a3b4c5d",
"data": {
"announcement": {
"id": "6b1f0f1e-0c2a-4c5e-9a3d-2f5b8c7d1e40",
"title": "Parada de la línea 3 el jueves",
"body": "El jueves 4 a las 06:00 para la línea 3 por mantenimiento programado.",
"kind": "news",
"language": "es",
"requires_acknowledgement": true,
"audience": {
"type": "departments",
"names": [
"Mantenimiento",
"Calidad"
]
},
"community": "Planta de Getafe",
"author": {
"id": "3f2a1b0c-9d8e-4f7a-8b6c-5d4e3f2a1b0c",
"name": "Ana García",
"email": "ana.garcia@empresa.com",
"external_id": "EMP-0042"
},
"url": "https://app.blastack.com/inicio#post-6b1f0f1e-0c2a-4c5e-9a3d-2f5b8c7d1e40",
"status": "published"
}
}
}Lo de dentro cambia con el suceso; el sobre no. El identificador no cambia entre reintentos, así que descartar un repetido es un condicional de una línea en vuestro lado.
| Cabecera | Para qué |
|---|---|
| blastack-event | Qué ha pasado. Es el valor por el que se ramifica. |
| blastack-event-id | El identificador del suceso. No cambia entre reintentos: es el que sirve para no ejecutar dos veces lo mismo. |
| blastack-delivery-attempt | Qué intento es este, empezando por el primero. |
| blastack-signature | La firma. Es lo único que separa un aviso nuestro de uno que se haya inventado quien descubra vuestra dirección. |
La firma es HMAC-SHA256 sobre la marca de tiempo, un punto y el cuerpo en crudo, sin parsear. Es el mismo formato que usan las pasarelas de pago, o sea el que probablemente ya habéis escrito una vez.
import { createHmac, timingSafeEqual } from "node:crypto";
export function verify(rawBody, headers, secret) {
const sent = headers["blastack-signature"] ?? "";
const t = /t=(\d+)/.exec(sent)?.[1];
const v1 = /v1=([0-9a-f]+)/.exec(sent)?.[1];
if (!t || !v1) return false;
const mine = createHmac("sha256", secret)
.update(`${t}.${rawBody}`, "utf8")
.digest("hex");
if (v1.length !== mine.length) return false;
if (!timingSafeEqual(Buffer.from(v1, "hex"), Buffer.from(mine, "hex"))) {
return false;
}
return Math.abs(Date.now() / 1000 - Number(t)) < 300;
}Comparad en tiempo constante y rechazad lo que tenga una marca de más de unos minutos. Sin esa segunda comprobación, una copia de un envío antiguo se os puede reenviar durante años y la firma seguirá siendo válida.
Errores
Todo error devuelve el mismo objeto, con un código pensado para que le escribáis un condicional encima. Los códigos no se renombran nunca: se añaden. El mensaje de al lado es para quien lee el registro y ese sí puede cambiar.
{
"error": {
"status": 403,
"code": "insufficient_scope",
"message": "This API key does not have the \"metrics:read\" scope.",
"required_scope": "metrics:read",
"granted_scopes": [
"people:read"
]
},
"documentation": "https://blastack.com/en/developers"
}Un parámetro desconocido se rechaza, no se ignora. Un filtro que se descarta en silencio os devuelve más datos de los que pedisteis y nada os avisa.
RateLimit-Limit · RateLimit-Remaining · RateLimit-Reset · Retry-After
| Código | Cuándo sale |
|---|---|
| invalid_token | 401 · Falta la clave, está mal escrita o se ha revocado. No decimos cuál de las tres. |
| insufficient_scope | 403 · La clave es buena pero no tiene el ámbito que pide esa llamada. La respuesta dice cuál hacía falta y cuáles tiene. |
| module_required | 403 · La empresa no tiene contratado el módulo de identidad corporativa, que es al que pertenece la API. |
| read_only | 403 · La prueba gratuita ha terminado y la empresa está en solo lectura. Solo afecta a lo que escribe. |
| rate_limited | 429 · Más peticiones de la cuenta en un minuto. La respuesta dice cuántos segundos hay que esperar. |
| invalid_request | 400 · Falta un campo, sobra o no se entiende. El mensaje nombra el parámetro exacto y lista los valores que admite. |
| not_found | 404 · No hay nada vuestro con ese identificador. Es también lo que se recibe por algo de otra empresa: distinguirlo sería una forma de averiguar qué existe fuera. |
| conflict | 409 · Choca con algo que ya está: dos personas con el mismo correo, dos direcciones de aviso iguales. La respuesta trae el identificador de lo que ya existía, para poder seguir. |
| server_error | 500 · Se ha roto algo de nuestro lado. Nada del error sale en la respuesta; entero se queda en nuestro registro. |
Límites
Lo que no hace
Quien va a integrar necesita saber los límites antes de empezar, no cuando ya lleva medio escenario montado. Esta lista es lo que un desarrollador va a buscar y no va a encontrar, y se actualiza cuando algo deja de ser cierto.
Para máquinas
Media evaluación de compra la hace hoy alguien preguntándole a un asistente si esto se conecta con algo. Así que la respuesta está escrita para que se pueda leer sin cuenta y sin adivinar nada.
Lo que más se pida es lo siguiente que se escribe. Y si algo de esta página no cuadra con lo que responde el servidor, es un fallo nuestro y queremos saberlo el mismo día.