docs · v2026.06.r3

Operator docs.

Everything you need to run OEAN.ai end-to-end: schema, partner API, signed audit log, realtime channels, RBAC, and the embedded 炉头 ERP agent. Examples are copy-pasteable; endpoints are the same ones the product runs against in production.

REST·/v1/partner-api
Auth·X-API-Key
Rate·60 req/min default
SLA·99.95% rolling 90d
00 · overview

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.

01 · get started

Quickstart

Provision a tenant, invite operators, and post your first sales order in under ten minutes.

bashcopy
# 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)
02 · platform

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)
note
No client-side caching for transactional reads. gcTime: 5m, staleTime: 0, PWA disabled — the floor needs the truth, not yesterday's number.
03 · platform

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.

sqlcopy
-- 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()
  )
);
10 · module

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
11 · module

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
12 · module

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.

13 · module

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.

14 · module

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.

15 · module

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.

16 · module

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.

20 · platform

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.

bashcopy
# 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"
jsoncopy
{
  "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
21 · platform

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.

bashcopy
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"
}
22 · platform

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.

tscopy
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();
23 · platform

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.

jsoncopy
{
  "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"
}
warning
Audit rows are insert-only. There is no UPDATE or DELETE policy on the table — not even for tenant admins. If a row needs correction, write a compensating entry.
24 · platform

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.

sqlcopy
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
  )
$$;
25 · platform

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.

30 · ai layer

炉头 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)
note
Writes are gated behind an Approvals queue. The agent can draft a purchase order, but a human approves it before the PO is sent.
31 · surfaces

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.

next

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.