Start here
An Ostra line is one iPhone on a wall in Los Angeles, on one carrier SIM, held by one verified customer, an individual or a business. Your code reaches it two ways: its proxy ports carry your requests out through the phone’s cellular connection, and the API reads the texts and calls that reach its number.
Start on the sandbox line. Every account has it from the moment it is made, verified or not, and the samples on these pages use it. Ostra’s own checks run every sample against the API’s code.
LAX-V-07 is an example; your lines’ IDs come from GET /v1/lines.LAX-V-07
- LAX, the metro Los Angeles, where the phone is and where its gateway answers
- V, the network T for T-Mobile, A for AT&T, V for Verizon
- 07, the slot The phone’s place on the wall, and the end of each of its ports
- HTTPS proxy
30007With a username and secret, and no list of addresses - SOCKS5 proxy
10007From the source addresses you list, with no credential - HTTP proxy
20007From the source addresses you list, with no credential, in plain text
The SOCKS5 and HTTP ports answer nobody until you list the line’s source addresses: Source addresses.
Each line also has its own phone number. Texts and calls to it reach you through the API and webhooks: Texts and webhooks. A line receives; nothing here texts or calls out from it.
How an account gets a line
Sign up
Sign in to the dashboard with your email: Ostra emails a 6-digit code, good for 15 minutes, with a link that asks before it signs you in. There is no password, and you can turn on two-step sign-in with an authenticator app. If the dashboard asks for an access code first, enter the one Ostra gave you. Then create your account: an individual’s, for you alone, or a business’s organization, for your team.
Get verified
An admin fills in the sign-up check: the person’s legal name (and a business’s legal name), a US address, and a US mobile number, proved with a 6-digit code Ostra texts to it. They state they are 18 or older, accept the acceptable use policy and submit. Ostra checks that the request comes from the United States and screens every name against the US sanctions lists at once: a record that clears is approved there and then, and only one the check cannot clear waits for a person at Ostra. No document is uploaded, and no government ID number is asked for.
Build on the sandbox
Your organization has
LAX-SANDBOX-00from the moment it is made, verified or not. Make an API key and build against it.Get a line
Order lines from the dashboard or with
POST /v1/orders: how many, and a carrier or any. A line that is ready is assigned at once and its first month charged then; the rest wait in a first-come queue, with nothing charged, and you get an email as each line is ready, usually within a day. An order that takes your organization above 5 lines is checked by a person at Ostra first, and one account holds at most 25 lines, individual or business. Each order states what its lines are for.
The sandbox line
LAX-SANDBOX-00 is a simulator for building and testing. No phone, carrier or traffic is behind it, every answer about it says "sandbox": true, and it can never become a real line. Texts, waits, cursors, a new carrier IP and webhooks behave as they do on a real line; a proxy credential for it is well formed and works nowhere; its connection record and usage stay empty.
- Line ID
LAX-SANDBOX-00- Number
+13105550199- Proxy host
sandbox.invalid, ports300001000020000; nothing answers there- Exit addresses
- From
192.0.2.0/24and2001:db8::/32, networkAS64496 - Simulated texts
- 60 an hour, from
+13105550100unless you name a sender - New carrier IP
- The job reads
succeededafter 20 seconds and the new address follows within about 10; the same limits as a real line
Your first call
An admin makes an API key in the dashboard, under Keys, and picks its scopes (which scope allows what). The secret is shown once. Keep it and the API’s address, https://api.ostranetwork.com, in your shell for the samples on these pages:
export OSTRA_API_BASE_URL=https://api.ostranetwork.com
read -rs OSTRA_API_KEY && export OSTRA_API_KEY#!/bin/sh
# Your lines, then the sandbox line every account has.
curl -sS "$OSTRA_API_BASE_URL/v1/lines" \
-H "Authorization: Bearer $OSTRA_API_KEY"Every request carries the key as a bearer token. A key belongs to one organization and can do only what its scopes allow.
The curl samples put the key on curl’s command line, where other accounts on the same computer can see it in the process list. On a shared computer, use the CLI or the Python and Node.js samples, which keep it in the environment.
A new organization holds no line yet, so the answer is the sandbox line alone:
{
"lines": [
{
"id": "LAX-SANDBOX-00",
"sandbox": true,
"internal_testing": false,
"notice": "Sandbox: a simulator for building and testing. No phone, carrier or traffic is behind it.",
"metro": "lax",
"carrier": "sandbox",
"carrier_name": "Sandbox",
"number": "+13105550199",
"status": "active",
"assignment": null,
"proxy": { "host": "sandbox.invalid", "https_port": 30000, "socks5_port": 10000, "http_port": 20000 },
"ip": { "ipv4": "192.0.2.41", "ipv6": "2001:db8::5a1", "asn4": 64496, "asn6": 64496, "checked_at": "2026-10-12T17:04:11.000Z" },
"health": { "state": "ok", "checked_at": "2026-10-12T17:04:11.000Z", "families": { "ipv4": true, "ipv6": true } }
}
]
}Read your first code
The sandbox lets you play both sides: your service texting the line, and your test reading the code. Start waiting for the next text on the line:
#!/bin/sh
# Wait up to 60 seconds for the next text on the sandbox line.
# It answers 200 with the text when one arrives, or 204 when none did.
curl -sS "$OSTRA_API_BASE_URL/v1/lines/LAX-SANDBOX-00/messages/wait?timeout=60&limit=1" \
-H "Authorization: Bearer $OSTRA_API_KEY"While it waits, in a second terminal, send the line a simulated text:
#!/bin/sh
# A simulated text to the sandbox line, as your own service would send one.
curl -sS -X POST "$OSTRA_API_BASE_URL/v1/lines/LAX-SANDBOX-00/simulate/message" \
-H "Authorization: Bearer $OSTRA_API_KEY" \
-H 'content-type: application/json' \
-d '{"body": "Your Example code is 482915."}'{
"message": {
"id": "6c1f0e52-7d1b-4f8e-9a51-2b7c4e0d93aa",
"line_id": "LAX-SANDBOX-00",
"sandbox": true,
"sender": "+13105550100",
"sender_label": null,
"received_at": "2026-10-12T17:05:02.114Z",
"source": "simulated",
"service": null,
"has_code": true
},
"webhook_event": false
}The wait answers at once with the text. Lists and waits never carry a body unless you ask, so the text says only that a code was found:
{
"line_id": "LAX-SANDBOX-00",
"messages": [
{
"id": "6c1f0e52-7d1b-4f8e-9a51-2b7c4e0d93aa",
"line_id": "LAX-SANDBOX-00",
"sandbox": true,
"sender": "+13105550100",
"sender_label": null,
"received_at": "2026-10-12T17:05:02.114Z",
"stored_at": "2026-10-12T17:05:02.114Z",
"source": "simulated",
"service": null,
"has_code": true,
"code_expires_at": "2026-10-12T17:20:02.114Z"
}
],
"next_cursor": "s.Vb8nR2qLxT0eJm4KcZ7wPA"
}Open it to read the code. Ostra writes every opened text to its audit log before it decrypts it, and keeps a code for 15 minutes:
#!/bin/sh
# Open one text: its body and the code Ostra read from it.
# Needs messages:read_content; Ostra writes the view to its audit log first.
curl -sS "$OSTRA_API_BASE_URL/v1/lines/LAX-SANDBOX-00/messages/$MESSAGE_ID" \
-H "Authorization: Bearer $OSTRA_API_KEY"MESSAGE_ID is the id from the wait's answer. The key needs messages:read and messages:read_content.
{
"message": {
"id": "6c1f0e52-7d1b-4f8e-9a51-2b7c4e0d93aa",
"line_id": "LAX-SANDBOX-00",
"sandbox": true,
"sender": "+13105550100",
"sender_label": null,
"received_at": "2026-10-12T17:05:02.114Z",
"stored_at": "2026-10-12T17:05:02.114Z",
"source": "simulated",
"service": null,
"has_code": true,
"code_expires_at": "2026-10-12T17:20:02.114Z",
"body": "Your Example code is 482915.",
"code": "482915"
}
}The same in one program, in Python or Node.js: Read a code.
API keys or OAuth
An API key, osk_
For your own backends, scripts, CI, the CLI and the MCP server.
- Belongs to one organization; has its scopes, and optionally only some lines.
- Made and revoked by an admin in the dashboard. Revoking stops it at once.
- A key with only
:readscopes cannot ask for a new carrier IP, issue a proxy credential or change a webhook. It can still send the sandbox line a simulated text.
An OAuth token, osa_
For an app acting for a person: the Ostra apps, the browser extension, an MCP client Ostra registered.
- Authorization code with PKCE; the person picks the organization and allows the scopes.
- Works only at the resource it was issued for, the API or the MCP server, for 60 minutes; refresh tokens work once.
- Ostra registers each client. How the flow works.
Either one goes in the Authorization: Bearer header. The dashboard itself uses a signed-in session; keys, members, verification and billing are managed only from there.
Rules that hold on every line
- Use a line only to test sign-up, sign-in and verification flows of services you own or are authorized to test, under the acceptable use policy your organization accepted.
- A line receives texts and calls. Nothing in the API, the apps, the MCP server or the CLI texts or calls out from a line.
- One SIM in one phone, held by one organization at a time. You see only what happened while your organization held the line.
- The gateway refuses connections to private and internal address ranges, to the mail ports 25, 465 and 587, to ports 8100 and 9100, which the line’s phone keeps for itself, and to the sites on the acceptable use policy’s blocked list (which ones).