Skip to main content

tSM Public API (v2) Guide

tSM is a modular, microservice-based platform with publicly documented APIs for integrating with external systems (CRM, ERP, portals). It is intended for entity operations, workflow triggering, and data access via secure endpoints.

Who is this API for?

  • Non-technical users / Business analysts: simple list reads, filtering, and form lookups.
  • Integrators / Developers: full CRUD, bulk operations, pagination, relationship expansion, correlation headers, and request tracing.

How to call the API

Base URLs and HTTP methods

Each module has its own base URL (e.g., 'https://yourServerUrl/tsm-config-form/api/v2/', 'https://yourServerUrl/tsm-customer/api/v2/', …). All entities follow consistent REST patterns:

OperationMethodURL pattern
CreatePOST/api/v2/{entity}
Read (detail)GET/api/v2/{entity}/{id} or {idOrCode} or {idOrKey}
Full updatePUT/api/v2/{entity}/{id} or {idOrCode} or {idOrKey}
Partial updatePATCH (JSON Merge Patch)/api/v2/{entity}/{id} or {idOrCode} or {idOrKey}
DeleteDELETE/api/v2/{entity}/{id} or {idOrCode} or {idOrKey}
Bulk operationsGET/POST/PUT/PATCH/DELETE/api/v2/{entity}/bulk
Paginated listingGET/api/v2/{entity}/page
Quick searchGET/api/v2/{entity}/search

PATCH = JSON Merge Patch (RFC 7396) — send only the changes; setting a field to null removes it. [RFC Editor]: https://www.rfc-editor.org/rfc/rfc7396

Authentication & headers

  • Authorization: Bearer <JWT> (typical)
  • X-Request-Id: unique request identifier (useful for debugging)
  • X-Correlation-Id: correlate calls across services
  • Trace Context (optional): traceparent, tracestate per W3C for end-to-end distributed tracing. [W3C]: https://www.w3.org/TR/trace-context/

Date/time format

Use ISO 8601 and UTC “Z” (e.g., 2025-09-26T12:34:56Z). [Wikipedia]: https://en.wikipedia.org/wiki/ISO_8601

Listing, Search, and Pagination

tSM provides two universal endpoints: /search and /page. Both support sorting, expansion, and multiple filters.

/search vs /page — when to use which

Capability/search/page
Primary purposequick lookups, dropdowns, exportstabular views, lazy loading
HTTP methodGETGET
Filteringyesyes
Pagination paramssize (max number of items)page + size
Total count (totalElements)usually noyes
Sorting (sort)yesyes
Expansion (expand)yesyes

Page parameters follow the common Spring Data convention: page, size, and repeated sort=field,ASC|DESC. [Stack Overflow]: https://stackoverflow.com/

Common parameters

  • Sorting: sort=name,ASC&sort=createdAt,DESC (use multiple sort params as needed).
  • Relationship expansion: expand=ROLES&expand=GROUPS (exact values depend on the entity/module and are listed in that module’s spec).
  • Filtering: field__op=value (operator form)

Filter operators (consistent across the API)

In tSM Public API v2, filtering is expressed using the syntax:

field__operator = value

Multiple filters in a request combine via AND logic by default (i.e. all must match). Below is a detailed explanation of each operator type, what it does, and examples.

Comparisons

Operators for numeric, date, or comparable fields.

OperatorMeaningExampleExplanation
__eqequal tostatus__eq=ACTIVEselect records where status exactly equals “ACTIVE”
__noteqnot equalstatus__noteq=DELETEDexclude records with status = “DELETED”
__gtgreater thanamount__gt=1000amount strictly greater than 1000
__gtegreater than or equalamount__gte=1000amount ≥ 1000
__ltless thanamount__lt=500amount strictly less than 500
__lteless than or equalamount__lte=500amount ≤ 500

Text / String operators

These operators work on string (text) fields, for substring matching, prefix/suffix, etc.

OperatorMeaning / BehaviorExampleExplanation
__likecontains substring (wildcard match)name__like=%25Acme%25select names containing “Acme” anywhere (%25 is URL-encoded %)
__containsalias for “contains” (similar to __like)description__contains=testsame as description__like=%25test%25
__notcontainsdoes not contain substringnote__notcontains=errorexclude texts containing “error”
__startswithbegins with given substringcode__startswith=REG.codes that start with “REG.”
__endswithends with given substringemail__endswith=@domain.comemails ending with “@domain.com”

Important: when using % in __like or similar, you must URL-encode % as %25, e.g.:

value__like=%25Mgr%25

This matches “Mgr” anywhere in the field.

Lists & (non)emptiness

Used when dealing with multi-valued fields (arrays) or checking empty/non-empty status.

