API documentation

Integrate the SAMO-TRANS exchange into your TMS or CRM

API documentation

Endpoint reference

Every public API method: paths, parameters, request schemas and response codes.

Base URL

https://api.samo-trans.com/api/public/v1

An interactive console with request schemas is available in the Swagger UI on the API host.

Public · Reference

GET/reference/countriesreference:read

List countries served by the exchange

Parameters

localequerystringLocale for display names (default "ua")

* required field

Responses

  • 200
GET/reference/regionsreference:read

List regions/oblasts for a country

Parameters

country*querystringISO 3166-1 alpha-2 country code
localequerystringLocale for display names (default "ua")

* required field

Responses

  • 200
GET/reference/car-typesreference:read

Car body types (grouped) — valid `carTypes` codes for a proposal

Parameters

No parameters

Responses

  • 200
GET/reference/load-typesreference:read

Loading/unloading types — valid `loadUnloadTypes` codes

Parameters

No parameters

Responses

  • 200
GET/reference/permitsreference:read

Transport permit/document types — valid `carPermits` codes

Parameters

No parameters

Responses

  • 200
GET/reference/currenciesreference:read

Supported currencies — valid `currencyType` values

Parameters

No parameters

Responses

  • 200
GET/reference/payment-typesreference:read

Payment forms — valid `paymentType` values

Parameters

No parameters

Responses

  • 200

Public · Localities

Public · My proposals

POST/proposals/cargoproposals:write

Create a CARGO proposal

companyId is always resolved from the API key — never from the request body. type is fixed by this route. contactPersonId is optional: if given, it must be the company owner or an ACTIVE employee of the key's company; if omitted, it defaults to the key's owner.

Parameters

No parameters

Request body

PublicCreateProposalDto(required)

Responses

  • 201Proposal created
POST/proposals/transportproposals:write

Create a TRANSPORT proposal

companyId is always resolved from the API key — never from the request body. type is fixed by this route. contactPersonId is optional: if given, it must be the company owner or an ACTIVE employee of the key's company; if omitted, it defaults to the key's owner.

Parameters

No parameters

Request body

PublicCreateProposalDto(required)

Responses

  • 201Proposal created
POST/proposals/batchproposals:write

Create up to 25 proposals in one request

Each item is validated and created independently — the response carries a per-item result ({index, status:"created", proposal} or {index, status:"error", error}). The whole batch consumes N of the daily write quota (not 1); if it would exceed the quota the request is rejected with 429 before creating anything. Combine with an Idempotency-Key for safe retries.

Parameters

No parameters

Request body

PublicBatchCreateDto(required)

Responses

  • 200Per-item batch result
GET/proposals/myproposals:read

List the calling key’s own proposals

Scoped via ProposalsService.getMyProposals — never leaks other companies’ proposals.

Parameters

typequery"TRANSPORT" | "CARGO"Filter by proposal type
statusquery"ACTIVE" | "INACTIVE"Filter by status
limitquerynumberMax rows to return (default 20, capped at 100)
cursorquerystringOpaque pagination cursor returned as `nextCursor` from a previous page
sortByquery"createdAt" | "viewCount"Sort field (default createdAt)
sortOrderquery"asc" | "desc"Sort order (default desc)

* required field

Responses

  • 200Page of the key’s own proposals
PATCH/proposals/{id}proposals:write

Update a proposal

Parameters

id*pathstringProposal id

* required field

Request body

PublicUpdateProposalDto(required)

Responses

  • 200Proposal updated
DELETE/proposals/{id}proposals:write

Delete a proposal permanently

Parameters

id*pathstringProposal id

* required field

Responses

  • 200Proposal deleted
POST/proposals/{id}/bumpproposals:write

Bump a proposal to the top of search results

