Migrating from Legacy API to v1
The legacy API endpoints are deprecated and will be removed in the future. This page lists the v1 equivalent of each legacy endpoint and what you need to change in your integration. The Client ID / Secret Key from the panel stay the same.
General Changes
- The base URL is
https://gateway.zemlog.cominstead ofhttps://api.zemlog.com. The legacy endpoints keep working onhttps://api.zemlog.comuntil they are removed. - Endpoint paths have changed; the v1 path of each endpoint is listed in the table below.
- Successful responses use the
{ success, message, data }envelope; lists returnitems, plusmetawhen paginated. The legacy API returned the record directly and lists as plain arrays (the invoice list withdataandpagination). See Getting Started for details. - The error body has the form
{ success: false, code, message, errors? }; use thecodefield rather thanmessagein your integration. Validation errors return 422VALIDATION_FAILEDinstead of 400;errors[].pathreplacesviolations[].propertyPath, and only the first error per field is listed. - Business rule errors that returned 406 in the legacy API return 400 with an operation-specific
codein v1; field-bound ones (e.g. a missing customs zip code) return 422. - When the API is temporarily unavailable, the legacy API returns 500
Unexpected error.and v1 returns 503DEPENDENCY_UNAVAILABLE. - The legacy API translated business rule messages to the
Accept-Languagelanguage. In v1, success, business rule, authorization and 503 messages are always in Turkish; name fields (country, unit of measure, status, package additional fee) and validation messages follow the language. v1 treats anAccept-Languagevalue starting withen(e.g.en-US,tr;q=0.9) as English. - Dates use
2026-10-10T10:24:19.000Zinstead of2026-10-10T10:24:19+00:00. - List endpoints are paginated (
page,perPageat most 200). The defaultperPageis 25 for the invoice list and 50 for helper lists; iterate overmeta.pageCountpages to get the whole list. sortOrdervalues areasc/desc(case-insensitive). An undefinedsortByvalue returns 422.- Query parameters are validated: a parameter sent empty (e.g.
search=) or with an undefined value is not ignored and returns 422.
Endpoint Mapping
| Legacy endpoint | v1 endpoint | Changes |
|---|---|---|
POST /auth/application | POST /api/v1/auth/application | The token is returned in data.token instead of the root-level token. Invalid client credentials return 401 CLIENT_ID_OR_SECRET_INVALID; a missing field returns 422 instead of 400. |
POST /api/calculate/price | POST /api/v1/shipment/price-calculations | Carrier quotes are returned in items instead of a root array. fuelSurcharges was renamed to fuelSurcharge. Package rows no longer return id, piece and perimeter; packages[].id in the request is not supported. Package additional fees use { slug, name, amount } instead of { type, desc, value }. null counts as not sent only for city, zipCode, date and piece; other fields (e.g. carrierId, packages) return 422. |
POST /api/calculate/price/country/validate | POST /api/v1/shipment/price-calculations/validate-destination | Returns 200 with { success, message } instead of 204 on success. An unsuitable destination returns 422 with field-specific messages (e.g. Zip code is required for this country. instead of This value should not be blank.). |
POST /api/consignment/document | POST /api/v1/shipment/shipments/documents | Returns 201 instead of 200; the shipment is in data. A senderAddressId that does not belong to the organization returns 400 BAD_REQUEST instead of 406. The response field changes are the same as for the package shipment. |
POST /api/consignment/package/advance | POST /api/v1/shipment/shipments/packages | Products send unitOfMeasureCode instead of unitOfMeasure: { code, name }. Returns 201 instead of 200; the shipment is in data. Business rule errors that returned 406 return 400 with a code (e.g. CARRIER_IS_NOT_FOUND, SENDER_ADDRESS_TAX_INFORMATION_IS_NOT_COMPLETED; city and county candidates in params.options); a missing customs zip code and a city that does not match the county in the new sender address return 422. An empty package or product list returns 422 (packages / products) instead of a 400 message. A completed single point import returns SINGLE_POINT_IMPORT_HAS_ALREADY_BEEN_COMPLETED_BEFORE instead of Single point import is not found.. In the response, country info is in the flags and tax objects (instead of flagW20 / flagW40 and taxType / taxMaxValidPrice); currency.updatedAt and orderData are not returned; singlePointImport.upsShipmentId replaces singlePointImport.shipmentId. Type, status, country and origin country names follow the Accept-Language language instead of always being Turkish. |
POST /api/single/point/import | POST /api/v1/shipment/single-point-imports | Returns 201 instead of 200; the record is in data. upsShipmentId (GCCN) replaces shipmentId; senderAddress and createdAt are not returned. A name that is already in use returns 422 (name) instead of 400. The SINGLE_POINT_IMPORT_CHARGE_FAILED error returns 400 instead of 500. |
POST /api/single/point/import/complete | POST /api/v1/shipment/single-point-imports/{id}/complete | The ID is sent in the path instead of the body. Returns 200 with { success, message } instead of 204 on success. |
POST /api/invoice/list | GET /api/v1/finance/invoices | A GET request; filters are sent as query parameters instead of the body. isPaid replaces isPayment, dateFrom / dateTo replace dateAtBetween.startAt / endAt, and paymentExpirationFrom / paymentExpirationTo replace paymentExpirationAtBetween.startAt / endAt; dates are only accepted as YYYY-MM-DD. The value 4 was removed from the statusId filter. The response returns items instead of data, meta (total, pageCount) instead of pagination (count, lastPage), and currencyCode instead of currency.code; organizationId was added. |
GET /country/all | GET /api/v1/public/shared/countries | Paginated. flags.w20 / flags.w40 replace flagW20 / flagW40, and tax.type / tax.maxValidPrice replace taxType / taxMaxValidPrice. The isGuarantee filter is a boolean instead of "1" / "0" (true / false, 1 / 0 are also accepted); other values are not ignored and return 422. search also matches Turkish names. |
GET /city/all | GET /api/v1/public/shared/cities | Paginated. countryId or countryCode is no longer required; without them, the cities of all countries are returned. The ids, nameExact and namePrefix filters were added. |
GET /county/all | GET /api/v1/public/shared/counties | Paginated. cityId is no longer required; without it, all counties are returned. The ids, nameExact and namePrefix filters were added. |
GET /api/currency | GET /api/v1/shared/currencies | Paginated. The CBRT rates rates.sellingTry / rates.buyingTry (TRY) and rates.updatedAt replace rate and updatedAt; flagPath was added. The sortBy values are sellingTry / buyingTry instead of sellingRateTry / buyingRateTry. The codes and isDefined filters were added. |
GET /api/unit/of/measure/all | GET /api/v1/shared/units-of-measure | Paginated. An undefined sortBy value does not fall back to orderNumber; it returns 422. |
GET /api/export/code | GET /api/v1/shared/export-codes | Paginated. An undefined sortBy value does not fall back to orderNumber; it returns 422. |
Country, currency, unit of measure and export code IDs and codes have not changed; the tables under Helper Docs are valid for both versions.
