Stream inbound texts
curl --request GET \
--url https://api.lumisreach.com/api/v1/sms/stream \
--header 'Authorization: Bearer <token>'import requests
url = "https://api.lumisreach.com/api/v1/sms/stream"
headers = {"Authorization": "Bearer <token>"}
response = requests.get(url, headers=headers)
print(response.text)const options = {method: 'GET', headers: {Authorization: 'Bearer <token>'}};
fetch('https://api.lumisreach.com/api/v1/sms/stream', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.lumisreach.com/api/v1/sms/stream",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://api.lumisreach.com/api/v1/sms/stream"
req, _ := http.NewRequest("GET", url, nil)
req.Header.Add("Authorization", "Bearer <token>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.get("https://api.lumisreach.com/api/v1/sms/stream")
.header("Authorization", "Bearer <token>")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.lumisreach.com/api/v1/sms/stream")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Get.new(url)
request["Authorization"] = 'Bearer <token>'
response = http.request(request)
puts response.read_body<string>{
"error": {
"code": "<string>",
"message": "<string>",
"fields": {},
"doc_url": "<string>"
}
}{
"error": {
"code": "<string>",
"message": "<string>",
"fields": {},
"doc_url": "<string>"
}
}{
"error": {
"code": "<string>",
"message": "<string>",
"fields": {},
"doc_url": "<string>"
}
}{
"error": {
"code": "<string>",
"message": "<string>",
"fields": {},
"doc_url": "<string>"
}
}{
"error": {
"code": "<string>",
"message": "<string>",
"fields": {},
"doc_url": "<string>"
}
}{
"error": {
"code": "<string>",
"message": "<string>",
"fields": {},
"doc_url": "<string>"
}
}Inbound SMS
Stream inbound texts
Receive the texts people send to your numbers as Server-Sent Events (SSE).
GET
/
v1
/
sms
/
stream
Stream inbound texts
curl --request GET \
--url https://api.lumisreach.com/api/v1/sms/stream \
--header 'Authorization: Bearer <token>'import requests
url = "https://api.lumisreach.com/api/v1/sms/stream"
headers = {"Authorization": "Bearer <token>"}
response = requests.get(url, headers=headers)
print(response.text)const options = {method: 'GET', headers: {Authorization: 'Bearer <token>'}};
fetch('https://api.lumisreach.com/api/v1/sms/stream', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.lumisreach.com/api/v1/sms/stream",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://api.lumisreach.com/api/v1/sms/stream"
req, _ := http.NewRequest("GET", url, nil)
req.Header.Add("Authorization", "Bearer <token>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.get("https://api.lumisreach.com/api/v1/sms/stream")
.header("Authorization", "Bearer <token>")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.lumisreach.com/api/v1/sms/stream")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Get.new(url)
request["Authorization"] = 'Bearer <token>'
response = http.request(request)
puts response.read_body<string>{
"error": {
"code": "<string>",
"message": "<string>",
"fields": {},
"doc_url": "<string>"
}
}{
"error": {
"code": "<string>",
"message": "<string>",
"fields": {},
"doc_url": "<string>"
}
}{
"error": {
"code": "<string>",
"message": "<string>",
"fields": {},
"doc_url": "<string>"
}
}{
"error": {
"code": "<string>",
"message": "<string>",
"fields": {},
"doc_url": "<string>"
}
}{
"error": {
"code": "<string>",
"message": "<string>",
"fields": {},
"doc_url": "<string>"
}
}{
"error": {
"code": "<string>",
"message": "<string>",
"fields": {},
"doc_url": "<string>"
}
}Opens a Server-Sent Events (SSE) connection that pushes each text people send to your workspace’s numbers,
on either carrier, a second or two after it arrives.
Sent as soon as the stream opens.
Sent for each text someone sends to one of your numbers.
For a server-to-server integration, prefer inbound SMS webhooks: no connection to keep
open, signed deliveries and automatic retries.
Authentication
Send your API key as a Bearer token. A key restricted with scopes needssms:read.
curl -N "https://api.lumisreach.com/api/v1/sms/stream" \
-H "Authorization: Bearer YOUR_API_KEY"
Events
connected
Sent as soon as the stream opens.
event: connected
data: {"timestamp":1716000000000}
sms_received
Sent for each text someone sends to one of your numbers.
id: 48213
event: sms_received
data: {"message_id":"3f0c9a52-6f1e-4b8e-9d2a-0c7e5b1d2a44","from":"+14155551234","to":"+14155559999","body":"Hello!","received_at":"2026-05-22T18:30:00.000Z"}
| Field | Type | Description |
|---|---|---|
message_id | string | The text’s id |
from | string | Who sent it (E.164) |
to | string | Your number that received it (E.164) |
body | string | The message text |
received_at | string | When it arrived (ISO 8601) |
Heartbeat
A comment line every 30 seconds keeps idle proxies from closing the connection:: heartbeat
Connection behavior
- Scope: every number in the workspace the API key belongs to.
- Duration: a stream stays open for about 5 minutes, then closes. Reconnect when it does.
- No gaps on reconnect: each
sms_receivedcarries an SSEid. Send the last one you handled in theLast-Event-IDheader and the new stream starts right after it, including texts that arrived while you were disconnected. Without the header, a stream starts from now.
Example
The browser’s built-inEventSource cannot send an Authorization header, and an API key must never reach a
browser anyway. Open the stream from your server, or proxy it through your backend.
let lastEventId;
async function listen() {
const res = await fetch("https://api.lumisreach.com/api/v1/sms/stream", {
headers: {
Authorization: `Bearer ${process.env.LUMISREACH_API_KEY}`,
...(lastEventId ? { "Last-Event-ID": lastEventId } : {}),
},
});
const decoder = new TextDecoder();
let buffer = "";
for await (const chunk of res.body) {
buffer += decoder.decode(chunk, { stream: true });
let end;
while ((end = buffer.indexOf("\n\n")) !== -1) {
const block = buffer.slice(0, end);
buffer = buffer.slice(end + 2);
const field = (name) => block.match(new RegExp(`^${name}: (.*)$`, "m"))?.[1];
if (field("event") === "sms_received") {
const text = JSON.parse(field("data"));
console.log(`New text from ${text.from}: ${text.body}`);
lastEventId = field("id");
}
}
}
listen(); // The stream closed: reconnect and resume.
}
listen();
import json
import os
import requests
url = "https://api.lumisreach.com/api/v1/sms/stream"
last_event_id = None
while True:
headers = {"Authorization": f"Bearer {os.environ['LUMISREACH_API_KEY']}"}
if last_event_id:
headers["Last-Event-ID"] = last_event_id
with requests.get(url, headers=headers, stream=True) as r:
event = {}
for line in r.iter_lines(decode_unicode=True):
if line == "":
if event.get("event") == "sms_received":
text = json.loads(event["data"])
print(f"New text from {text['from']}: {text['body']}")
last_event_id = event.get("id")
event = {}
elif not line.startswith(":") and ": " in line:
key, value = line.split(": ", 1)
event[key] = value
Authorizations
Bearer authentication header of the form Bearer <token>, where <token> is your auth token.
Response
An open Server-Sent Events stream of connected and sms_received events
The response is of type file.