Best practices
Hard-won recommendations for production integrations. Most of the pain described below comes from real support tickets — save yourself the trip.
Don't hardcode carriers and services
Fetch the service list from the API instead of hardcoding it. The set of available carriers and services changes over time — carriers are added and removed, the services available to your account depend on your contract, and the catalogue also differs between the sandbox and production. A hardcoded list silently goes stale: you either offer services that fail at order time or miss carriers you are already paying for.
Call service_structure (SDK: getServiceStructure()) to get the
current services, package types, options and pickup types for your account:
Code
The structure changes rarely — cache it (e.g. refresh once a day or on deployment) rather than calling it on every request, but treat the API as the source of truth, never your code.
Watch out: unknown payload keys are silently ignored
The API does not reject unknown fields in the request payload — a
misnamed key does not error, it just silently drops your data and the
API falls back to defaults. The classic trap: shipment dimensions are
dimension1 / dimension2 / dimension3 (cm) — send length / width /
height and the order is created with default dimensions and a wrong
price, with no warning anywhere. Build payloads against the
API v2 reference (or use the SDK's typed
request objects) and verify created orders with getOrder() during
development.
Never trust the HTTP status code
The API returns HTTP 200 for errors too — the real outcome is the
status field in the response body. Check it on every call, or use the
SDK, which does it for you and throws ApiException. See
Error semantics.
Be careful with retries
A timeout does not mean the request never reached the server. Blindly
retrying non-idempotent operations — order_send above all — can create
duplicate orders (and duplicate charges). Retry network failures only
for read operations, and reconcile writes via getOrders() before retrying
them. The SDK's RetryingHttpClient deliberately retries network failures
only, never API errors.
Remember: prices are in groszy
Valuation and order prices are returned in groszy (1 PLN = 100 gr). Divide by 100 before showing them to a human, and store them as integers — floats and money end badly.
Keep your credentials server-side
The app_secret signs every request — anyone holding it can create orders
on your account. Never embed it in frontend code, mobile apps or public
repositories. See Authentication.
Sign exactly what you send
If you integrate without the SDK: compute the signature over the exact
request string you send. Re-serializing the payload after signing
(different key order, whitespace, escaping) is the most common cause of
Invalid signature errors — see Request signing.
Verify webhook signatures
If you use push tracking, always
verify the notification signature before processing it — otherwise anyone
who finds your endpoint URL can inject fake status updates. The SDK's
PushTrackingWebhook refuses unverified payloads by design.
Identify your integration
Send a descriptive User-Agent (application name and version) with every
request. When something misbehaves — a burst of failing orders, a retry
storm — Apaczka can then tell which integration and which release is behind
it and reach out before it becomes an incident. The PHP SDK does this
for you (AlsendoOneSDK/<version> PHP/<version>); raw integrations should set
the header themselves.
Test in the sandbox first
Use the sandbox environment for development and CI — production orders are real orders with real couriers and real invoices.