Ownership here is narrower than update/delete/archive: only the proposal’s author or contact person may bump it (company owner/manager alone is not enough). May fail with BUMP_NOT_OWNER, BUMP_TOO_SOON, or BUMP_LIMIT_REACHED. These are internal { code, message } exceptions, not public-API error codes — PublicApiExceptionFilter remaps them onto the stable public set: BUMP_NOT_OWNER (ForbiddenException) becomes 403 { error: { code: 'forbidden' } }; BUMP_TOO_SOON and BUMP_LIMIT_REACHED (both BadRequestException) become 400 { error: { code: 'validation_error' } }. The specific reason is always preserved in the envelope’s `message`, never in `code`.

Parameters

id*pathstringProposal id

* required field

Responses

  • 200Proposal bumped
POST/proposals/{id}/archiveproposals:write

Archive (deactivate) a proposal — idempotent

There is no dedicated archive() in ProposalsService, only toggleStatus() which flips ACTIVE⇄INACTIVE. This route calls ProposalsService.setProposalStatus(id, actorUserId, 'INACTIVE'), which verifies ownership FIRST and only then no-ops if the proposal is already INACTIVE — so a key with no rights over the proposal gets 403 even when it is already in the target state, instead of silently succeeding.

Parameters

id*pathstringProposal id

* required field

Responses

  • 200Proposal is INACTIVE (already was, or just deactivated)
POST/proposals/{id}/restoreproposals:write

Restore (reactivate) a proposal — idempotent

Mirror of /archive: calls ProposalsService.setProposalStatus(id, actorUserId, 'ACTIVE'), which verifies ownership FIRST and only then no-ops if already ACTIVE. Reactivating is a real ProposalsService side effect — it RESETS createdAt and viewCount to 0 (proposals.service.ts toggleStatus/setProposalStatus), the same behavior the web app has today. Bringing a proposal back online consumes an active-proposal slot from the company plan exactly as creating one does, so this returns 400 when the company is at its maxActiveProposals cap. The already-ACTIVE no-op never consumes a slot and stays 200 even at the cap.

Parameters

id*pathstringProposal id

* required field

Responses

  • 200Proposal is ACTIVE (already was, or just reactivated)
GET/proposals/{id}proposals:read

Get a single proposal by id

Parameters

id*pathstringProposal id
localequerystringLocale for display names (default "ua")

* required field

Responses

  • 200The matching proposal

Public · Companies

GET/companies/{id}reference:read

Get a company profile (name, legal form, rating + reviews summary, contacts)

Parameters

id*pathstringCompany id

* required field

Responses

  • 200
GET/companies/{id}/reviewsreference:read

Paginated APPROVED reviews of a company (counterparty reputation)

Parameters

id*pathstringCompany id
pagequerystringPage (default 1)
limitquerystringPage size (default 10, capped at 50)

* required field

Responses

  • 200

Public · Employees

GET/employeesemployees:read

List the key's company staff

Returns every membership of the company the key belongs to, including PENDING invitations. Use `userId` (not `id`) as a proposal `contactPersonId`.

Parameters

No parameters

Responses

  • 200Company staff
GET/employees/{id}/proposals-countemployees:read

Count the listings an employee is contact person for

Useful before deactivating someone — those listings keep pointing at them.

Parameters

id*pathstringMembership id (the `id` field of an employee)

* required field

Responses

  • 200Listing count
PATCH/employees/{id}/statusemployees:write

Activate or deactivate an employee

DEACTIVATED removes the person's access to the company dashboard — they land on the suspended screen — but only if they have no other ACTIVE employment; it does not block signing in to the platform and does not end an existing session. Their listings stay published and keep pointing at them as contact person — check /employees/{id}/proposals-count first. Idempotent: setting the status an employee already has succeeds and changes nothing.

Parameters

id*pathstringMembership id

* required field

Request body

SetEmployeeStatusDto(required)

Responses

  • 200Updated employee
PATCH/employees/{id}employees:write

Change an employee's role or display name

The OWNER role cannot be assigned — ownership is not transferable through this API.

Parameters

id*pathstringMembership id

* required field

Request body

UpdateEmployeeDto(required)

Responses

  • 200Updated employee

