Multi-tenancy
Two patterns cover almost every B2B2C build on top of Oligon.
Pattern A — One Oligon org per your customer
Use this when your customers expect their own billing, their own keys, and their own dashboard.
- Create an Oligon org for each customer via the partner API.
- Provision an
adminkey, store it in your secrets store. - Optionally invite the customer's email as
owner— they can then log in via the portal, see their receipts, and rotate their own keys.
from oligon_receipts import OligonReceipts
partner = OligonReceipts(api_key=os.environ["OLIGON_PARTNER_KEY"])
new_org = partner.organizations.create(
name="Acme Inc.",
primary_owner_email="founder@acme.com",
plan="indie",
)Pros — clean isolation, dashboard out of the box, customers can churn without affecting your other tenants.
Cons — Stripe customer per tenant, quota counted per-tenant.
Pattern B — One Oligon org, tag with metadata
Use this when you are the SaaS, your customers do not need a dashboard, and you want a single Oligon bill.
- Keep one Oligon org owned by you.
- Pass
metadata: { "tenant_id": "<your-tenant-id>" }on everyextractcall. The value is stored verbatim and indexed. - Filter
/v1/receiptsbymetadata.tenant_id=...to scope reads.
await client.extract({
file,
metadata: { tenant_id: "tnt_xy", customer_email: "u@acme.com" },
});
// later
for await (const r of client.receipts.list({ "metadata.tenant_id": "tnt_xy" })) {
...
}Pros — minimal moving parts, one bill, fast onboarding.
Cons — no native dashboard for your end-users, you build any UX layer.
What Oligon does under the hood
Whichever pattern you choose, isolation between Oligon orgs is enforced
in Postgres via row-level security (opens in a new tab).
Every connection sets SET LOCAL app.current_org = '...' and every table
has a policy:
CREATE POLICY org_isolation ON receipts
USING (org_id = current_setting('app.current_org')::uuid);A buggy query that forgets a WHERE org_id = ? clause will return zero
rows instead of leaking cross-tenant data.
We run nightly fuzz tests that swap API keys mid-transaction and assert no rows leak. Results are public at trust.oligontech.com (opens in a new tab).