Skip to navigation

Eilith Public REST API — Overview

View as Markdown

Quick Start

Authorization: Bearer <YOUR_ACCESS_TOKEN>
Base URL (Production):
https://krjtnboxzkrhejhqtwgm.supabase.co/functions/v1/platform-api/api/v1
Base URL (Staging):
https://pmjidplmzxohyqsbajvg.supabase.co/functions/v1/platform-api/api/v1

All endpoints return Content-Type: application/json. CORS is enabled for all origins.


Authentication

Every request requires an Authorization: Bearer <api_key> header.

Key format: uk_live_ + 40 hex characters (64 chars total).

Keys are SHA-256 hashed before storage — the plaintext key is only shown once at creation time via the generate_api_key RPC from the Flutter UI.

Key Management (via Flutter UI, not REST)

ActionRPCReturns
Create keygenerate_api_key(tenant_id, name, scopes?){ id, key, prefix, name, scopes }
List keyslist_api_keys(tenant_id)[{ id, name, key_prefix, scopes, is_active, ... }]
Revoke keyrevoke_api_key(key_id)boolean

The full key is only returned at creation. After that, only key_prefix (first 16 chars + ...) is stored.


Plan Gating

API access is tiered by subscription plan:

PlanMonthly LimitNotes
unibasic0 (no access)Returns 403 with upgrade_required: true
uniledger_plus10,000 requests
uniledger_plusplus100,000 requests

Exceeding the limit returns 429 with { error, limit, used, resets_at }.


Pagination

All list endpoints use cursor-based pagination with consistent parameters:

ParamTypeDefaultDescription
limitint25Items per page (1–100)
cursorstring—ID of last item from previous page
sortstringcreated_atSort field
orderstringdescasc or desc

Response shape:

{
"data": [...],
"pagination": {
"next_cursor": "uuid-or-null",
"has_more": true
}
}

To paginate: pass cursor from pagination.next_cursor of the previous response.


Endpoints

Products (Inventory Items)

MethodEndpointDescription
GET/productsList products (paginated)
POST/productsCreate a product
GET/products/{id}Get a product
PUT/products/{id}Update a product
DELETE/products/{id}Soft-delete a product
POST/products/{id}/imageUpload product image (multipart)
DELETE/products/{id}/imageDelete product image

List query params: status (active/inactive/archived), item_type (stock/service/consumable/assembly), search (name or SKU).

Create required fields: sku, name, item_type, default_uom.

Update allowed fields: sku, name, item_type, status, default_uom, cost_method, description, reorder_point, reorder_qty, is_stockable, is_purchasable, is_sellable, is_manufacturable, standard_cost, image_url.

Image upload: Content-Type: multipart/form-data with a file field. Accepted types: JPEG, PNG, WebP, GIF. Max 5MB. Returns { image_url } (signed URL, 7-day expiry).

Soft-delete sets deleted_at and status: "archived".


Stock

MethodEndpointDescription
GET/stockList stock balances (paginated)
GET/stock/{item_id}Get stock for a product across locations
POST/stock/adjustCreate a stock adjustment

List query params: location_id, item_id.

Adjust required fields: inventory_item_id, location_id, quantity (positive = add, negative = subtract).

Adjust optional fields: uom (default ea), unit_cost (default 0), notes.


Customers

MethodEndpointDescription
GET/customersList customers (paginated)
POST/customersCreate a customer
GET/customers/{id}Get a customer
PUT/customers/{id}Update a customer

List query params: search (name, email, or phone).

Create required fields: customer_code, name.

Update allowed fields: customer_code, name, email, phone, address_line1, address_line2, city, state, postal_code, country, is_active.


Orders (Sales Orders)

MethodEndpointDescription
GET/ordersList orders with line items (paginated)
POST/ordersCreate an order with lines
GET/orders/{id}Get an order (includes lines)
PUT/orders/{id}Update order header fields

List query params: status (draft/confirmed/fulfilled/cancelled).

Create required: lines array (min 1 item), each with quantity and unit_price.

Line item fields: inventory_item_id, description, quantity, unit_price, discount_pct (default 0), tax_pct (default 0).

The API auto-calculates subtotal, tax_total, grand_total, and per-line total. Orders are created with source: "integration".

Update allowed fields: customer_name, customer_email, customer_phone, status, currency_code.


Usage

MethodEndpointDescription
GET/usageCurrent month’s API usage stats

Response:

{
"plan": "uniledger_plus",
"month": "2026-08",
"used": 1234,
"limit": 10000,
"active_keys": 2
}

Error Responses

All errors return { "error": "message" } with an appropriate HTTP status:

StatusMeaning
400Validation error (missing required field, invalid payload)
401Missing/invalid API key, revoked key, or expired key
403Plan does not include API access
404Resource not found
405HTTP method not allowed
429Monthly rate limit exceeded
500Internal server error

Architecture

Request → Supabase Edge Function (platform-api)
→ authenticateApiKey() — SHA-256 hash lookup, plan check, usage check
→ handlePublicApi() — route by path + method
→ logApiUsage() — async insert to api_usage_logs (fire-and-forget)
→ Response

Key tables:

TablePurpose
uniledger.tenant_api_keysAPI key hashes, scopes, plan limits
uniledger.api_usage_logsPer-request logging (endpoint, method, status_code)
uniledger.tenant_webhook_endpointsWebhook URLs + secrets (Phase 2, not yet wired)
uniledger.inventory_itemsProducts (image_url, image_gallery columns added)
uniledger.sales_ordersOrders (source: "integration" for API-created)
uniledger.customersCustomer master data

Rate Limiting

Rate limits are checked per-tenant (not per-key). Each key on the same tenant shares the same monthly counter. The monthly_limit field on tenant_api_keys overrides the plan default if set.

Usage resets on the 1st of each month (UTC).


Phase 2 (Planned)

  • Webhooks:tenant_webhook_endpoints table is ready. Events will include order.created, order.updated, stock.adjusted, etc.
  • GraphQL: After REST stabilizes.
  • Additional integrations: Shopify, WooCommerce, Square.