Skip to content

Sargalay — AI API Gateway

AI API gateway for Myanmar developers — one OpenAI-compatible endpoint in front of 400+ models, billed per token with MMQR top-ups from any local banking app (no USD card). Edge-first Hono API with end-to-end typed RPC and shared Zod schemas across a public Next.js site and four Vite dashboards. Spendable credit is derived from the transaction ledger rather than a mutable balance, metered from real upstream token counts, and confirmed on screen over SSE. The same tool-calling assistant answers on the site, in Telegram, in Messenger and behind Discord commands; an MCP endpoint exposes the account to the user's own agent. A second product, Bizsite, turns pasted text and uploaded documents into a published Burmese business site with its own visitor assistant, billed to the owner's gateway credit.

sargalay.com
Full Cycle
Next.jsNext.jsViteViteTypeScriptTypeScriptHonoHonoCloudflareCloudflareDrizzle ORMDrizzle ORMTanStack RouterTanStack RouterTanStack QueryTanStack QueryPayloadPayloadshadcnshadcnTailwind CSSTailwind CSSGemini AIGemini AI
Screenshot of Sargalay — AI API Gateway

Sargalay — AI API Gateway for Myanmar Developers

One OpenAI-compatible endpoint in front of 400+ AI models, paid for with a local QR banking app instead of a USD card.

Overview

Myanmar developers cannot subscribe to frontier AI providers: the APIs want an international card, and local cards do not work. Sargalay removes that wall. A developer signs in, tops up in MMK by scanning an MMQR code with any local banking app, and gets a single sk- key that speaks the OpenAI API — so existing SDKs, agents and editors work after a one-line base URL change. Every account is created with a key and a welcome credit already in place, so the first successful call happens before the first payment.

It is a live commercial product: paying users, real payouts, and a support surface that has to answer questions at 2am without me. Uptime, mean latency and the most-used models are published on the site and on a public status page, derived from the same log the invoices come from. The platform has since grown a second product on the same spine — Bizsite, an AI website builder for Myanmar businesses, which is itself a customer of the gateway.

Key Features

  • Entitlement, not a balance column — Spendable credit is derived absolutely from the ledger of completed transactions and projected onto the upstream provider's per-user key limit; re-syncing is idempotent, so a credit that reached the ledger but not the provider heals itself on the next sync instead of needing a manual fix
  • Metering from real token counts — The streaming response is split: one branch reaches the caller untouched apart from currency rewriting, the other is read in the background for the provider's own usage figures, so billing never estimates and never delays a token
  • Per-key budgets — Each API key carries its own spending cap and usage history, so a leaked or experimental key cannot drain the account behind it
  • QR top-ups with live confirmation — Pay with any local banking app; the screen confirms over Server-Sent Events the moment the provider's callback lands, with no refresh and no polling loop for the user to sit through
  • Idempotent payment callbacks — A callback is verified, amount-checked against the stored transaction, then claimed with a single conditional update, so a retried delivery credits nobody twice; a manual re-sync endpoint doubles as the recovery path when a callback never arrives
  • Invoices from the first click — An invoice number is minted with the pending transaction, not after payment, so a customer who pays has a receipt that already existed; receipts are emailed on approval
  • Welcome credit that expires — Free credit has a 30-day life unless a real payment ever lands; a nightly sweep claws back only what was granted, writes the debit markers before touching the provider limit, and aborts for anyone who paid mid-run. The "your credit expires soon" banner is built from the sweep's own predicates, so the warning cannot contradict the job
  • Prepaid top-up codes — Staff-minted codes credit an account without a bank transfer; the claim is one conditional update, expiry is checked after the claim so an expired code reports expired rather than already used, and redeemed credit is ledgered under its own source so it is never counted as cash received
  • Points for humans, exact maths underneath — Prices, balances and usage are shown in points with a secondary MMK figure; the accounting unit never reaches the browser
  • Model catalogue — 400+ models browsable by modality, capability, context window and price, grouped by what they actually output, with stale-serve fallback so an upstream catalogue outage cannot empty the page
  • Assistant on four channels — One tool-calling assistant answers in the site and console widget, in Telegram, in Messenger and behind Discord slash commands, with conversations persisted per channel
  • MCP endpoint for the user's own agent — Account, usage, transactions, model list and top-up creation are exposed as MCP tools, each gated by the calling key's permissions, so a developer can point their own agent at their Sargalay account
  • Quick Start that runs — The console builds a working sample for the chosen stack and capability from one template per stack, renders the account's real key, and runs the request from the page, so onboarding ends in a 200 rather than in a copy-paste

