Webhooks

What are Webhooks

A webhook is a mechanism that allows your application to receive automatic webhooks about events occurring in the MassAccess system. When an event happens — a message status changes, a client phone number is updated, or a client interacts with your bot — MassAccess sends an HTTP POST request to your server with event data in JSON format.

Requests are sent to the Webhook URL configured in your company dashboard (Settings → Webhook URL). Each request includes an HMAC-SHA256 signature (X-Signature header) so you can verify its authenticity.

How to Set Up Webhooks

To start receiving webhooks, log in to your company dashboard, go to Settings, and specify your server URL in the «Webhook URL» field. After saving, MassAccess will begin sending HTTP POST requests to this URL for every event in your company. No additional API calls are needed — webhooks are sent automatically for all message status changes, phone number updates, and client interactions with your bots.

Event Types

MassAccess sends webhooks for the following events:

EventDescription
message.statusSent when a message reaches a terminal status: sent or failed. The data.state field contains the current status. If data.state is "failed", additional data.error_code and data.error_description fields are included.
client.phone.changedSent when a client's phone number is changed via the /api/v1/change_phone endpoint. The data.state field is "completed" on success. If the change fails, data.state is "failed" with error_code and error_description fields.
client.checkSent when checking client existence by phone number via the /api/v1/client/check endpoint. data.exists indicates whether the client was found, and data.bots contains the list of bots the client is connected to.
client.registeredSent when a new client registers through your bot in Telegram or MAX. The payload includes phone, bot_name, and service fields. This event is sent once per client registration.

Webhook Format

Format of incoming webhooks about message status. Notifications are sent to the URL in company settings.

Request Headers

HeaderDescription
X-SignatureHMAC-SHA256 signature of the request body
Content-Typeapplication/json
User-AgentMassAccess-Webhook/2.0

Payload Structure

Every webhook request body is a JSON object with a common structure. The top-level fields are always present; the contents of the data field depend on the event type. Below are examples for each event type.

Message status change

Sent when a message is successfully delivered to the recipient. data.state = sent.

json
{
  "event": "message.status",
  "event_id": "550e8400-e29b-41d4-a716-446655440000",
  "timestamp": "2025-01-15T10:30:05Z",
  "data": {
    "state": "sent"
  }
}

Message status change (error)

Sent on delivery failure. data.state = failed + error_code and error_description.

json
{
  "event": "message.status",
  "event_id": "550e8400-e29b-41d4-a716-446655440000",
  "timestamp": "2025-01-15T10:30:05Z",
  "data": {
    "state": "failed",
    "error_code": "NETWORK_ERROR",
    "error_description": "Network error"
  }
}

Phone number change

Sent on successful phone number change via API /api/v1/change_phone. data.state = completed.

json
{
  "event": "client.phone.changed",
  "event_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "timestamp": "2025-01-15T10:30:05Z",
  "data": {
    "state": "completed"
  }
}

Phone number change (error)

Sent on phone change failure. data.state = failed + error_code and error_description.

json
{
  "event": "client.phone.changed",
  "event_id": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
  "timestamp": "2025-01-15T10:30:05Z",
  "data": {
    "state": "failed",
    "error_code": "INVALID_PHONE",
    "error_description": "Invalid phone number format"
  }
}

Client Registration in Bot

Sent when a new client registers through a bot (Telegram/MAX). Contains phone, bot_name, service. One-time event.

json
{
  "event": "client.registered",
  "event_id": "c3d4e5f6-a7b8-9012-cdef-123456789012",
  "timestamp": "2025-01-15T10:30:05Z",
  "data": {
    "phone": "12345678901",
    "bot_name": "my_telegram_bot",
    "service": "telegram"
  }
}

Client Check (exists = true)

Sent when the client with the specified number is found in the company. data.exists = true, data.bots contains a list of bots with name and service fields.

json
{
  "event": "client.check",
  "event_id": "3bd290b8-09b4-48c4-920b-85b93fc3859b",
  "timestamp": "2026-07-28T21:45:31Z",
  "data": {
    "exists": true,
    "bots": [
      {
        "name": "main_max",
        "service": "max"
      }
    ]
  }
}

Client Check (exists = false)

Sent when the client with the specified number is not found. data.exists = false, data.bots is empty.

json
{
  "event": "client.check",
  "event_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "timestamp": "2026-07-28T21:45:31Z",
  "data": {
    "exists": false,
    "bots": []
  }
}

Webhook Fields

NameTypeRequiredDescription
eventstringYesEvent type: message.status | client.phone.changed | client.registered
event_idstring (UUID)YesUnique message identifier (UUID)
timestampstring (ISO 8601)YesEvent time in ISO 8601 UTC
data.statestringNoMessage status: new, processing, sent, failed
data.error_codestringNoError code (present only on failure)
data.error_descriptionstringNoHuman-readable error description (present only on failure)
data.client_idintegerNoClient identifier (for client.* events)
data.phonestringNoClient phone number (for client.* events)
data.bot_namestringNoBot name that triggered the event (for client.* events)
data.existsbooleanNoWhether the client exists (client.check event)
data.botsarrayNoList of bots the client is connected to (client.check event). Each bot has name (string) and service (string) fields.

Sending Architecture

Your Server Response

To confirm receipt, your server must return HTTP 200 OK. If response is not 200, MassAccess will retry with increasing intervals.

Your server must respond within 10 seconds. If the server returns a non-2xx status or times out, MassAccess retries delivery with exponential backoff. After 5 failed attempts, the webhook is discarded.

HTTP StatusDescription
200 OKWebhook delivered successfully — MassAccess stops retrying.
4xx / 5xxWebhook delivery failed — MassAccess will retry with exponential backoff (delays: ~1s, ~5s, ~25s, ~125s, ~625s).

Signature Verification

The X-Signature header contains an HMAC-SHA256 signature of the raw request body (before JSON parsing). To verify the webhook comes from MassAccess:

  1. Get the raw HTTP request body as bytes (do not parse JSON first)
  2. Compute HMAC-SHA256 of the raw body bytes using your API key as the secret
  3. Compare the resulting HEX string with the X-Signature header. Use a constant-time comparison function from your language's standard library (e.g. compare_digest in Python, hash_equals in PHP, timingSafeEqual in Node.js) — not the regular == operator. Constant-time comparison always takes the same amount of time regardless of whether strings match, preventing attackers from guessing the signature by measuring server response time. If they don't match, reject the request — it is forged.

Verify the X-Signature header (HMAC-SHA256) to ensure webhook authenticity:

python
import hmac
import hashlib

def verify_signature(payload: bytes, signature: str, secret: str) -> bool:
    """Verify webhook HMAC-SHA256 signature."""
    expected = hmac.new(
        secret.encode(),
        payload,
        hashlib.sha256
    ).hexdigest()
    return hmac.compare_digest(expected, signature)