# Reading Pakkeshopper Data in Shopify: Integrator Guide

Pakkeshopper writes shipping and pickup-point information to Shopify in **two places**:

1. **The shipping line `code` field**: a compact, machine-readable string identifying the carrier, the delivery type, and (when applicable) the chosen pickup point.
2. **Order attributes** *(newer, recommended)*: a set of named, human-readable `_pakkeshopper_*` attributes on the order containing the carrier, delivery type, and the full pickup-point details.

For most integrations the **order attributes are the easiest and most robust source**. The `code` field remains available for backwards compatibility and as a cross-check.

---

## 1. The shipping line `code` field

**Where to find it**

| API | Field |
|-----|-------|
| REST Admin API | `order.shipping_lines[].code` |
| GraphQL Admin API | `order.shippingLines.edges[].node.code` |
| Carrier Service callback | the rate's `service_code` |

> The older `order.shipping_method.code` is the same value under the legacy field name.

### 1.1 Format (current: "Pakkeshopper 2.0")

```
pakkeshopper:P{ID}_{carrier}_{type}[_{pickup_point_id}]
```

| Segment | Meaning |
|---------|---------|
| `pakkeshopper` | Fixed prefix identifying our app. |
| `:P{ID}` | Our internal shipping-rate ID (the `P` is literal, joined with a colon, e.g. `:P7`). |
| `{carrier}` | Lowercase carrier token (see table below). |
| `{type}` | Delivery type: `droppoint`, `delivery_home`, `delivery_company`, or `other_other`. |
| `{pickup_point_id}` | Only present for `droppoint`. The carrier's own pickup-point ID, or `*` (see §1.3). |

**Carrier tokens**

| Token | Carrier | Token | Carrier |
|-------|---------|-------|---------|
| `gls` | GLS | `dhl` | DHL |
| `postnord` | PostNord | `instabox` | Instabox |
| `postnl` | PostNL | `budbee` | Budbee |
| `bring` | Bring | `helthjem` | Helthjem |
| `dao` | DAO | `hermes` | Hermes |
| `ups` | UPS | `evri` | Evri |
| `burd` | Burd | | |

### 1.2 Examples

```
pakkeshopper:P7_gls_droppoint_95218        Pickup point (GLS shop #95218)
pakkeshopper:P42_postnord_delivery_home    Home delivery (PostNord)
pakkeshopper:P13_gls_delivery_company      Company/business delivery (GLS)
pakkeshopper:P9_other_other_other_Express  Custom delivery method named "Express"
```

The pickup-point ID is the **carrier's own ID** for the selected shop, passed through verbatim. It is usually numeric but can be alphanumeric for some carriers: treat it as a string.

### 1.3 The new `_*` form (pickup-point "list mode")

When a merchant enables **list mode** for pickup points (a checkout where the buyer picks their shop live, from a list of the nearest points), the rate is created **before** any specific point is chosen. In that case the code ends with `_*`:

```
pakkeshopper:P7_gls_droppoint_*
```

The `*` is a placeholder meaning *"no specific pickup point yet: the buyer will choose one during checkout."*

> **Important:** When the code ends in `_droppoint_*`, do **not** try to read the pickup point from the code. The buyer's actual choice is stored in the **order attributes** instead (see §2). This is the main reason we added the order-attributes layer.

### 1.4 Other code formats you may encounter

| Code | When it appears |
|------|-----------------|
| `pakkeshopper_{carrier}_{type}[_{id}]` | **Legacy ("Pakkeshopper 1.0")**: identical structure but **without** the `:P{ID}` segment. |
| `{cc}_{carrier}_service_point_{id}` | **Shipmondo format** (e.g. `dk_gls_service_point_Z9`): used only if the merchant selected the Shipmondo data format. `{cc}` is a lowercase ISO country code. |
| `pakkeshopper_other_other_fallback` | Fallback rate shown when no pickup points were found near the buyer. |
| `pakkeshopper_combine_shipments_{orderId}` | "Combine open orders" option: references another order, not a carrier. |
| *(merchant-defined)* | If the merchant configured a **custom format** for a rate, the code is their literal template and bypasses everything above. |

