What you're reading
OEAN.ai is a bilingual (EN / 中文) ERP that unifies sales, purchasing, manufacturing, inventory, accounting, HR, logistics, and CRM into one operator workspace. It is built for daily use across multiple legally separate company entities, with strict data isolation, realtime synchronization, and an embedded AI agent (炉头) that reasons over the entire business in real time.
Everything in this doc maps to a live endpoint, table, or function in the running product. Where a section shows a curl call, that call works against your tenant the moment a partner API key is provisioned.
Quickstart
Provision a tenant, invite operators, and post your first sales order in under ten minutes.
# 1. Health check against your tenant
curl https://api.oean.ai/v1/partner-api/health \
-H "X-API-Key: $OEAN_API_KEY"
# → { "status": "ok", "partner": "acme-erp", "timestamp": "..." }From there, the recommended bring-up order is: chart of accounts → company entities → products & BOMs → opening stock → first quote → convert to SO → post invoice.
- Base URL
- https://api.oean.ai/v1/partner-api
- Auth header
- X-API-Key: <key>
- Content type
- application/json
- Default rate
- 60 requests / minute / key
- Pagination
- ?page=1&limit=50 (max 100)
Architecture
The product is a single React application backed by managed Postgres with row-level security, edge functions for business logic and AI, and Cloudflare R2 for documents. Centralized financial logic lives in a single computeOrderTotals path — every quote, SO, invoice, and credit memo settles through it.
- Frontend
- React 19 · TanStack Start · Tailwind v4
- Backend
- Managed Postgres + RLS · edge functions (Deno)
- AI
- Gemini 2.5 / 3 family via shared ai-router
- Documents
- Cloudflare R2 (signed URLs only)
- Realtime
- Postgres channels · per-table filters
- Mobile
- Capacitor (iOS + Android)
gcTime: 5m, staleTime: 0, PWA disabled — the floor needs the truth, not yesterday's number.Multi-entity isolation
Every transactional row carries a company_entity_id. Stock, AR, AP, ledgers, and reports are scoped to the active entity and cannot bleed across without an explicit inter-company transfer. RLS enforces this at the database layer — application bugs cannot leak data between entities.
-- Every operational table follows this pattern
alter table sales_orders enable row level security;
create policy "entity isolation"
on sales_orders for select
to authenticated
using (
company_entity_id = any (
select company_entity_id
from user_entity_access
where user_id = auth.uid()
)
);Sales & CRM
Quotation Orders → Sales Orders → Invoices → Receipts. Per-entity stock deduction at SO post; AR auto-opens at invoice. The CRM ships with Kanban pipeline, activity timeline, VIP loyalty tiering, and a passwordless customer portal (UUID-based) for orders, invoices, support tickets, and documents.
- Pipeline
- QO → SO → INV → RCPT (immutable IDs)
- Credit holds
- auto, via notify-credit-hold function
- Lead scoring
- ai-erp agent + crm-score-lead
- Email tracking
- open + click via crm-email-track
- Commission
- rule engine, posted at receipt
Purchasing & suppliers
Purchase Orders with receiving logs and auto AP bill creation. Supplier intelligence streams competitor pricing, dossier enrichment, and category coverage. An inter-company BOT auto-generates matching SO/PO pairs when one entity sources from another.
- PO lifecycle
- DRAFT → SENT → PARTIAL → RECEIVED → BILLED
- Auto categorize
- auto-categorize-supplier-products edge fn
- Price monitoring
- competitor-price-scraper (daily)
- Dossier
- market-intel-dossier + enrich-supplier-info
Inventory
Per-company stock with a cross-entity heatmap and AI transfer suggestions. Smart reorder alerts, ABC classification, and inventory audits with variance posting. Movements are logged with operator, location, and a before / after snapshot.
Manufacturing & MRP
BOM aggregation, routings, work orders, and a shop-floor terminal with QR-based punch in / out. Production Orders compute direct and indirect cost in real time; MRP rolls demand forward across multiple weeks of forecast plus open SOs.
Accounting
Double-entry general ledger with multi-currency support, AR, AP, cash drawer reconciliation, tax reporting (NY 8.875% configured by default), and P&L. Bank and cash balances are derived from posted journal entries — no separately maintained balance you can drift from the ledger.
HR & time tracking
Payroll with full tax deductions, AI performance reviews, headcount and leave. Clock-in supports geofencing, face recognition (face-api), and QR punch locations. An auto clock-out cron enforces 40h soft and 60h hard weekly caps.
Logistics
Carrier tracking (FedEx rates wired by default), partial shipments, backorder (BO-) workflow, AI-driven autonomous routing, alerts, and a delivery calendar that respects each entity's cutoff times.
Partner REST API
Stable REST endpoints, cursor pagination, per-key permissions, and a rolling 60 req / minute rate limit (configurable per partner key). Every request is logged with latency and response code for billing and forensics.
# List products in your tenant
curl "https://api.oean.ai/v1/partner-api/products?category=fryers&limit=50" \
-H "X-API-Key: $OEAN_API_KEY"{
"products": [
{
"id": "8c2f...",
"name": "Panda PR-60 6-burner range",
"category": "ranges",
"series": "Pro",
"price": 4280.00,
"stock": 14,
"width": 36, "depth": 32, "height": 57, "weight": 480
}
],
"total": 142,
"page": 1,
"limit": 50
}The price field is stripped automatically if the calling key does not carry the pricing permission. Endpoint access is enforced per-key — calling a route you don't own returns 403 with the list of permissions you do hold.
- GET /health
- key validity + permissions
- GET /products
- ?category=&search=&page=&limit=
- GET /inventory
- ?product_id=&company_id=
- GET /orders
- ?status=&order_id=&page=&limit=
- POST /orders
- create draft sales order
Webhooks
Subscribe to operational events. Payloads are signed with HMAC-SHA256 using the secret returned at subscription time; verify before processing. Delivery retries 8 times with exponential backoff.
X-OEAN-Event: order.posted
X-OEAN-Signature: t=1735689600,v1=8a3f...
{
"event": "order.posted",
"order_id": "SO-22841",
"entity": "lt-equipment-ny",
"total": 48200.00,
"by": "m.chen@oean.ai",
"at": "2026-06-01T09:41:22Z"
}Realtime channels
Postgres realtime broadcasts row-level changes filtered per active entity. Pages that need the truth (shop-floor terminal, dispatch board, AR collections) subscribe directly instead of polling.
const channel = supabase
.channel("sales_orders:" + entityId)
.on("postgres_changes", {
event: "*",
schema: "public",
table: "sales_orders",
filter: `company_entity_id=eq.${entityId}`,
}, (payload) => apply(payload))
.subscribe();Audit log
Every mutation writes a row to audit_log with the operator, IP, before / after diff, and an HMAC signature. The signature uses a per-tenant secret rotated quarterly; exports are verifiable offline without contacting our servers.
{
"at": "2026-06-01T09:34:51Z",
"by": "j.wong@oean.ai",
"entity": "lt-equipment-ny",
"table": "gl_periods",
"row": "GL-7712",
"diff": { "status": ["draft", "locked"] },
"sig": "sha256=4c1e...verified"
}RBAC & permissions
Roles live in a dedicated user_roles table (never on profiles, to avoid privilege-escalation attacks). Access is checked via a SECURITY DEFINER function so RLS policies stay recursion-free.
create or replace function has_role(_user_id uuid, _role app_role)
returns boolean
language sql stable security definer set search_path = public
as $$
select exists (
select 1 from user_roles
where user_id = _user_id and role = _role
)
$$;SSO / SAML
Okta, Azure AD, JumpCloud, and any generic SAML 2.0 IdP. SCIM provisioning supported on the Enterprise plan; group → role mapping is configured per tenant.
炉头 ERP agent
炉头 is an agentic assistant with read access to 200+ tables and 18 tool functions covering supply chain, CRM, finance, HR, procurement, and shipping. It runs multi-step reasoning, drafts emails, scores leads, predicts churn, narrates reports, and explains ledger movements in operator language.
- Model tiering
- flash-lite (classify) / flash (compose) / pro (reason)
- Sub-agents
- cx · finance · hr · procurement · shipping · supply-chain
- Schedules
- hourly digest · daily CEO briefing · night-shift sweep
- Voice
- STT + TTS via ai-stt / ai-tts (low-latency edge)
Customer portal
Passwordless UUID auth. Customers see their orders, invoices, shipments, support tickets, and documents — scoped to their account only via RLS. A parallel Supplier Portal exists for inbound PO acknowledgements, ship-by-date confirmation, and ASN upload.
Get a partner key.
Implementation engineers pair with you through go-live for tenants above 100 seats. For a partner key, the security review packet, or an SLA addendum, talk to us directly.