Download OpenAPI specification:
API documentation for SimRelay.
SimRelay supports multiple authentication methods:
The SimRelay Chrome Extension uses OAuth2 Authorization Code flow with PKCE for secure authentication. Simply install the extension and sign in with your SimRelay account.
Token Expiration:
For server-to-server integrations, use API keys. Create them at Settings > API Keys.
Authorization: Bearer sk_live_xxxxxxxxxxxxx
For mobile apps or simple integrations, use the /api/auth/login endpoint to obtain a token.
Queues an SMS for delivery and returns immediately with a message id.
The from number must be one you control: either a BYOS number (a SIM in
your own Android device) or a customer-owned SIM we host for you at our
SMS provider. Sending must also be switched on for that number under
Numbers in the dashboard. Numbers from the shared pool can never send.
Delivery is attempted at most once. If the message cannot be sent before
timeout_seconds elapses it is marked expired and any credits reserved for
it are returned. Credits are only ever consumed by a message the network
accepted.
Supply Idempotency-Key to make retries safe: repeating a request with the
same key and the same body returns the original message instead of sending a
second one.
| Idempotency-Key | string <= 255 characters Client-generated key making this request safe to retry. Unique per organization. |
| from required | string One of your sending-enabled numbers, in E.164 format. |
| to required | string Destination number in E.164 format. |
| text required | string <= 1530 characters Message body. Billing is per segment: 160 characters per segment for GSM-7 text (153 when the message spans several segments), or 70/67 when the body contains characters outside the GSM-7 alphabet and has to be sent as UCS-2. |
| timeout_seconds | integer [ 10 .. 3600 ] Default: 120 How long to keep trying before the message fails. Messages from a BYOS number need at least 60 seconds, because the device has to pick the message up first. |
| webhook_url | string <uri> HTTPS URL to receive status callbacks for this message. Omit it and no callbacks are sent. Must resolve to a public address. |
| webhook_secret | string >= 8 characters Signs this message's callbacks. The |
| client_reference | string <= 255 characters Your own identifier, echoed back on the message and its callbacks. |
{- "from": "+4915112345678",
- "to": "+4915198765432",
- "text": "Your code is 123456",
- "timeout_seconds": 120,
- "webhook_secret": "stringst",
- "client_reference": "string"
}{- "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
- "status": "queued",
- "from": "string",
- "to": "string",
- "text": "string",
- "encoding": "gsm7",
- "segments": 0,
- "credits_charged": 0,
- "client_reference": "string",
- "error_code": "string",
- "error_message": "string",
- "expires_at": "2019-08-24T14:15:22Z",
- "sent_at": "2019-08-24T14:15:22Z",
- "failed_at": "2019-08-24T14:15:22Z",
- "created_at": "2019-08-24T14:15:22Z"
}| uuid required | string <uuid> |
{- "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
- "status": "queued",
- "from": "string",
- "to": "string",
- "text": "string",
- "encoding": "gsm7",
- "segments": 0,
- "credits_charged": 0,
- "client_reference": "string",
- "error_code": "string",
- "error_message": "string",
- "expires_at": "2019-08-24T14:15:22Z",
- "sent_at": "2019-08-24T14:15:22Z",
- "failed_at": "2019-08-24T14:15:22Z",
- "created_at": "2019-08-24T14:15:22Z"
}Checks database, cache, and queue system. Returns detailed status for each service.
{- "status": "ok",
- "timestamp": "2019-08-24T14:15:22Z",
- "checks": {
- "database": {
- "status": "ok",
- "message": "Database connection successful",
- "error": "string"
}, - "cache": {
- "status": "ok",
- "message": "Database connection successful",
- "error": "string"
}, - "queue": {
- "status": "ok",
- "message": "Database connection successful",
- "error": "string"
}
}
}| hosted_sim required | integer |
| per_page | integer [ 1 .. 100 ] Number of messages per page (default 50) |
| limit | integer [ 1 .. 100 ] Alternative to per_page for compatibility (default 50) |
| page | integer >= 1 Page number for pagination (default 1) |
Public endpoint. Returns an inactive API key that must be claimed by a
human via the claim_url before it can be used. Rate-limited per IP
(configurable via site_settings.agent_registration_rate_limit_per_hour,
default 5/hour). Unclaimed registrations expire after 72 hours.
If a callback_url is provided, a one-shot webhook_secret is also
returned — store it immediately. All future callbacks to that URL are
signed with HMAC-SHA256: X-Webhook-Signature, X-Webhook-Signature-256
and X-Webhook-Timestamp headers (5-minute replay window).
| name required | string <= 255 characters |
| description | string <= 2000 characters |
| callback_url | string <uri> HTTPS URL that receives signed event callbacks. |
object |
{- "name": "MyAgent v1.0",
- "description": "string",
- "metadata": { }
}{- "api_key": "sk_aBcDeFgH...",
- "verification_code": "A7K3M9X2",
- "expires_at": "2019-08-24T14:15:22Z",
- "webhook_secret": "string",
- "message": "string"
}Authenticated via the agent's API key (Bearer sk_…).
{- "status": "unclaimed",
- "name": "string",
- "claim_url": "string",
- "expires_at": "2019-08-24T14:15:22Z",
- "organization_id": 0,
- "claimed_at": "2019-08-24T14:15:22Z"
}Creates a pending NumberRequest the human owner must approve. Deduped per (organization, type, country_code) — a second pending request with the same triple returns 409.
| type required | string Enum: "shared" "exclusive" |
| country_code | string = 2 characters Optional. ISO 3166-1 alpha-2. |
| hosted_sim_id | integer Optional. Specific SIM to request rather than picking from pool. |
| metadata | object |
{- "type": "shared",
- "country_code": "st",
- "hosted_sim_id": 0,
- "metadata": { }
}When numbers become available, the agent's registered callback_url
receives a signed waitlist.number_available event. Idempotent —
re-posting for the same country returns the existing entry.
| country_code required | string = 2 characters |
{- "country_code": "US"
}Returns user information and authentication context (Sanctum token or API key)
{- "user": {
- "id": 0,
- "name": "string",
- "email": "string",
- "email_verified_at": "2019-08-24T14:15:22Z",
- "created_at": "2019-08-24T14:15:22Z",
- "updated_at": "2019-08-24T14:15:22Z"
}, - "auth_type": "api_key",
- "organization": {
- "id": 1,
- "name": "Example Corp",
- "created_at": "2019-08-24T14:15:22Z",
- "updated_at": "2019-08-24T14:15:22Z"
}, - "api_key": {
- "id": 0,
- "name": "string",
- "permissions": [
- "string"
]
}
}| email required | string <email> |
| password required | string <password> |
{- "email": "user@example.com",
- "password": "pa$$word"
}{- "2fa_required": true,
- "tmp_token": "string"
}| tmp_token required | string |
| code required | string TOTP code from authenticator app |
{- "tmp_token": "string",
- "code": "string"
}{- "token": "string",
- "user": {
- "id": 0,
- "name": "string",
- "email": "string",
- "email_verified_at": "2019-08-24T14:15:22Z",
- "created_at": "2019-08-24T14:15:22Z",
- "updated_at": "2019-08-24T14:15:22Z"
}
}Retrieve all SIMs that the authenticated user has access to through their team and organization memberships, or for API keys, all SIMs in the organization.
{- "data": [
- {
- "id": 1,
- "number": "+1234567890",
- "provider": "seven",
- "status": "active",
- "type": "shared",
- "shared_max_orgs": 5,
- "messages_received_count": 123,
- "organizations": [
- {
- "id": 1,
- "name": "Example Corp",
- "alias": "Sales Team SIM",
- "team_id": 1
}
], - "teams": [
- {
- "id": 1,
- "name": "Sales Team"
}
], - "created_at": "2019-08-24T14:15:22Z",
- "updated_at": "2019-08-24T14:15:22Z"
}
]
}Returns all API keys belonging to the authenticated user's organization. Only accessible via Sanctum authentication, not API keys.
{- "data": [
- {
- "id": 1,
- "name": "Production API Key",
- "key_prefix": "sk_live_1234",
- "permissions": [
- "numbers.read",
- "messages.read"
], - "last_used_at": "2024-01-15T10:30:00Z",
- "created_at": "2024-01-01T00:00:00Z",
- "updated_at": "2024-01-01T00:00:00Z"
}
]
}Creates a new API key for the authenticated user's organization. Only accessible via Sanctum authentication.
| name required | string Display name for the API key |
| permissions required | Array of strings Items Enum: "numbers.read" "numbers.lock" "messages.read" "webhooks.receive" Array of permission strings |
{- "name": "Production API Key",
- "permissions": [
- "numbers.read",
- "messages.read"
]
}{- "api_key": {
- "id": 1,
- "name": "Production API Key",
- "key_prefix": "sk_live_1234",
- "permissions": [
- "numbers.read",
- "messages.read"
], - "last_used_at": "2024-01-15T10:30:00Z",
- "created_at": "2024-01-01T00:00:00Z",
- "updated_at": "2024-01-01T00:00:00Z"
}, - "plain_text_key": "sk_live_1234567890abcdef"
}Returns details of a specific API key. Only accessible via Sanctum authentication.
| api_key required | integer API key ID |
{- "id": 1,
- "name": "Production API Key",
- "key_prefix": "sk_live_1234",
- "permissions": [
- "numbers.read",
- "messages.read"
], - "last_used_at": "2024-01-15T10:30:00Z",
- "created_at": "2024-01-01T00:00:00Z",
- "updated_at": "2024-01-01T00:00:00Z"
}Updates an existing API key's name or permissions. Only accessible via Sanctum authentication.
| api_key required | integer API key ID |
| name | string Display name for the API key |
| permissions | Array of strings Items Enum: "numbers.read" "numbers.lock" "messages.read" "webhooks.receive" Array of permission strings |
{- "name": "string",
- "permissions": [
- "numbers.read"
]
}{- "id": 1,
- "name": "Production API Key",
- "key_prefix": "sk_live_1234",
- "permissions": [
- "numbers.read",
- "messages.read"
], - "last_used_at": "2024-01-15T10:30:00Z",
- "created_at": "2024-01-01T00:00:00Z",
- "updated_at": "2024-01-01T00:00:00Z"
}Regenerates the secret key for an existing API key. The old key will immediately stop working. Only accessible via Sanctum authentication.
| api_key required | integer API key ID |
{- "api_key": {
- "id": 1,
- "name": "Production API Key",
- "key_prefix": "sk_live_1234",
- "permissions": [
- "numbers.read",
- "messages.read"
], - "last_used_at": "2024-01-15T10:30:00Z",
- "created_at": "2024-01-01T00:00:00Z",
- "updated_at": "2024-01-01T00:00:00Z"
}, - "plain_text_key": "sk_live_9876543210fedcba"
}Returns all webhook integrations for the authenticated user's organization.
{- "data": [
- {
- "id": 1,
- "name": "My Webhook",
- "has_secret": true,
- "headers": {
- "property1": "string",
- "property2": "string"
}, - "timeout_seconds": 30,
- "retry_count": 3,
- "is_active": true,
- "mappings": [
- {
- "id": 1,
- "team_id": 1,
- "team_name": "Sales Team",
- "forwarding_mode": "always",
- "is_active": true
}
], - "created_at": "2019-08-24T14:15:22Z",
- "updated_at": "2019-08-24T14:15:22Z"
}
], - "organization": {
- "id": 0,
- "name": "string"
}
}Creates a new webhook integration. A secure secret is auto-generated and returned (only shown once).
| name required | string <= 255 characters Display name for the webhook |
| url required | string <uri> <= 2048 characters URL to send webhook payloads to |
object Custom headers to include in webhook requests | |
| timeout_seconds | integer [ 1 .. 120 ] Request timeout in seconds (default 30) |
| retry_count | integer [ 0 .. 10 ] Number of retry attempts on failure (default 3) |
{- "name": "My Webhook",
- "headers": {
- "X-Custom-Header": "value"
}, - "timeout_seconds": 30,
- "retry_count": 3
}{- "message": "Webhook integration created successfully.",
- "data": {
- "id": 1,
- "name": "My Webhook",
- "has_secret": true,
- "headers": {
- "property1": "string",
- "property2": "string"
}, - "timeout_seconds": 30,
- "retry_count": 3,
- "is_active": true,
- "mappings": [
- {
- "id": 1,
- "team_id": 1,
- "team_name": "Sales Team",
- "forwarding_mode": "always",
- "is_active": true
}
], - "created_at": "2019-08-24T14:15:22Z",
- "updated_at": "2019-08-24T14:15:22Z",
- "secret": "abc123..."
}
}Returns details of a specific webhook integration including its mappings.
| webhook_integration required | integer Webhook integration ID |
{- "data": {
- "id": 1,
- "name": "My Webhook",
- "has_secret": true,
- "headers": {
- "property1": "string",
- "property2": "string"
}, - "timeout_seconds": 30,
- "retry_count": 3,
- "is_active": true,
- "mappings": [
- {
- "id": 1,
- "team_id": 1,
- "team_name": "Sales Team",
- "forwarding_mode": "always",
- "is_active": true
}
], - "created_at": "2019-08-24T14:15:22Z",
- "updated_at": "2019-08-24T14:15:22Z"
}
}Updates an existing webhook integration.
| webhook_integration required | integer Webhook integration ID |
| name required | string <= 255 characters |
| url required | string <uri> <= 2048 characters |
object | |
| timeout_seconds | integer [ 1 .. 120 ] |
| retry_count | integer [ 0 .. 10 ] |
| is_active | boolean Enable or disable the webhook |
{- "name": "string",
- "headers": {
- "property1": "string",
- "property2": "string"
}, - "timeout_seconds": 1,
- "retry_count": 10,
- "is_active": true
}{- "message": "Webhook integration updated successfully.",
- "data": {
- "id": 1,
- "name": "My Webhook",
- "has_secret": true,
- "headers": {
- "property1": "string",
- "property2": "string"
}, - "timeout_seconds": 30,
- "retry_count": 3,
- "is_active": true,
- "mappings": [
- {
- "id": 1,
- "team_id": 1,
- "team_name": "Sales Team",
- "forwarding_mode": "always",
- "is_active": true
}
], - "created_at": "2019-08-24T14:15:22Z",
- "updated_at": "2019-08-24T14:15:22Z"
}
}Deletes a webhook integration and all its mappings.
| webhook_integration required | integer Webhook integration ID |
{- "message": "Webhook integration deleted successfully."
}Regenerates the webhook secret. The old secret will immediately stop working.
| webhook_integration required | integer Webhook integration ID |
{- "message": "Webhook secret regenerated successfully.",
- "data": {
- "id": 0,
- "name": "string",
- "secret": "string",
- "updated_at": "2019-08-24T14:15:22Z"
}
}Sends a test payload to the webhook URL to verify connectivity.
| webhook_integration required | integer Webhook integration ID |
{- "success": true,
- "http_status": 200,
- "response_body": "string",
- "response_time_ms": 150
}Returns all mappings for a specific webhook integration.
| webhook_integration required | integer Webhook integration ID |
{- "data": [
- {
- "id": 1,
- "team_id": 1,
- "team_name": "Sales Team",
- "forwarding_mode": "always",
- "is_active": true,
- "created_at": "2019-08-24T14:15:22Z",
- "updated_at": "2019-08-24T14:15:22Z"
}
], - "webhook_integration": {
- "id": 0,
- "name": "string"
}
}Creates a new mapping to associate a team with a webhook integration.
| webhook_integration required | integer Webhook integration ID |
| team_id required | integer Team ID to associate with this webhook |
| forwarding_mode required | string Enum: "always" "lock_required" When to forward messages:
|
{- "team_id": 1,
- "forwarding_mode": "always"
}{- "message": "Webhook mapping created successfully.",
- "data": {
- "id": 1,
- "team_id": 1,
- "team_name": "Sales Team",
- "forwarding_mode": "always",
- "is_active": true,
- "created_at": "2019-08-24T14:15:22Z",
- "updated_at": "2019-08-24T14:15:22Z"
}
}Returns details of a specific webhook mapping.
| webhook_integration required | integer Webhook integration ID |
| webhook_mapping required | integer Webhook mapping ID |
{- "data": {
- "id": 1,
- "team_id": 1,
- "team_name": "Sales Team",
- "forwarding_mode": "always",
- "is_active": true,
- "created_at": "2019-08-24T14:15:22Z",
- "updated_at": "2019-08-24T14:15:22Z"
}
}Updates an existing webhook mapping.
| webhook_integration required | integer Webhook integration ID |
| webhook_mapping required | integer Webhook mapping ID |
| forwarding_mode required | string Enum: "always" "lock_required" |
| is_active | boolean Enable or disable the mapping |
{- "forwarding_mode": "always",
- "is_active": true
}{- "message": "Webhook mapping updated successfully.",
- "data": {
- "id": 1,
- "team_id": 1,
- "team_name": "Sales Team",
- "forwarding_mode": "always",
- "is_active": true,
- "created_at": "2019-08-24T14:15:22Z",
- "updated_at": "2019-08-24T14:15:22Z"
}
}Deletes a webhook mapping.
| webhook_integration required | integer Webhook integration ID |
| webhook_mapping required | integer Webhook mapping ID |
{- "message": "Webhook mapping deleted successfully."
}Returns the WebSocket server URL, port, authentication method, and other necessary details for clients to connect to the configured broadcaster (e.g., Reverb).
{- "broadcaster": "reverb",
- "key": "your_reverb_app_key",
- "ws_url": "ws://localhost:8080",
- "ws_host": "localhost",
- "ws_port": 8080,
- "ws_scheme": "http",
- "force_tls": false,
- "auth_method": "bearer_token",
- "auth_header": "Authorization: Bearer <token>",
}Returns a paginated list of BYOS devices for the authenticated user's organizations. Supports filtering by device status.
| status | string Enum: "active" "disabled" "revoked" "pending_verification" Filter devices by status |
| per_page | integer [ 1 .. 100 ] Default: 15 Number of devices per page |
{- "data": [
- {
- "id": 1,
- "install_id": "550e8400-e29b-41d4-a716-446655440000",
- "phone_number": "+14155551234",
- "device_name": "Samsung Galaxy S24",
- "status": "active",
- "is_online": true,
- "created_at": "2019-08-24T14:15:22Z",
- "twilio_number": "+12025551234",
- "sim_cards": [
- {
- "id": 1,
- "phone_number": "+14155551234",
- "sim_slot": 1,
- "status": "active",
- "signal_strength": 75,
- "is_active": true,
- "hosted_sim_id": 10,
- "created_at": "2019-08-24T14:15:22Z",
- "updated_at": "2019-08-24T14:15:22Z"
}
], - "battery_level": 85,
- "last_seen_at": "2019-08-24T14:15:22Z"
}
], - "meta": {
- "path": "string",
- "per_page": 0
}, - "links": {
- "first": "string",
- "last": "string",
- "prev": "string",
- "next": "string"
}
}Registers a new device with SIM slot declarations. Each SIM slot receives a verification code that must be sent via SMS to the Twilio verification number to prove SIM ownership.
Flow:
sim_slots array (e.g. [1] or [1, 2] for dual-SIM)pending_verification status with pending SIM cardsverification_code per SIM and the twilio_number to SMS it totwilio_numberactive on first SIM verification| install_id required | string <uuid> App-generated UUID, persisted in app local storage on first launch. Not a hardware identifier. |
| sim_slots required | Array of integers [ 1 .. 2 ] items [ items [ 1 .. 10 ] ] SIM slot numbers to register. Each slot will receive a verification code. |
| device_model required | string <= 100 characters Android device model |
| app_version required | string <= 50 characters SimRelay app version |
| device_name | string <= 255 characters User-friendly device name (optional) |
{- "install_id": "550e8400-e29b-41d4-a716-446655440000",
- "sim_slots": [
- 1
], - "device_model": "SM-S921B",
- "app_version": "1.2.3",
- "device_name": "Work Phone"
}{- "id": 1,
- "install_id": "550e8400-e29b-41d4-a716-446655440000",
- "device_name": "Work Phone",
- "device_model": "SM-S921B",
- "app_version": "1.2.3",
- "status": "pending_verification",
- "twilio_number": "+12025551234",
- "sim_cards": [
- {
- "id": 1,
- "sim_slot": 1,
- "phone_number": null,
- "status": "pending",
- "verification_code": "SR-A2B3",
- "twilio_number": "+12025551234"
}
], - "created_at": "2019-08-24T14:15:22Z"
}Returns full details for a specific BYOS device including organization info and message count.
| device required | integer BYOS device ID |
{- "id": 1,
- "install_id": "550e8400-e29b-41d4-a716-446655440000",
- "phone_number": "+14155551234",
- "device_name": "Samsung Galaxy S24",
- "status": "active",
- "is_online": true,
- "created_at": "2019-08-24T14:15:22Z",
- "twilio_number": "+12025551234",
- "sim_cards": [
- {
- "id": 1,
- "phone_number": "+14155551234",
- "sim_slot": 1,
- "status": "active",
- "signal_strength": 75,
- "is_active": true,
- "hosted_sim_id": 10,
- "created_at": "2019-08-24T14:15:22Z",
- "updated_at": "2019-08-24T14:15:22Z"
}
], - "device_model": "SM-S921B",
- "app_version": "1.2.3",
- "config_version": 1,
- "battery_level": 85,
- "last_seen_at": "2019-08-24T14:15:22Z",
- "messages_received_count": 42,
- "organization": {
- "id": 1,
- "name": "Example Corp"
}, - "updated_at": "2019-08-24T14:15:22Z"
}Updates device name, phone number, or app version. Phone number changes are synced to the HostedSim record.
| device required | integer BYOS device ID |
| device_name | string <= 255 characters |
| app_version | string <= 50 characters |
| fcm_token | string or null <= 255 characters Firebase Cloud Messaging registration token for this device. Use this endpoint when the token rotates between heartbeats; otherwise send it on the heartbeat. |
{- "device_name": "Updated Device Name",
- "app_version": "1.3.0",
- "fcm_token": "string"
}{- "id": 1,
- "install_id": "550e8400-e29b-41d4-a716-446655440000",
- "phone_number": "+14155551234",
- "device_name": "Samsung Galaxy S24",
- "status": "active",
- "is_online": true,
- "created_at": "2019-08-24T14:15:22Z",
- "twilio_number": "+12025551234",
- "sim_cards": [
- {
- "id": 1,
- "phone_number": "+14155551234",
- "sim_slot": 1,
- "status": "active",
- "signal_strength": 75,
- "is_active": true,
- "hosted_sim_id": 10,
- "created_at": "2019-08-24T14:15:22Z",
- "updated_at": "2019-08-24T14:15:22Z"
}
], - "device_model": "SM-S921B",
- "app_version": "1.2.3",
- "config_version": 1,
- "battery_level": 85,
- "last_seen_at": "2019-08-24T14:15:22Z",
- "messages_received_count": 42,
- "organization": {
- "id": 1,
- "name": "Example Corp"
}, - "updated_at": "2019-08-24T14:15:22Z"
}Temporarily disables a device. Device status becomes 'disabled' and will not receive messages.
| device required | integer BYOS device ID |
{- "id": 1,
- "install_id": "550e8400-e29b-41d4-a716-446655440000",
- "phone_number": "+14155551234",
- "device_name": "Samsung Galaxy S24",
- "status": "active",
- "is_online": true,
- "created_at": "2019-08-24T14:15:22Z",
- "twilio_number": "+12025551234",
- "sim_cards": [
- {
- "id": 1,
- "phone_number": "+14155551234",
- "sim_slot": 1,
- "status": "active",
- "signal_strength": 75,
- "is_active": true,
- "hosted_sim_id": 10,
- "created_at": "2019-08-24T14:15:22Z",
- "updated_at": "2019-08-24T14:15:22Z"
}
], - "device_model": "SM-S921B",
- "app_version": "1.2.3",
- "config_version": 1,
- "battery_level": 85,
- "last_seen_at": "2019-08-24T14:15:22Z",
- "messages_received_count": 42,
- "organization": {
- "id": 1,
- "name": "Example Corp"
}, - "updated_at": "2019-08-24T14:15:22Z"
}Re-enables a disabled device. Device status becomes 'active' and will resume receiving messages.
| device required | integer BYOS device ID |
{- "id": 1,
- "install_id": "550e8400-e29b-41d4-a716-446655440000",
- "phone_number": "+14155551234",
- "device_name": "Samsung Galaxy S24",
- "status": "active",
- "is_online": true,
- "created_at": "2019-08-24T14:15:22Z",
- "twilio_number": "+12025551234",
- "sim_cards": [
- {
- "id": 1,
- "phone_number": "+14155551234",
- "sim_slot": 1,
- "status": "active",
- "signal_strength": 75,
- "is_active": true,
- "hosted_sim_id": 10,
- "created_at": "2019-08-24T14:15:22Z",
- "updated_at": "2019-08-24T14:15:22Z"
}
], - "device_model": "SM-S921B",
- "app_version": "1.2.3",
- "config_version": 1,
- "battery_level": 85,
- "last_seen_at": "2019-08-24T14:15:22Z",
- "messages_received_count": 42,
- "organization": {
- "id": 1,
- "name": "Example Corp"
}, - "updated_at": "2019-08-24T14:15:22Z"
}Returns server configuration for the device including intervals, limits, features, and current plan details.
| device required | integer BYOS device ID |
{- "device": {
- "id": 1,
- "phone_number": "+14155551234",
- "status": "active"
}, - "intervals": {
- "heartbeat_seconds": 300,
- "config_refresh_seconds": 3600,
- "retry_backoff_seconds": [
- 10,
- 30,
- 60,
- 300
]
}, - "limits": {
- "max_devices": 1,
- "current_devices": 1,
- "monthly_messages": 100,
- "current_month_messages": 42,
- "remaining_messages": 58
}, - "features": {
- "compression": false,
- "priority_support": false,
- "debug_mode": false
}, - "metadata": {
- "config_version": 1,
- "server_time": "2019-08-24T14:15:22Z",
- "plan_name": "Pro"
}
}Updates device health metrics and last_seen timestamp. Marks device as online and broadcasts status change event if device was offline.
The server uses adaptive timeouts based on Doze Mode state:
Email alerts are deferred until two consecutive intervals are missed.
This is also the preferred place to keep the device's FCM registration token
current: send fcm_token on every heartbeat. Use PATCH /api/byos/devices/{device}
when the token rotates between heartbeats.
| device required | integer BYOS device ID |
| battery_level | integer [ 0 .. 100 ] Current battery percentage (optional) |
| signal_strength | integer [ 0 .. 100 ] Current signal strength percentage (optional, backward compat - updates first SIM card) |
| is_in_doze_mode | boolean Default: false Whether the Android device is currently in Doze Mode. Server uses a longer timeout (960s vs 360s) for doze devices. |
Array of objects Per-SIM signal strength updates (optional) | |
| fcm_token | string <= 255 characters Firebase Cloud Messaging registration token for this device, used to wake it when a message is queued for sending. Safe to send on every heartbeat: the server compares it and only writes when it changed. Omitting it, or sending an empty value, never clears a stored token. |
{- "battery_level": 85,
- "signal_strength": 75,
- "is_in_doze_mode": false,
- "sim_cards": [
- {
- "phone_number": "+4915123456789",
- "signal_strength": 80
}
], - "fcm_token": "string"
}{- "success": true,
- "next_heartbeat_in": 300,
- "server_time": "2019-08-24T14:15:22Z"
}Submits an SMS message received by the BYOS device. Message is validated and queued for async processing.
| device required | integer BYOS device ID |
| from required | string <= 20 characters Sender phone number |
| body required | string <= 1600 characters SMS message content |
| message_id required | string <uuid> Unique message identifier from device |
| received_at | string <date-time> Timestamp when device received message (optional) |
| sim_slot | integer [ 1 .. 2 ] SIM slot number for dual-SIM devices (optional) |
{- "from": "+19876543210",
- "body": "Your verification code is 123456",
- "message_id": "550e8400-e29b-41d4-a716-446655440000",
- "received_at": "2019-08-24T14:15:22Z",
- "sim_slot": 1
}{- "message": "SMS received and queued for processing",
- "message_id": "550e8400-e29b-41d4-a716-446655440000"
}Claims the messages waiting to be sent from this device's SIMs and returns them.
This call mutates state. Returning a message is the lease — it is handed
out exactly once and a later call will not return it again. It is therefore not
a safe read to retry blindly: if the response is lost in transit, those messages
stay leased and end up failing as device_no_ack when the lease expires. That is
deliberate. Re-offering a message could put the same SMS on the recipient's
handset twice, which is worse than dropping it and cannot be detected downstream.
The device is normally woken by a silent FCM data push
({"type": "outbox.pending", ...}), but the push is only a latency optimisation:
it can be dropped or delayed by Doze. Poll this endpoint every next_poll_in
seconds regardless.
Never transmit a message after its expires_at — the server has given up on it
by then and has already refunded the customer. Compare against server_time
rather than the device clock.
| device required | integer BYOS device ID |
{- "messages": [
- {
- "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
- "to": "+4915198765432",
- "from": "+4915112345678",
- "text": "string",
- "sim_slot": 0,
- "sim_phone_number": "string",
- "lease_token": "d990dcdf-9d21-4e38-9863-7b208f605067",
- "lease_expires_at": "2019-08-24T14:15:22Z",
- "expires_at": "2019-08-24T14:15:22Z"
}
], - "server_time": "2019-08-24T14:15:22Z",
- "next_poll_in": 20
}Reports what happened to a message this device leased.
Report on the sentIntent outcome — whether the carrier accepted the message —
not the delivery report. Delivery reports are carrier-dependent, often never
arrive, and must never gate this call. For a multipart message the result is
sent only when every part returned RESULT_OK.
| device required | integer BYOS device ID |
| uuid required | string <uuid> The message id from the lease |
| lease_token required | string <uuid> The token issued with this message's lease. |
| result required | string Enum: "sent" "failed" |
| error_code | string or null <= 100 characters Android result code, passed through verbatim — e.g.
|
| error_message | string or null <= 500 characters |
| segments | integer or null [ 1 .. 255 ] Parts actually sent. Used to reconcile against the estimate billed at accept time. |
{- "lease_token": "d990dcdf-9d21-4e38-9863-7b208f605067",
- "result": "sent",
- "error_code": "string",
- "error_message": "string",
- "segments": 1
}{- "status": "sent"
}Adds a new SIM card slot to an existing device, pending SMS verification. Returns a verification code that must be sent via SMS to the Twilio number.
| device required | integer BYOS device ID |
| sim_slot required | integer [ 1 .. 10 ] Physical SIM slot number on the device |
{- "sim_slot": 2
}{- "id": 1,
- "sim_slot": 1,
- "phone_number": null,
- "status": "pending",
- "verification_code": "SR-A2B3",
- "twilio_number": "+12025551234"
}Updates SIM card metadata like phone number or slot number. Phone number changes sync to the associated HostedSim.
| device required | integer BYOS device ID |
| simCard required | integer SIM card ID |
| phone_number | string Phone number in E.164 format |
| sim_slot | integer or null [ 1 .. 10 ] |
{- "phone_number": "+14155559999",
- "sim_slot": 2
}{- "id": 1,
- "phone_number": "+14155551234",
- "sim_slot": 1,
- "status": "active",
- "signal_strength": 75,
- "is_active": true,
- "hosted_sim_id": 10,
- "created_at": "2019-08-24T14:15:22Z",
- "updated_at": "2019-08-24T14:15:22Z"
}Generates a new verification code for a pending or inactive SIM card. Use this when the original code has expired (codes expire after 5 minutes). Cannot be used on already-active SIM cards.
| device required | integer BYOS device ID |
| simCard required | integer SIM card ID |
{- "id": 1,
- "sim_slot": 1,
- "phone_number": null,
- "status": "pending",
- "verification_code": "SR-A2B3",
- "twilio_number": "+12025551234"
}