{
  "contract": "appsmax-outbound-webhooks",
  "contractVersion": "1.0.0",
  "published": "2026-08-09",
  "provider": {
    "name": "AppsMax.ru",
    "documentation": "https://appsmax.ru/developers/",
    "support": "https://appsmax.ru/contacts/"
  },
  "scope": "Исходящие события заявок из AppsMax во внешние HTTPS-системы. Это контракт AppsMax, а не официальный API MAX, Telegram, Битрикс24 или amoCRM.",
  "transport": {
    "method": "POST",
    "mediaType": "application/json",
    "timeoutSeconds": 10,
    "maximumResponseBytes": 262144,
    "redirectsFollowed": false,
    "successStatusRange": "200-299",
    "endpointPolicy": {
      "scheme": "https",
      "publicNetworkOnly": true,
      "credentialsInUrlAllowed": false,
      "localhostAllowed": false,
      "privateReservedAndLinkLocalTargetsAllowed": false
    }
  },
  "headers": {
    "event": {
      "name": "X-Appsmax-Event",
      "required": true,
      "value": "Имя события из поля event"
    },
    "signature": {
      "name": "X-Appsmax-Signature",
      "requiredWhenSecretConfigured": true,
      "algorithm": "HMAC-SHA256",
      "encoding": "lowercase hexadecimal",
      "signedValue": "Точные необработанные байты HTTP request body",
      "prefix": null,
      "verification": "Вычислите HMAC-SHA256 от raw body с общим secret и сравните с заголовком без преобразования JSON, используя constant-time comparison."
    }
  },
  "events": [
    {
      "name": "application.created",
      "description": "Создана новая заявка."
    },
    {
      "name": "application.updated",
      "description": "Изменились данные существующей заявки."
    },
    {
      "name": "application.status_changed",
      "description": "Изменился статус заявки."
    }
  ],
  "testEvent": {
    "name": "application.test",
    "description": "Синтетическая проверка endpoint из кабинета; не является реальной заявкой и должна игнорироваться бизнес-обработкой."
  },
  "delivery": {
    "semantics": "at-least-once",
    "maximumAttempts": 5,
    "backoffSeconds": [60, 300, 900],
    "retryableHttpStatuses": [408, 429, "500-599"],
    "retryableTransportFailures": true,
    "otherHttpStatusesRetried": false,
    "deduplication": "Получатель обязан допускать повторную доставку. Для идемпотентной обработки используйте сочетание event, application.id и фактического состояния объекта; отдельный стабильный delivery_id сейчас не публикуется."
  },
  "rawPayloadSchema": {
    "type": "object",
    "required": ["event", "occurred_at", "organization_id", "bot_id", "application", "changes", "meta"],
    "properties": {
      "event": {"type": "string", "enum": ["application.created", "application.updated", "application.status_changed", "application.test"]},
      "occurred_at": {"type": "string", "format": "date-time"},
      "organization_id": {"type": ["integer", "null"]},
      "bot_id": {"type": "integer"},
      "application": {
        "type": "object",
        "required": ["id", "status", "owner_id", "source", "contact", "created_at", "updated_at"],
        "properties": {
          "id": {"type": ["integer", "string"]},
          "status": {"type": ["string", "null"]},
          "owner_id": {"type": ["integer", "null"]},
          "source": {"type": ["string", "null"]},
          "contact": {
            "type": "object",
            "required": ["name", "phone", "email"],
            "properties": {
              "name": {"type": ["string", "null"]},
              "phone": {"type": ["string", "null"]},
              "email": {"type": ["string", "null"]}
            }
          },
          "created_at": {"type": ["string", "null"], "format": "date-time"},
          "updated_at": {"type": ["string", "null"], "format": "date-time"}
        }
      },
      "changes": {"type": "object"},
      "meta": {
        "type": "object",
        "required": ["application_url"],
        "properties": {
          "application_url": {"type": "string", "format": "uri"}
        }
      }
    }
  },
  "example": {
    "event": "application.created",
    "occurred_at": "2026-08-09T12:00:00Z",
    "organization_id": 12,
    "bot_id": 44,
    "application": {
      "id": 123,
      "status": "new",
      "owner_id": null,
      "source": "miniapp",
      "contact": {
        "name": "[TEST AppsMax] Проверка интеграции",
        "phone": "+70000000000",
        "email": "appsmax-test@example.test"
      },
      "created_at": "2026-08-09T11:59:20Z",
      "updated_at": "2026-08-09T12:00:00Z"
    },
    "changes": {},
    "meta": {
      "application_url": "https://example.test/logic/applications/test-123"
    }
  },
  "connectors": [
    {
      "id": "raw",
      "name": "Своя система: JSON",
      "delivery": "AppsMax отправляет raw payload на endpoint получателя."
    },
    {
      "id": "bitrix24_direct",
      "name": "Битрикс24 напрямую",
      "events": ["application.created", "application.status_changed"],
      "delivery": "AppsMax самостоятельно вызывает входящий webhook Битрикс24 и создаёт либо обновляет лид.",
      "note": "Этот режим не отправляет raw payload в пользовательский обработчик."
    },
    {
      "id": "bitrix24",
      "name": "Битрикс24 через обработчик",
      "delivery": "AppsMax отправляет подготовленный lead payload на HTTPS-обработчик пользователя; обработчик выполняет авторизацию и вызов Битрикс24."
    },
    {
      "id": "amocrm",
      "name": "amoCRM через обработчик",
      "delivery": "AppsMax отправляет подготовленные lead/contact/note поля на HTTPS-обработчик пользователя; обработчик выполняет авторизацию и вызов amoCRM.",
      "note": "Прямой коннектор amoCRM сейчас не заявлен."
    }
  ],
  "securityNotes": [
    "Не передавайте secret в URL, browser JavaScript, аналитику, скриншоты или публичный репозиторий.",
    "Проверяйте подпись до разбора и бизнес-обработки JSON.",
    "Минимизируйте сохранение контактных данных и установите собственные сроки удаления.",
    "При компрометации отключите endpoint либо замените secret и проверьте журнал доставок."
  ]
}
