API Reference
SMS Development API
Complete API reference for integrating with sms-dev local development environment.
Overview
The sms-dev API provides both Relay-compatible endpoints for seamless integration and simulator-specific endpoints for development features.
Relay-Compatible Endpoints
These endpoints match the production Relay API exactly, allowing you to switch between development and production seamlessly.
- • Same request/response format
- • Compatible with Relay SDKs
- • Easy production migration
Simulator-Only Endpoints
These endpoints are specific to sms-dev and provide development-focused features not available in production.
- • Webhook configuration
- • Message simulation
- • Development status
Base URL
Development Environment
http://localhost:4001By default, sms-dev runs on port 4001. You can customize this with the --api-port flag.
SDK Configuration
JavaScript/TypeScript:
import { RelayClient } from '@relay-works/sdk-js'
const relay = new RelayClient({
baseUrl: 'http://localhost:4001' // Point to sms-dev
})Alternative (fetch API):
// Direct API calls work too
const baseUrl = 'http://localhost:4001'Authentication
No Authentication Required
sms-dev does not require API keys or authentication. This simplifies development but should never be used in production.
Example Request
curl -X POST http://localhost:4001/v1/messages \
-H "Content-Type: application/json" \
-d '{
"to": "+1234567890",
"body": "Hello from sms-dev!"
}'Relay-Compatible Endpoints
These endpoints are fully compatible with the production Relay API and can be used with existing SDKs and code.
Send Message
Relay-Compatible/v1/messagesSend an SMS message. In sms-dev, messages are simulated and appear instantly in the Virtual Phone UI.
Request Body
{
"to": "+1234567890", // Required: Recipient phone number
"from": "+1987654321", // Optional: Sender number
"body": "Hello world!" // Required: Message content (input field)
}Response
{
"id": "msg_abc123",
"to": "+1234567890",
"from": "+1987654321",
"message": "Hello world!", // Response uses 'message' field
"status": "queued",
"created_at": "2024-01-15T10:30:00Z",
"cost": 0.01
}Example
// Using fetch
const response = await fetch('http://localhost:4001/v1/messages', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
to: '+1234567890',
body: 'Hello from my app!'
})
})
const message = await response.json()
console.log('Message ID:', message.id)Send Template Message
Relay-Compatible/v1/messages/send-templateSend an SMS using a pre-defined template with variable interpolation. Production requires templates for Starter tier users.
Production Difference
In production, Starter tier users must use templates (no raw message sending). SMS-Dev allows both for testing flexibility.
Request Body
{
"template": "otp-verification", // Required: Template ID
"to": "+1234567890", // Required: Recipient
"from": "+15551234567", // Optional: Sender
"data": { // Required: Template variables
"code": "123456",
"expiry": "10"
},
"variant": "default", // Optional: Template variant
"metadata": {} // Optional: Custom metadata
}Response
{
"id": "msg_abc123",
"to": "+1234567890",
"from": "+15551234567",
"message": "Your verification code is 123456. It expires in 10 minutes.",
"status": "queued",
"template": {
"template_id": "otp-verification",
"variant": null,
"resolved_from": "mock",
"variables_used": ["code", "expiry"],
"variables_missing": [],
"character_count": 60,
"sms_segments": 1
},
"created_at": "2024-01-15T10:30:00Z",
"cost": 0.01
}Example
// Send a template-based message
const response = await fetch('http://localhost:4001/v1/messages/send-template', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
template: 'otp-verification',
to: '+1234567890',
data: {
code: '123456',
expiry: '10'
}
})
})
const message = await response.json()
console.log('Template message sent:', message.id)
console.log('Rendered text:', message.message)Get Message
Relay-Compatible/v1/messages/:idRetrieve details of a specific message by its ID.
Response
{
"id": "msg_abc123",
"to": "+1234567890",
"from": "+1987654321",
"body": "Hello world!", // Internal storage uses 'body'
"status": "delivered",
"created_at": "2024-01-15T10:30:00Z",
"delivered_at": "2024-01-15T10:30:01Z"
}Example
const response = await fetch('http://localhost:4001/v1/messages/sim_msg_abc123')
const message = await response.json()
console.log('Status:', message.status)List Messages
Relay-Compatible/v1/messagesRetrieve a paginated list of messages with optional filtering.
Query Parameters
limitnumberNumber of messages to return (default: 20)offsetnumberNumber of messages to skip (default: 0)phonestringFilter by phone number (to or from)statusstringFilter by message statusResponse
{
"messages": [
{
"id": "msg_abc123",
"to": "+1234567890",
"from": "+1987654321",
"body": "Hello world!",
"status": "delivered",
"created_at": "2024-01-15T10:30:00Z",
"delivered_at": "2024-01-15T10:30:01Z"
}
],
"pagination": {
"limit": 20,
"offset": 0,
"total": 1,
"has_more": false
}
}Example
const response = await fetch('http://localhost:4001/v1/messages?limit=10&phone=1234567890')
const data = await response.json()
console.log(`Found ${data.total} messages`)List Available Templates
Relay-Compatible/v1/messages/templatesRetrieve the list of available templates with their metadata and required variables.
Response
{
"templates": [
{
"id": "otp-verification",
"name": "OTP Verification",
"category": "authentication",
"description": "2FA verification code template",
"variables": ["code", "expiry"],
"campaign_type": "2FA"
},
{
"id": "appointment-reminder",
"name": "Appointment Reminder",
"category": "transactional",
"description": "Appointment reminder notification",
"variables": ["provider", "date", "time"],
"campaign_type": "CUSTOMER_CARE"
}
],
"total": 5,
"note": "These are mock templates for local development..."
}Example
const response = await fetch('http://localhost:4001/v1/messages/templates')
const data = await response.json()
console.log(`Found ${data.total} templates`)
data.templates.forEach(t => console.log(`- ${t.id}: ${t.name}`))Simulator-Only Endpoints
These endpoints are specific to sms-dev and provide development features not available in production.
Development Status
Simulator-Only/v1/dev/statusGet the current status of the sms-dev environment, including server information and statistics.
Development Only
This endpoint is not available in production Relay API and should only be used for development debugging.
Response
{
"service": "sms-dev-api",
"version": "1.0.0",
"status": "running",
"uptime": 3600,
"stats": {
"total_messages": 42,
"total_conversations": 8,
"webhook_deliveries": 15
},
"config": {
"api_port": 4001,
"ui_port": 4000,
"webhook_url": "http://localhost:3000/webhook"
}
}Example
const response = await fetch('http://localhost:4001/v1/dev/status')
const status = await response.json()
console.log(`sms-dev has been running for ${status.uptime} seconds`)Webhook Configuration
Simulator-Only/v1/webhooks/configureConfigure the webhook URL for receiving inbound message notifications.
Request Body
{
"url": "http://localhost:3000/webhook/sms"
}Example
await fetch('http://localhost:4001/v1/webhooks/configure', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
url: 'http://localhost:3000/webhook/sms'
})
})/v1/webhooks/configGet the current webhook configuration.
Response
{
"url": "http://localhost:3000/webhook/sms",
"configured_at": "2024-01-15T10:30:00Z"
}Simulate Inbound Message
Simulator-Only/v1/webhooks/simulate-inboundSimulate an inbound SMS message that will be sent to your configured webhook URL.
Testing Feature
This endpoint simulates receiving an SMS reply from a user and forwards it to your webhook for testing conversation flows.
Request Body
{
"from": "+1234567890", // User's phone number
"to": "+1987654321", // Your app's number
"body": "Yes, I'm interested" // User's reply message
}Webhook Payload
Your webhook will receive this payload:
{
"id": "sim_inbound_xyz789",
"from": "+1234567890",
"to": "+1987654321",
"body": "Yes, I'm interested",
"received_at": "2024-01-15T10:35:00Z",
"type": "inbound"
}Example
// Simulate a user replying "YES" to an opt-in request
await fetch('http://localhost:4001/v1/webhooks/simulate-inbound', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
from: '+1234567890',
to: '+1987654321',
body: 'YES'
})
})Data Models
Message Object
idstringYesUnique message identifiertostringYesRecipient phone numberfromstringOptionalSender phone numberbodystringYesMessage contentstatusstringYesqueued, sent, delivered, failedcreated_atstringYesISO 8601 timestampdelivered_atstringOptionalISO 8601 timestamp when deliveredError Object
All API errors return a consistent error format:
{
"error": {
"type": "validation_error",
"message": "Missing required field: to",
"details": {
"field": "to",
"code": "required"
}
}
}Common Error Types
validation_errorInvalid request datanot_foundResource not foundinternal_errorServer error