Schemas

ProposalLocationInput
type*"locality" | "region" | "country"Location type: locality, region, or country
osmIdstringOSM ID for locality
regionCodestringRegion code (e.g., UA-59)
countryCodestringCountry code (e.g., UA)

* required field

PublicCreateProposalDto
startLocations*ProposalLocationInput[]Start locations (multiple allowed)
endLocations*ProposalLocationInput[]End locations (multiple allowed)
dataLoadStart*stringDate when loading starts
dataLoadEnd*stringDate when loading ends
capacity*numberCapacity in tons
volumenumberVolume in m3
description*stringDescription of the proposal
pricenumberPrice value
currencyType"USD" | "EUR" | "UAH" | "PLN"
paymentType"CASH" | "CASHLESS" | "MIXED"
priceVatbooleanPrice includes VAT
priceRequestbooleanRequest price
priceForstringPrice for (per_km, per_ton, etc)
lengthnumber
widthnumber
heightnumber
carTypes*string[]Car body type codes (see GET /reference/car-types)
loadUnloadTypesstring[]
carPermitsstring[]
separateCarboolean
carNumbernumber
shipperRolestring
addLoadboolean
addLoadPossibleboolean
contactPersonIdstringContact Person ID (optional, defaults to user)

* required field

PublicBatchProposalItemDto
startLocations*ProposalLocationInput[]Start locations (multiple allowed)
endLocations*ProposalLocationInput[]End locations (multiple allowed)
dataLoadStart*stringDate when loading starts
dataLoadEnd*stringDate when loading ends
capacity*numberCapacity in tons
volumenumberVolume in m3
description*stringDescription of the proposal
pricenumberPrice value
currencyType"USD" | "EUR" | "UAH" | "PLN"
paymentType"CASH" | "CASHLESS" | "MIXED"
priceVatbooleanPrice includes VAT
priceRequestbooleanRequest price
priceForstringPrice for (per_km, per_ton, etc)
lengthnumber
widthnumber
heightnumber
carTypes*string[]Car body type codes (see GET /reference/car-types)
loadUnloadTypesstring[]
carPermitsstring[]
separateCarboolean
carNumbernumber
shipperRolestring
addLoadboolean
addLoadPossibleboolean
contactPersonIdstringContact Person ID (optional, defaults to user)
type*"TRANSPORT" | "CARGO"CARGO or TRANSPORT

* required field

PublicBatchCreateDto
items*PublicBatchProposalItemDto[]Between 1 and 25 proposals to create in one request.

* required field

PublicUpdateProposalDto
startLocationsProposalLocationInput[]Start locations (multiple allowed)
endLocationsProposalLocationInput[]End locations (multiple allowed)
dataLoadStartstringDate when loading starts
dataLoadEndstringDate when loading ends
capacitynumberCapacity in tons
volumenumberVolume in m3
descriptionstringDescription of the proposal
pricenumberPrice value
currencyType"USD" | "EUR" | "UAH" | "PLN"
paymentType"CASH" | "CASHLESS" | "MIXED"
priceVatbooleanPrice includes VAT
priceRequestbooleanRequest price
priceForstringPrice for (per_km, per_ton, etc)
lengthnumber
widthnumber
heightnumber
carTypesstring[]Car body type codes (see GET /reference/car-types)
loadUnloadTypesstring[]
carPermitsstring[]
separateCarboolean
carNumbernumber
shipperRolestring
addLoadboolean
addLoadPossibleboolean
contactPersonIdstringContact Person ID (optional, defaults to user)
versionnumberCurrent version for optimistic locking

* required field

SetEmployeeStatusDto
status*"ACTIVE" | "DEACTIVATED"ACTIVE = grant access; DEACTIVATED = remove access to the company dashboard (only when the person has no other ACTIVE employment) — it does not block signing in to the platform.

* required field

UpdateEmployeeDto
role"MANAGER" | "DRIVER" | "ACCOUNTANT" | "DISPATCHER"
namestring

* required field