# Easyship Shipping for GoCommerce — Open-Source Module

> Book Easyship shipments with the label in one call from a GoCommerce order: token, courier service, ship-from address, units, customs items and limits.

- Canonical: https://kitcommerce.store/integrations/fulfill-easyship/
- Last updated: 2026-09-25

---

Shipping

## Easyship shipping for GoCommerce

A Go package that creates an Easyship shipment and buys its label in the same call when an operator ships an order. It declares the parcel’s contents for customs and hands the engine a tracking number and the label — or a precise error.

- **Shipping** module
- **12** settings
- **8** tests
- **ext/fulfill-easyship**

### What it does

The module registers “easyship” as a fulfillment provider. An operator ships through the engine’s own POST /api/admin/create-fulfillment with the provider easyship — the engine keeps the order’s state and its events, and the module only talks to Easyship.

It sends buy_label with the shipment, so one call either produces a tracking number or fails. The package gives the reason: the engine has no state for a shipment that exists but cannot be tracked, and inventing one would make “booked” and “half booked” look identical on the order. The courier service is a configured choice for the same reason the Shippo module gives — picking the cheapest rate automatically would quietly put somebody’s express order on a slow service.

In the admin it describes itself as “Cross-border labels and customs paperwork through Easyship, booked on a courier service you choose.” The API version is pinned to 2024-09, and there is no SDK.

### Configuration

Four things are required: the access token, a default courier service, and a ship-from address line and country. Set them in Config from your own main(), or leave Config empty and fill them in under Settings › Shipping providers.

*Easyship module settings — 12 settings, 4 required*

| Setting | Environment variable | Required | What it does |
| --- | --- | --- | --- |
| `Token` Access token | `EASYSHIP_TOKEN` | Yes | An Easyship API access token, sent as a bearer. It needs the public.shipment:write and public.label:write scopes. |
| `CourierServiceID` Courier service ID | `EASYSHIP_COURIER_SERVICE_ID` | Yes | The courier service to book by default. Meta courier_service_id overrides it for one parcel. |
| `From.Line1` From: address line 1 | — | Yes | Where parcels are sent from. The order knows where it is going, never where it came from, so this is configuration. |
| `From.CountryAlpha2` From: country (ISO 2) | — | Yes | The ship-from country, as two letters: SG, US, IN. |
| `From.Line2, From.City, From.State, From.PostalCode, From.ContactName, From.ContactPhone` From: line 2, city, state, postal code, contact name, contact phone | — | No | The rest of the ship-from address. |
| `From.ContactEmail, From.CompanyName` | — | No | Also sent with the origin address, settable in Go only; not in the panel. |
| `Incoterms` Incoterms | — | No | Who pays duty on a cross-border parcel: DDU, the recipient, or DDP, the store. Empty leaves Easyship’s own default. |
| `Imperial` Account uses pounds and inches | — | No | Set it when the Easyship account reads pounds and inches. Get it wrong and every parcel is declared at about 2.2 times the wrong weight. |
| `DefaultWeightGrams` Default weight (grams) | — | No | Per unit, for a parcel holding a variant with no weight recorded. 500 by default. |
| `DefaultLengthMM, DefaultWidthMM, DefaultHeightMM` | — | No | The box sent when the engine has no unambiguous size, which is any parcel holding more than one unit. 150 × 150 × 100 mm by default. Go only. |
| `BaseURL` API base URL | — | No | Overrides https://public-api.easyship.com, for tests. Empty for production. |
| `Client` | — | No | Replaces the HTTP client, which otherwise times out after 45 seconds. Go only; not in the panel. |

The module reads its Config struct, not the environment. The variable names are the ones the package’s own example or the reference binary uses; in your own `main()` you choose where each value comes from. Where a setting has a panel label, the admin’s settings drawer can hold it too, and a value typed there wins over Config.

### Setting it up

1. **Get a token and a courier** — In Easyship, create an access token with the public.shipment:write and public.label:write scopes, and note the id of the courier service to book by default.
2. **Install the module** — Import it and pass easyship.New to gocommerce.New, as below — or run the reference binary with -carriers, which installs every carrier module idle.
3. **Fill in the settings** — Token, courier service and ship-from address in Config or under Settings › Shipping providers — and tick the pounds-and-inches box if that is what your Easyship account reads.
4. **Ship an order** — POST /api/admin/create-fulfillment with the order id and the provider easyship. The engine records the tracking number and label URL and moves the order to shipped, or to partial.

main.go

