A phone number is who you are
Sign-in by SMS code is a first-class identity, not an email in disguise. Email, magic links, Google, GitHub, Apple, Facebook and anonymous sessions sit beside it.
Product
Platform overview Integrations Verified, not claimed What isn't built yetSolutions
Mobile-first startups Agencies building for SMEs Fintechs & regulated teams What people buildMigrate
How a move works From Supabase From Appwrite From Firebase From AWS Amplify From any Postgres PricingDevelopers
Documentation Next.js quickstart JavaScript quickstart Changelog Support Contribute Platform statusCompany
Why we exist Customers Blog Partners Contact Sign in Start buildingPostgres, auth, storage, realtime and functions — designed around phone-first users, budgets that run on prepaid, and data that can stay on the continent. Not translated into this market afterwards. Started here.
Early access: sign-up needs an invite for now. Ask for yours — we answer personally.
import { createClient } from "@affineq/js";
const affineq = createClient("aq_pub_…");
// a code by SMS to a number, not an email
await affineq.auth.signInWithOtp({
phone: "+254712345678",
});
const { data } = await affineq.auth.verifyOtp({
phone: "+254712345678",
token: "482913", type: "sms",
});
// your Postgres, as an API.
// row-level security decides who sees what
const { data } = await affineq
.from("orders")
.select("id, total, customer(name)")
.eq("status", "paid")
.order("created_at", { ascending: false })
.limit(20);
// the shop's screen updates when an order is
// paid; after a dropped connection it catches up
affineq.channel("orders")
.on("postgres_changes", {
event: "UPDATE",
table: "orders",
filter: "status=eq.paid",
}, (change) => notifyShop(change))
.subscribe();
// functions/on-order-paid/index.ts
export default async function (req, ctx) {
// a table change fired us
const row = ctx.event?.record;
if (row?.status !== "paid") {
return Response.json({ skipped: true });
}
// the secret key skips row-level security,
// so the receipt is written here
return Response.json({ ok: true });
}
Supabase and Firebase are excellent, and neither was designed around a user whose identity is a phone number or a developer whose bills are prepaid. So each team rebuilds the same foundations, under deadline.
Sign-in by SMS code is a first-class identity, not an email in disguise. Email, magic links, Google, GitHub, Apple, Facebook and anonymous sessions sit beside it.
You top up, you spend, you stop at zero. A project degrades gracefully when the wallet is empty; it never goes into debt. Payments run on your own processor accounts — we never hold your money.
Writes return nothing unless you ask. Uploads resume after a drop. Live streams survive the networks that kill WebSockets, and catch up on reconnect. Bytes cost money here.
It’s plain Postgres and an S3-style API. Point storage at a bucket you already pay for. We earn your stay; we don’t lock you in.
Make a project and you get a Postgres, a key, and everything around them: sign-in, files, live updates, server code, logs and backups. Eight pieces, each one finished. What isn’t built is listed further down, in plain words.
A Postgres per project with an automatic REST API, a table editor, a SQL editor that tells you what a statement will do before it runs, and row-level security.
Phone, email, magic links, OAuth and anonymous sign-in. Users live in your own database, inside its isolation and its backups.
Buckets, signed links, access policies and resumable uploads. You choose where the files live, with Cape Town and Johannesburg built for data that must stay in Africa — or bring a bucket you already own.
Change subscriptions, broadcast and presence that reconnect and catch up without your app thinking about it.
Server-side code next to your data, in TypeScript, Python, Java, Dart or PHP, with sealed secrets, hooks on table changes and sign-ups, and a cron that survives a restore.
Every request, filterable and live. Reports on errors and latency, and advisors that read your database and offer the fix.
Nightly, encrypted, and restored in a drill every week — read back as your own app’s users would read it.
Deploy functions and push migrations from your repo. The JavaScript SDK speaks the dialect you already know.
M-Pesa, Airtel Money and MTN MoMo behind one API, and SMS, WhatsApp and USSD as building blocks. This is the wedge, and it’s next.
A shop, a school, a ticketing app, a lender. Here is who we are building for first, and what works for each of them today.
Phone sign-in, light payloads and a backend you don’t have to assemble before you have a user.
See the fit →One project per client, predictable cost, and a client who never sees a surprise bill.
See the fit →Isolation you can test, backups you can verify, and honesty about what isn’t there yet.
See the fit →Same code shape, your data and functions moved over as a copy, and a run in parallel before you switch.
See the fit →Anything we say on this page is something a script can check. A backup you’ve never restored is a hope, so we restore ours every week.
Not a screenshot of a green tick. These are the commands we run, and the status page is public: affineq.com/dashboard/status.
You top up, you spend, and at zero your project slows down gracefully instead of going into debt. We are in early access and haven’t put prices on anything yet. That is deliberate, and the pricing page explains why.
Plans define what’s included; the wallet pays for it. Top-ups by mobile money are what we’re building toward.
Every plan below a dedicated database shares one with limits. You learn that from us, in advance, not from an incident.
Payments you take run on your own processor accounts. We are never a party to the funds.
Same query builder, same auth calls, same row-level security. Moving a codebase is a change of import and key.
Today we run migrations alongside you rather than as a button. Tell us what you’re moving and we’ll tell you plainly how it goes.
// before import { createClient } from "@supabase/supabase-js";const db = createClient(url, anonKey); // after import { createClient } from "@affineq/js";const db = createClient("aq_pub_…"); // everything below stays as it was const { data } = await db.from("orders").select(); await db.auth.signInWithOtp({ phone });
We are in early access, so there is no wall of logos here. There is one real story, told honestly.
A live store on a self-hosted Supabase, imported onto AffinEQ as a copy: every table and every user count matches, its server code runs on Workers unchanged, and its backup has been restored in a drill.
Read the story →
If you build software in Africa, you already know the list.
A phone number is how people identify themselves, so sign-in has to start there. Money moves by M-Pesa long before it moves by card. A 20 MB upload dies halfway on a bad connection, and the platform bill arrives in dollars, to a card many of us don’t have.
The tools we admire were built somewhere else, for somewhere else, and every team here quietly builds the same three things on top of them — phone codes, mobile money, and a way to pay the platform — badly, because the deadline was yesterday.
AffinEQ starts from the other end. We treat the African context as the design brief, not a market to localise into later. That’s why you’ll find a prepaid wallet where others have a credit card, and a plain list on this page of what we haven’t built yet — we’d rather you read it here than find it in week three.
We’re a small team and we’d rather ship four things properly than ten for show. If something here doesn’t work the way we said it would, tell us. We’ll fix it and we’ll thank you.
This list is part of the product. It’s updated when the work ships, and it’s in the order we plan to do it.
Building our nightly backups found three bugs, and a restore drill found one of them.
Read → Security · 30 Sep 2026Why it must answer a stranger and an account holder the same way, in time as well as in words.
Read → Migration · 1 Oct 2026How we imported a running store without touching it, and checked every table and user.
Read →Payment processors, SMS gateways, identity and KYC providers, startups with something developers keep rebuilding: apply to be an AffinEQ integration, and reach them where they start.
Make a project, create a table, sign in with a phone number. It takes about as long as reading this page did.