Documentation

REST API reference

obop's REST API: sign in, agent tokens, commands, meta, the generated get, list, search, create, update and delete endpoints, workflow actions, approvals, filters, errors and status codes.

The REST API is generated from the installed modules. There is no OpenAPI document yet; this page lists everything that exists. All bodies are JSON and all calls except login need a bearer token.

text
Authorization: Bearer obop_...
Content-Type: application/json

Endpoints ​

Method and pathPurpose
POST /api/auth/loginBody {"username","password"}. Returns {"token"}, a session token valid for 12 hours. 401 for wrong credentials, 429 when locked out.
POST /api/auth/agent-tokenBody {"scopes":[...],"label":"..."}. A person creates a scoped agent token. Returns {"token"} once.
GET /api/commandsThe commands this caller may use: {"commands":[{"id","description","needs_approval"}]}.
GET /api/metaThe modules, models, readable fields and actions this caller may see. Drives the web app.
POST /api/{model}/{command}Runs a command. The body is the command input.
POST /api/approve/{plan_id}A person approves a proposed plan.

Instances also serve the web app from /. The control plane serves its own endpoints under /api/platform/ (sign-up, sign-in, instances); they are used by the sign-up app and are not covered here.

Command responses ​

Success: 200 with

json
{"denied": ["party.tax_id"], "value": { ... }}

denied lists the field names this caller may not see. Needs approval (for an agent): 202 with {"approval_required":true,"plan_id":"..."}.

Generated commands ​

Every model gets these. For model lead:

CommandInputResult
lead.get{"id"}The record.
lead.listoptional filter, order, limit, offsetAn array of records.
lead.searchsame as list{"rows":[...],"total":N}, where total counts matches before limit and offset.
lead.createthe fieldsThe new record, with its id. Governed fields start at their first option.
lead.update{"id", ...fields}The updated record. Changing a governed field runs the matching action.
lead.delete{"id"}{"id"}. Refused while another record refers to it. Agents only propose it.

Each workflow action is one more command: lead.qualify, invoice.post. Input is {"id"}, plus any required fields it needs.

Filters, order and paging ​

json
{
  "filter": [
    { "field": "stage", "op": "in", "value": ["new", "qualified"] },
    { "field": "expected_revenue_minor", "op": "gte", "value": 100000 }
  ],
  "order": [{ "field": "expected_revenue_minor", "dir": "desc" }, { "field": "title" }],
  "limit": 50,
  "offset": 100
}
  • Conditions are combined with AND; at most 50.
  • Operators: eq, ne, contains (case-insensitive substring, text only), in (up to 1000 values), gt, gte, lt, lte (not for bool or ref), is_null. ne also matches records where the field is empty; other operators never match an empty value.
  • order: up to 5 keys, dir asc (default) or desc; empty values sort last.
  • limit 1 to 5000 (default: no limit), offset 0 or more.
  • id can be used as a field. Unknown keys, operators or malformed values are 400.
  • Filtering and sorting only work on fields the caller can read in clear. Other fields fail with unknown field <model>.<field>.

Field values ​

KindJSON
text, longtextstring
numberinteger (no floats)
moneyinteger in minor currency units: 12.34 is 1234
date"YYYY-MM-DD", a real calendar date
booltrue or false
selectone of the field's options
refthe id of an existing record of the target model

Status codes ​

CodeMeaning
200Success.
202Approval required; nothing changed.
400Input refused. {"error":"..."} says why.
401Missing, unknown, expired or revoked token; or wrong sign-in.
404Unknown, hidden or forbidden command; unknown plan. Indistinguishable on purpose.
415Body not sent as application/json.
429Sign-in lockout (five failures, one minute).

MCP ​

The same commands are also exposed as MCP tools at POST /mcp (Streamable HTTP, protocol revisions 2025-11-25, 2025-06-18 and 2025-03-26), with the same bearer token, permissions, masking and approvals. A command lead.search is the tool lead__search; its input is the REST body, described by a JSON Schema generated from the model. An approval-required step is a normal result with "status": "approval_required" and a plan_id instead of HTTP 202; a refused input is a tool result with isError: true instead of HTTP 400; a hidden or unknown tool is a JSON-RPC error instead of 404. The OpenAPI document advertises the endpoint in x-mcp, and GET /api/meta in a Link header (rel="mcp"). Setup, limits, discovery documents and client configuration: Connect over MCP.

Versioning ​

There is no version header or deprecation policy yet. The product plan calls for a dated version header; until it exists, treat the API as unstable during the beta and check the changelog.

See Use the REST API for worked examples and the module manifest reference for how fields and actions are declared.