Connecfy
An SMS gateway built on ordinary Android phones. Each phone’s SIM card is a sender; Connecfy is the queue, the routing and the API in front of them.
- Role
- Backend, Android client, documentation
- Platform
- Web API, staff console and Android sender app
- Status
- Accounts are set up by hand
- Links
- Documentation
Overview
Connecfy sends SMS through real SIM cards in Android phones that act as gateways. A client queues a message from the dashboard or through an API key; a phone picks it up, sends it, and reports back.
There is no self-service signup. Staff create client accounts and pair phones from a separate staff console, and a client either sends through their own paired phones or through a shared pool.
How it fits together
POST /api/sms/send/The message is written as a row with status queued.GET /api/devices/<id>/tasks/A phone polls for work, roughly every 20 seconds, and atomically claims the row.SIM → mobile networkThe phone sends the message through its own SIM card.POST /api/sms/<id>/status/The phone reports sent, delivered or failed.
Separately, every phone sends a heartbeat about every 15 seconds with its battery and signal level.
Engineering notes
Polling on purpose
Nothing in the chain is push-based. Phones ask for work over plain HTTP instead of keeping a socket open, which works on any phone on any network, and means there is no WebSocket server or message broker to run.
Claiming work atomically
SMS.claim_for_device() flips a message from queued to sending with SELECT … FOR UPDATE SKIP LOCKED on PostgreSQL. A duplicate poll can’t be handed a row another poll already holds.
Recovery without a background worker
If a phone claims a message and then disappears, the stale claim is noticed the next time any of that account’s phones polls: the message is requeued, or failed once its retries run out. A device is marked offline the same lazy way, whenever its status is read and its last heartbeat is too old.
A deliberately narrow API
The external API can send a message and nothing else. An API key can’t read message history or device details, so a leaked key can’t be used to read anything.
Staff checks on the server
The staff console lives at its own URL, separate from Django’s admin, and every one of its views checks is_staff on the server, not only in the templates that hide the links.
Stack
- Backend
- Python, Django, Django REST Framework, PostgreSQL
- Clients
- Android sender app; server-rendered dashboard and staff console
- Security
- Device tokens, rate limiting, django-axes
- Languages
- English and French, with Django i18n
- Docs
- Static site at docs.connecfy.com