# ShipStation Labels for GoCommerce — Open-Source Module

> Buy ShipStation V2 labels from a GoCommerce order: the API key, service code, ship-from address, how units convert, and what the module does not do.

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

---

Shipping

## ShipStation labels for GoCommerce

A Go package that buys a label through ShipStation’s V2 API when an operator ships an order, and gives the engine back a tracking number and a label to print. The order stays in GoCommerce; nothing is pushed into ShipStation first.

- **Shipping** module
- **10** settings
- **7** tests
- **ext/fulfill-shipstation**

### What it does

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

It speaks the newer V2 API at api.shipstation.com, with a single API-Key header, and buys a label from a shipment described in the request with one POST /v2/labels. The package explains why not the older ssapi, which is organised around orders pushed into ShipStation first: that would make ShipStation a second place an order exists, and a second place it can change.

In the admin it describes itself as “Multi-carrier labels through ShipStation’s V2 API, on the service you name.” It uses Go’s standard library and no SDK.

### Configuration

Four things are required: the API key, a default service code, 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.

*ShipStation module settings — 10 settings, 4 required*

| Setting | Environment variable | Required | What it does |
| --- | --- | --- | --- |
| `APIKey` API key | `SHIPSTATION_API_KEY` | Yes | A ShipStation V2 API key, sent as the API-Key header. |
| `ServiceCode` Service code | — | Yes | The default service — usps_priority_mail, ups_ground and the rest. Meta service_code overrides it for one parcel. |
| `From.AddressLine1` 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.CountryCode` From: country (ISO 2) | — | Yes | The ship-from country, as two letters. |
| `From.Name, From.Phone, From.CityLocality, From.StateProvince, From.PostalCode` From: name, phone, city, state, postal code | — | No | The rest of the ship-from address. |
| `From.CompanyName, From.AddressLine2, From.Residential` | — | No | Go only. Residential is ShipStation’s address_residential_indicator; left alone, the carrier decides. |
| `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://api.shipstation.com, for tests. Empty for production. |
| `Client` | — | No | Replaces the HTTP client, which otherwise times out after 30 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 V2 API key** — In ShipStation, create a key for the V2 API and note the service code to buy by default. A key and secret for the older ssapi will not work here.
2. **Install the module** — Import it and pass shipstation.New to gocommerce.New, as below — or run the reference binary with -carriers, which installs every carrier module idle.
3. **Fill in the settings** — Key, service code and ship-from address, in Config or under Settings › Shipping providers. A value typed into the panel counts on the next shipment, with no restart.
4. **Ship an order** — POST /api/admin/create-fulfillment with the order id and the provider shipstation. 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"
	shipstation "github.com/itswadesh/gocommerce/ext/fulfill-shipstation"
)

app, err := gocommerce.New(cfg,
	shipstation.New(shipstation.Config{
		APIKey:      os.Getenv("SHIPSTATION_API_KEY"),
		ServiceCode: "usps_priority_mail",
		From: shipstation.Address{
			Name: "Acme", AddressLine1: "215 Clayton St",
			CityLocality: "San Francisco", StateProvince: "CA",
			PostalCode: "94117", CountryCode: "US", Phone: "+15553334444",
		},
	}),
)
```

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

### How it works

- **A label from a described parcel**

  POST /v2/labels carries the service code, the ship-from and ship-to addresses and one package. The answer is a tracking number and a label download; the module keeps the PDF link, or the generic link, or the PNG.

- **No tracking number, no shipment**

  A label that comes back without a tracking number is an error, not a shipment, so the order is never moved to shipped on a parcel nobody can trace.

- **Centimetres, keeping the tenth**

  ShipStation takes grams, which the engine stores, and centimetres, which it does not. Sides are divided by ten and sent as decimals — 305 mm is 30.5 cm, not 30 — because, as the package puts it, rounding down is how a parcel gets refused at the counter for being over its band.

- **A unique id per parcel**

  Each label carries external_shipment_id: the order number on a first shipment, with -2, -3 appended for later parcels against the same order, so ShipStation never sees a repeat.

- **Per-parcel overrides**

  Meta on the ship request can name a different service_code, or a carrier_id, for one parcel.

- **Carrier worked out**

  The carrier code comes from what ShipStation reports, or from the service code — USPS (Stamps.com and Endicia labels too, since USPS carries them), FedEx, UPS, DHL, Canada Post, Australia Post — so the tracking number gets the right link.

### 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** — The service is a setting or a meta override. The module never asks ShipStation for rates or picks one for you.
- **No tracking updates** — No webhook and no stored state: delivery events in ShipStation do not reach the order. Marking it delivered is still the operator’s job.
- **No void call** — There is no call to cancel a label. Deleting the fulfillment in GoCommerce does not touch the label in ShipStation.
- **V2 keys only** — Credentials for the older ssapi — a key and a secret over HTTP Basic — do not work; the module speaks only the V2 API.
- **No customs declaration** — The request carries no customs items, so a cross-border parcel that needs a declaration is not covered by the module as written.
- **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 ShipStation module

**Does it push orders into ShipStation?**

No, deliberately. The package keeps GoCommerce as the system of record and buys a label from a shipment described in the request, so the order never exists in two places that could disagree.

**Which API key do I need?**

A V2 API key, sent as the API-Key header. The older ShipStation API at ssapi.shipstation.com, with a key and secret over HTTP Basic, is not what this module speaks.

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

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

**Is this a ShipStation partnership?**

No. The module calls ShipStation’s public API with a key you supply. ShipStation 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-shipstation`](https://github.com/itswadesh/gocommerce/tree/main/ext/fulfill-shipstation) in the GoCommerce repository, MIT licensed. When this page and the code disagree, the code is right and this page is out of date.ShipStation 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-shipstation on GitHub](https://github.com/itswadesh/gocommerce/tree/main/ext/fulfill-shipstation)
- [ShipStation](https://www.shipstation.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-shipstation) · [All integrations](https://kitcommerce.store/integrations/)
