API conventions
Hosts, pagination, the filterator query language, batch operations, errors and realtime.
The Feeef API is a REST JSON API. The interactive, always-current endpoint reference lives at
api.feeef.org/openapi (OpenAPI + Scalar UI, also served as
/openapi.json and /openapi.yaml). This page documents the conventions that apply across all
endpoints.
Base URLs & versioning
| Environment | Base |
|---|---|
| Production (dedicated API host) | https://api.feeef.org/v1 |
| Production (path-prefixed mount) | https://…/api/v1 |
| Local development | http://localhost:3333/api/v1 |
Requests should send Accept: application/json. Resources follow REST-ish naming:
GET /products, POST /orders, PUT /stores/:id, DELETE /apps/:id, plus RPC-style
sub-actions (POST /orders/calculate, POST /store_templates/:id/install).
Pagination
List endpoints accept page and limit and return the Adonis paginator envelope:
{
"data": [ { "id": "..." } ],
"meta": { "total": 128, "currentPage": 1, "perPage": 50 }
}Some lightweight lists return a bare array instead — both SDKs normalize the two shapes into a
single ListResponse with data, total, page, limit.
Filtering: flat params & the filterator
Most lists accept flat query params (e.g. products: store_id, q, status,
category_id, price_min, price_max, in_stock, order_by=field:dir, page, limit).
For arbitrary conditions, pass a filterator — a URL-encoded JSON query:
{
"filtering": {
"condition": "and",
"filters": [
{ "field": "status", "operation": "equals", "value": "published" },
{ "field": "price", "operation": "lessOrEqual", "value": 5000 }
],
"groups": []
},
"ordering": [{ "field": "sold", "dir": "desc" }],
"paging": { "limit": 12, "offset": 0 }
}curl "https://api.feeef.org/v1/products?store_id=STORE&filterator=%7B...%7D"Operators (long names preferred; short aliases in parentheses):
| Operator | Meaning |
|---|---|
equals (eq) / notEquals (neq) | Equality |
contains, startsWith, endsWith | String matching |
greaterThan (gt), greaterOrEqual (gte), lessThan (lt), lessOrEqual (lte) | Comparison |
inList (in), notIn | Set membership — pass values: [...] |
isNull, isNotNull | Null checks (no value) |
Field names are camelCase and mapped to snake_case columns server-side. Groups nest with their
own condition for (A and B) or C trees. The same filterator surface powers the app list
(GET /apps), orders, products and more.
Batch operations
Bulk endpoints use AIP-style :actions on the collection:
| Endpoint | Purpose |
|---|---|
POST /{resource}:batchDelete | Delete many by ids |
POST /{resource}:batchUpdate | Update many with an updateMask + shared fields |
POST /{resource}:batchCreate | Create many |
POST /inventory/reservations:batchRelease | Domain-specific batch actions |
Responses are a partial-success envelope:
{
"resources": [ { "id": "..." } ],
"failedRequests": {
"orders/abc123": { "code": 9, "message": "FAILED_PRECONDITION: ..." }
},
"summary": { "total": 10, "succeeded": 9, "failed": 1 }
}HTTP 200 does not mean every item succeeded — always inspect summary and
failedRequests. A fully-failed batch may come back as 400 with the same envelope; both SDKs
parse it either way.
Errors
| Status | Shape | Meaning |
|---|---|---|
401 | { "errors": [{ "message": "Unauthorized access" }] } | Missing/expired token |
403 | { "error": "insufficient_scope", "requiredScope": "store.integrations" } + WWW-Authenticate | Token lacks a required scope (RFC 6750) |
404 | { "message": "Row not found" } | Unknown resource (or hidden for privacy) |
422 | { "errors": [{ "message": "...", "rule": "required", "field": "name" }] } | Validation (VineJS) — one entry per violation |
429 | rate-limit error | Sensitive endpoints (signup, auth-code consume) are rate limited |
OAuth endpoints return RFC 6749-style bodies instead: { "error": "invalid_grant", "error_description": "..." }.
Realtime (SSE)
The backend broadcasts CRUD events over Server-Sent Events (AdonisJS Transmit) at the API
origin without the version prefix: https://api.feeef.org/__transmit/events.
- Channels follow resource paths, e.g.
stores/{storeId}/orders,users/{userId}. - Events are
{ "event": "created" | "updated" | "deleted", "data": { ... } }. - Subscribing requires the bearer token on the subscription POST.
Both SDKs ship helpers — see JS realtime and the Dart
Feeef.instance.realtime.
File uploads
POST /services/storage/upload accepts multipart uploads (field file, optional folder) and
returns { "url": "https://storage.feeef.net/..." }. The SDKs wrap it with progress callbacks.
OpenAPI
The complete generated reference (rebuilt from the backend spec with npm run api:regen):
- Interactive UI:
https://api.feeef.org/openapi - Raw spec:
https://api.feeef.org/openapi.json/.yaml - Docs site: API reference