A lightweight HTTP API for sending SMS messages through a Huawei GSM modem connected via serial port. Distributed as a single .exe file for Windows.
Incoming SMS requests are saved to an in-memory queue and processed one at a time by a background worker, so no messages are lost under heavy traffic.
Before running the gateway, the Huawei modem must be bound to a COM port.
- Connect the Huawei modem via USB.
- Use the
balong_usbdload.exeutility to initialize the modem so that Windows exposes it as a serial COM port:tools\balong_usbdload.exe - Open Device Manager and verify the modem appears under Ports (COM & LPT) — note the assigned port name (e.g.
COM3).
You can verify the modem is working correctly using the standalone PowerShell test script — see scripts/README.md for details.
Note: The phone number must include the country code (e.g.
+420for Czech Republic). This was confirmed as required during testing on a Huawei E3372h-153 modem — without the country code the modem rejects the message.
Requires Go 1.25+.
go build -o sms-gateway.exe .
sms-gateway.exe [flags]
| Flag | Default | Description |
|---|---|---|
-port |
COM3 |
Serial port name (e.g. COM3, /dev/ttyUSB0) |
-baud |
115200 |
Serial port baud rate |
-listen |
127.0.0.1:8080 |
HTTP listen address (e.g. 127.0.0.1:8080, :8080) |
-queue-size |
100 |
Maximum number of SMS jobs waiting in the queue |
-history-size |
1000 |
Maximum number of completed SMS jobs to keep in history |
-simulator |
false |
Run in simulator mode without a real modem |
-admin-password |
(generated) | Admin dashboard password (overwrites stored value; if empty, uses SMS_GATEWAY_ADMIN_PASSWORD, stored hash, or generates new) |
-keys-file |
keys.json |
Path to the API keys and admin password JSON file |
# Run with defaults (COM3, 115200 baud, listen on 127.0.0.1:8080, queue size 100)
sms-gateway.exe
# Custom port and listen address
sms-gateway.exe -port COM5 -listen 127.0.0.1:9000
# Larger queue for high-traffic use
sms-gateway.exe -queue-size 500
# Run in simulator mode (no real modem required)
sms-gateway.exe -simulator
All /api/* endpoints require a valid API key. Include the key in every request using one of:
- Header:
X-API-Key: <key> - Header:
Authorization: Bearer <key>
Requests without a valid key receive a 401 Unauthorized response:
{"error": "invalid or missing API key"}API keys are managed through the admin dashboard (available at / after login). Keys are stored in the file specified by -keys-file (default keys.json).
The file stores hashed credentials only; newly created API keys are shown once in the admin UI and must be copied immediately.
All responses are JSON with Content-Type: application/json.
Returns the current modem status.
Response:
{
"connected": true,
"network_registered": true,
"signal_strength": 21,
"signal_strength_desc": "good",
"manufacturer": "huawei",
"model": "E3372"
}| Field | Type | Description |
|---|---|---|
connected |
bool | Whether the modem responds to AT commands |
network_registered |
bool | Whether the modem is registered on a mobile network |
signal_strength |
int | Raw signal value (0–31, 99 = unknown) |
signal_strength_desc |
string | Human-readable signal level (weak, fair, good, excellent, unknown) |
manufacturer |
string | Modem manufacturer |
model |
string | Modem model |
Enqueues an SMS message for delivery. Returns immediately with a job ID that can be used to track the result.
Request body:
{
"phone": "+420123456789",
"message": "Hello!"
}| Field | Type | Required | Description |
|---|---|---|---|
phone |
string | yes | Destination phone number (E.164) |
message |
string | yes | Text message to send |
Response (202 Accepted):
{
"id": "a1b2c3d4e5f67890",
"status": "queued",
"pending": 3
}| Field | Type | Description |
|---|---|---|
id |
string | Unique job ID for tracking |
status |
string | Always queued on success |
pending |
int | Number of jobs currently waiting in the queue |
If the queue is full, the endpoint returns 503 Service Unavailable.
Returns the current status of a queued SMS job.
Response:
{
"id": "a1b2c3d4e5f67890",
"phone": "+420123456789",
"message": "Hello!",
"status": "sent",
"result": {
"success": true,
"message_reference": "42"
},
"created_at": "2025-01-15T10:30:00Z",
"updated_at": "2025-01-15T10:30:05Z"
}| Field | Type | Description |
|---|---|---|
id |
string | Unique job ID |
phone |
string | Destination phone number |
message |
string | Text message |
status |
string | One of queued, sending, sent, failed |
result |
object | Modem send result (present once sending completes) |
error |
string | Error description (present when status is failed) |
created_at |
string | Timestamp when the job was enqueued |
updated_at |
string | Timestamp of the last status change |
Returns 404 Not Found if the job ID does not exist.
See LICENSE.