Skip to content

Mobile Messaging API

The Mobile Messaging API is a simple and future-proof interface for sending SMS and RCS messages to phone numbers. By integrating with this API, you are automatically prepared for RCS messaging. Messages seamlessly fall back to SMS when recipients are not RCS-capable, without requiring any changes to your code. The API handles SMS-specific details such as character encoding and message classes automatically.

Read more in our help center.

API endpoint & authentication

All API calls must be made against one of the following domains, depending on your setup:

  • messaging.gatewayapi.com
  • messaging.gatewayapi.eu

Authentication is performed using a Token Header from your API keys API keys can be generated and managed in the GatewayAPI Dashboard.

Sending a message

A minimal example for sending a single message:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
POST /mobile/single HTTP/1.1
Host: messaging.gatewayapi.com
Authorization: Token <your-api-token>
Accept: application/json
Content-Type: application/json

{
    "sender": "ExampleSMS",
    "message": "Hello world!",
    "recipient": 4512345678
}

The successful response looks like:

1
2
3
4
5
{
    "msg_id": "01JNN696A9E0WS89FPYGT15NBX",
    "recipient": 4512345678,
    "reference": "<client provided reference>"
}

We provide a full OpenAPI spec, as well as Swagger and Redoc.

Optional fields

The following optional fields can be included when sending a message.

label

1
2
3
{
    "label": "2fa-login"
}
A client-defined label used to retrieve statistics grouped by label via our Statistics API or directly within the self-service Customer Dashboard App The label field is compatible with the existing label feature used across GatewayAPI and can be used to retrieve usage statistics grouped by label via the Statistics API.

  • The label is stored for reporting purposes only
  • It is not returned in the send-message response
  • It is not included in webhook payloads
  • It does not affect message delivery or routing

reference

1
2
3
{
    "reference": "order-12345"
}
A client-defined reference that is echoed back in delivery reports and webhook callbacks.

expiration

1
2
3
{
    "expiration": "PT1H"
}
Specifies how long the message is valid for delivery. If the message cannot be delivered before the expiration time, it will expire and no further delivery attempts will be made. If not set, the default expiration is 5 days. This is also the max expiration.

The value can be provided in one of the following formats:

  • ISO 8601 duration (recommended). Example: PT1H (1 hour), PT10M (10 minutes), P1D (1 day)
  • ISO-8601 date-time (absolute expiration time). Format: YYYY-MM-DD[T]HH:MM:SS[.ffffff][Z or [+ or -]HH[:]MM]. Example: 2026-02-19T18:00:00Z
  • Unix timestamp (seconds since epoch). Example: 1760896800
  • Seconds offset (relative expiration time). Example: 18000 (expires in 5 hours)

Webhook callbacks

All webhook callbacks use a consistent event envelope format:

1
2
3
4
5
6
7
8
{
    "event_id": "09c829a2-a77b-422d-8562-2f88e784dfb5",
    "timestamp": "2025-04-01T11:50:12.003029+00:00",
    "event_type": "<event type indicator string>",
    "event": {
        ...
    }
}

The event_type field indicates the type of event and determines the schema of the event object. Supported event types:

  • message.status.sms
  • message.status.rcs
  • user-message.text.rcs
  • user-message.location.rcs
  • user-message.file.rcs

SMS status callback example:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
{
    "event_id": "09c829a2-a77b-422d-8562-2f88e784dfb5",
    "timestamp": "2025-04-01T11:50:12.003029+00:00",
    "event_type": "message.status.sms",
    "event": {
        "msg_id": "01JQRJWK259Y1YEECJZB50908V",
        "recipient": 4512345678,
        "reference": "<client provided reference>",
        "status": "DELIVERED",
        "status_at": "2025-04-01T11:49:12.005021+00:00",
        "error": null
    }
}

RCS status events are similar to SMS, but the status field can only contain EXPIRED, DELIVERED, ENROUTE and READ:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
{
    "event_id": "09c829a2-a77b-422d-8562-2f88e784dfb5",
    "timestamp": "2025-04-01T11:50:12.003029+00:00",
    "event_type": "message.status.rcs",
    "event": {
        "msg_id": "01JQRJWK259Y1YEECJZB50908V",
        "recipient": 4512345678,
        "reference": "<client provided reference>",
        "status": "READ",
        "status_at": "2025-04-01T11:49:12.005021+00:00"
    }
}