```
import (
	"os"

	"github.com/itswadesh/gocommerce/core"
	easyship "github.com/itswadesh/gocommerce/ext/fulfill-easyship"
)

app, err := gocommerce.New(cfg,
	easyship.New(easyship.Config{
		Token:            os.Getenv("EASYSHIP_TOKEN"),
		CourierServiceID: os.Getenv("EASYSHIP_COURIER_SERVICE_ID"),
		From: easyship.Address{
			ContactName: "Acme", Line1: "Kennedy Town", City: "Hong Kong",
			PostalCode: "0000", CountryAlpha2: "HK",
			ContactPhone: "+852-3008-5678", ContactEmail: "ship@acme.test",
		},
	}),
)
```

The package doc’s own example, with its imports; cfg is your gocommerce.Config. With an empty easyship.Config the module installs idle and waits for the panel. Import path `github.com/itswadesh/gocommerce/ext/fulfill-easyship`.

### How it works

- **One call, with the label**

  The shipment, the courier service and buy_label go to Easyship together. A shipment that comes back with no tracking number is an error that names the Easyship shipment id and says to cancel it in Easyship before retrying.

- **Customs from what is in the box**

  Each line in this parcel goes as a customs item — title, SKU, quantity, and the unit price as declared value in the line’s currency. The weight is sent once, for the whole parcel, because the engine knows it per parcel, not per line.

- **The units the account reads**

  Metric by default: grams to kilograms, millimetres to centimetres. With Imperial set, pounds and inches instead. Conversions round half up, the direction that over-declares rather than under-declares a parcel.

- **The label, not the paperwork**

  Of the documents Easyship issues, the module stores the one in the label category as the shipment’s label URL. Commercial invoices and packing slips stay in Easyship.

- **The order, named**

  The order number and its creation time go to Easyship as order_data, under the platform name gocommerce, so the shipment can be found from the store’s own number.

- **Carrier worked out**

  The carrier code is read from the courier Easyship reports — USPS, FedEx, UPS, DHL, Aramex, Delhivery, Royal Mail — so the tracking number gets the right link. Anything else is left for the engine to work out from the number.

### What it does not do

Read these before an order depends on it. No store is known to run GoCommerce in production yet, so these come from the code, not from anyone’s experience.

- **No rate shopping** — One courier service, from settings or meta. The module never asks Easyship for rates or picks the cheapest.
- **No tracking updates** — No webhook and no stored state: delivery events in Easyship do not reach the order. Marking it delivered is still the operator’s job.
- **No cancel call** — A half-booked shipment has to be cancelled in Easyship by hand, and deleting the fulfillment in GoCommerce does not touch it.
- **Customs values assume two decimals** — The declared value is the unit price in minor units divided by 100. For a currency with no minor unit, such as the yen, that declares a hundredth of the price.
- **Thin customs items** — Items carry title, SKU, quantity and value only — no HS code and no country of origin, although the package comment lists origin country among them.
- **A guessed box for several items** — For anything but a single unit, the size sent is the configured default, not a measurement. The engine does not pack cartons.

FAQ

### Questions about the Easyship module

**Does it choose the cheapest courier?**

No. The courier service is a setting, and meta courier_service_id changes it for one parcel. The package argues that picking by price automatically would quietly put an express order on a slow service.

**My Easyship account uses pounds. What changes?**

Switch on Imperial — “Account uses pounds and inches” in the panel. The module then converts grams to pounds and millimetres to inches; left off, it sends kilograms and centimetres, and a pound-reading account would take every parcel as about 2.2 times the wrong weight.

**Can I use it without writing Go?**

Yes. The reference binary’s -carriers flag installs every carrier module with an empty Config. Switch Easyship on under Settings › Shipping providers and fill in the token, courier service and ship-from address there.

**Is this an Easyship partnership?**

No. The module calls Easyship’s public API with a token you supply. Easyship does not endorse GoCommerce, and no store is known to run this module in production yet.

### Source

Everything on this page is read from [`ext/fulfill-easyship`](https://github.com/itswadesh/gocommerce/tree/main/ext/fulfill-easyship) in the GoCommerce repository, MIT licensed. When this page and the code disagree, the code is right and this page is out of date.Easyship is a trademark of its owner; this module talks to its public API and implies no endorsement. See [trademarks](https://kitcommerce.store/about/#trademarks).

- [ext/fulfill-easyship on GitHub](https://github.com/itswadesh/gocommerce/tree/main/ext/fulfill-easyship)
- [Easyship](https://www.easyship.com)
- [All GoCommerce modules](https://kitcommerce.store/integrations/)

### Try it against a store of your own

The one-command stack gives you GoCommerce’s API and admin on your own machine in minutes. Add this module to it and try it on test orders before a real one depends on it.

[Deploy in minutes](https://kitcommerce.store/#one-command) · [Read the module](https://github.com/itswadesh/gocommerce/tree/main/ext/fulfill-easyship) · [All integrations](https://kitcommerce.store/integrations/)
