Integration quickstart
This page shows the shortest reliable path from “I have a Moltaro URL and a user” to reading and changing records through the public API. It is written for developers and for AI agents: every Moltaro workspace describes itself through the same endpoints, so one flow works against any installation — discover the schema first, then act on it.
All examples below were executed against a real Moltaro installation; response
bodies are real, trimmed for length. The complete contract is in the
API reference and in the raw OpenAPI document
(moltaro-public-v1.json, also
served by every installation at /openapi/moltaro-public-v1.json).
Conventions
Section titled “Conventions”WORKSPACE_API_BASE_URLis the API host of the deployed installation, for examplehttps://ops.example.com; all endpoints live under its/api/workspace/path. It is nothttps://moltaro.com.- Authentication is a bearer token:
Authorization: Bearer <token>. Tokens come from the login endpoint or from an administrator-issued API key (see Service accounts and API keys). - JSON responses use an envelope:
{ "Data": …, "Errors": [], "Warnings": [], "Success": true }. CheckSuccess, then readData. The exceptions are file downloads (raw file content) and low-level protocol failures such as malformed JSON or an unknown route, which return a plain HTTP problem response instead of the envelope. - JSON property names are PascalCase. Enums are numeric in JSON; the OpenAPI
schema carries the member names as
x-enum-varnames(and, where provided, member descriptions asx-enum-descriptions). - User docs say record type and record; the API says entity definition and entity instance. See the terminology bridge.
Service accounts and API keys
Section titled “Service accounts and API keys”Unattended integrations and AI agents should not run on a person’s login. The credential handoff is a one-time administrator action in the Moltaro Portal or administrative Web Application interface. Installing or registering the Web Application and provisioning access are not part of an agent’s API workflow.
For a trusted AI agent, the administrator opens Administration > Agent
integration, selects Create service user for agent, and confirms the
high-privilege handoff. Moltaro creates a new Service user with the Admin and
Configurator roles, issues a 90-day API key, and downloads a ZIP containing
AGENTS.md, CLAUDE.md, .moltaro/credentials.env, and a nested
.moltaro/.gitignore. The key appears only in that download.
Extract the kit into the intended project, delete the ZIP, and keep
.moltaro/credentials.env local. Never commit, upload, paste, echo, or print
the credentials file or MOLTARO_API_KEY. Other unattended integrations can
still use Administration > Users to create a narrowly scoped Service user and
API key manually.
An agent receives the prepared guide, URL, and token. If they are missing, it asks the user to complete Agent integration in the Portal or administrative interface. It does not create its own service user, issue a key, change roles, or use administrative Agent integration endpoints to discover an unknown workspace.
A service key is gated by roles only — exactly like a human user. With the runtime roles a key covers the record-centric integration surface; with the Admin or Configurator role it can also use the administrative configuration API, including creating and changing entity definitions. Grant the narrowest role set that covers the integration’s task. The one exception is account self-service (profile, password): service accounts have nothing to manage there, and those endpoints answer them with an explicit error.
The curated administrative surface is documented in the Configuration API reference, starting with entity definition and schema field management.
The generated Markdown files contain the installation’s real base URL, the
discovery flow, secret-handling rules, and links to the installation-local
OpenAPI documents. This is a completed administrative handoff, not an agent
task. The agent-integration routes are administrative endpoints of the
configured Moltaro Application, not the Portal. An authorized agent may call
the read endpoints after it already knows WORKSPACE_API_BASE_URL and has a
key; it must not call the access-kit endpoint to provision or elevate itself.
For retry, concurrency, polling, recovery, and audit behavior, apply Reliable API automation.
Step 1 — Read the workspace context
Section titled “Step 1 — Read the workspace context”GET /api/workspace/context requires no authentication and returns the
installation’s locale, time zone, and authoring mode:
curl -s https://ops.example.com/api/workspace/context{ "Data": { "DisplayName": "Acme Operations", "Locale": "en", "TimeZone": "Etc/UTC", "RegionCode": "us", "EnabledFeatures": [], "GoogleMapsApiKey": null, "BusinessLogicAuthoringMode": "NetOperationProject" }, "Errors": [], "Warnings": [], "Success": true}Step 2 — Authenticate
Section titled “Step 2 — Authenticate”POST /api/workspace/auth/login exchanges credentials for an access token.
(For unattended integrations and agents, prefer a
service-account API key and skip this step —
API keys are sent the same way, as bearer tokens.)
curl -s -X POST https://ops.example.com/api/workspace/auth/login \ -H "Content-Type: application/json" \ -d '{ "UserNameOrEmail": "owner@acme.test", "Password": "<password>" }'{ "Data": { "AccessToken": "<jwt-access-token>", "UserId": "f4ac8709-c8ac-4404-a30a-7ddbcfcc8115", "UserName": "owner", "WorkspaceName": "default", "Locale": "en", "TimeZone": "Etc/UTC", "Roles": ["Admin", "Everyone"], "Modules": { "Boards": { "Enabled": false } } }, "Success": true}Send the token on every following request:
-H "Authorization: Bearer <jwt-access-token>". GET /api/workspace/auth/me
returns the same profile for an existing token.
Step 3 — List entity definitions
Section titled “Step 3 — List entity definitions”GET /api/workspace/entity-definitions
returns every record type the caller may see — the workspace’s data model:
{ "Data": [ { "Id": "I0ub2IyjFcx4", "Name": "SupportTicket", "DisplayNameSingular": "Support ticket", "DisplayNamePlural": "Support tickets", "Description": "Demo record type for the integration quickstart.", "CommentsEnabled": true, "AttachmentsEnabled": true, "TagsEnabled": true, "SearchTargetCount": 0, "AuditTrailEnabled": true, "IsReadOnly": false, "DisabledOperations": 0, "UsageType": 0 } ], "Success": true}Name is the stable internal name. Instance endpoints accept either the Id
or this name as {entityIdOrKey}.
Entity Definition discovery does not imply record access. Every record query
and direct read is constrained by the definition’s current Security configuration.
Missing, invalid, disabled, or stale root View Permission Assignments fail closed.
Step 4 — Read one definition to learn its schema
Section titled “Step 4 — Read one definition to learn its schema”GET /api/workspace/entity-definitions/{entityDefinitionId}
returns the full definition, including tables and fields. This is what tells an agent which
field keys exist, their types, and their rules:
{ "Data": { "Id": "I0ub2IyjFcx4", "Name": "SupportTicket", "Tables": [ { "Id": "oGp2IzJVF8Sn", "IsPrimary": true, "Fields": [ { "Key": "Title", "FieldType": 10, "RequiredWhen": "TRUE", "StringMaxLength": 200 }, { "Key": "DueDate", "FieldType": 1 }, { "Key": "Priority", "FieldType": 9, "Options": [ { "Key": "low", "Value": "Low" }, { "Key": "medium", "Value": "Medium" }, { "Key": "high", "Value": "High" } ] }, { "Key": "EstimatedHours", "FieldType": 6, "DecimalPrecision": 6, "DecimalScale": 2 } ] } ] }, "Success": true}FieldType is the numeric EntityFieldTypeEnum (10 = String, 1 = DateOnly,
2 = TimeOnly, 9 = Select, 6 = Decimal, 13 = Reference, 17 = DateTimeOffset,
…). The full list with
semantics is in
Fields and schema.
RequiredWhen: "TRUE" means always required; other values are conditional
expressions.
DateTimeOffset values are ISO 8601 timestamps with an explicit Z or numeric
offset. For example, "2026-07-27T10:15:42.123456+02:00" is returned as the
same instant in UTC: "2026-07-27T08:15:42.123456Z". The API and database
preserve microseconds, but not the sender’s original offset. Never send a local
timestamp without an offset.
Step 5 — Create a record
Section titled “Step 5 — Create a record”POST /api/workspace/entity/{entityIdOrKey}/instances takes a Fields map
keyed by field key:
curl -s -X POST https://ops.example.com/api/workspace/entity/SupportTicket/instances \ -H "Authorization: Bearer <token>" -H "Content-Type: application/json" \ -d '{ "Fields": { "Title": "Printer in hall B is jammed", "DueDate": "2026-07-25", "Priority": "high", "EstimatedHours": 1.5 } }'{ "Data": { "InstanceId": "c24878e1ff334648aeb4022e19f6cd96", "RowVersion": "53cedb7e-dad6-425c-86c1-edd23dacfd59", "ReadableAfterMutation": true, "Instance": { "Id": "c24878e1ff334648aeb4022e19f6cd96", "Number": "1", "DisplayName": "1", "Fields": { "Title": { "State": 0, "Value": "Printer in hall B is jammed" }, "DueDate": { "State": 0, "Value": "2026-07-25" }, "Priority": { "State": 0, "Value": "high" }, "EstimatedHours": { "State": 0, "Value": 1.5 } }, "Permissions": { "CanRead": true, "CanUpdate": true, "CanArchive": true, "CanDelete": false } } }, "Success": true}Note the read shape: each field comes back as an envelope with a State
(0 = value present, 1 = restricted — the caller may not read this field) and
the Value. Permissions are server-computed for the calling user — trust
them instead of guessing.
Step 6 — Query records
Section titled “Step 6 — Query records”POST /api/workspace/entity/{entityIdOrKey}/instances/query supports paging,
a nested filter tree, sorting, and reference includes:
For filters and sorts that traverse references, use Target.Path instead of
the direct Field property. The
Entity Instance Query guide gives
complete payloads, limits, reference-ID operand rules, access semantics, and
saved/shared-view behavior.
{ "Page": 1, "PageSize": 25, "Filter": { "Operator": 0, "Conditions": [ { "Field": { "FieldKey": "Priority" }, "Operator": 0, "Values": [ { "Value": "high" } ] } ] }, "Sort": [ { "Field": { "FieldKey": "DueDate" }, "Direction": 0 } ]}The response is a page: { "Items": [...], "TotalCount": 1, "Page": 1, "PageSize": 25, "Permissions": { "CanCreate": true, … } }. Condition
Operator is numeric (0 = Eq, 7 = In, 9 = Contains, 13 = IsNull, …);
the full operator list is in the
query operation
schema. A single record is read with
GET .../instances/{instanceId}.
Step 7 — Update with optimistic concurrency
Section titled “Step 7 — Update with optimistic concurrency”PATCH /api/workspace/entity/{entityIdOrKey}/instances/{instanceId} sends only
the fields to change plus the RowVersion you last saw. Omitted keys stay
unchanged; an explicit null clears a value:
curl -s -X PATCH https://ops.example.com/api/workspace/entity/SupportTicket/instances/c24878e1ff334648aeb4022e19f6cd96 \ -H "Authorization: Bearer <token>" -H "Content-Type: application/json" \ -d '{ "Fields": { "Priority": "medium" }, "RowVersion": "53cedb7e-dad6-425c-86c1-edd23dacfd59" }'A successful mutation returns the new RowVersion
("75ee761a-0dd1-44f5-ab36-33089bd37a6e" here) — keep it for the next write.
A stale RowVersion fails the request instead of silently overwriting someone
else’s change.
Error handling
Section titled “Error handling”Failures return the same envelope with Success: false and structured errors.
This 400 came from creating a record without the required Title:
{ "Data": null, "Errors": [ { "Code": "moltaro.instances.validation.requiredFields", "Message": "Fill in all required fields and try again.", "Field": null, "Type": 1, "Severity": 2 }, { "Code": "moltaro.instances.field.required", "Message": "This field is required.", "Field": "Title", "Type": 1, "Severity": 2 } ], "Success": false}Code is stable and machine-readable; Field names the offending field key or
PascalCase request property; Message is localized for display. Agents should
branch on Code and Field, not on Message. The error shape is documented
in Errors and responses, and a curated catalog of
core entity and record-write codes is in the
error code reference.
Field value shapes when writing
Section titled “Field value shapes when writing”| Field type | Write value |
|---|---|
| Yes/No | true / false |
| Date / Time | "2026-07-25" / "14:30:00" |
| String / Text | JSON string |
| Whole number | JSON number |
| Decimal | JSON number or invariant decimal string on write; canonical invariant string padded to configured scale in Entity Instance field envelopes and form-evaluation patches |
| Select | option key string; array of keys when the field allows multiple |
| Classifier | Catalog node id string from the Classifier Catalog bound to the field |
| User / Role / File / Reference | id string of the target |
| Inverse reference | array of source record ids |
| Money | { "Amount": 10.5, "CurrencyCode": "EUR" } — amounts come back as strings |
| Address | object with FullAddress, street/locality/region/postal/country parts, optional Latitude/Longitude |
| Table | managed through table-row payloads, not a scalar value |
Details per type: Fields and schema.
Invoke business logic
Section titled “Invoke business logic”Workspaces can publish their own C# business logic as callable functions.
Which keys exist is workspace-specific — publications are part of the
workspace configuration, so discover them per installation. Accounts with the
Admin role can list every function and its publications through the
Function Catalog in the
Configuration API
(GET /api/workspace/admin/function-catalog); other integrations receive
their callable keys from the workspace administrator.
A command runs synchronously and returns its result in the response.
POST /api/workspace/commands/{functionKey} takes optional Args (JSON
matching the command’s argument type) and an optional CorrelationId; an
empty body is allowed for commands without arguments:
curl -s -X POST https://ops.example.com/api/workspace/commands/close-overdue-tickets \ -H "Authorization: Bearer <token>" -H "Content-Type: application/json" \ -d '{ "Args": { "GraceDays": 3 }, "CorrelationId": "req-8842" }'{ "Data": { "RunId": "b6f3e0d8a3c14f7f9a1de2c07f6b4a51", "Message": "Closed 4 overdue tickets.", "Result": { "ClosedCount": 4 }, "DurationMs": 118, "CorrelationId": "req-8842" }, "Success": true}Each publication either allows any authenticated user or requires a specific
permission; callers without it get 403. Unknown, disabled, and retired keys
all return the same 404 (moltaro.functionCommands.notFound), so command
keys cannot be probed.
Long-running logic is published for asynchronous execution instead.
POST /api/workspace/functions/{functionKey}/enqueue accepts optional Args,
a DeduplicationKey (reuses an active queued job), RunAfter (earliest
execution time), and CorrelationId, and returns the queued job:
{ "Data": { "JobId": "0d2f6c1e58b747a2a9c3f1b4e6d80a97", "FunctionKey": "rebuild-reporting-snapshots", "Status": 0, "CreatedAt": "2026-07-22T09:14:03+00:00", "NextRunAt": null, "AttemptCount": 0, "DeduplicationKey": null, "CorrelationId": null, "LastError": null, "CanCancel": true, "Run": null }, "Success": true}Poll GET /api/workspace/functions/jobs/{jobId} until Status reaches a
terminal value (0 = Queued, 1 = Leased, 2 = Completed, 3 = Failed,
4 = Cancelled). A finished job carries its latest execution under Run,
including the structured ResultData payload and a bounded ErrorSummary on
failure. While CanCancel is true,
POST /api/workspace/functions/jobs/{jobId}/cancel cancels the queued job.
Job visibility matches invocability: the initiator and every holder of the
publication permission can read and cancel the job.
The full contract — publication and permission model, request options, job lifecycle, and error codes — is in Commands and API functions.
Where to go next
Section titled “Where to go next”- Entity UI returns the configured list, details, drawer, card, and form payloads when an integration should render records the way the product does.
- Entity Data Transfer moves record data in bulk through admin-defined profiles.
- The Configuration quickstart
creates the entity definition this page discovers — schema authoring through
the Configuration API
(raw contract:
moltaro-config-v1.json). - The Boards walkthroughs cover process automation end to end: board configuration and items and moves.
- The Entitlement Operations walkthrough goes from module setup to grant, consume, and the ledger.
- Record history and audit reads the change feed this page’s create and update produced.
- AI agents can start from
/llms.txtfor a machine-oriented map of this documentation, or fetch the complete documentation as one Markdown file at/docs/llms-full.txt(abridged variant:/docs/llms-small.txt).