API documentation
Base URL https://pushferry.com · updated 5 September 2026
Pushferry sends and receives SMS through your own Android phone and your own SIM. The API queues a message in the cloud; your phone picks it up over a persistent connection and puts it on the radio. Message text is passed through in memory and is not stored in our database.
Authentication
Every request carries a bearer token in the Authorization header. Issue a key in the
cabinet under API keys. The key is shown once and cannot be recovered
afterwards — if you lose it, issue a new one.
curl https://pushferry.com/v1/devices \
-H "Authorization: Bearer YOUR_API_KEY"
A request without a valid key returns 401.
Send a message
curl -X POST https://pushferry.com/v1/messages \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"phoneNumber": "+14155550100",
"text": "Your code is 4821"
}'
Request fields
| Field | Required | Meaning |
|---|---|---|
phoneNumber | yes | Recipient in E.164 form. Alias: to. |
text | yes | Message body. Alias: message. |
deviceId | no | Send from a specific phone or SIM. Omit to use any online device. |
requestId | no | Your own idempotency key. Repeating it will not send twice. |
fanout | no | Send through every online device at once; the first acknowledgement wins. |
ignoreQuietHours | no | Send immediately even inside your quiet-hours window. Use it for one-time codes and delivery notices. |
Responses
| Status | Meaning |
|---|---|
200 | The phone acknowledged the send. |
202 | Accepted and handed to the phone; watch the message record for the outcome. |
400 | Bad input, or no phone is linked to the account (no_device). |
402 | Your plan's monthly allowance is used up (upgrade_required). |
409 | The addressed device is offline (device_offline). |
429 | Rate limit for your plan (rate_limited). |
503 | No device is online to take the message (no_online_device). |
When the message falls inside your quiet-hours window, it is neither refused nor
lost: the job is handed to your phone with a send time at the end of the window, and the response carries
"status": "scheduled" together with scheduledAt.
Other endpoints
| Method and path | What it does |
|---|---|
GET /v1/messages | List messages. Filters: deviceId, status, direction, limit, offset. |
GET /v1/messages/{id} | One message and its delivery state. |
POST /v1/messages/bulk | One text to many recipients: {phoneNumbers: [...], text, deviceId?}. |
GET /v1/messages/bulk/{id} | Progress of a bulk batch. |
GET /v1/messages/scheduled | Jobs already handed to a phone for later sending. |
GET /v1/devices | Your phones and SIMs, and whether they are online. |
GET /v1/forwarders | Where incoming messages are forwarded. |
POST /v1/forwarders | Add a destination: ntfy, Telegram, Slack, Discord, e-mail, SMS or a webhook. |
GET /v1/webhooks | Your webhook subscriptions for inbound messages. |
GET /v1/billing/plans | Plans and their limits. |
GET /v1/billing/usage | What you have used this period. |
GET /v1/health | Service health. No authentication. |
Receiving messages
Incoming SMS and forwarded app notifications can be delivered to you in two ways, and they are not exclusive:
- Forwarders — the phone sends straight to ntfy, Telegram, Slack, Discord, another number or your webhook. For these the cloud never sees the message text at all.
- Webhooks — our server posts the message to a URL you own.
Not yet documented here: exact JSON schemas for every object, the WebSocket protocol your phone speaks, and the MCP endpoint for AI agents. If you need one of these before we write it up, ask at hello@pushferry.com and we will send you the details.