Laravel: Receive Android SMS Gateway API Webhooks

Featured illustration for Laravel: Receive Android SMS Gateway API Webhooks

Receive Android SMS Gateway API webhooks in Laravel: middleware verification, jobs, idempotency, and monitoring.

Written by the SMS Gateway team for operators who run phones and airtime themselves — not for theoretical cloud SMS demos.

InformationAndroid SMS GatewayAPIDevelopers
Article
Published
May 8, 2026
Updated
May 10, 2026
Reading time
17 minute read

Key Takeaways

  • A Laravel webhook receiver is a POST route that HMAC-verifies X-SmsGateway-Signature on the raw body, persists Event-Id, returns 200, and dispatches a job.
  • There is no official Composer “Complete Laravel SDK.” Illuminate HTTP talks REST HTTPS/JSON. Confirm headers in Developer Center.
  • VerifyCsrfToken must except the webhook path. CSRF is browser protection, not a substitute for HMAC.
  • message.delivered is DLR. message.received is inbound. message.failed is a send-path failure. Different parsers.
  • Do not run STOP lists or OTP state machines on the request thread. Horizon exists so the gateway does not mark you unhealthy.

Teams search laravel android sms gateway api receive webhook after the first DLR hits a 419. Laravel’s CSRF middleware treated the POST like a form. The gateway is not a browser. This Hub C recipe is the Laravel-shaped receiver: raw body, HMAC, job. Hub: Android SMS gateway API. Product surface: SMS webhook integration. Live fields stay in the Developer Center.

You need a working Android phone with a SIM and SMS credit from your mobile operator. Operator message costs are yours—we do not sell carrier SMS balance. Service pricing is based on device count and total SMS sent through the gateway. See device and SMS volume pricing.

v1=HMAC-SHA256

Horizon job

200 first. Parser second. CRM never on the FPM thread.

Laravel HTTP, not a Laravel SDK

Illuminate’s HTTP client is a client. It is not a vendor SDK, a Composer package we ship, or a replacement for OpenAPI. Sending still uses Bearer JSON to POST /api/v1/messages. Receiving still uses the headers documented under webhooks. PHP HTTPS samples: PHP REST samples. Laravel’s own HTTP docs: HTTP client.

Laravel’s CSRF middleware is for browsers. Gateway webhooks are not browsers. Except the path, then HMAC the bytes.

CSRF exception is not auth

A 419 on POST /webhooks/sms-gateway usually means VerifyCsrfToken ate the request. Except that path. Keep CSRF on login, billing, and every other form. The webhook is authenticated by HMAC, not by a session cookie. If you “fix” 419 by turning CSRF off globally, you have a different incident.

// App\Http\Middleware\VerifyCsrfToken — conceptual
protected $except = [
    'webhooks/sms-gateway',
];

Prefer a dedicated route file that never loads the web middleware group. Confirm the live register URL at https://app.sms-gateway.app/api/v1/webhooks in Developer Center — this blog does not own the catalog.

HMAC the raw body

X-SmsGateway-Signature is v1= hex of HMAC-SHA256 over {timestamp}.{body}. Timestamp is X-SmsGateway-Timestamp (unix seconds). Reject skew beyond about five minutes. Use hash_equals. Do not pretty-print, do not run the body through$request->all() before you sign.

Clock skew after a deploy is a common false 401. NTP on the app hosts is part of the webhook, not “ops later.”

ACK 200, then a job

Gateways retry when you hang. A controller that talks to Stripe, Zendesk, and a STOP list on the same request will look like an outage. Persist Event-Id, return 200, dispatch. Horizon failed-jobs is where STOP parsers belong. Two-way product: two-way SMS. STOP handling: STOP and opt-outs.

DLR vs inbound vs failed

typeMeansLaravel job should
message.deliveredOperator DLR when the radio got oneMark the OTP/order delivered; never overwrite with a late Pending
message.failedSend path failedAlert; do not infinite-retry the same challenge
message.receivedInbound on the SIMThread, STOP, or agent — separate parser from DLR

Delivery reports also exist as polls: SMS delivery reports. One mapper for webhook and poll so precedence does not fork.

Payload and PHP verify

Illustrative delivered event. Confirm field names in OpenAPI when they disagree with this blog.

{
  "id": "evt_01K2F8QW3N4RXB7M",
  "type": "message.delivered",
  "createdAt": "2026-08-12T14:04:09Z",
  "apiVersion": "2026-08-12",
  "data": {
    "message": {
      "id": 41823,
      "number": "+14155552671",
      "text": "Your verification code is 481920",
      "status": "Delivered",
      "campaignId": 17,
      "deviceId": 3,
      "metadata": { "orderId": "1234" },
      "sentAt": "2026-08-12T14:04:02Z",
      "deliveredAt": "2026-08-12T14:04:09Z"
    }
  }
}

Conceptual PHP verify (same headers Laravel must implement):

<?php
header('Content-Type: application/json');

