Skip to content

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.com instead of https://api.zemlog.com. The legacy endpoints keep working on https://api.zemlog.com until 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 return items, plus meta when paginated. The legacy API returned the record directly and lists as plain arrays (the invoice list with data and pagination). See Getting Started for details.
  • The error body has the form { success: false, code, message, errors? }; use the code field rather than message in your integration. Validation errors return 422 VALIDATION_FAILED instead of 400; errors[].path replaces violations[].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 code in 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 503 DEPENDENCY_UNAVAILABLE.
  • The legacy API translated business rule messages to the Accept-Language language. 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 an Accept-Language value starting with en (e.g. en-US,tr;q=0.9) as English.
  • Dates use 2026-10-10T10:24:19.000Z instead of 2026-10-10T10:24:19+00:00.
  • List endpoints are paginated (page, perPage at most 200). The default perPage is 25 for the invoice list and 50 for helper lists; iterate over meta.pageCount pages to get the whole list.
  • sortOrder values are asc / desc (case-insensitive). An undefined sortBy value 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 endpointv1 endpointChanges
POST /auth/applicationPOST /api/v1/auth/applicationThe 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/pricePOST /api/v1/shipment/price-calculationsCarrier 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/validatePOST /api/v1/shipment/price-calculations/validate-destinationReturns 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/documentPOST /api/v1/shipment/shipments/documentsReturns 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/advancePOST /api/v1/shipment/shipments/packagesProducts 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/importPOST /api/v1/shipment/single-point-importsReturns 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/completePOST /api/v1/shipment/single-point-imports/{id}/completeThe ID is sent in the path instead of the body. Returns 200 with { success, message } instead of 204 on success.
POST /api/invoice/listGET /api/v1/finance/invoicesA 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/allGET /api/v1/public/shared/countriesPaginated. 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/allGET /api/v1/public/shared/citiesPaginated. 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/allGET /api/v1/public/shared/countiesPaginated. cityId is no longer required; without it, all counties are returned. The ids, nameExact and namePrefix filters were added.
GET /api/currencyGET /api/v1/shared/currenciesPaginated. 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/allGET /api/v1/shared/units-of-measurePaginated. An undefined sortBy value does not fall back to orderNumber; it returns 422.
GET /api/export/codeGET /api/v1/shared/export-codesPaginated. 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.