Overview
Icon for Packeta

Packeta

Packeta (Zásilkovna) shipping for Central European delivery

medusa-plugin-packeta

Packeta (Zásilkovna) fulfillment provider for Medusa v2 — pickup points (Z-Point, Z-BOX, external carrier PUDOs), home delivery through Packeta's carrier network across Europe, cash on delivery, labels (PDF/ZPL, Packeta or carrier), push tracking, returns via the claim assistant, plus an admin UI and a storefront widget helper.

Disclaimer: This is an unofficial, community-built integration. It is not affiliated with, endorsed by, or maintained by Packeta s.r.o. / Zásilkovna s.r.o. "Packeta", "Zásilkovna" and their logos are trademarks of their respective owners, used here only to identify the service this plugin integrates with.

npm | Packeta API docs | Medusa Fulfillment Module

Features

  • Fulfillment options: Copy to clipboardpacketa-pickup (any Packeta / partner pickup point chosen in the widget), Copy to clipboardpacketa-home-delivery (Packeta home-delivery carrier picked from the shipping country), Copy to clipboardpacketa-return, and — optionally — one option per carrier from Packeta's live carrier feed (Copy to clipboardpacketa-carrier-<id>), so you can price Z-BOX, PPL ParcelShop, InPost, DHL … separately.
  • Checkout validation: the selected pickup point is re-validated server-side against Packeta's widget validate endpoint (exists, allowed for your account, currently accepting packets).
  • Packets: Copy to clipboardcreatePacket on fulfillment with COD, insured value, weight from variant weights, note, earliest delivery date, adult content, dimensions, carrier services, customs declarations for non-EU carriers.
  • COD decided automatically from the order's payment provider (Copy to clipboardpp_system_default by default), overridable per packet in the admin.
  • Labels: Packeta PDF (all formats), ZPL (203/300 dpi), external carrier PDF/ZPL, bulk PDF for many packets. Tracking number + label link attached to the Medusa fulfillment.
  • Tracking: signed push-tracking webhook (Copy to clipboard/hooks/packeta) + a polling job fallback; statuses mark the Medusa fulfillment shipped / delivered automatically.
  • Cancel packets (and the fulfillment) before hand-over; returns create claim-assistant packets with a drop-off password.
  • Admin UI (EN/CS): order-page card with status, destination, COD, labels, refresh, cancel, "Create packet" (split orders into several packets); Copy to clipboard/packeta page listing all packets with filters, bulk label printing and integration health.
  • Storefront helper Copy to clipboardmedusa-plugin-packeta/widget: dependency-free wrapper around Packeta widget v6 + HD widget that returns exactly the Copy to clipboarddata this provider expects.
  • Zero runtime dependencies (fetch + Copy to clipboardnode:crypto).

Install

bun add medusa-plugin-packeta # or npm / yarn / pnpm

Configure

The same options object is passed to the fulfillment provider and to the plugin's Copy to clipboardpacketa module (routes/workflows use the module, the checkout uses the provider). The plugin entry loads the API routes, subscribers, job and admin UI.