$secret = getenv('SMS_GATEWAY_WEBHOOK_SECRET');
$timestamp = $_SERVER['HTTP_X_SMSGATEWAY_TIMESTAMP'] ?? '';
$sigHeader = $_SERVER['HTTP_X_SMSGATEWAY_SIGNATURE'] ?? '';
$eventId = $_SERVER['HTTP_X_SMSGATEWAY_EVENT_ID'] ?? '';
$body = file_get_contents('php://input');

if ($timestamp === '' || $sigHeader === '' || $secret === '') {
    http_response_code(400);
    echo json_encode(['error' => 'missing_signature']);
    exit;
}

if (abs(time() - (int) $timestamp) > 300) {
    http_response_code(401);
    echo json_encode(['error' => 'stale_timestamp']);
    exit;
}

$expected = 'v1=' . hash_hmac('sha256', $timestamp . '.' . $body, $secret);
$ok = false;
foreach (preg_split('/\s+/', $sigHeader) as $candidate) {
    if (hash_equals($expected, $candidate)) {
        $ok = true;
        break;
    }
}
if (!$ok) {
    http_response_code(401);
    echo json_encode(['error' => 'bad_signature']);
    exit;
}

// Deduplicate on X-SmsGateway-Event-Id (retries and replays reuse it).
$data = json_decode($body, true) ?: [];
$type = $data['type'] ?? '';
$message = $data['data']['message'] ?? [];

if ($type === 'message.received') {
    $from = $message['number'] ?? '';
    $text = $message['text'] ?? '';
    error_log("Inbound SMS $eventId from $from: $text");
}

http_response_code(200);
echo json_encode(['ok' => true]);

Java sibling for the same HMAC: receive webhooks in Java.

Ship checklist

  • Webhook secret in the environment; rotated after contractor access.
  • CSRF excepted on one path; CSRF still on everywhere else.
  • Raw-body HMAC with skew window and Event-Id unique index.
  • 200 before Horizon; failed jobs paged.
  • OTP codes never logged. MSISDNs masked.
  • Staff inbound canary on the Free 300 lifetime SMS allowance before production.

Next steps

Point staging at a signed URL, send a staff inbound, watch Event-Id land once. Device setup · Android app · Laravel SMS gateway hub. Laravel CSRF docs: CSRF protection.

Jump to the live product docs for this topic—not another long-form article.

FAQ

Frequently asked questions

Direct answers about android sms gateway api.

How do I receive Android SMS gateway API webhooks in Laravel?

Register an HTTPS URL with POST /webhooks, except it from CSRF, read php://input (or the raw request content), HMAC-SHA256 timestamp + "." + body, compare to v1= hex with hash_equals, upsert X-SmsGateway-Event-Id, return 200, dispatch a queued job.

Is there an official Laravel SMS SDK?

No. Use Laravel’s HTTP client or Guzzle against the documented REST API. PHP samples on /codebase-php are HTTPS/JSON shapes, not a packaged SDK product.

Can I json_decode first, then sign the array?

No. Re-encoding JSON changes bytes. HMAC the exact body the gateway sent.

Does a 200 from Laravel mean the SMS was delivered?

No. 200 means you accepted the event. Delivery is a later message.delivered (or a DLR poll). Inbound is message.received.

Who pays for the SMS that triggered the webhook?

You do — operator airtime on the SIM. Service pricing is based on device count and total SMS sent through the gateway. Free includes 300 SMS lifetime for a staging URL.
Keep learning

Topically related guides—chosen by subject overlap, not a fixed sitewide footer.

Information
android sms gateway api webhook

Android Sms Gateway Api Webhook: In-Depth Guide

Android Sms Gateway Api Webhook: In-Depth Guide. Long-tail article focused on exact query "android sms gateway api webhook". Expand with examples, limits, FAQ, and links to hub C. Priced by devices and SMS send volume; BYO phone and operator credit. Developer Center owns live API parameters.

Dec 24, 202516 min
Read article
Information
android sms gateway troubleshooting webhook timeout

Android SMS Gateway Troubleshooting: Webhook timeout

Android SMS Gateway Troubleshooting: Webhook timeout. KB article diagnosing webhook timeout. Symptoms, likely causes, validation steps, recovery. Priced by devices and SMS send volume; BYO phone and operator credit.

Jul 6, 202516 min
Read article
Practical
laravel sms gateway checklist

API production readiness Checklist for Laravel / Frameworks

API production readiness Checklist for Laravel / Frameworks. Printable-style API production readiness checklist mapped to laravel sms gateway. Each item includes why it matters and a verification step. Priced by devices and SMS send volume; BYO phone and operator credit.

Mar 30, 202616 min
Read article

Browse the full Android SMS gateway knowledge base or return to how an Android SMS gateway works.

Get started

Test the gateway on your own Android phone

Install the app, pair one device, and validate your API flow before choosing a paid plan.

You supply the phone, SIM, and operator SMS credit.