Skip to content
P
API for partners
Connecting your application — access, authentication and endpoints
API for partners / CRUD endpoint pattern

CRUD pattern#

All resources share the same set of actions. Every call below (addresses are shortened to the path) carries the X-API-Key header with the application key; writes and "my" data additionally carry Authorization: Bearer <token>.

🔴 Parameters go in the URL#

The request body is not read at all for CRUD actions

Neither form-encoded nor JSON. Everything — including the data to be saved — goes in the query string in PHP array notation: data[title]=…&data[category][id]=3.

The exceptions are the authentication endpoints, which conversely read a form body, and the actions with passwords and keys (update-password, reset-password, is-auth-valid), which read them only from the body — form or JSON.

The HTTP method does not matter#

GET /api/…/delete?id=5 really does delete

The action comes from the path, not the method. Do not put API addresses into prefetching or anywhere a crawler will follow them.

Parameter shapes are checked#

A parameter of the wrong shape is not cast "somehow" — the request ends with HTTP 400 and code 2 before anything is read or written. Text instead of an array (filters=x), an unknown boolean spelling (computeTotalCount=yes) or a list instead of a single ID therefore does not return different data but an error. The full overview is in Error codes.

Actions#

Action Purpose
get one record by id
get-by one record by filter
get-all a listing
save insert and update alike
update-columns changing selected fields
delete deletion
delete-all deletion by filter

Read parameters#

Parameter Meaning
id identifier
filters filters, see below
orderBys ordering
offset, limit pagination; see the note on the limit below
includes loading relations
computeTotalCount 1 computes the total; only 1/0, true/false, on/off

columns does not work on a listing from outside

Narrowing the returned fields with columns on get-all ends with HTTP 400. The response carries every field you may read.

A listing without limit returns 50 records, at most 500

For an external get-all, a missing, non-numeric or smaller-than-1 limit returns 50 records. A larger value is cut to 500 — it is not an error, the response simply holds at most 500 records. Fetch further records by paging: repeat the call with a growing offset and stop by totalCount (send computeTotalCount=1).

Ordering#

orderBys[created]=1     ascending
orderBys[created]=0     descending
orderBys[category.name]=1

The direction is a truthy value, not asc / desc

orderBys[name]=desc is a non-empty string, therefore true — it sorts ascending. Use 1 and 0.

Relations#

includes[]=translations&includes[]=user.roles
includes=translations

includes is one path or a list of paths.

includes=* works only on get

On a detail it pulls in every relation — an easy route to a huge response. On get-all it ends with HTTP 400. List what you need.

Filters#

A filter is a triple [type, field, value]:

?filters[0][0]=equal&filters[0][1]=activated&filters[0][2]=1
&filters[1][0]=like&filters[1][1]=title&filters[1][2]=notebook&filters[1][matchType]=both
&filters[2][0]=in&filters[2][1]=id&filters[2][2][]=1&filters[2][2][]=2

Filters combine with AND.

Type Extra keys
equal, notEqual
like, notLike matchType: both, start, end
in, notIn
isNull, isNotNull
between from, to
range inclusive
greaterThan, greaterOrEqual short forms gt, gte
lessThan, lessOrEqual short forms lt, lte
comparison operator
date, dateBetween, dateAfter, dateBefore operator, format
group, or, and filters, logic
fulltext mode, minScore

An unknown or malformed filter returns 400

A typo in the type name, a filter without a field or a malformed nested group ends with HTTP 400 and code 2. The extra keys inclusive and isNull expect a boolean, which a URL cannot carry — do not use them in the query string.

You may filter and order only by fields you may read

A filter or ordering over a password, an IP address, another user's e-mail or a non-existent field returns HTTP 400 with the message Field path "…" is not allowed. See Permissions.

Saving#

One endpoint for insert and update. They are distinguished by the presence of data[id].

/api/blog/article/save?data[title]=News&data[category][id]=3

data, meta and skipFields must be arrays; text instead returns HTTP 400. Empty data returns code 602.

Values are converted by the field type#

From the URL every value arrives as text. The server converts it by the type of the field it belongs to:

Field type Accepts Rejects
integer 12, -3, 0 1.9, 1e2, +1, 01, whitespace
decimal 1.5, -0.25, .5, 2e3 a comma, whitespace
boolean 1, 0, true, false, on, off (lower case) yes, ano, TRUE
date and time whatever PHP new DateTime() reads, e.g. 2026-10-03 14:30 a non-existent date (2026-02-31), surrounding whitespace
text anything —

An empty string means null for a field that may be empty; for a required one it is an error. A value that cannot be converted returns code 604 with the field name and nothing is saved:

{"success":false,"code":604,"message":"Invalid value for field \"currencyId\": expected int, got \"1.9\"."}

Part of your data is silently discarded#

The response reports success but the value is not saved

Discarded are:

  • meta — entirely, for most resources,
  • fields marked read-only from outside (the server sets the value),
  • fields your account may not write — an owner cannot change, for example, the credits, discount or e-mail of their account (see Editing your own account),
  • nested collections the resource does not permit.

On update the original value stays in the database, on insert the default. The only trace is in the server log. After writing, read the data back and check.

The server fills in the owner and the IP address

On insert the owner comes from the sign-in and the IP address from the request. Any user identifier or address you send is not used.

Editing your own account#

A signed-in user may edit their own account through /api/system/user/save (data[id] = their id; another id ends with 403).

May change Will not change (the field is silently dropped)
first and last name, phone, street, city, postcode, state and country username and e-mail
company, company ID, VAT ID, the company flag, user type credits, points, discount and discount level
payment region, language, website, "about me" membership (memberUntil)
avatar (assetPath), third-party sign-in link (type, externalId)
the password — only update-password changes it

The region is derived from the payment region

The server computes the user's regionId from paymentRegionId; a value you send is overwritten.

A deleted account cannot be saved

For an anonymized account the save returns code 120.

Similar fields are locked on the other records an owner edits: you cannot move a comment, a rating or an advert response to another thread or advert, and you cannot set moderation or "read" flags through save.

Content creation quota#

New responses to an advert or a company profile, discussion posts and reports, comments, advert reports and contact form messages have an hourly cap. Above it save returns success: false and code 404 (with HTTP 200). The values are in Limits.

Changing selected fields#

/api/eshop/order/update-columns?ids[]=5&values[status]=2

ids must be a non-empty array and values an array; otherwise HTTP 400.

From outside this does not work on most resources

The list of permitted columns defaults to empty, so the call ends with code 607. It works only for columns with an explicit exception.

No partial writes

One non-permitted column brings the whole call down.

Deleting#

/api/bazaar/watchdog/delete?id=123
/api/bazaar/watchdog/delete?id[]=1&id[]=2

Bulk deletion never passes the ownership check

For the owner role it is always denied.

delete-all without filters deletes the whole collection

If the caller may use the action, a missing filter means "everything". A malformed filter, on the other hand, returns HTTP 400 and deletes nothing.

Tree resources#

Categories and menus additionally offer move-up, move-down, move-subtree, move-subtree-to, insert-tree-node and delete-node.

delete-node deletes the whole subtree

Not just the node.

Events#

register-event increments a counter by one:

/api/comcat/advert/register-event?ids[]=1&property=views

Permitted counter names are restricted by a list. ids must be a non-empty list of positive integers, otherwise HTTP 400.

🔴 Many operations are unreachable from outside#

A resource may exist and still return 404

The system contains many operations used only internally by the website that have no HTTP wrapper. These include the cart, order checkout, "my" listings, availability and current prices.

You recognise it because the action returns 404 with HTML even though the resource and the permission both exist.

Cookie settings