Incoming RCS message payload:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
{
    "event_id": "09c829a2-a77b-422d-8562-2f88e784dfb5",
    "timestamp": "2025-04-01T11:50:12.003029+00:00",
    "event_type": "user-message.text.rcs",
    "event": {
        "msg_id": "01K33QFGSHXXM0Q7HM7QS34PY6",
        "sender": 4512345678,
        "content": "Hello world!",
        "sent_at": "2025-04-01T11:49:12.005021+00:00"
    }
}

RCS allows for more complex payloads for file and location information from the end user, which is reflected in the content field being an object.

RCS location example:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
{
    "event_id": "09c829a2-a77b-422d-8562-2f88e784dfb5",
    "timestamp": "2025-04-01T11:50:12.003029+00:00",
    "event_type": "user-message.location.rcs",
    "event": {
        "msg_id": "01K33QFGSHXXM0Q7HM7QS34PY6",
        "sender": 4512345678,
        "content": {
            "latitude": 55.6806024,
            "longitude": 12.5790722
        },
        "sent_at": "2025-04-01T11:49:12.005021+00:00"
    }
}

Files are hosted on a Google-provided CDN. If you need to retain files, you should download them, as long-term availability is not guaranteed. Depending on the file type, fields such as thumbnail and category may be null.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
{
    "event_id": "09c829a2-a77b-422d-8562-2f88e784dfb5",
    "timestamp": "2025-04-01T11:50:12.003029+00:00",
    "event_type": "user-message.file.rcs",
    "event": {
        "msg_id": "01K33QFGSHXXM0Q7HM7QS34PY6",
        "sender": 4512345678,
        "content": {
            "category": null,
            "thumbnail": {
                "mime_type": "image/jpeg",
                "size_bytes": 5246,
                "name": null,
                "uri": "https://rcs-<region>.googleapis.com/blob/<conversation-id>/<object-id>"
            },
            "payload": {
                "mime_type": "image/jpeg",
                "size_bytes": 24285,
                "name": "9112194760767796361.jpg",
                "uri": "https://rcs-<region>.googleapis.com/blob/<conversation-id>/<object-id>"
            }
        },
        "sent_at": "2025-04-01T11:49:12.005021+00:00"
    }
}

Webhook Signature Verification

All webhook events are signed and timestamped to verify their authenticity and integrity as originating from GatewayAPI.

The signature is provided in the Signature header using the format: v1=<hex-encoded-hmac>

To verify the signature: Calculate an HMAC-SHA-256 value of the raw request body using the secret key configured for the webhook in the GatewayAPI dashboard. Hex-encode the result and compare it with the value provided in the Signature header.

Key length

Longer secret keys increase the cryptographic strength of HMAC signatures. Beyond a certain length, however, the added security benefit becomes negligible, as the effort required to brute-force the key is already impractical.

For this reason, GatewayAPI enforces a maximum length for webhook secret keys to ensure strong security without unnecessary processing overhead.

An example of a function that can check if the request is signed as expected, could be:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import hmac
import secrets

class Request:
    """Generic request model."""

    body: bytes
    headers: dict[str, str]

def verify_request(request: Request, signature_key: bytes) -> bool:
    signature = request.headers.get("Signature")
    if not signature:
        return False

    expected_signature = hmac.digest(
        signature_key,
        request.body,
        "SHA256",
    )
    return secrets.compare_digest(
        signature.removeprefix("v1="),
        expected_signature.hex(),
    )

Webhook events include a timestamp, allowing you to safely reject old or replayed requests.

Changelog & compatibility

The Mobile Messaging API is designed to be stable and reliable. We do not introduce breaking changes without proper deprecation warnings.

GatewayAPI places a strong emphasis on API stability. Once an integration is in place, existing functionality will not change without advance notice and an appropriate deprecation period.

A breaking change is defined as the removal of existing fields or a fundamental change in their meaning.

The addition of new fields, options, or event types is not considered a breaking change. Integrations should therefore be implemented defensively and tolerate additional fields in API responses and webhook payloads.

As the API evolves, new event types may be introduced to provide additional insight into your messaging traffic

January 2026 - Added label parameter

The API now accepts the optional label parameter. This is to be backwards compatible with the existing label feature from the legacy REST API which gives usage statistics by label.