Перейти к содержимому

Разработчики

Вебхуки и Zapier

Вебхуки отправляют HTTP-запрос на ваш сервер, как только в рабочем пространстве что-то происходит, например приложение приостанавливается или кто-то кликает по умной ссылке. С их помощью можно обновлять собственные системы, публиковать сообщения в Slack или подключить mapps.one к Zapier, Make или n8n без собственного сервера. Вебхуки доступны на плане Studio, до 10 эндпоинтов на рабочее пространство.

Добавление эндпоинта

  1. В панели управления откройте Разработчики → Вебхуки и нажмите Добавить эндпоинт. Это могут делать владельцы и администраторы рабочего пространства.
  2. Введите публичный URL с https, выберите события для отправки (или все события) и добавьте описание.
  3. Скопируйте секрет подписи и сохраните его вместе с другими секретами вашего сервера.
  4. Нажмите Отправить тестовое событие, чтобы проверить, что эндпоинт отвечает.

На странице эндпоинта показываются недавние доставки со статусом ответа, первыми 2000 символами тела ответа и длительностью каждой попытки. Эндпоинты также можно создавать и удалять через API.

События

ТипКогда отправляется
app.createdВ рабочее пространство добавлено приложение.
app.updatedИзменились данные приложения, ссылки или настройки страницы.
app.deletedПриложение удалено.
app.status_changedПриложение стало активным, было приостановлено или заблокировано.
app.metadata_fetchedДля приложения импортированы метаданные из магазина.
legal.publishedОпубликована политика конфиденциальности или условия использования.
domain.verifiedСобственный домен подтверждён.
link.clickedКаждый клик по умной ссылке. Для популярного приложения это может быть очень много запросов.
analytics.daily_summaryИтоги кликов за предыдущий день по каждому приложению, отправляются раз в день.

Формат запроса

Каждое событие отправляется как запрос POST с телом в формате JSON.

ЗаголовокЗначение
Content-Typeapplication/json
User-Agentmapps.one-Webhooks/1.0
X-Mapps-EventТип события, например link.clicked
X-Mapps-DeliveryID события. Он не меняется при повторных попытках.
X-Mapps-TimestampВремя этой попытки доставки в формате Unix
X-Mapps-Signaturet={timestamp},v1={signature}

Тело запроса — это конверт одинаковой структуры для всех событий:

{
  "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"
  }
}

Содержимое data зависит от события. События, связанные с приложением, содержат объект app с полями id, slug и 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 указывается в UTC. device_type принимает значения mobile, tablet или desktop. destination — это ключ магазина, web или landing. Неизвестные поля имеют значение null. Посещения ботов никогда не вызывают это событие.

app.metadata_fetched

"data": {
  "app": { "id": "01j8b2k6r3xq9v7m4n5p0s1t2w", "slug": "habitly", "name": "Habitly" },
  "source": "app_store"
}

Проверка подписей

Проверяйте подпись каждого запроса, чтобы убедиться, что он пришёл от mapps.one и не был изменён по пути:

  1. Прочитайте t и v1 из заголовка X-Mapps-Signature.
  2. Вычислите HMAC-SHA256 от {t}.{raw request body} с секретом подписи вашего эндпоинта в виде шестнадцатеричной строки в нижнем регистре.
  3. Сравните результат с v1 с помощью сравнения за постоянное время.
  4. Отклоните запрос, если t отличается от часов вашего сервера больше чем на пять минут, чтобы блокировать повторно отправленные запросы.
Используйте исходное тело запроса. Подписывайте именно те байты, которые получили, до любого разбора JSON. Повторное кодирование JSON может изменить слеши или символы Unicode и нарушить подпись.

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);

В Laravel прочитайте тело с помощью $request->getContent(), а заголовок — с помощью $request->header('X-Mapps-Signature') и исключите маршрут из 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);

Ответы и повторные попытки

  • Отвечайте любым статусом 2xx в течение 10 секунд. Долгую работу выполняйте в фоновой задаче после ответа.
  • Перенаправления не выполняются, поэтому указывайте конечный URL.
  • URL должен быть публичным и использовать https. Частные и локальные сетевые адреса отклоняются.
  • Если ваш эндпоинт не отвечает вовремя или возвращает что-либо кроме 2xx, мы повторяем попытку ещё до пяти раз: примерно через 1 минуту, 5 минут, 30 минут, 2 часа и 6 часов. У каждой попытки новая метка времени и новая подпись.
  • Одно и то же событие может прийти несколько раз, а события могут приходить не по порядку. Используйте X-Mapps-Delivery, то есть id события, чтобы отбрасывать дубликаты, и created_at, чтобы упорядочивать события.
  • После 20 неудачных попыток подряд эндпоинт отключается, и на него больше ничего не отправляется. Устраните проблему, а затем снова включите эндпоинт в панели управления.

Zapier, Make и n8n

Вы можете подключить mapps.one к тысячам приложений без собственного сервера.

Zapier

  1. Создайте Zap с триггером Webhooks by Zapier и выберите Catch Hook.
  2. Скопируйте URL вебхука, который выдаст Zapier.
  3. В mapps.one добавьте эндпоинт с этим URL и выберите нужные события, например app.status_changed или analytics.daily_summary.
  4. Нажмите Отправить тестовое событие в mapps.one, а затем Test trigger в Zapier. Теперь поля из содержимого события, например data.app.name, можно использовать в следующих шагах.

Примеры: отправлять сообщение в Slack, когда приложение приостановлено, добавлять клики за каждый день в таблицу Google Sheets или писать клиенту, когда его домен подтверждён.

Make и n8n

В Make используйте триггер Webhooks → Custom webhook. В n8n используйте узел Webhook с методом POST. Затем добавьте URL как эндпоинт в mapps.one, как описано выше.

Создание интеграции

Если вы создаёте интеграцию mapps.one для других пользователей, подписывайтесь через API, когда пользователь подключается (POST /api/v1/webhooks), отписывайтесь, когда он отключается (DELETE /api/v1/webhooks/{id}), используйте GET /api/v1/events для выбора событий и GET /api/v1/workspace для проверки подключения. Подробнее — в справочнике API.