OperatorMeaningExampleExplanation
__infield value is in the given liststatus__in=ACTIVE,PENDINGinclude records whose status is either “ACTIVE” or “PENDING”
__notinfield value not in the given liststatus__notin=DELETED,ARCHIVEDexclude records whose status is either “DELETED” or “ARCHIVED”
__emptyfield is empty or nulldescription__empty=trueselect records where description is null or empty
__notemptyfield is non-empty (has some value)remarks__notempty=trueselect records where remarks is not null/empty

Ranges & Time (including relative)

These operators handle ranges (between) and time-based filtering. Very useful for dates or numeric ranges.

OperatorMeaning / BehaviorExampleExplanation
__btwbetween two values (inclusive or as backend)amount__btw=100,500select amounts between 100 and 500
__btw with datesbetween two timestampscreatedAt__btw=2025-09-25T00:56:37.851Z,2025-09-26T00:56:37.851Zselect records created in year 2025
__btwrreverse between (backend-specific support)(only use if API supports)same as btw but reversed semantics in some modules
Relative time (via __gtr, __ltr)filter relative to “now”updatedAt__gtr=-P7Drecords updated in last 7 days

Duration syntax (ISO 8601): durations are expressed as P[n]Y[n]M[n]DT[n]H[n]M[n]S, e.g. PT2H30M for 2 hours 30 minutes. [Wikipedia]: https://en.wikipedia.org/wiki/ISO_8601 A prefix “–” (minus) can signal backward duration.

Example:

updatedAt__gtr=-P7D

This means “updatedAt greater than (now minus 7 days)”, i.e. last 7 days.

Example summary in one request:

GET /api/v2/customers/page?
status__eq=ACTIVE&
name__contains=Tech&
whenInserted__btw=2025-09-25T00:56:37.851Z,2025-09-26T00:56:37.851Z&
revenue__gt=10000&
remarks__notempty=true&
sort=whenInserted,desc&
page=0&
size=50

This would return customers who:

  • have status = ACTIVE
  • whose name contains “Tech”
  • were created between 2025-09-25T00:56:37.851Z and 2025-09-26T00:56:37.851Z
  • have revenue > 10000
  • and non-empty remarks
  • sorted descending by creation date
  • first page, 50 items per page

Practical examples

1) Quick lookup (/search)

GET /api/v2/register-values/search?
register.code__eq=Reg.Crm.Titul.Pred.Menom&
size=100

2) Paginated view with filter & sorting (/page)

GET /api/v2/register-values/page?
register.code__eq=Reg.Crm.Titul.Pred.Menom&
value__like=%25Mgr%25&
page=0&
size=20&
sort=order,asc

Pagination & sorting follow the Spring Data convention. [Docs Spring Data]: https://docs.spring.io/spring-data/rest/reference/paging-and-sorting.html

3) Filter by related entity ID

GET /api/v2/register-values/search?
register.id__eq=019970a1-7797-73ba-a6d5-b424443a7dcb

4) Non-empty values

GET /api/v2/register-values/page?
value__notempty=true&page=0&size=50

5) Multiple filters + sorting

GET /api/v2/register-values/page?
register.code__eq=Reg.Crm.Type.Partner&
value__contains=VIP&
sort=order,desc&
size=30

CRUD & Bulk — quick reference with templates

Create (POST)

curl -X POST "https://<module-host>/api/v2/customers" \
-H "Authorization: Bearer <JWT>" \
-H "Content-Type: application/json" \
-d '{"code":"CUS-001","name":"Acme s.r.o.","active":true}'

Detail (GET + expand)

curl "https://<module-host>/api/v2/users/U123?expand=ROLES&expand=GROUPS" \
-H "Authorization: Bearer <JWT>"

Full update (PUT)

curl -X PUT "https://<module-host>/api/v2/entity-catalogs/CAT-001" \
-H "Authorization: Bearer <JWT)" \
-H "Content-Type: application/json" \
-d '{"id":"…","code":"CAT-001","name":"Internet 300","active":true}'

Partial update (PATCH, JSON Merge Patch)

curl -X PATCH "https://<module-host>/api/v2/tickets/T-1001" \
-H "Authorization: Bearer <JWT>" \
-H "Content-Type: application/merge-patch+json" \
-d '{"assignee":null,"tags":["VIP","urgent"]}'

Behavior per RFC 7396. [RFC Editor]: https://www.rfc-editor.org/rfc/rfc7396

Bulk (GET/POST/PUT/PATCH/DELETE)

  • GET /api/v2/{entity}/bulk?idOrCodes=…&idOrCodes=…
  • POST /bulk — array of new objects
  • PUT /bulk — array of full objects
  • PATCH /bulk — array of { id|code, patch:{…} }
  • DELETE /bulk?idOrCodes=…

