API conventions
Every Nimles service follows the same conventions, so what you learn on one applies to all of them.
https://api.nimles.com/{service}/api/tenant/{tenantId}/{Resource}[/{id}[/{action}]]{service}is the service prefix:identity,cms,media,pim,e-commerce,directory,communication,crm,lms,accounting,property-management,aiorwork-management.{Resource}is plural, such asPages,ProductsorOrders. Routes are not case-sensitive.- Ids are GUIDs.
| Request | Means |
|---|---|
GET {Resource} |
List (with filters as query parameters, such as ?ids=…&ids=…) |
GET {Resource}/{id} |
Read one |
POST {Resource} |
Create |
PUT {Resource}/{id} |
Update with the whole item |
PATCH {Resource}/{id} |
Change some members, with JSON Patch |
DELETE {Resource}/{id} |
Delete |
PUT {Resource}/{id}/{action} |
An action, such as publish on a page or submit on an order |
Requests and responses
Section titled “Requests and responses”Bodies are JSON, with camelCase members. Times are UTC in ISO 8601. Responses wrap their content in data:
{ "data": { "id": "bf8a927c-…", "title": "Hello, Nimles" }, "fields": {} }A list is { "data": [ … ] }. fields is always there and normally empty. Members whose value is null are left
out of responses.
Every item has the platform members id, tenantId, creatorId, createdDate, lastModified and version,
which the service sets.
Status codes
Section titled “Status codes”| Code | When |
|---|---|
200 OK |
A read, or a change that returns the item |
201 Created |
A create. The body is { "data": { "id": … } } (or the whole item). Location is the item’s path within the service, /api/tenant/{tenantId}/{Resource}/{id}, without the /{service} prefix api.nimles.com routes by: use the id from the body |
204 No Content |
A change or delete without a body. Most PUT, PATCH, DELETE and actions answer this |
400 Bad Request |
A malformed request |
401 Unauthorized |
No token, or an expired or invalid one (no body) |
403 Forbidden |
The caller has no role that allows the action at this scope. Some lists answer only what the caller may read instead |
404 Not Found |
No such item in the tenant |
409 Conflict |
The item kept changing under concurrent writes; read it again and retry |
422 Unprocessable Entity |
The body or the change is invalid; see Errors |
429 Too Many Requests |
A rate limit |
Errors
Section titled “Errors”Validation errors answer 422 with a ValidationProblemDetails: the messages by member, or by JSON Pointer for a
patch.
{ "title": "One or more validation errors occurred.", "status": 422, "errors": { "Title": ["'Title' must not be empty."] }}A body that cannot be read as the expected type at all (wrong JSON, a missing required member) also answers 422,
keyed by member or $:
{ "$": ["JSON deserialization for type '…PageCreateModel' was missing required properties including: 'title', 'uniqueName'."]}404 answers an RFC 9457 problem (application/problem+json) with type, title and status. 403 answers either
without a body or with a problem whose detail names the action you lack:
{ "type": "https://datatracker.ietf.org/doc/html/rfc9110#section-15.5.4", "title": "Forbidden", "status": 403, "detail": "The principal does not have permission to perform the action 'Nimles.Cms/pages/read'."}JSON Patch
Section titled “JSON Patch”PATCH {Resource}/{id} takes a JSON Patch (RFC 6902) document: an array
of add, remove, replace, move, copy and test operations, sent as application/json.
[ { "op": "test", "path": "/title", "value": "Hello, Nimles" }, { "op": "replace", "path": "/title", "value": "Hello again, Nimles" }, { "op": "add", "path": "/titleLocalized/sv", "value": "Hej igen, Nimles" }, { "op": "remove", "path": "/summary" }]- The operations apply in order, and all or none: if one fails, nothing changes. A failed
testanswers422with the path, which makestesta cheap check that a member still has the value you read. - A patch can change exactly what a
PUTcan change, and must pass the same validation. - The platform members cannot be patched:
id,tenantId,resourceGroupId,organizationId,createdDate,creatorId,lastModified,version,accessibility,permissions,relationsandisDeleted(teston them is refused too). Each answers422, for example{ "errors": { "/id": ["'/id' cannot be patched."] } }. - Items in lists keep their ids: change a list item at
/elements/0/contentrather than replacing the whole list. - A patch that changes nothing succeeds and stores nothing.
PUT sends the whole item: members you leave out are generally cleared. Use PATCH to change a few members.
Lists are not paged
Section titled “Lists are not paged”REST lists answer every matching item in one response; there is no paging, sorting or text search. Filter with the
query parameters each list takes (often ids, resourceGroupId or a parent id).
For paging and filtering, use GraphQL on CMS, PIM, Ecommerce and Directory: pages of up to 100 items with cursors.
Reads catch up with writes
Section titled “Reads catch up with writes”Writes are stored first and then projected into the read models the GETs and lists answer from. A read right after
a write can, for a moment, miss a new item or show the previous version. Use the id from the create’s answer rather
than looking the item up in a list, and expect a short delay before lists show a change.
Browsers and CORS
Section titled “Browsers and CORS”The API sends no CORS headers: browsers cannot call it from another origin. That is on purpose: the secret of an API client (an OAuth 2.0 client in your( )tenant that exchanges its client id and secret for a short-lived access token) must never be in a browser, and a( )user’s token is better kept on a server too.
Call the API from your backend, and give your web app its own endpoints (a backend for frontend). Public sites built on Nimles work the same way: their server reads content with a token and renders or proxies it.