A canvas for your relations
See every entity and how they connect. Drag from one entity to another to create a relation, choose one or many, and name the field that points back. Open drafts can be reviewed and published together.
Closed beta · by invitation
Design your entities in the browser, or hand the job to your AI agent. PLEXXER hosts the database and puts a JSON API on top of it, with validation, relations, aggregations, scoped access tokens, row security for your users, and backups. Your team and the AI agents working with it start from the same documented endpoints.
POST/d/{appKey}/Order/read
{1 "status:eq": "paid", "placedAt:gte": "2026-09-01T00:00:00Z", "query": { "sort": { "placedAt": -1 }, "limit": 2,2 "related": ["customer"],3 "count": true }}Response● 200
{ "success": true, "documents": [ { "_id": "6a3f09c2e1b74d5a8c0f2b11", "orderNumber": 10482, "status": "paid", "total": 129.5, "placedAt": "2026-09-28T14:02:11Z", "customer": { "_id": "6a3e77b0e1b74d5a8c0f1a02", "name": "Acme Inc" } }, { "orderNumber": 10479, … } ], "total": 243}A filter is a field and an operator in one key. Several keys combine with AND.
The customer of every order comes back in the same response, with no second request.
Ask for the total number of matches when you need it for paging.
POST/d/{appKey}/Customer/create?return=graph
{ "name": "Acme Inc",1 "address": { "street": "Main 1", "city": "Amsterdam" }, "orders": [2 "6a3f09c2e1b74d5a8c0f2b11",3 { "orderNumber": 10483, "total": 29.95 } ]}Response● 200
{ "success": true, "created": { "_id": "6a41c2d4e1b74d5a8c0f3c20", "name": "Acme Inc", "address": { "_id": "6a41c2d4e1b74d5a8c0f3c21", "street": "Main 1", "city": "Amsterdam" }, "orders": [ { "_id": "6a3f09c2e1b74d5a8c0f2b11", … }, { "_id": "6a41c2d4e1b74d5a8c0f3c22", … } ] }}An inline object creates the related document and links it.
An id connects a document that already exists.
Everything is written in one transaction. If any part fails validation, nothing is stored.
POST/d/{appKey}/Order/aggregate
{1 "filter": { "placedAt:gte": "2026-01-01T00:00:00Z" },2 "groupBy": ["country"], "metrics": {3 "revenue": { "sum": "total" }, "orders": "count" }, "sort": { "revenue": -1 }, "limit": 3}Response● 200
{ "success": true, "results": [ { "country": "NL", "revenue": 48210, "orders": 182 }, { "country": "DE", "revenue": 31775.5, "orders": 121 }, { "country": "BE", "revenue": 12940, "orders": 57 } ]}The same filter grammar as a read.
Group by up to four fields, or leave grouping out for a single row of totals.
Count, sum, average, minimum and maximum, each under a name you choose.
POST/d/{appKey}/Order/update
{1 "status:eq": "pending", "placedAt:lt": "2026-09-01T00:00:00Z",2 ":set": { "status": "expired" }, ":inc": { "reminders": 1 }}Response● 200
{ "success": true, "matchedDocuments": 12, "modifiedDocuments": 12}The filter decides which documents change. A request without one changes nothing.
Operators start with a colon: set, unset, increment, and four for arrays.
01 — AI agents
A coding agent does good work when the API explains itself and its credentials say how far it may go. PLEXXER provides both.
Claude Code, Cursor, VS Code and any other MCP client connect with one access token. The agent gets tools for exactly what the token allows, and nothing it may not use is even listed.
Every app publishes an OpenAPI 3.1 document of its entities, typed field by field and cut to the token that asks. Import it into an agent framework or a client generator.
The complete API reference as plain Markdown, with no sign-in. Point an agent at it and it has the grammar, the endpoints and every error code.
02 — How it works
You describe what you store. The API for it exists the moment you publish, and it changes when your schema does.
Add entities and fields in the schema designer: types, required fields, validators, input filters, indexes and relations between entities.
Changes stay in a draft until you publish them. Publishing shows what will change and puts the new version live at once, with no migration to write.
Each entity answers on create, read, update, delete and aggregate. Call the JSON API from any language, or download a typed C# or TypeScript client generated from your schema. Writes that belong together go in one batch, across entities, and are applied all or not at all.
| Field | Type | Rules |
|---|---|---|
| orderNumber | number | required, unique index |
| status | string | required |
| total | number | min 0, 2 decimals |
| placedAt | date | required, indexed |
| note | string | trim, max length 500 |
| customer | relation | one Customer |
| items | relation | many OrderItem |
03 — Day after launch
A schema is never finished. Three tools for the day after launch.
See every entity and how they connect. Drag from one entity to another to create a relation, choose one or many, and name the field that points back. Open drafts can be reviewed and published together.
Orderdraft against version 7needs confirmation
Edit in a draft while the published version keeps serving. Before publishing you see exactly what changes, and a change that can affect stored data is flagged. Every version is kept, and you can roll back to any of them.
Copy an app, with or without its data, to get a test environment. When a change is ready, replace production from the copy: its key, tokens and address stay the same, and a backup is taken before anything is overwritten.
04 — Query language
A filter is plain JSON: the field, a colon, the operator. The same keys select what a read returns, what an update changes and what a delete removes.
| Compare | eqnegtgteltlte |
|---|---|
| Match a list | innin |
| Match text | like |
| Check presence | exists |
| Combine | :and:or |
| Change | :set:unset:inc:push:pull:addtoset |
| Shape the result | sortlimitoffsetincludeFieldsexcludeFieldscount |
POST/d/{appKey}/Customer/read
{ "country:in": ["NL", "BE"], "query": { "related": [ "address.country", { "field": "orders", "filter": { "status:eq": "paid" }, "sort": { "placedAt": -1 }, "limit": 3 } ] }}05 — Realtime
Switch realtime on for an entity and your app hears of every row that is added, changed or removed, the moment it is saved. In the browser, on your server, in code an agent writes for you. Only where you want it: entities without the switch work exactly as before.
// hear of every open task, as it is saved{ "type": "subscribe", "id": "tasks", "entity": "Task", "filter": { "status": "open" } } { "type": "event", "id": "tasks", "kind": "insert", "document": { "_id": "6a3f…", "title": "Write launch post", "status": "open", "tenantId": "acme" } }{ "type": "event", "id": "tasks", "kind": "leave", "document": { "_id": "6a3f…" } }Not everything your app shares is a row. Someone is typing, a cursor moved, an export finished. A broadcast channel carries it to everyone listening, the moment it is sent, over the same connection that brings the changes.
{ "type": "subscribe", "id": "room", "channel": "chat:acme:general" }{ "type": "send", "channel": "chat:acme:general", "payload": { "event": "typing", "user": "alice" }, "id": "m1", "ack": true } // every listener in the room, as it was sent{ "type": "message", "id": "room", "channel": "chat:acme:general", "payload": { "event": "typing", "user": "alice" } }{ "type": "sent", "id": "m1" }"policies": [ { "name": "rooms", "pattern": "chat:{org}:**", "mode": "permissive", "operations": ["receive", "send"], "rule": { "org": "$claims.org" } }]06 — Security
Access is granted per token and per entity, and always written out in full. There is no admin shorthand to hand out by accident.
| Entity | Grant | read | aggregate | create | update | delete |
|---|---|---|---|---|---|---|
| Product | allowed | allowed | refused | refused | refused | |
| Order | allowed | allowed | allowed | allowed | allowed | |
| Customer | refused | refused | allowed | allowed | allowed |
Let the users of your app call PLEXXER directly, from a browser or a phone, with a token your backend signs. Row policies on each entity decide which rows a user may read and change, and the fields that tie a row to its tenant and owner are filled in from the token, never taken from the request. A forgotten filter cannot leak a row, because the caller is not the one adding it.
"policies": [ { "name": "tenant", "mode": "restrictive", "operations": ["read", "create", "update", "delete"], "rule": { "tenantId": "$claims.org" } }, { "name": "members", "mode": "permissive", "operations": ["read"], "rule": true }, { "name": "owner", "mode": "permissive", "operations": ["update", "delete"], "rule": { "ownerId": "$claims.sub" } }],"stamp": { "tenantId": "$claims.org", "ownerId": "$claims.sub" }Mark a field as encrypted and send your key with the request. PLEXXER encrypts the value before it is stored and decrypts it on the way back, and never keeps the key: not in the database, not in a log. A request without the key gets the rest of the record and is told which fields it did not get.
{ "_id": "66f0…", "name": "Alice", "ssn": "123-45-6789" }{ "_id": "66f0…", "name": "Alice", "_encrypted": ["ssn"] }07 — Operations
The parts a team usually builds in its second year are there on the first day, and they stay the same as an app grows from a prototype to millions of calls a month.
Back up configuration, data or both, from the dashboard or on your own schedule through the API, and restore any of them when you need to.
Declare unique, compound and expiring indexes next to your fields. They are applied on publish, and you can compare what is live with what is declared.
Calls per app, per verb and per day, and the most recent errors with their codes, in the dashboard and over the API.
Every error carries a code that does not change and says what it concerns: the field, the entity or the limit. The full catalogue is one request away.
Download a C# or TypeScript client generated from your published schema: typed entities and a project that builds as it is.
Requests are answered quickly and stay quick as an app grows. Paging, field selection and bulk create keep each call small.
08 — Licences
Every licence includes the whole product. Licences differ in how much you can run, and in one thing: where your data may live.
For trying PLEXXER and for small projects.
For products in production.
For teams running many apps or heavy traffic.
Every limit lifted, by agreement.
402 plan-limit-reached and names the limit. When a database is full, reading and deleting keep working.PLEXXER is in closed beta and accounts are created by invitation. Tell us what you are building and we will e-mail you an invitation link. You choose your own password when you accept it.
Already have an account? Sign in to the dashboard