# How we draft our monthly investor update A monthly agent we run on Kortix — connected to Postgres, Stripe, and last month's update. It pulls the core metrics, compares them to last month and to plan, and drafts the update in our format for a founder to finalize. Canonical page: https://kortix.com/use-cases/investor-update The monthly investor update is a small, recurring task that eats a founder's time. The numbers live in a few different places, they have to be pulled and compared to last month and to plan, and then the whole thing has to be written up in a consistent format. None of it is hard; it's just an hour or two of gathering and formatting that comes around every month. We run an agent on Kortix that does the gathering and the first draft. This is how we draft our own investor update, including the connections and guardrails involved. - **Team:** Kortix - **Runs on:** A monthly cron - **Connected systems:** Postgres · Stripe · Last month's update - **Mode:** Read-only · a founder finalizes and sends ## The problem Writing the update every month means pulling MRR and revenue from Stripe, active accounts and growth from the product database, and burn and runway from the finance numbers, then setting each against last month and against plan. The data is spread across systems, the comparisons are done by hand, and the write-up has to match the format investors are used to seeing. The common fixes are incomplete. A BI dashboard shows the current numbers but doesn't write the narrative or compare against plan. A saved template still needs someone to fill in every figure. Doing it by hand each month is reliable but it's the founder's time going into gathering and formatting rather than into the commentary that actually matters. ## What we built On Kortix, a monthly cron triggers an agent. It spawns an isolated session (a cloud sandbox) with read-only access to the product database, Stripe, and last month's update. It pulls the core metrics — MRR, growth, burn, runway, active accounts — compares them to last month and to plan, and drafts the update in our usual format as a document. A founder edits it and sends it. ## How it works ### Trigger the draft on a monthly cron A **cron trigger** fires once a month and spawns a fresh **session** in its own sandbox. Each run pulls the current month's numbers and produces one draft. One run maps to one session on one disposable machine, and nothing carries over between months except what's read from the source systems. ### Give the agent our format and what matters The shape of our update lives as **skills** and **memory** that travel with the agent: which metrics we report, how we define each one, the format and section order investors expect, and the plan targets to compare against. Last month's update is the reference for tone and structure. When we change how we report, we write it down and the agent follows it next month. ### Connect Postgres, Stripe, and last month's update Through scoped **connectors**, brokered server-side so no raw token reaches the model, the agent can: - **Query Postgres** — active accounts, growth, and the product metrics we track. - **Read Stripe** — MRR and revenue for the month. - **Read last month's update** — the prior numbers to compare against and the format to match. - **Draft into a document** — the numbers and the narrative assembled in our format for a founder to edit. ### Set the guardrails The agent is **read-only** across every connected system: it reads the database, Stripe, and last month's update, and it writes nothing back to any of them. Its one output is a draft document. A founder finalizes and sends; the agent never sends anything. Credentials are encrypted in the Secrets Manager and injected at runtime, never shown to the model or written to logs. ### Hand a founder a finished first draft With that in place, the start of each month produces a draft with the metrics pulled, the month-over-month and against-plan comparisons filled in, and the narrative written in our format. The founder edits the commentary, checks the numbers, and sends it. The gathering and formatting are done; the judgment stays with a person. > **The pattern** > A monthly **cron** spawns a session with read-only **connectors** into Postgres, > Stripe, and last month's update. Our format and metric definitions are encoded as > **skills** and **memory**. The agent drafts; a founder finalizes and sends. ## Guardrails The agent reads revenue and product data to draft a document, so the access is scoped and contained: - **Isolation.** Every run happens in its own per-task microVM sandbox. The session reads only the systems it's scoped to, and only the draft document leaves the sandbox. - **Scoped secrets.** The Postgres and Stripe credentials are encrypted in the Secrets Manager and injected into the sandbox at runtime, never exposed to the model or the logs. - **Read-only.** The agent reads every source and writes back to none of them. Its only output is a draft, and it never sends. A founder owns the send. - **Everything is code.** The agent's configuration, skills, and per-system permissions are files in the repo, versioned and changed through a reviewed **change request** rather than a dashboard setting. ## The outcome - **Every month:** A finished first draft ready at the start of the month - **Read-only:** Nothing written back to any source system - **3 systems:** Postgres, Stripe, and last month's update in one agent The monthly update now arrives as a draft with the numbers pulled, the comparisons done, and the narrative written in our format. The founder spends their time on the commentary and the send rather than on gathering and formatting, and the data is only ever read.