Architecture

One edge API, five frontends, typed end to end

A single Hono worker on an edge runtime owns every backend surface: the OpenAI-compatible proxy, account and billing, webhooks, the assistant, MCP, and the cron sweep. In front of it sit a statically regenerated Next.js marketing site (landing, catalogue, pricing, blog) and four Vite + TanStack Router SPAs — developer console, account portal, admin console and the Bizsite studio. Every frontend consumes the API through a typed RPC client built from the worker's own route type, with request and response shapes defined once as shared Zod schemas: no code generation step, no schema registry, and a renamed field breaks the build instead of production. Shared UI, charts and client glue live in workspace packages so four SPAs do not become four design systems.

Entitlement instead of a stored balance

There is no balance column. The ledger of completed transactions is the only truth, and a user's spend ceiling is that ledger's sum pushed onto their provisioned upstream key as an absolute limit. That single decision removes a whole class of bugs: no read-modify-write race on a hot row, no divergence between "what we think you have" and "what the provider will let you spend", and a repair path that is just running the same calculation again. The proxy still guards the key's own budget before forwarding, and an upstream refusal is translated into a clean 402 that tells the caller how many points the request needs and where to top up — never anything about the provider or the key behind it.

Metering a stream without slowing it down

For a streaming call the response body is teed. One branch is piped to the caller through a transform that rewrites the cost fields into display units; the other is drained in the background, finds the chunk carrying the provider's usage block, and writes the billing row after the user already has their tokens. Failures there are loud on purpose — a lost usage row is lost revenue, so it is logged as critical rather than swallowed.

Money separated by source

Every credit carries where it came from — QR cash, redeemed code, granted bonus, staff adjustment, or credit gifted by a Bizsite purchase. Keeping them apart is what makes the rest honest: expiry protection applies only to real payments, reclaimed bonus is netted against what was granted instead of inflating totals, and credit sitting in an account is treated as service owed rather than money earned. Collapsing the sources into one number would make the product look healthiest exactly when it is most exposed.

Analytics that are re-derivable, and public

Each proxied call writes one usage row, and four counter tables are updated beside it in a best-effort batch wrapped so their failure can never roll back the billing row. Every counter can be rebuilt from the logs, which is what makes publishing them safe: the dashboard's per-day requests and tokens, and the public availability, mean latency and top-model figures, all come from the same source as the invoices. Availability deliberately excludes client errors — counting a caller's malformed request as downtime would flatter nobody.

Assistant and tools

A tool-calling assistant built on a Gemini Flash model answers product questions: it searches and groups the live catalogue, and reads the FAQ and policy set. Model IDs in its answers are wrapped in a reference syntax that renders as an inline chip or a full model card, so a recommendation is clickable rather than copy-paste, and older turns are compressed into a rolling summary to keep long conversations cheap. The same runner is mounted behind the Telegram, Messenger and Discord webhooks. The Telegram bot is deliberately the strictest surface: private chats only, and an unconnected sender gets a sign-up invitation with no model call at all, so strangers cannot spend the platform's assistant budget. Connecting an account is one tap — the console mints a single-use code that the bot redeems, with the redemption written as a conditional update so two taps cannot both win. Connected users get the read slice of the account tools; creating a payment is excluded, because a chat message must not open a transaction.

Operating it alone

Admin work runs through an HTTP MCP endpoint and an interactive CLI agent alongside the admin console, so account lookups, transaction checks and platform stats happen from my editor. The nightly cron handles bonus expiry and retention sweeps in bounded batches to stay inside the platform's per-invocation budget. The customer-facing blog is a Payload CMS mounted on the public site with its own editor accounts, kept separate from gateway customers. Around 30 test suites run against a real SQLite database rather than mocks, concentrated on the parts where a bug costs money: payment claims, expiry clawback, voucher races, invoice numbering, usage rollups and the renderer contract.

