Kora: AI Back Office

Inspiration

Anyone who works for themselves is running a business, whether or not they ever called it one. There are clients to keep, invoices to raise, contracts to sign, tax to set aside and cash to forecast. The work that earns money happens during the day. Everything else waits for the evening.

The software that exists for this was built for the people who do it professionally. Accounting tools assume an accountant. Contract tools assume somebody who reads contracts for a living. Each one covers a quarter of the job, and every one of them waits for you to log in and do the work yourself. A person running a two-person studio ends up as their own bookkeeper, their own credit controller and their own paralegal, at nine in the evening, badly, because there is nobody else.

The thing that has never existed is a back office that simply runs. Not a dashboard that displays the problem more attractively, but something that notices the invoice went unpaid and chases it while you sleep. That is what I set out to build.

What it does

Kora is a set of AI agents that watch a small business around the clock, do the administrative work themselves, and surface only the decisions that genuinely need a person.

You tell it about your business once, across eight profile sections covering your identity, brand voice, offerings, customers, operations, goals and legal details. Then you connect your environment: Stripe for the money moving through the business, Notion for the work your team already tracks, and Google Workspace for Gmail, Calendar, Drive and Meet. From that point the agents have a shared picture of the business and start acting on it.

Bookkeeping ingests statements or pulls directly from Stripe, categorises every transaction in batches, marks what is deductible, and routes anything it is unsure about to a review queue rather than guessing. Profit and loss comes out as a PDF.

The invoice agent chases what goes unpaid. Day three it sends a gentle reminder. Day seven it firms up. Day fourteen it reads the payment terms out of the signed contract and attaches a formal demand letter. Each message is written from the real invoice number, amount and client name, and every send is logged.

Contracts are drafted from three answers, jurisdiction-aware, with every clause explained in plain English and unusual terms flagged. Paste in an agreement somebody sent you and it reviews that one from your side. Mark a contract signed and it creates the milestone invoices by itself.

Cash flow projects ninety days across three scenarios, weighting each open invoice by how likely it is to actually be paid, and raises an alert before the conservative line goes negative.

The Business Manager is conversational. Ask what to worry about this week and it answers from live numbers. Ask it to chase a client and it queues the action for approval rather than sending anything on its own authority.

Butler handles the relationship side: client health, proposals, retainers, calendar, and meeting intelligence that turns a transcript into decisions, commitments and next steps.

Underneath all of it, Kora learns. A playbook records your corrections, so recategorising the same expense twice stops it asking a third time, and rewriting one of its follow-ups shapes the next. A relationship graph links every client to their invoices, contracts and history, and a semantic recall layer lets any agent search everything it knows by meaning before it drafts a word.

Every action any agent takes is written to an execution log with the real model name, token counts, latency, cost and outcome. That log is filterable and exportable, so any decision the system made can be read back and defended.

How we built it

A Next.js 14 thin client talks over HTTP and JSON to a Python and FastAPI backend that owns all data and all AI. The frontend holds no business logic, which keeps every rule in one place and testable.

The backend runs 29 route modules and 51 services on Python 3.11 with Pydantic v2. Data lives in Supabase Postgres, reached through a store abstraction with two adapters, so the whole stack boots on an in-memory store and a deterministic mock model with zero secrets configured.

Production runs on Google Vertex AI. Every reasoning agent calls gemini-2.5-flash and semantic recall uses gemini-embedding-001, both authenticated with Application Default Credentials so no keys sit in the image. Agents never import a provider directly. They call a provider abstraction, which is why the same code can serve Vertex, any OpenAI-compatible gateway, or the offline mock without a single agent changing.

Both services deploy to Cloud Run in us-central1 via Cloud Build, with CPU throttling disabled so background agent work actually completes after a response is returned. Scheduled runs fire through GitHub Actions cron against secret-guarded endpoints.

Eighteen agents do the work. Twelve reason with Gemini. Six are deliberately deterministic, because payment reconciliation and cross-module triggers must never be improvised. Outbound email and calendar writes queue as approval rows rather than executing, and a post-generation validator rejects any amount or entity the model invented.

Challenges we ran into

A refactor quietly removed tenant isolation. Consolidating the data layer dropped the user_id filter from eleven destructive write paths. Nothing failed and no test caught it, because every test ran as a single tenant. It surfaced only during an independent verification pass. The fix was the easy part. The lesson was that a class of bug this severe cannot rely on anyone noticing, so it is now covered by a dedicated multi-tenant regression suite and an AST lint that fails the build if a destructive write is missing its scope.

