Flow API

Catalog, stock, partners, sales, invoices, bills, payments and webhooks.

Base: https://flow.eel.software/api/public/v1

The finance and operations app. Every POST that creates or records something requires an Idempotency-Key header, with one exception: the two webhook management routes at the bottom of this page. See the shared rules for pagination, errors and pace, once for all four apps.

Catalog and stock

Method Path Scope
GET /products flow:catalog:read
GET /products/{upid} flow:catalog:read
PATCH /products/{upid} flow:catalog:write
GET /products/{upid}/stock flow:stock:read
GET /warehouses flow:catalog:read
GET /financial-accounts flow:sales:write

GET /products is cursor-paginated and filters with q (name or SKU), active, or updated_since (ISO date). Each row carries its UPID, the id every other product route takes: sku, name, active, list_price, currency, tax and uom.

PATCH /products/{upid} edits name, list_price (decimal string, null clears it), tax_code (null clears it) or active. sku and uom are deliberately immutable through the API.

GET /products/{upid}/stock returns per-warehouse on_hand and available-to-sell (available = on_hand minus active reservations), optionally filtered to one warehouse. It is eventually consistent with checkout.

GET /warehouses lists all active warehouses (CENTRAL and STORE) with the warehouse_id that sales and stock reads take; STORE rows also carry the partner_id of the partner that represents that store.

GET /financial-accounts lists active CASH / BANK / PROCESSOR accounts with their currency, identity only, no balances. It is the source of the financial_account_id that a sale or a payment takes.

Partners

Method Path Scope
GET /partners flow:partners:write
GET /partners/{id} flow:partners:write
POST /partners flow:partners:write

There is no dedicated read scope for partners: reading and searching both ride flow:partners:write, the same asymmetry the scopes catalogue notes for Wrap's notes and follow-ups.

GET /partners?q= searches by name (contains) or tax id prefix. POST /partners upserts a partner (customer/supplier) by tax id (tax_id_type + tax_id_number) or email: it returns the existing record when one matches, so it also doubles as a lookup by NIT. NIT verification digits are validated, or computed when omitted.

Field Type Notes
name string, 1 to 200 chars Required.
tax_id_type NIT\ CC\
tax_id_number string, up to 40 chars Optional.
verification_digit string, up to 2 chars The NIT DV. Computed if omitted.
email string Optional.
roles string[] E.g. CUSTOMER, SUPPLIER, EMPLOYEE.

Sales

Method Path Scope
POST /sales flow:sales:write
GET /sales/{id} flow:sales:write

POST /sales is the one-call sale: it draws stock from a warehouse, invoices the customer, and optionally records the payment.

Field Type Notes
external_ref string, up to 200 chars Your own order id. Also the dedup anchor.
warehouse_id uuid Required.
customer `{ partner_id? \ name/tax_id/email }`
lines { upid, qty, unit_price? }[], min 1 unit_price overrides the resolved list price when given.
payment { method, financial_account_id?, processor_ref?, amount? } method is CASH or PROCESSOR.
emit_einvoice boolean
notes string, up to 2000 chars

GET /sales/{id} returns the invoice number and status, the outbound reference, and dian_status (the status of DIAN e-invoicing).

Invoices and bills

Method Path Scope
GET /invoices flow:documents:read
GET /bills/list flow:documents:read
POST /bills flow:bills:write

GET /invoices browses AR documents (customer invoices) newest-issued first, cursor-paginated: the "revenue this month" view. GET /bills/list is the same shape for AP documents (supplier bills), plus supplier_number and PENDING_APPROVAL as an extra status. flow_list_open_items (below) only shows the unpaid subset of either.

Both filter by status, partner_id, or an issued_from/issued_to window (ISO dates), and both return the formatted number (null while DRAFT), partner_id/name, string money and the retention-aware remaining.

Surface Statuses
Invoices DRAFT, OPEN, PARTIAL, PAID, VOID
Bills DRAFT, OPEN, PARTIAL, PAID, VOID, PENDING_APPROVAL

POST /bills creates a DRAFT purchase bill; a human posts it inside Flow. Reference the supplier by partner_id or pass an inline partner object (upserted by tax id or email). A duplicate (partner, supplier_number) pair is rejected with conflict and the existing bill_id in the error details.

Field Type Notes
partner_id uuid, optional
partner object, optional Inline supplier: name, tax_id_type, tax_id_number, email.
supplier_number string, 1 to 60 chars The supplier's own invoice number.
issue_date ISO date Required.
due_date ISO date, optional
currency string, 3 chars, optional
notes string, up to 2000 chars, optional
lines { description, qty, unit_price, tax_code?, product_upid? }[], min 1 Money as numbers, not strings.

Payments

Method Path Scope
POST /invoices/{id}/payments flow:payments:write
POST /bills/{id}/payments flow:payments:write

Both record a payment (a receipt against an invoice, or a disbursement against a bill), with the same rules: amount is a decimal string in the document's own currency, and the financial_account_id must match that currency. Rejected when the document is DRAFT/VOID/already PAID, the amount exceeds what remains, or the fiscal month is closed.

Field Type Notes
financial_account_id uuid From GET /financial-accounts.
amount decimal string E.g. "150000.00", in the document's currency.
date ISO date Required.
reference string, up to 120 chars, optional

A payment against a bill at or above the workspace's approval threshold goes through only when the caller is a workspace administrator. Both routes return the payment plus the document's post-payment remaining and status.

Open items

Method Path Scope
GET /open-items flow:open-items:read

Open (unpaid) documents with their remaining balance: side=AP for supplier bills owed, side=AR for customer invoices owed to us. Filter by partner_nit or an as_of date. This is the route behind "what do we owe X", "who owes us", and cartera questions generally.

Webhooks

Method Path Scope
GET /webhooks flow:webhooks:manage
POST /webhooks flow:webhooks:manage
DELETE /webhooks/{id} flow:webhooks:manage
GET /webhook-deliveries flow:webhooks:manage
POST /webhook-deliveries/{id}/redeliver flow:webhooks:manage

These two POST routes are the exception to the idempotency rule above: POST /webhooks and POST /webhook-deliveries/{id}/redeliver neither require nor look at Idempotency-Key.

POST /webhooks takes url (up to 2000 chars) and events (at least one of invoice.posted, invoice.paid, bill.posted, payment.created, stock.changed). The response includes the signing secret once, at create, never again. GET /webhook-deliveries?webhook= lists recent deliveries, optionally filtered to one webhook; POST /webhook-deliveries/{id}/redeliver re-queues one for the next drain.

Delivery is at-least-once: a webhook consumer must be idempotent on its own side, keying off the event body, not off delivery count.