> The rate ID may also appear in the **shipping line title** as a `[P{ID}]` suffix (e.g. `GLS Pickup [P7]`).

---

## 2. Order attributes *(recommended source)*

For every Pakkeshopper order, we write a set of named attributes to the order. These are the cleanest way to read the shipping choice: they're already decoded, human-readable, and (for pickup points) fully enriched with the shop's name and address.

**Where to find them**

| API | Field |
|-----|-------|
| REST Admin API | `order.note_attributes[]`: array of `{ name, value }` |
| GraphQL Admin API | `order.customAttributes[]`: array of `{ key, value }` |

> These keys start with an underscore (`_`), which means Shopify treats them as **hidden** attributes: they won't show on the customer-facing order-status page, but they are fully available through the Admin API.

### 2.1 Attribute keys

| Key | Description | Example |
|-----|-------------|---------|
| `_pakkeshopper_carrier` | Carrier (display name) | `GLS` |
| `_pakkeshopper_transport_type` | Delivery type (display) | `Pickup point` |
| `_pakkeshopper_pickup_point_id` | Pickup-point ID (carrier's own ID) | `95218` |
| `_pakkeshopper_pickup_point_name` | Pickup-point name | `Kiosk Nine` |
| `_pakkeshopper_pickup_point_address1` | Street address | `Vej 9` |
| `_pakkeshopper_pickup_point_address2` | Address line 2 (optional) | `Suite B` |
| `_pakkeshopper_pickup_point_zip` | Postal code | `9000` |
| `_pakkeshopper_pickup_point_city` | City | `Aalborg` |
| `_pakkeshopper_pickup_point_country_code` | ISO country code | `DK` |

`_pakkeshopper_transport_type` values: `Pickup point`, `Delivery Home`, `Delivery Company`, `Other`.

The `_pakkeshopper_pickup_point_*` keys are only present for **pickup-point** orders. Home- and company-delivery orders carry just `_pakkeshopper_carrier` and `_pakkeshopper_transport_type`.

### 2.2 Example (a pickup-point order)

```
_pakkeshopper_carrier:                   GLS
_pakkeshopper_transport_type:            Pickup point
_pakkeshopper_pickup_point_id:           95218
_pakkeshopper_pickup_point_name:         Kiosk Nine
_pakkeshopper_pickup_point_address1:     Vej 9
_pakkeshopper_pickup_point_zip:          9000
_pakkeshopper_pickup_point_city:         Aalborg
_pakkeshopper_pickup_point_country_code: DK
```

> In list mode (§1.3), the buyer's live selection is first captured at checkout as a cart attribute (`_pakkeshopper_pickup_point_id` + `_pakkeshopper_pickup_carrier`) and then expanded by Pakkeshopper into the full set of `_pakkeshopper_*` attributes above on the finalized order.

---

## 3. Recommended integration approach

1. **Prefer the order attributes.** Read `_pakkeshopper_carrier`, `_pakkeshopper_transport_type`, and the `_pakkeshopper_pickup_point_*` fields directly: no string parsing needed, and it works the same whether or not list mode is in use.
2. **Use the `code` field as a fallback / cross-check** if you need the internal rate ID, or for older integrations already parsing it.
3. **If you parse the code,** split on `_`:
   - first segment is `pakkeshopper` or `pakkeshopper:P{ID}`,
   - next segment is the carrier,
   - the remainder is the type (and pickup-point ID for `droppoint`).
   - Treat a trailing `_*` as "pickup point not yet chosen: read it from the order attributes instead."

---

## 4. Customization

The data format is configurable per merchant:

- The merchant can switch between the **Pakkeshopper 2.0** format (with `:P{ID}`), the **legacy** format, or **Shipmondo** format.
- A merchant can also define a **fully custom `code` format** per shipping rate if they need a specific layout for their own systems.

If you need a particular format on your side, let us know and we can align on it.

---

If anything here doesn't match what you're seeing on a specific order, send us the order number at [hej@pakkeshopper.dk](mailto:hej@pakkeshopper.dk) and we'll confirm exactly what was written.
