Developers
A REST API that reads like any other.
Server-to-server, bearer-authenticated, scoped to a tenant with one header. If you’ve used a REST API, you already know how this works.
Authentication
Three headers, from your backend.
Fluidlee is a server-to-server API. Your backend holds the key and calls us on behalf of the logged-in user, always from your server.
| Header | What it does | |
|---|---|---|
Authorization: Bearer <key> | Required | Your project API key (prefix flk_). Identifies the project; the entity comes from the URL, not the key. |
X-Tenant-ID: <uuid> | Required | The tenant this request is scoped to. Every row read or written is confined to it. |
X-Actor-ID: <string> | Optional | An opaque id for the end user acting. Recorded in the audit log purely as a label. |
Endpoints
Five per entity.
The project comes from your API key; the entity comes from the URL. For an entity named
patient, replace :entity with patient.
| Method | Path | Does |
|---|---|---|
| GET | /:entity | List records, filtered, sorted, paginated |
| POST | /:entity | Create a record |
| GET | /:entity/:id | Fetch one record by id |
| PATCH | /:entity/:id | Partial update |
| DELETE | /:entity/:id | Soft-delete (sets deleted_at) |
Listing
Filter, sort, paginate.
GET https://api.fluidlee.com/appointment?status=confirmed&scheduled_at[gte]=2026-07-01&orderBy=scheduled_at&orderDir=asc&page=1&pageSize=25 Authorization: Bearer flk_live_a8f3... X-Tenant-ID: 550e8400-e29b-41d4-a716-446655440000
{
"data": [
{ "id": "3f2c...", "status": "confirmed", "scheduled_at": "2026-07-03T09:00:00Z" }
],
"total": 128,
"page": 1,
"pageSize": 25
} | Syntax | Means |
|---|---|
?field=value | Exact match |
?field[eq]= | Equal to |
?field[gt]= | Greater than |
?field[gte]= | Greater than or equal |
?field[lt]= | Less than |
?field[lte]= | Less than or equal |
?field[like]= | Pattern match (SQL LIKE, % wildcard) |
?field[isnull]=1 | Field is null or absent |
?field[notnull]=1 | Field is present |
| Param | Means |
|---|---|
page | Page number (1-based). Default 1. |
pageSize | Records per page. Default 50, capped server-side. |
orderBy | Field to sort by. Default created_at. |
orderDir | asc or desc. Default desc. |
Writing
Create a record.
Send the fields flat in the body. The platform validates types, checks foreign keys against
the same tenant, runs before_create rules, and returns the stored record,
computed fields included.
curl https://api.fluidlee.com/patient \ -H "Authorization: Bearer flk_live_a8f3..." \ -H "X-Tenant-ID: 550e8400-e29b-41d4-a716-446655440000" \ -H "X-Actor-ID: usr_2f10" \ -H "Content-Type: application/json" \ -d '{ "first_name": "Jane", "mrn": "MRN-4471", "clinician_id": "9b1c..." }'
# 201 Created: computed fields included, FK validated against this tenant { "data": { "id": "7d90c4b2-...", "first_name": "Jane", "mrn": "MRN-4471", "created_at": "2026-07-24T14:22:11Z" } }
// Always from your backend. The key stays server-side. const res = await fetch(`https://api.fluidlee.com/invoice/${id}`, { headers: { 'Authorization': `Bearer ${process.env.FLUIDLEE_API_KEY}`, 'X-Tenant-ID': tenantId, }, }); const { data } = await res.json();
Errors
What can come back.
Every error is JSON with a stable code you can branch on. These are the ones
you’ll actually meet.
| Status | Code | Meaning |
|---|---|---|
| 400 | INVALID_ENTITY / INVALID_ID | The entity name or record id in the URL is malformed. |
| 404 | ENTITY_NOT_FOUND / NOT_FOUND | No such entity in the project, or no such record for this tenant. |
| 422 | VALIDATION_ERROR | A field failed type, required, or foreign-key validation. The body lists each field error. |
| 422 | BLOCKED_BY_RULE | A before_* rule blocked the mutation. The rule’s message is returned. |
| 403 | FEATURE_NOT_IN_TIER | The request used a capability the project’s plan doesn’t include. |
| 429 | RATE_LIMIT_EXCEEDED | Too many requests per minute for the tier. Slow down and retry. |
| 429 | REQUEST_QUOTA_EXCEEDED | The project’s request allowance for the billing period is spent. The response says when it resets. |
| 507 | STORAGE_QUOTA_EXCEEDED | Storage is full. Writes are blocked; reads keep working. |
| 503 | PROJECT_PAUSED | A Free project was paused after 7 idle days. Reactivate it from the dashboard. |
A spec that matches your schema
Each project publishes an OpenAPI document generated from its current schema, so your client code, types and tooling stay in step with what the API accepts. Find it in the dashboard under API reference.
Ready to make the first call?
Request access, define a schema, and your project’s API is live before you finish your coffee.