Second Product — Bizsite

Bizsite is a Burmese-first AI website builder that shares the gateway's account system, billing spine and worker. A shop owner pastes a description or uploads documents and photos; a model extracts them into eight kinds of typed, editable facts; the owner corrects anything wrong, picks a theme, layout and assistant persona, and publishes. Zawgyi input is detected and converted to Unicode on the way in, because the encoding split is still real for Myanmar users typing on older keyboards.

  • Publishing is deterministic, not generative — Facts compose into a page spec by fixed rules; the model is a fallback for malformed input, never the author of layout or copy. A published page is one self-contained HTML document with no client framework, served from an edge cache, so a visitor on a slow connection is not downloading a site builder
  • The visitor assistant answers only from approved facts — It may also emit a restricted UI spec, which the server re-renders through an allowlist before it reaches the page, so a model can choose a component but can never inject markup. Answers stack as slides in a panel, and the owner can take the thread over live, with the handover pushed both ways over a websocket room
  • Design is owner-controlled with a correctness gate — Theme presets, layouts and per-section variants are all overridable, and a contrast check rejects an unreadable palette when it is saved rather than when a customer sees it. Uploaded images are validated on real byte length and stored on a CDN; if storage is unavailable the request fails loudly instead of returning a URL that will 404 tomorrow
  • It is a customer of the gateway — Terms are prepaid in MMK, part of each purchase is credited back as gateway credit, and every visitor answer is billed to the owner's gateway balance. Running out is a product state, not an outage: the page keeps working, the assistant is what pauses. The same ingestion pipeline is also resold as a small Business API for developers holding a gateway key

Key Technical Decisions

  • Measure, do not estimate — Billing reads the provider's own token and cost figures out of the response; estimating would have meant reconciling drift forever
  • Derive the limit, never mutate a balance — An absolute, idempotent re-sync from the ledger is self-healing; an incrementing balance column is a race waiting for traffic
  • One schema, many consumers — Shared Zod plus typed RPC removed the entire class of frontend/backend contract drift across five frontends
  • Conditional updates over locks — Payment approval, voucher redemption and link-code consumption all claim a row only while it is still unclaimed, which makes duplicate deliveries and double taps harmless without distributed locking
  • Expire what was given away — Granted credit has a lifetime and a nightly sweep, with the debit written before the provider limit moves, so a crash mid-sweep leaves the customer better off rather than worse
  • Points for humans, exact units for maths — Display units keep local pricing readable without letting rounding into the ledger
  • Publish the reliability numbers — Availability and latency are on the public site and a status page; a gateway that hides them is asking to be trusted on nothing
  • Static where possible — Catalogue, marketing pages and published Bizsite pages are served from cache, so the read path does not depend on the database

Tech Stack

Backend: Hono on an edge runtime, TypeScript, typed RPC, Drizzle ORM, edge SQL (SQLite) database, Zod, hand-authored SQL migrations, scheduled cron workers, websocket rooms for realtime

Frontend: Next.js 16 (public site, SSG/ISR), four Vite + TanStack Router SPAs (developer console, account portal, admin console, Bizsite studio), TanStack Query, shadcn/ui, Tailwind CSS, Recharts, EN/MM i18n

AI: Gemini Flash models for the assistant, document extraction and site answers, tool calling via the Vercel AI SDK, 400+ models exposed through an OpenAI-compatible proxy, MCP server for user and admin tools

CMS: Payload CMS for blog and news, managed Postgres, object storage plus CDN for media

Payments: Local QR payments (MMQR) from any Myanmar mobile-banking wallet, signature-verified callbacks, SSE confirmation, prepaid top-up codes, emailed receipts and generated invoice numbers

Tooling: pnpm monorepo with shared UI, types, core and renderer packages, Vitest against a real SQLite database, MCP server and CLI agent for admin operations

Role

Founder · Solo Developer — product, pricing and billing model, edge API, LLM proxy, payment integration, every frontend, the assistant and its bot channels, the Bizsite builder and renderer, the shared design system, admin tooling, and deployment.