Migrating from Ecolet — overview
You integrate with the Ecolet API (panel.ecolet.ro, v1/v2) and your
shipments are moving to the Apaczka platform. This section maps what you
already have onto the Apaczka API v2 so you can rewrite the transport layer
without rediscovering every difference yourself.
Three pages:
- This overview — the concepts that change, and in what order to tackle them.
- Endpoint mapping — call by call, what replaces what.
- Gaps and workarounds — what Apaczka has no equivalent for, and what to do instead.
Your business logic survives; the protocol does not
Orders, parcels, COD, labels and tracking all exist on both sides. What changes is how you talk to the API — authentication, transport, the shape of an address, and the fact that order creation is synchronous. Budget the work there.
The five differences that drive the work
| # | Area | Ecolet | Apaczka API v2 |
|---|---|---|---|
| 1 | Authentication | Bearer JWT from oauth/token | Per-request HMAC-SHA256 signature |
| 2 | Transport | GET / POST / DELETE, JSON body, HTTP 401/422/500 | POST only, form-urlencoded envelope, always HTTP 200 |
| 3 | Order creation | Asynchronous — send-order → order_to_send_id → polling | Synchronous — one call returns the order and its waybill number |
| 4 | Money | RON, decimal strings ("16,28") | Integers in minor units (grosze: 1 PLN = 100 gr) |
| 5 | Addresses | Structured RO fields + locality_id | line1 / line2 + free-text city + postal_code |
Everything else — labels, multi-parcel shipments, COD, pickup windows — is a mechanical transformation.
1. Authentication — no tokens, sign every request
There is no login call and no session. Each request carries your app_id,
an expires timestamp and an HMAC-SHA256 signature computed over the
route, the payload and the expiry, using your app_secret.
Code
Practical consequences:
- Delete your token cache and refresh logic. There is nothing to store or renew.
expiresis a short validity window per request, not a session TTL. Your server clock matters — skew causes mass rejections.- The
app_secretnever leaves your server. Signing in a browser or mobile app leaks it.
Full algorithm with worked examples: Request signing (HMAC). Using PHP? The SDK signs for you and this whole section becomes two constructor arguments.
2. Transport — one verb, one status code
Every endpoint is a POST to a path with a trailing slash, with the
payload sent as a JSON string inside a form field:
Code
The response is always HTTP 200. The real outcome lives in the body:
Code
So:
- Rewrite
GET /order/{id}asPOST order/{id}/,DELETE /order/{id}asPOST cancel_order/{id}/. - Stop branching on HTTP status codes. Branch on the body's
status. Any HTTP code other than 200 is a transport failure, not an API error. - Ecolet's field-level
errors[]has no direct counterpart — errors arrive as a singlemessage. If your UI highlights individual invalid fields, see Gaps and workarounds.
Details: Error semantics.
3. Order creation — synchronous, and not idempotent
Ecolet's flow was: send-order → order_to_send_id → poll
order-to-send/{id} until ordered or error → then read the order.
Apaczka's order_send does all of it in one call and returns the order id,
the carrier waybill number and the tracking URL. The
order_to_send state machine disappears — there is no intermediate entity,
nothing to poll, and no order_to_send/destroy.
Remove the polling loop, add retry discipline
order_send is not idempotent. Where Ecolet let you re-poll a pending
job safely, a retried Apaczka call after a timeout can create a second
order and a second waybill. Treat a timeout as "unknown" and reconcile via
the order list — never as "failed, retry". See best
practices.
The full flow, end to end: Order lifecycle.
4. Money — integers, minor units
| Ecolet | Apaczka | |
|---|---|---|
| Type | decimal string, comma separator ("16,28") | integer |
| Unit | RON | minor units — grosze in the reference (1 PLN = 100 gr) |
| Fee breakdown | itemised fees[] | net / gross totals |
Convert at the edges of your system and keep integers internally. 1628
means 16.28 — an off-by-100 here becomes a real invoice.
COD amounts and shipment_value follow the same rule; cod.currency
carries the currency code explicitly.
Confirm currency and VAT handling before go-live
The RON ↔ PLN question (which currency your account is settled in, and whether COD payouts convert) is a commercial setting, not an API flag. Confirm it with your Alsendo contact rather than inferring it from a test response. VAT differs between markets and is an invoicing concern, not an API one.
5. Addresses — two lines, not eight fields
Ecolet models a Romanian address as discrete fields. Apaczka takes two free address lines plus city, postal code and country:
| Ecolet | Apaczka | How |
|---|---|---|
street_name, street_number | line1 | concatenate |
block, entrance, floor, flat | line2 | concatenate with prefixes |
locality_id | city | resolve the id to a name (see gaps) |
locality | city | direct |
county | state_code | direct |
| — | name, contact_person, email, phone | required contact fields |
The join is lossy — keep your own copy
line1 + line2 cannot be reliably split back into block / entrance /
floor / flat. Romanian couriers often want those parts separately, so
store the structured address in your own database and treat the Apaczka
representation as a rendering of it, not as the record.
When composing line2, use the conventional Romanian prefixes so a human
(and the courier) can still parse it:
Code
Watch the field length limits in the API v2 reference — a long Romanian address can exceed them.
Parcel points (lockers, PUDO) are attached differently too: the point id
goes into foreign_address_id on the relevant address and the pickup type
stays SELF. See Pickup types.
Suggested order of work
Each step is independently testable, and the earlier ones unblock the later ones:
- Signing — get one
service_structurecall to returnstatus: 200. Nothing else works until this does. - Service dictionary — map your Ecolet service slugs onto the numeric
service_ids your account actually offers. Do not hardcode the list; read it fromservice_structureand cache it (refresh daily). - Address rendering — build and unit-test the Ecolet →
line1/line2transformation, including thelocality_idlookup. - Order creation on the sandbox —
order_send, thenwaybill. At this point you can ship. - Tracking — switch from Ecolet's batch status call to per-waybill
tracking, or (better) to push webhooks. - The rest — COD, options, parcel points, pickups, cancellation.
- The gaps — locations, address book,
/meusage. Read Gaps and workarounds before step 1 so none of them surprise you at the end.
Next: Endpoint mapping · Gaps and workarounds · Quick start

