Desarrolladores
Webhooks y Zapier
Los webhooks envían una solicitud HTTP a tu servidor en el momento en que ocurre algo en tu espacio de trabajo, como cuando se pausa una app o alguien hace clic en un enlace inteligente. Úsalos para actualizar tus propios sistemas, publicar en Slack o conectar mapps.one con Zapier, Make o n8n sin escribir un servidor. Los webhooks están disponibles en el plan Studio, con hasta 10 endpoints por espacio de trabajo.
Añadir un endpoint
- En el panel, abre Desarrolladores → Webhooks y elige Añadir endpoint. Pueden hacerlo los propietarios y administradores del espacio de trabajo.
- Introduce una URL
httpspública, elige los eventos que quieres enviar (o todos los eventos) y añade una descripción. - Copia el secreto de firma y guárdalo junto con los demás secretos de tu servidor.
- Elige Enviar evento de prueba para comprobar que tu endpoint responde.
La página del endpoint muestra las entregas recientes con el estado de la respuesta, los primeros 2000 caracteres del cuerpo de la respuesta y cuánto tardó cada intento. También puedes crear y eliminar endpoints con la API.
Eventos
| Tipo | Se envía cuando |
|---|---|
app.created | Se añadió una app al espacio de trabajo. |
app.updated | Cambiaron los detalles, enlaces o ajustes de página de una app. |
app.deleted | Se eliminó una app. |
app.status_changed | Una app se activó, se pausó o se suspendió. |
app.metadata_fetched | Se importaron los metadatos de tienda de una app. |
legal.published | Se publicó una política de privacidad o un documento de términos. |
domain.verified | Se verificó un dominio personalizado. |
link.clicked | Cada clic en un enlace inteligente. Puede suponer muchas solicitudes en una app popular. |
analytics.daily_summary | Los totales de clics del día anterior de cada app, enviados una vez al día. |
Formato de la solicitud
Cada evento se envía como una solicitud POST con un cuerpo JSON.
| Encabezado | Valor |
|---|---|
Content-Type | application/json |
User-Agent | mapps.one-Webhooks/1.0 |
X-Mapps-Event | El tipo de evento, como link.clicked |
X-Mapps-Delivery | El ID del evento. Se mantiene igual en cada reintento. |
X-Mapps-Timestamp | Hora Unix de este intento de entrega |
X-Mapps-Signature | t={timestamp},v1={signature} |
El cuerpo es un sobre con la misma estructura para todos los eventos:
{
"id": "evt_01j8c3m9t5v2x7z4b6d8f0h2k4",
"type": "app.status_changed",
"created_at": "2026-09-14T09:41:07+00:00",
"workspace": {
"id": "01j8a0f4c7d2e9h6k3m5n8q1r4",
"name": "Habitly Labs"
},
"data": {
"app": {
"id": "01j8b2k6r3xq9v7m4n5p0s1t2w",
"slug": "habitly",
"name": "Habitly"
},
"status": "paused"
}
}
El contenido de data depende del evento. Los eventos sobre una app incluyen un objeto app con su id, slug y name.
link.clicked
"data": {
"app": { "id": "01j8b2k6r3xq9v7m4n5p0s1t2w", "slug": "habitly", "name": "Habitly" },
"click": {
"occurred_at": "2026-09-14 09:41:07",
"os": "iOS",
"device_type": "mobile",
"country": "TR",
"source": "instagram",
"destination": "app_store",
"utm_source": "instagram",
"utm_medium": "social",
"utm_campaign": "launch"
}
}
occurred_at está en UTC. device_type es mobile, tablet o desktop. destination es una clave de tienda, web o landing. Los campos desconocidos son null. Las visitas de bots nunca activan este evento.
app.metadata_fetched
"data": {
"app": { "id": "01j8b2k6r3xq9v7m4n5p0s1t2w", "slug": "habitly", "name": "Habitly" },
"source": "app_store"
}
Verificar las firmas
Comprueba la firma de cada solicitud para saber que procede de mapps.one y que no se modificó por el camino:
- Lee
tyv1del encabezadoX-Mapps-Signature. - Calcula un HMAC-SHA256 de
{t}.{raw request body}con el secreto de firma de tu endpoint, en hexadecimal y en minúsculas. - Compáralo con
v1mediante una comparación de tiempo constante. - Rechaza la solicitud si
tse aleja más de cinco minutos del reloj de tu servidor, para bloquear solicitudes reutilizadas.
PHP
<?php
$secret = getenv('MAPPS_WEBHOOK_SECRET');
$body = file_get_contents('php://input');
parse_str(str_replace(',', '&', $_SERVER['HTTP_X_MAPPS_SIGNATURE'] ?? ''), $parts);
$timestamp = (int) ($parts['t'] ?? 0);
$expected = hash_hmac('sha256', $timestamp.'.'.$body, $secret);
if (! hash_equals($expected, (string) ($parts['v1'] ?? '')) || abs(time() - $timestamp) > 300) {
http_response_code(400);
exit;
}
$event = json_decode($body, true);
if ($event['type'] === 'link.clicked') {
// Store $event['data']['click'] ...
}
http_response_code(200);
En Laravel, lee el cuerpo con $request->getContent() y el encabezado con $request->header('X-Mapps-Signature'), y excluye la ruta de la protección CSRF.
Node.js (Express)
import crypto from 'node:crypto';
import express from 'express';
const app = express();
const secret = process.env.MAPPS_WEBHOOK_SECRET;
app.post('/webhooks/mapps', express.raw({ type: 'application/json' }), (req, res) => {
const header = req.get('X-Mapps-Signature') ?? '';
const parts = Object.fromEntries(header.split(',').map((pair) => pair.split('=')));
const timestamp = Number(parts.t);
const body = req.body.toString('utf8');
const expected = crypto.createHmac('sha256', secret).update(`${timestamp}.${body}`).digest();
const received = Buffer.from(parts.v1 ?? '', 'hex');
const valid = received.length === expected.length && crypto.timingSafeEqual(received, expected);
if (!valid || Math.abs(Date.now() / 1000 - timestamp) > 300) {
return res.sendStatus(400);
}
const event = JSON.parse(body);
if (event.type === 'link.clicked') {
// Store event.data.click ...
}
res.sendStatus(200);
});
app.listen(3000);
Respuestas y reintentos
- Responde con cualquier estado 2xx en un plazo de 10 segundos. Haz el trabajo lento en una tarea en segundo plano después de responder.
- Las redirecciones no se siguen, así que usa la URL final.
- La URL debe ser
httpspública. Las direcciones de redes privadas y locales se rechazan. - Si tu endpoint agota el tiempo de espera o devuelve cualquier estado distinto de 2xx, reintentamos hasta cinco veces más: después de aproximadamente 1 minuto, 5 minutos, 30 minutos, 2 horas y 6 horas. Cada intento lleva una nueva marca de tiempo y una nueva firma.
- El mismo evento puede llegar más de una vez, y los eventos pueden llegar desordenados. Usa
X-Mapps-Delivery, eliddel evento, para ignorar los duplicados, ycreated_atpara ordenarlos. - Tras 20 intentos fallidos seguidos, el endpoint se desactiva y no se le envía nada más. Soluciona el problema y luego vuelve a activarlo en el panel.
Zapier, Make y n8n
Puedes conectar mapps.one con miles de apps sin ejecutar un servidor.
Zapier
- Crea un Zap con Webhooks by Zapier como disparador y elige Catch Hook.
- Copia la URL de webhook que te da Zapier.
- En mapps.one, añade un endpoint con esa URL y elige los eventos que quieras, por ejemplo
app.status_changedoanalytics.daily_summary. - Elige Enviar evento de prueba en mapps.one y luego Test trigger en Zapier. Los campos del payload, como
data.app.name, ya se pueden usar en los pasos siguientes.
Ejemplos: publica un mensaje en Slack cuando se pause una app, añade los clics de cada día a una hoja de cálculo de Google Sheets o envía un correo electrónico a un cliente cuando se verifique su dominio.
Make y n8n
En Make, usa el disparador Webhooks → Custom webhook. En n8n, usa el nodo Webhook con el método POST. Luego añade la URL como endpoint en mapps.one, como se explicó arriba.
Crear una integración
Si estás creando una integración de mapps.one para otras personas, suscríbete con la API cuando un usuario se conecte (POST /api/v1/webhooks), cancela la suscripción cuando se desconecte (DELETE /api/v1/webhooks/{id}), usa GET /api/v1/events para el selector de eventos y GET /api/v1/workspace para probar la conexión. Consulta la referencia de la API.