Examples
Ready-to-run curl for dictionaries, search, listing creation, batches and sync.
All examples assume two environment variables:
export SAMO_API="https://api.samo-trans.com/api/public/v1"
export SAMO_KEY="samo_live_XXXXXXXXXXXXXXXXXXXXXXXX"
Dictionaries
Before creating your first listing, fetch the dictionaries — these are the codes the API accepts. They change rarely, so cache them on your side.
curl "$SAMO_API/reference/car-types" -H "Authorization: Bearer $SAMO_KEY"
curl "$SAMO_API/reference/load-types" -H "Authorization: Bearer $SAMO_KEY"
curl "$SAMO_API/reference/permits" -H "Authorization: Bearer $SAMO_KEY"
curl "$SAMO_API/reference/currencies" -H "Authorization: Bearer $SAMO_KEY"
curl "$SAMO_API/reference/payment-types" -H "Authorization: Bearer $SAMO_KEY"
Each entry carries a code and labels in five languages:
{ "code": "tent", "labels": { "uk": "Тент", "en": "Tent", "ru": "Тент", "de": "Plane", "pl": "Plandeka" } }
Geography:
curl "$SAMO_API/reference/countries?locale=en" -H "Authorization: Bearer $SAMO_KEY"
curl "$SAMO_API/reference/regions?country=UA&locale=en" -H "Authorization: Bearer $SAMO_KEY"
# locality search → this is where route osmIds come from
curl "$SAMO_API/localities/search?q=Kovel&country=UA&locale=en" \
-H "Authorization: Bearer $SAMO_KEY"
Searching the exchange
curl -G "$SAMO_API/proposals/search" \
-H "Authorization: Bearer $SAMO_KEY" \
--data-urlencode "type=CARGO" \
--data-urlencode "startCountries=UA" \
--data-urlencode "endCountries=PL" \
--data-urlencode "minCapacity=15" \
--data-urlencode "dateFrom=2026-08-10" \
--data-urlencode "limit=50"
Geo filters are arrays, so repeat the parameter:
curl -G "$SAMO_API/proposals/search" \
-H "Authorization: Bearer $SAMO_KEY" \
--data-urlencode "startRegions=UA-07" \
--data-urlencode "startRegions=UA-05" \
--data-urlencode "carTypes=tent" \
--data-urlencode "carTypes=ref"
A single listing:
curl "$SAMO_API/proposals/clx7k2n9p0001/?locale=en" -H "Authorization: Bearer $SAMO_KEY"
Contacts appear in the response only if your plan allows seeing them — otherwise
"contact": null.
Creating a listing
Cargo:
curl -X POST "$SAMO_API/proposals/cargo" \
-H "Authorization: Bearer $SAMO_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: tms-order-88213" \
-d '{
"startLocations": [{ "type": "locality", "osmId": "3678531", "countryCode": "UA" }],
"endLocations": [{ "type": "country", "countryCode": "PL" }],
"dataLoadStart": "2026-08-12T00:00:00.000Z",
"dataLoadEnd": "2026-08-14T00:00:00.000Z",
"capacity": 20,
"volume": 86,
"description": "Pallets, 20 t, side curtain access",
"carTypes": ["tent"],
"loadUnloadTypes": ["side", "back"],
"carPermits": ["cmr", "t1"],
"price": 24500,
"currencyType": "UAH",
"paymentType": "CASHLESS"
}'
Transport uses the same body shape and a different path:
curl -X POST "$SAMO_API/proposals/transport" \
-H "Authorization: Bearer $SAMO_KEY" \
-H "Content-Type: application/json" \
-d '{ "startLocations": [ … ], "endLocations": [ … ], "dataLoadStart": "…", "dataLoadEnd": "…", "capacity": 22, "description": "Tilt 92 m³, free from Monday", "carTypes": ["tent"] }'
Required fields: startLocations, endLocations, dataLoadStart, dataLoadEnd, capacity,
description, carTypes.
Do not send companyId — it always comes from the key; companyId in the body is not
ignored, it fails the request with 400 validation_error. The contact person
(contactPersonId) is optional — omit it and the contact becomes whoever issued the key. See
"Assigning a listing to an employee" below for details.
Batch creation
Up to 25 listings in one request. The response is always 200, with a per-item result.
curl -X POST "$SAMO_API/proposals/batch" \
-H "Authorization: Bearer $SAMO_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: tms-batch-2026-08-05-01" \
-d '{
"items": [
{ "type": "CARGO", "startLocations": [ … ], "endLocations": [ … ], "…": "…" },
{ "type": "TRANSPORT", "startLocations": [ … ], "endLocations": [ … ], "…": "…" }
]
}'
{
"created": 1,
"failed": 1,
"results": [
{ "index": 0, "status": "created", "proposal": { "id": "clx…", "…": "…" } },
{ "index": 1, "status": "error", "error": { "code": "validation_error", "message": "capacity must be a number" } }
]
}
A batch consumes as many units of the daily quota as it has items. If the quota would not
cover it, the whole request is rejected with 429 quota_exceeded and nothing is created.
Your own listings
# list (only the company the key belongs to)
curl -G "$SAMO_API/proposals/my" \
-H "Authorization: Bearer $SAMO_KEY" \
--data-urlencode "status=ACTIVE" \
--data-urlencode "limit=100"
# partial update — send only the fields that changed
curl -X PATCH "$SAMO_API/proposals/clx7k2n9p0001" \
-H "Authorization: Bearer $SAMO_KEY" \
-H "Content-Type: application/json" \
-d '{ "price": 26000, "description": "Updated: part loads possible" }'
# push back to the top of search
curl -X POST "$SAMO_API/proposals/clx7k2n9p0001/bump" -H "Authorization: Bearer $SAMO_KEY"
# take off air / put back
curl -X POST "$SAMO_API/proposals/clx7k2n9p0001/archive" -H "Authorization: Bearer $SAMO_KEY"
curl -X POST "$SAMO_API/proposals/clx7k2n9p0001/restore" -H "Authorization: Bearer $SAMO_KEY"
# delete permanently
curl -X DELETE "$SAMO_API/proposals/clx7k2n9p0001" -H "Authorization: Bearer $SAMO_KEY"
Worth knowing about these actions:
bumpis available to the listing's author or its contact person. Being the company owner is not enough on its own. Bumping too soon or exhausting the bump limit returns400 validation_errorwith the reason inmessage.archiveandrestoreare idempotent: calling either on a listing already in that state returns200and changes nothing.restoreis not free: putting a listing back on air resets itscreatedAtand view counter, and it occupies an active-listing slot on your plan. At the cap you get a400.- Touching another company's listing returns
404, not403.
Employees
# list the staff of the company the key belongs to
curl "$SAMO_API/employees" -H "Authorization: Bearer $SAMO_KEY"
# how many listings a specific employee is on — worth checking before deactivating them
curl "$SAMO_API/employees/clx9f4k7r0004/proposals-count" -H "Authorization: Bearer $SAMO_KEY"
# temporarily block access
curl -X PATCH "$SAMO_API/employees/clx9f4k7r0004/status" \
-H "Authorization: Bearer $SAMO_KEY" \
-H "Content-Type: application/json" \
-d '{ "status": "DEACTIVATED" }'
# restore access
curl -X PATCH "$SAMO_API/employees/clx9f4k7r0004/status" \
-H "Authorization: Bearer $SAMO_KEY" \
-H "Content-Type: application/json" \
-d '{ "status": "ACTIVE" }'
# change role
curl -X PATCH "$SAMO_API/employees/clx9f4k7r0004" \
-H "Authorization: Bearer $SAMO_KEY" \
-H "Content-Type: application/json" \
-d '{ "role": "DISPATCHER" }'
List response:
[
{
"id": "clx9f4k7r0004",
"userId": "clx2h6m3t0005",
"email": "[email protected]",
"name": "Оксана Ковальчук",
"role": "DISPATCHER",
"status": "ACTIVE",
"invitedAt": "2026-01-02T10:00:00.000Z",
"joinedAt": "2026-01-03T09:30:00.000Z",
"avatar": null,
"phones": [{ "phone": "+380501234567", "isPrimary": true, "hasTelegram": true, "hasWhatsApp": false, "hasViber": false }]
}
]
Note the two different identifiers: id is the membership and goes in the path of these
endpoints; userId is the person, and that is what a listing's contactPersonId takes.
Deactivating someone does not unpublish their listings — those stay on air and keep showing
them as the contact. Check proposals-count first and reassign with PATCH /proposals/{id} if
needed.
Creating and deleting employees is not available through the API — that is done in the dashboard.
Assigning a listing to an employee
curl -X POST "$SAMO_API/proposals/cargo" \
-H "Authorization: Bearer $SAMO_KEY" \
-H "Content-Type: application/json" \
-d '{
"startLocations": [{ "type": "country", "countryCode": "UA" }],
"endLocations": [{ "type": "country", "countryCode": "PL" }],
"dataLoadStart": "2026-08-12T00:00:00.000Z",
"dataLoadEnd": "2026-08-14T00:00:00.000Z",
"capacity": 20,
"description": "Pallets",
"carTypes": ["tent"],
"contactPersonId": "clx2h6m3t0005"
}'
contactPersonId is the userId from the employee list. Only the company owner and employees
with status ACTIVE are accepted; anyone else fails with 400 validation_error. Omit the field
and the contact becomes whoever issued the key.
Counterparty lookup
curl "$SAMO_API/companies/clx4a1b2c0001" -H "Authorization: Bearer $SAMO_KEY"
curl "$SAMO_API/companies/clx4a1b2c0001/reviews?page=1&limit=20" -H "Authorization: Bearer $SAMO_KEY"
Only approved reviews are returned, and limit is capped at 50.
Incremental sync
Pull a delta instead of walking the whole exchange:
curl -G "$SAMO_API/proposals/search" \
-H "Authorization: Bearer $SAMO_KEY" \
--data-urlencode "updatedSince=2026-08-05T09:00:00.000Z" \
--data-urlencode "limit=100"
Then page through nextCursor, deduplicating by id. The details and pitfalls are in
Pagination and sync.