Honest failure is harder to build than success. The first GDPR erasure implementation reported deleted: true whether or not the Google token revoke actually succeeded. Making it tell the truth meant threading real failure states through every step and returning deleted: false when any of them did not complete. Software that lies about what it did is worse than software that cannot do it.

Agents that write to clients cannot sound like agents. Early drafts of the follow-up emails were fluent and obviously machine written, full of em dashes and stock phrasing. Since the client reads that message as coming from the freelancer, the tell costs the user credibility with their own customer. The fix became a house style block inherited by every prose prompt, plus a deterministic post-processing pass, because prompt compliance alone is probabilistic.

Long-running work does not survive a serverless request. Import jobs held state in memory and vanished between instances, and the chat endpoint blocked the event loop during model calls. Both had to move: jobs to store-backed durable state, model calls onto a thread.

A shared demo account is one click from destruction. Publishing credentials for evaluators meant any one of them exercising account deletion would wipe the seeded business and delete the identity behind the published login. Protected tenants now refuse deletion with an honest 409 that names what still works.

Deployment order matters more than it should. The frontend bakes its API URL at build time, so the backend has to deploy first, and CORS needs the production origin before the frontend can talk to it at all.

Accomplishments that we're proud of

It is live in production on Google Cloud, seeded with a demo business, and a judge can sign in and use it right now.

The audit trail is the accomplishment I care about most. Every AI action carries its real model, tokens, latency, cost and outcome. Nothing about the system is a black box, which is the only honest basis on which to let software act without asking first.

845 backend tests pass across 72 test modules. Tenant isolation, Stripe webhook signature verification, plan gating and GDPR export were each verified against the live production deployment rather than a local run, and a UAT gate runs against production before every release. That gate has already caught a real anonymous-access bug that no unit test would have found.

The whole stack boots with zero secrets, on an in-memory store and a deterministic mock model, so anyone can clone it and have it running in a minute without an account anywhere.

And it was built by one person in sixty days.

What we learned

Determinism is a feature. The instinct is to let the model do everything, but payment reconciliation, plan enforcement and cross-module triggers all became more trustworthy as plain code. The model is best at judgement calls, which is exactly where rules are worst.

Verification has to be adversarial to be worth anything. The tenant isolation regression passed every test and every review that assumed the code was correct. It was found by a pass that assumed the opposite.

Autonomy is only acceptable with an audit trail. Users forgive an agent that made a debatable call. They do not forgive one that cannot explain what it did.

Writing style is a product requirement, not a polish step. When your software sends messages under a user's name, prose quality is functionality.

Building with AI collapsed the timeline, and it also changed what breaks. Code arrives faster than understanding does, so the review, the tests and the independent verification pass are where the real work moved.

What's next for Kora-AI Back Office

Connect your environment and let it run. The next phase is breadth of connection. Payments beyond Stripe, so a business can bring whichever processor it already uses. Payroll, so paying people becomes part of the same picture as getting paid. CRM, so the client relationship and the money owed stop living in different systems. Logistics and inventory, so a business that ships physical goods gets the same treatment as one that sells time. Accounting exports that hand a clean set of books to whoever files the return.

The onboarding goal is a business connecting its existing environment and nothing else. No migration, no data entry, no rebuilding a workflow inside somebody else's product. You connect the accounts you already have, Kora maps the business from them, and it starts running.

More specialised agents, each precise about one thing. The current eighteen cover the back office of a service business. The same pattern extends: a procurement agent that watches reorder points and raises purchase orders, a payroll agent that runs the cycle and reconciles it against the books, a collections agent that escalates a debt through the full sequence, a tax agent that sets money aside all year instead of in March, a vendor agent that tracks renewals and flags a subscription nobody uses, a support agent that triages the inbox before anyone opens it. Narrow agents with a single clear goal outperform one general assistant, and each new one inherits the memory, the audit trail and the approval gates that already exist.

The destination is an autonomous office. Most of what a small business pays a team to do is mundane and repetitive: the chasing, the filing, the reconciling, the reminding, the routine drafting, the endless keeping of records straight. None of it needs a person. It needs attention, consistency, and memory, which are the three things software is better at than we are.

The goal for Kora is to become that office. A place where the repetitive work of a full back-office team happens on its own, correctly, with every action logged and every consequential decision still routed to a human. The people who own these businesses started them to do the work they are good at. Kora exists so they can spend their time there.

Built With

Share this project:

Updates