// medusa-config.ts
const packeta = {
api_password: process.env.PACKETA_API_PASSWORD, // 32-hex API password
api_key: process.env.PACKETA_API_KEY, // 16-char API key (feeds, widget)
eshop: process.env.PACKETA_ESHOP, // sender indication (client section → Senders)
webhook_signing_key: process.env.PACKETA_WEBHOOK_SIGNING_KEY, // from Packeta support, see Push tracking
// optional — see the table below
// cod_payment_providers: ["pp_system_default"],
// enabled_carriers: ["106", "131", "3060"],
// label_format: "A6 on A6",
}
module.exports = defineConfig({
// ...
modules: [
{
resolve: "@medusajs/medusa/fulfillment",
options: {
providers: [
{ resolve: "@medusajs/medusa/fulfillment-manual", id: "manual" },

Then run the plugin's migration (creates the Copy to clipboardpacketa_packet table):

npx medusa db:migrate

The provider is registered as Copy to clipboardpacketa_packeta. In the admin: Settings → Locations & Shipping → your location → Fulfillment providers → add Packeta, then create shipping options for the service zone choosing the Packeta fulfillment options (pickup / home delivery / per-carrier). Prices are flat Medusa prices — Packeta has no price API.

Options

Option Required Default Description Copy to clipboardapi_password yes — REST API password (client section → Support). Copy to clipboardapi_key yes — API key used by the carrier feed, the widget and the widget validate endpoint. Copy to clipboardeshop yes — Sender indication. Use a dedicated test sender while integrating — Packeta has no sandbox. Copy to clipboardcod_payment_providers no Copy to clipboard["pp_system_default"] Payment provider ids that mean cash on delivery. Copy to clipboarddefault_weight_kg no Copy to clipboard0.5 Packet weight when the order has no variant weights. Copy to clipboardpackaging_weight_g no Copy to clipboard100 Added to the summed variant weights (grams). Copy to clipboardlabel_format no Copy to clipboard"A6 on A6" Copy to clipboardA6 on A6, Copy to clipboardA7 on A7, Copy to clipboardA6 on A4, Copy to clipboardA7 on A4, Copy to clipboard105x35mm on A4, Copy to clipboardA8 on A8. Copy to clipboardexpose_carriers no Copy to clipboardtrue Add one fulfillment option per carrier from the feed. Copy to clipboardenabled_carriers no Copy to clipboard"all" Carrier ids to expose (Copy to clipboard["106", "3060"]) or Copy to clipboard"all". Copy to clipboardfeed_ttl_s no Copy to clipboard86400 Carrier feed cache TTL. Copy to clipboardvalidate_pickup_point no Copy to clipboardtrue Validate the chosen point through Packeta at checkout. Copy to clipboardwebhook_signing_key no — Push-tracking signing key. Without it the webhook answers 503. Copy to clipboardallow_unsigned_webhook no Copy to clipboardfalse Accept unsigned webhooks (local development only; ignored in production). Copy to clipboardwebhook_tolerance_s no Copy to clipboard300 Max age of a webhook timestamp before it is treated as a replay. Copy to clipboardtracking_url no Copy to clipboardhttps://tracking.packeta.com/cs/?id={barcode} Copy to clipboard{barcode} / Copy to clipboard{id} placeholders. Copy to clipboardauto_ship_status_ids no Copy to clipboard[2,3,4,5,6,12] Packeta status ids that mark the fulfillment shipped. Copy to clipboardauto_deliver_status_ids no Copy to clipboard[7] Status ids that mark it delivered. Copy to clipboarddefault_size no — Copy to clipboard{ length, width, height } in mm, used when a carrier requires dimensions. Copy to clipboardcustoms no — Copy to clipboard{ ead, default_hs_code, default_origin_country, invoice_number } for non-EU carriers (see Customs). Copy to clipboardreturn_value_default no Copy to clipboard1 Insured value for return claims when none is passed. Copy to clipboardpoll_status / Copy to clipboardpoll_status_cron / Copy to clipboardpoll_status_batch / Copy to clipboardpoll_status_max_age_days no Copy to clipboardtrue / Copy to clipboard*/30 * * * * / Copy to clipboard100 / Copy to clipboard60 Status polling job (fallback for push tracking). Copy to clipboardbase_url, Copy to clipboardfeed_base_url, Copy to clipboardwidget_validate_url no Packeta production URLs Overridable for tests.

Environment

PACKETA_API_PASSWORD=
PACKETA_API_KEY=
PACKETA_ESHOP=
PACKETA_WEBHOOK_SIGNING_KEY=

Storefront

Packeta pickup points are chosen in Packeta's widget on your storefront. Pass the selection as the shipping method Copy to clipboarddata; the provider validates and normalises it.

import { pickPoint, pickAddress, pointToShippingMethodData, addressToShippingMethodData } from "medusa-plugin-packeta/widget"
// Pickup point (Z-Point, Z-BOX, partner PUDO). Filter with `vendors` / `country` if you like.
const point = await pickPoint(process.env.NEXT_PUBLIC_PACKETA_API_KEY!, { language: "cs", country: "cz,sk" })
if (point) {
await sdk.store.cart.addShippingMethod(cart.id, {
option_id: pickupShippingOption.id,
data: pointToShippingMethodData(point),
})
}
// Home delivery: the address from the cart is enough…
await sdk.store.cart.addShippingMethod(cart.id, { option_id: homeShippingOption.id, data: {} })
// …or let the customer pick a validated address in Packeta's HD widget (CZ/SK):
const carrierId = (await fetch(`${MEDUSA_URL}/store/packeta/carriers?country=cz`, { headers }).then((r) => r.json())).home_delivery_carrier_id
const address = await pickAddress(process.env.NEXT_PUBLIC_PACKETA_API_KEY!, { carrierId, language: "cs" })
if (address) {
await sdk.store.cart.addShippingMethod(cart.id, { option_id: homeShippingOption.id, data: addressToShippingMethodData(address, carrierId) })
}

The Copy to clipboarddata contract (what Copy to clipboardpointToShippingMethodData produces) if you drive the widget yourself:

// Packeta point
{ "point_id": "79", "point": { "id": "79", "name": "…", "street": "…", "city": "…", "zip": "…", "country": "cz", "group": "zbox", "type": "internal" } }
// Partner carrier point (InPost, PPL, …)
{ "carrier_id": "3060", "carrier_pickup_point_id": "BIA10M", "point": { "name": "…", "type": "external", "carrier_id": "3060", "carrier_pickup_point_id": "BIA10M" } }
// Home delivery with a specific address (optional; the cart address is the default)
{ "address": { "street": "Vinohradská", "house_number": "1", "city": "Praha", "zip": "12000", "country": "cz" } }

Copy to clipboardGET /store/packeta/carriers?country=cz returns the public carrier list (Copy to clipboardid, Copy to clipboardname, Copy to clipboardcountry, Copy to clipboardpickup_points, Copy to clipboarddisallows_cod, Copy to clipboardrequires_size, Copy to clipboardmax_weight) and Copy to clipboardhome_delivery_carrier_id — handy for widget Copy to clipboardvendors filters. See Copy to clipboarddocs/storefront-nextjs.md for a Next.js starter walkthrough.

Admin

  • Order page → Packeta card: packet barcode, status, destination, COD/value/weight, tracking link, label menu (PDF, ZPL, carrier PDF/ZPL), refresh, cancel, return password. Create packet opens a drawer to choose items (split into several packets), COD, weight, note, earliest delivery date, adult content and dimensions.
  • Packeta page (sidebar): all packets with search/filters, bulk Print labels, bulk Refresh, and health badges (API password, feed, webhook).
  • Fulfilling an order the native way (Fulfill items) also creates the packet — the plugin mirrors it into Copy to clipboardpacketa_packet and attaches the tracking label.
  • The admin UI is in English and Czech (follows the browser language).

Admin API

Route Copy to clipboardGET /admin/packeta/packets Copy to clipboard?order_id= ?fulfillment_id= ?kind= ?status_group= ?q= ?limit= ?offset= Copy to clipboardGET /admin/packeta/packets/:id Copy to clipboard:id = packet id, Copy to clipboardZ… barcode or record id Copy to clipboardGET /admin/packeta/packets/:id/label `?type=pdf Copy to clipboardPOST /admin/packeta/packets/labels Copy to clipboard{ packet_ids, format?, offset? } → one PDF Copy to clipboardPOST /admin/packeta/packets/:id/refresh pull status from Packeta Copy to clipboardPOST /admin/packeta/packets/:id/cancel cancel at Packeta + Medusa fulfillment Copy to clipboardPOST /admin/packeta/orders/:id/packet Copy to clipboard{ items?, cod?, cod_amount?, weight_kg?, note?, deliver_on?, adult_content?, size?, number? } Copy to clipboardGET /admin/packeta/carriers carrier feed (Copy to clipboard?country= ?refresh=1) Copy to clipboardGET /admin/packeta/health credentials / feed / webhook status

How it works

  1. CheckoutCopy to clipboardvalidateFulfillmentData receives the widget selection, checks it with Packeta's validate endpoint (pickup) or resolves the home-delivery carrier for the shipping country, and stores a normalised Copy to clipboard{ kind, point_id | carrier_id + carrier_pickup_point_id, point | address } on the shipping method.
  2. Copy to clipboardorder.placed — a subscriber looks at the payment provider; COD orders get Copy to clipboardmetadata.packeta_cod = true (the fulfillment provider cannot see payments itself).
  3. FulfillmentCopy to clipboardcreateFulfillment maps order + shipping data to Packeta Copy to clipboardcreatePacket (recipient, destination, value, weight, COD, note, size, customs). The packet id/barcode land in Copy to clipboardfulfillment.data; a subscriber mirrors them into Copy to clipboardpacketa_packet and attaches the tracking label to the fulfillment.
  4. Tracking — Packeta pushes status events to Copy to clipboard/hooks/packeta (HMAC-SHA256 signed, deduplicated by event id). Copy to clipboardarrived/Copy to clipboarddeparted/… mark the fulfillment shipped, Copy to clipboarddelivered marks it delivered. The polling job does the same for stores without push tracking.
  5. Cancel — before hand-over, Copy to clipboardcancelPacket at Packeta and the fulfillment is cancelled in Medusa. Afterwards Packeta refuses (Copy to clipboardCancelNotAllowedFault) and so does the plugin.
  6. Returns — a return shipping option using Copy to clipboardpacketa-return creates a claim-assistant packet (Copy to clipboardcreatePacketClaimWithPassword); the customer drops it at any pickup point with the password shown in the admin (Packeta e-mails it too when the return has an e-mail).

Push tracking (webhook)

Packeta enables webhooks per account: e-mail integrations@packeta.com with your HTTPS URL

https://<your-backend>/hooks/packeta

and they issue a signing keyCopy to clipboardwebhook_signing_key. Requests are verified with Copy to clipboardHMAC-SHA256(key, "{X-Webhook-Timestamp}.{rawBody}") in constant time and the timestamp must be within Copy to clipboardwebhook_tolerance_s (5 min) of the server clock — unsigned, tampered or replayed requests get 401, unknown packets 200 (so Packeta stops retrying). Events are deduplicated by Copy to clipboardX-Webhook-Event-Id, and a late/replayed event can never move a delivered / returned / cancelled packet backwards. Until the key is configured, the polling job (every 30 min by default, Copy to clipboardpoll_status_cron) keeps statuses fresh. Copy to clipboardallow_unsigned_webhook is for local development only and is ignored when Copy to clipboardNODE_ENV=production.

Cash on delivery

COD is applied when the order's payment provider is in Copy to clipboardcod_payment_providers (default: Medusa's manual Copy to clipboardpp_system_default): Copy to clipboardorder.metadata.packeta_cod is set to Copy to clipboardtrue at Copy to clipboardorder.placed. The admin "Create packet" drawer shows the detected state and lets you override amount/off. Carriers with Copy to clipboarddisallowsCod reject COD packets with a clear error.

Split shipments (several packets per order, from the drawer or native partial fulfillments): the customer pays the order once — the first packet carries the full COD, after which the flag flips to Copy to clipboard"collected" (with Copy to clipboardpacketa_cod_barcode) and every later packet is created with COD 0 unless you pass Copy to clipboardcod_amount explicitly. Each packet is insured for the value of the items it carries (plus shipping on the first one) and follow-up packets get a Copy to clipboard<order>-2, Copy to clipboard<order>-3 reference.

Customs (non-EU)

For carriers flagged Copy to clipboardcustomsDeclarations in the feed the plugin sends Copy to clipboardattributes (ead, deliveryCost, invoiceNumber, invoiceIssueDate) and Copy to clipboarditems built from the order lines (variant/product Copy to clipboardhs_code, Copy to clipboardorigin_country, title, value, units, weight). Configure Copy to clipboardcustoms.ead (Copy to clipboardcarrier default), fallbacks (Copy to clipboarddefault_hs_code, Copy to clipboarddefault_origin_country) and pass per-packet extras (Copy to clipboardinvoiceFile, Copy to clipboardeadFile, Copy to clipboardmrn, Copy to clipboardcustoms_items) via Copy to clipboardadditional_data.customs when creating the fulfillment programmatically. See Packeta's customs documentation.

Storefront-agnostic notes

  • Provider id: Copy to clipboardpacketa_packeta; fulfillment option ids: Copy to clipboardpacketa-pickup, Copy to clipboardpacketa-home-delivery, Copy to clipboardpacketa-return, Copy to clipboardpacketa-carrier-<id>.
  • Weight: Medusa variant Copy to clipboardweight is treated as grams.
  • Amounts: Medusa v2 major units map 1:1 to Packeta Copy to clipboardvalue/Copy to clipboardcod. Currencies other than CZK/EUR/HUF/PLN/RON fall back to the destination country's currency.
  • Phone numbers must be in Packeta's accepted formats (E.164 recommended).

Development

bun install
bun run check # format + lint + typecheck + unit tests
bun run build # medusa plugin:build → .medusa/server
bun run test:integration # boots a real Medusa app against a mocked Packeta API (needs Postgres: DB_HOST/DB_PORT/DB_USERNAME/DB_PASSWORD)

Live smoke against your Packeta account (creates and cancels a real, free packet — use a test sender):

PACKETA_API_PASSWORD=PACKETA_API_KEY=PACKETA_ESHOP=… bun run smoke
bun run smoke carriers cz # feed
bun run smoke validate 79 # widget validate endpoint
bun run smoke label <packetId> # write label PDF

Try it inside a Medusa app without publishing: Copy to clipboardscripts/dev-install.sh <path-to-medusa-app> builds, packs and installs the tarball, then follow the config above.

License

MIT

This project is an independent, unofficial integration and is not affiliated with or endorsed by Packeta s.r.o. / Zásilkovna s.r.o. All Packeta and Zásilkovna trademarks and logos are the property of their respective owners.

You may also like

Browse all integrations

Build your own

Develop your own custom integration

Build your own integration with our API to speed up your processes. Make your integration available via npm for it to be shared in our Library with the broader Medusa community.

gift card interface

Ready to build your custom commerce setup?