📡 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.

Base URL
http://signalhouse.live/api/v1

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.

Invite code: During beta, account registration requires an invite code. Contact us to request access.

🔐 Auth API

Endpoints for account creation, login, and token management. Base path: /api/v1/auth

POST /api/v1/auth/token

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
}
POST /api/v1/auth/register

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"
}
GET /api/v1/auth/user

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"
}
POST /api/v1/auth/refresh

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

POST /api/v1/sms/send

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"
}
GET /api/v1/sms/messages

List SMS messages for the authenticated account. Supports pagination and filtering.

Query parameters
NameTypeDescription
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
}
GET /api/v1/sms/messages/{id}

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 /api/v1/sms/send/status/{id}

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"
}
GET /api/v1/sms/stats

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 }
}
POST /api/v1/sms/archive/{id}

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
}
GET /api/v1/sms/qr-code

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

POST /api/v1/device/register

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"
}
GET /api/v1/device/list

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

Note: These endpoints are called by the Android app, not by your own code. They are documented here for transparency and custom integrations.
POST /api/v1/sms/gateway/devices

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"
}
POST /api/v1/sms/gateway/devices/{device_id}/receive-sms

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"
}
PATCH /api/v1/sms/gateway/devices/{device_id}/sms-status

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:

WS /ws/device/{device_id}?api_key=sh_live_xxxx

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.