Gaps and workarounds
Some of what the Ecolet API gave you has no counterpart in the Apaczka API v2. None of it is a blocker, but each one is a decision you should make before you start rewriting, not after — most of them mean data or state moves into your own system.
Locations API
Gone: locality, street, postal-code and county lookups
(locations/ro/localities/{code}, locations/{id}/streets/{street},
search-street-postal-codes, counties, countries).
Why it matters: Ecolet addresses are built around locality_id, an id
from Ecolet's own dictionary. Apaczka takes a free-text city and a
postal_code — there is no dictionary to query and no id to resolve.
What to do:
- Export the locality dictionary while you still have Ecolet access.
You need at minimum
locality_id → name, county, postal codes. Without it you cannot render historical addresses. - Own it going forward. Romanian postal data is available from public sources; refresh it on a schedule. This is the single largest piece of work created by the migration — budget for it explicitly.
- Keep validating on your side. Apaczka will accept an address it
cannot deliver to; a bad
city/postal_codepair surfaces as a carrier failure, not an API error.
Do this first
Address autocompletion is usually the most-called part of a shipping integration. If your checkout depends on street search, that feature has to exist on your side before you can cut over — it is not something to discover in the last sprint.
Address book
Gone: v1/address-book.
What to do: keep addresses in your own database. If you were using Ecolet as the system of record for sender/receiver addresses, export them before cut-over. Apaczka takes the full address on every order, so nothing is lost functionally — only the storage moves.
Order basket / async validation
Gone: order-to-send/{id} and order-to-send/destroy, and with them
the new / ordered / error state machine.
Why it matters: in Ecolet, send-order returned an order_to_send_id
and you polled until the order materialised. That intermediate entity was
also where you learned about validation problems.
What to do:
- Drop the polling loop and the intermediate state entirely —
order_send/returns the finished order or an error envelope. - If you show users a "ready to send" basket, keep it local. Apaczka has no concept of a draft order.
- To check a payload before committing, call
order_valuation/with the final order object. It fails on the same structural problems and also gives you the price.
Field-level validation errors
Gone: Ecolet's 422 with errors: { "postal_code": ["…"] }.
Apaczka returns a single message:
Code
What to do: validate on your side where the user can see it — required
fields, postal-code format, phone format, field lengths, option
availability. Treat the API's message as a last-resort diagnostic to log
and show to support staff, not as something to map back onto form fields.
See Error semantics.
Batch tracking
Gone: order/get-statuses-for-many-orders — one call, many orders.
tracking/{waybill}/ handles one waybill per call.
What to do:
- Preferred: switch to push tracking webhooks. Status changes arrive as signed notifications and the polling problem disappears.
- If you must poll: only poll orders that are actually in transit, back off as a shipment ages, and cache. Naïvely fanning out one request per open order per minute will not scale.
Parcel point lookup
points/{type}/ returns points by type / carrier, not by locality, and it
has no destination (sender / receiver) filter. Romanian lockers
frequently differ in whether they accept drop-offs, deliveries or both.
What to do: read option_send, option_deliver and option_cod on
each point and filter yourself before offering it to the user. Cache the
directory (it changes slowly) and select on your side by coordinates or
postal code.
Point ids are Apaczka's, not Ecolet's — if you stored Ecolet point ids on past orders, keep them, but resolve fresh ids for new ones.
COD bank account
Different: Ecolet always paid COD out to the account configured on the
user. Apaczka expects the payout account on the order — either
cod.bankaccount or cod.bankaccount_id referring to an account saved in
the panel.
What to do: save the payout account in the Apaczka panel once and send
cod.bankaccount_id. That reproduces the Ecolet behaviour without putting
account numbers in every request.
Account and credit information
me/ exists but is feature-flag-gated and does not reproduce Ecolet's
v1/me — credit limit, credit usage, unpaid invoices, blocked carriers and
services are not part of it.
What to do: if your integration gates order creation on the customer's
credit standing, raise it with your Alsendo contact before cut-over. Do not
build against me/ until you have confirmed it is enabled on your account
and returns what you need.
Smaller differences
| Area | Difference | What to do |
|---|---|---|
| Labels | Base64 inside JSON, not a binary stream | base64_decode() before writing the file |
| Label format | is_zebra is a request field; in Ecolet it came from user settings | Pass the format explicitly per request |
| Multi-parcel | parcel.amount / parcels object | One shipment[] entry per physical parcel |
| Pickup hours | Ecolet takes a single hour, Apaczka a range | Send hours_from / hours_to; read valid windows from pickup_hours/ |
| Coupon codes | No equivalent | Discounts are a commercial arrangement on the account, not an API field |
uit_code | No equivalent | Keep as your own metadata (ANAF high-tax-risk goods) |
sender_forklift / receiver_forklift | No equivalent | Pallet-specific; confirm handling per service with your Alsendo contact |
| Rate limiting | Different model and limits | Cache service_structure/ and points/ (refresh daily) and pickup_hours/ (minutes). A shared cache — Redis or equivalent — is the practical answer for multi-worker setups. |
Migration checklist
Before cut-over, make sure you have:
- Exported the Ecolet locality dictionary and any address book data
- A street / postal-code lookup running on your side
- A mapping table from Ecolet service slugs to Apaczka
service_ids, built fromservice_structure/and refreshed, not hardcoded - A mapping for additional services, with an explicit decision for the ones that have no equivalent
- Structured addresses stored on your side — the
line1/line2join is one-way - Money handled as integer minor units end to end
- The
order_to_sendpolling loop removed and retry behaviour reviewed (order_send/is not idempotent) - Tracking moved to webhooks, or polling reduced to open shipments only
- The COD payout account configured in the Apaczka panel
- Validation moved to your side, with API
messagevalues logged - The whole flow exercised on the sandbox
See also: Overview · Endpoint mapping · Best practices