Best practices

  • Prefer PATCH for routine updates — send only the diff; fewer conflicts. (RFC 7396) [Datatracker IETF]: https://datatracker.ietf.org
  • Use UTC + ISO 8601 consistently; let the UI handle local time conversion. [Wikipedia]: https://en.wikipedia.org/wiki/ISO_8601
  • Keep page/size/sort per the common Spring Data convention — most clients and tools expect it. [Stack Overflow]: https://stackoverflow.com/
  • Expand relationships deliberately — reduce round trips, but avoid over-fetching.
  • Trace requests — send X-Request-Id / X-Correlation-Id and, where possible, W3C Trace Context (traceparent, tracestate) for end-to-end tracing. [W3C]: https://www.w3.org/TR/trace-context/
  • For LIKE/contains, remember to URL-encode %%25.

Advanced filter syntax & combining conditions

Basic filter parameter shape

  • Syntax: field__operator=value

    • field: entity attribute name (e.g., name, status, createdAt)
    • operator: any supported operator (eq, noteq, contains, in, etc.)
    • value: a single value, or value1,value2 (for in, btw)
    • If you filter on an unknown field, expect 400 Bad Request with an explanatory message.

Combining multiple filters (AND)

Multiple filters are combined with AND by default (all must match). Example: status_eq=ACTIVE&name__contains=acme → only records where status = ACTIVE and name contains acme.

Ranges / “between”

  • __btw — two comma-separated values (e.g., 10,100 or two timestamps)

    • Example: amount__btw=100,500
    • Dates: createdAt__btw=2025-09-25T00:56:37.851Z,2025-09-26T00:56:37.851Z
  • __btwr — “between reversed”; use only if your backend supports it.

Relative time filters

  • __gtr — greater-than relative (e.g., updatedAt__gtr=-P7D for “last 7 days”)
  • __ltr — less-than relative
  • Use ISO 8601 duration for relative values (e.g., -P7D means 7 days back). [Wikipedia]: https://en.wikipedia.org/wiki/ISO_8601

Text operators

  • __like — contains substring (URL-encode % as %25)

    • Example: value__like=%25Mgr%25
  • __contains — alias for a contains-style match

  • __notcontains, __startswith, __endswith

Relationship expansion (expand)

Expanding returns related objects/fields inline rather than just identifiers.

  • Syntax: ?expand=REL1&expand=REL2
  • Each module defines its own allowed expand values in OpenAPI (e.g., ROLES, GROUPS, REGIONS for User).
  • Expansion reduces follow-up calls and simplifies clients.

Example:

GET /api/v2/users/U123?expand=ROLES&expand=GROUPS

The response will include expanded roles and groups rather than only their IDs.

Error handling — status codes & error payloads

HTTP codeMeaningTypical cause
400 Bad Requestinvalid parameter/filter/schemae.g., filter on an unknown field
401 Unauthorizedmissing or invalid tokenimproperly authorized request
403 Forbiddeninsufficient permissionsattempt to modify unauthorized data
404 Not Foundentity not foundGET /api/v2/users/xxx where xxx doesn’t exist
409 Conflictversion conflict, duplicate codePUT/POST with a uniqueness conflict
422 Unprocessable Entityvalidation errors in request bodymissing required fields, wrong types
500 Internal Server Errorunexpected server errorinternal exceptions

Error response (example)

{
"timestamp": "2025-09-25T00:56:37.851Z",
"status": 400,
"error": "Bad Request",
"message": "Unknown filter field 'foo'",
"path": "/api/v2/users"
}

Some modules may return detailed errors:

{
"status": 422,
"error": "Unprocessable Entity",
"errors": [
{ "field": "name", "message": "must not be blank" },
{ "field": "code", "message": "duplicated" }
]
}

Always document, per module, which fields can fail validation and the expected messages.

Security & limits

Token expiration

  • JWTs have an expiry (e.g., 15 minutes, 1 hour).
  • The client must handle refresh tokens or re-authentication on expiry.

Rate limiting

  • Modules may enforce request limits (e.g., 100 req/s).
  • On limit breach, servers respond with 429 Too Many Requests; apply backoff + retry.

Authorization

  • Tokens should carry claims describing user identity and permissions (modules & operations: read, write, delete).
  • Some fields/operations are restricted to admins or privileged roles.

Extended endpoints & special cases

/search as a GET lookup

  • Use when you don’t need full pagination — quick lists for lookups/autocomplete.
  • size sets the maximum number of results returned.

/page for full listings

  • Use for data tables, lazy-loaded UI, full paging, and total count (totalElements).
  • Responses contain page, size, totalPages, totalElements, and content.

bulk endpoints

  • For mass operations (import/sync).
  • Mind request size limits (item count, max payload).
  • On per-item errors, some modules may fail the whole bulk or return partial failures — document each module’s behavior.

Additional endpoints

  • /log — audit trail for a given entity
  • /save-diff — upsert by differences (send only the change set)
  • /search vs custom filter — some search endpoints may implement extra features (e.g., full-text index, relevance)