Overview
Signalhouse is a hosted SMS gateway that uses your Android phone as the radio. The API lets you send outbound SMS, receive inbound messages, and manage gateway devices over a persistent WebSocket connection — messages go out on your own SIM, not a shared carrier pool.
All request and response bodies are JSON (Content-Type: application/json).
All timestamps are ISO 8601 in UTC.
Authentication
All API endpoints require an API key. Create one on your account page by scanning or generating a key.
API Key Header
Authorization: Bearer sh_live_xxxxxxxxxxxx
Include this header in every request. The Android gateway app uses the same key — configured by scanning the QR code on your account page.
Auth API
Endpoints for account creation, login, and token management. Base path: /api/v1/auth
Authenticate with email and password. Returns a JWT access token.
Request body
{
"email": "you@example.com",
"password": "your-password"
}
Response 200
{
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"token_type": "Bearer",
"expires_in": 7776000
}
Create a new account. Requires invite code during beta.
Request body
{
"email": "you@example.com",
"password": "your-password",
"name": "Your Name",
"invite_code": "your-invite-code"
}
Response 201
{
"success": true,
"message": "Account created",
"user_id": "usr_abc123"
}
Return the currently authenticated user's profile.
Response 200
{
"id": "usr_abc123",
"email": "you@example.com",
"name": "Your Name",
"phone_number": "+15551234567",
"role": "user",
"created_at": "2026-04-15T09:12:00Z"
}
Exchange a refresh token for a new access token.
Request body
{
"refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}
Response 200
{
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"expires_in": 7776000
}
SMS API
Send and receive SMS messages through your paired Android gateway. Base path: /api/v1/sms
Send an SMS through your paired Android device. The message is queued and pushed to the device over WebSocket.
Request body
{
"to": "+15551234567", // E.164 format
"message": "Hello from Signalhouse"
}
Response 200
{
"status": "queued",
"message_id": "msg_abc123",
"to": "+15551234567"
}
List SMS messages for the authenticated account. Supports pagination and filtering.
Query parameters
| Name | Type | Description |
|---|---|---|
| limit opt | integer |
Max results to return. Default 50. |
| offset opt | integer |
Pagination offset. Default 0. |
| direction opt | string |
inbound or outbound. Omit for both. |
| since opt | string |
ISO 8601 timestamp — only messages after this time. |
Response 200
{
"messages": [
{
"id": "msg_abc123",
"from": "+15551234567",
"to": "+15559876543",
"message": "Hello",
"direction": "inbound",
"received_at": "2026-04-20T14:30:00Z",
"thread_id": null,
"archived": false
}
],
"count": 1,
"has_more": false
}
Retrieve a single SMS message by its UUID.
Response 200
{
"id": "msg_abc123",
"from": "+15551234567",
"to": "+15559876543",
"message": "Hello",
"direction": "inbound",
"received_at": "2026-04-20T14:30:00Z",
"ai_processed": false,
"archived": false,
"processing_status": "unprocessed"
}
Get the delivery status of a sent SMS by message ID.
Response 200
{
"id": "msg_abc123",
"to": "+15551234567",
"status": "delivered",
"sent_at": "2026-04-20T14:30:05Z",
"delivered_at": "2026-04-20T14:30:07Z"
}
Return aggregate statistics for the authenticated account.
Response 200
{
"total_received": 142,
"total_sent": 38,
"last_24h": { "received": 12, "sent": 5 },
"last_7d": { "received": 67, "sent": 21 }
}
Archive an SMS message by its UUID. Archived messages are excluded from the default messages list.
Response 200
{
"success": true,
"id": "msg_abc123",
"archived": true
}
Generate a QR code that pairs the Signalhouse Android app with your account. Returns a base64-encoded PNG data URL.
Response 200
{
"success": true,
"qr_code": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAA...",
"api_key": "sh_live_xxxxxxxxxxxx"
}
Device API
Register and list user devices. Base path: /api/v1/device
Register a new device for the authenticated user.
Request body
{
"name": "Pixel 6a",
"model": "Pixel 6a",
"os_version": "Android 14"
}
Response 201
{
"id": "dev_abc123",
"name": "Pixel 6a",
"paired_at": "2026-04-20T14:30:00Z",
"api_key": "sh_device_xxxxxxxxxxxx"
}
List all Android devices paired with the authenticated account.
Response 200
{
"devices": [
{
"id": "dev_abc123",
"name": "Pixel 6a",
"model": "Pixel 6a",
"is_active": true,
"last_seen_at": "2026-04-20T14:28:00Z",
"online": true
}
]
}
Gateway API
Endpoints called by the Signalhouse Android app to register itself and deliver messages. Base path: /api/v1/sms/gateway
Register a new Android gateway device. Called by the app after QR code scan. Authenticates with the scanned API key.
Request body
{
"device_name": "Pixel 6a",
"api_key": "sh_live_xxxxxxxxxxxx"
}
Response 201
{
"id": "gw_abc123",
"device_name": "Pixel 6a",
"registered_at": "2026-04-20T14:30:00Z"
}
Deliver an inbound SMS from the Android device to the API. Called by the app when an SMS arrives on the phone.
Request body
{
"from": "+15551234567",
"message": "Inbound SMS content",
"timestamp": "2026-04-20T14:30:00Z",
"device_id": "gw_abc123"
}
Response 200
{
"status": "received",
"message_id": "msg_xyz789"
}
Update delivery status for a sent SMS. Called by the app when the carrier confirms delivery.
Request body
{
"sms_id": "msg_abc123",
"status": "delivered",
"delivered_at": "2026-04-20T14:30:07Z"
}
Response 200
{ "success": true }
WebSocket
The gateway device holds a persistent WebSocket connection to receive outbound SMS commands in real time. Connection is maintained by the Android app — no polling required.
Connection URL
Connect using your device ID and API key:
Open a persistent WebSocket connection for a gateway device. The server pushes send_sms commands when outbound SMS are queued.
Auth via initial message (alternative to query param)
{
"type": "auth",
"api_key": "sh_live_xxxxxxxxxxxx"
}
Server → client: send_sms command
{
"type": "send_sms",
"sms_id": "msg_abc123",
"to": "+15551234567",
"message": "Hello from Signalhouse",
"timestamp": "2026-04-20T14:30:00Z"
}
Client → server: delivery confirmation
{
"type": "sms_status",
"sms_id": "msg_abc123",
"status": "delivered"
}
Error Codes
All errors return a JSON body with an error field describing the problem.
Error response shape
{
"error": "Human-readable error message"
}
| Status | Meaning |
|---|---|
| 200 OK | Request succeeded. |
| 201 Created | Resource was created successfully. |
| 400 Bad Request | Missing or invalid request parameters. Check the error field. |
| 401 Unauthorized | Missing, expired, or invalid API key / JWT token. |
| 403 Forbidden | Valid credentials but insufficient permissions for this resource. |
| 404 Not Found | The requested resource does not exist. |
| 409 Conflict | Resource already exists (e.g. duplicate email on registration). |
| 422 Unprocessable | Request body failed validation. Check required fields. |
| 429 Too Many Requests | Rate limit exceeded. Retry after the indicated delay. |
| 500 Internal Server Error | Unexpected server error. If this persists, file a report. |