Packfit
Reference

Packfit API

One endpoint. Send items and cartons, receive a packing plan, the chosen cartons and the billable weight. New here? Start with the quickstart.

Authentication

Every request carries a bearer key. Keys are environment-scoped: pf_test_… for development, pf_live_… for production. We store only an HMAC of your key — a lost key cannot be recovered, only replaced.

Authorization: Bearer pf_live_xxxxxxxxxxxxxxxxxxxxxxxx

POST /v1/pack

Units are integer millimetres and integer grams. Integers keep the packing deterministic; floating-point dimensions would let two servers disagree about whether an item fits.

Request

items[]requiredarrayThe units to pack. See item fields below.
cartons[]requiredarrayYour available carton range. The packer picks from these only — it never invents a box.
dimWeightobjectdivisorCm3PerKg (e.g. 5000) and optional roundUpToKg (default 0.5). Omit to skip billable-weight calculation.
maxCartonsintegerCap on parcels, default 50. Items that no longer fit are reported in unpacked.
rateCards[]arrayCarrier tariffs (carrierId, divisorCm3PerKg, weight brackets, optional surchargePerParcelCents and size limits). Supplied, the packer minimises real money and returns the cheapest carrier per parcel; omitted, it minimises billable weight plus a per-parcel allowance.
effortenumauto (default), fast or thorough. Controls how many packing strategies are evaluated. auto goes thorough up to 60 units.

Item fields

skurequiredstringYour identifier. Echoed back on every placement.
lengthMm / widthMm / heightMmrequiredintegerOuter dimensions of one unit.
weightGrequiredintegerWeight of one unit.
quantityintegerDefaults to 1. Expanded internally into individual units.
nonStackablebooleanNothing may be placed on top of this item.
thisSideUpbooleanRestricts rotation to yaw — the item is never laid on its side.
maxStackWeightGintegerCrush limit: total weight allowed to rest above this item.
hazmatenumADR class, e.g. class3_flammable_liquids. Incompatible classes are separated into different cartons.
tempRegimeenumambient | chilled | frozen. Regimes never share a carton.
warehousestringSource location, default default. Items from different warehouses never share a carton — each becomes its own entry in shipments.

Carton fields

idrequiredstringYour carton identifier, returned as cartonId.
innerLengthMm / innerWidthMm / innerHeightMmrequiredintegerUsable inner dimensions.
maxPayloadGrequiredintegerMaximum contents weight, excluding tare.
tareWeightGintegerEmpty carton weight, added to the gross and billable weight.
costCentsintegerPackaging cost, summed into packagingCostCents.
enabledbooleanSet false to exclude a carton without removing it from your catalogue.

Response

{
  "cartons": [
    {
      "cartonId": "M",
      "placements": [
        { "sku": "PAN", "x": 0, "y": 0, "z": 0,
          "lengthMm": 240, "widthMm": 240, "heightMm": 60,
          "weightG": 1450, "orientation": 0 }
      ],
      "grossWeightG": 6690,
      "contentWeightG": 6470,
      "fillRate": 0.64,
      "billableWeightG": 7000,
      "tempRegime": "ambient",
      "hazmatClasses": [],
      "valueCents": 0
    }
  ],
  "unpacked": [],
  "packagingCostCents": 55,
  "totalGrossWeightG": 6690,
  "totalBillableWeightG": 7000,
  "inputFingerprint": "12zzv44c",
  "trace": ["12 units expanded, sorted by decreasing volume", "opened carton M for PAN"]
}

x, y, z is the minimum corner of the item inside the carton; lengthMm/widthMm/heightMm are the dimensions after rotation, so you can render or instruct a packer directly. inputFingerprint is a stable hash of the request — identical input always yields an identical plan, so it is safe to use as a cache key.

Errors

400bad requestBody was not valid JSON.
401unauthorizedMissing, malformed, unknown or revoked key.
402payment requiredMonthly quota exhausted on a plan without overage. Upgrade or wait for the next period.
405method not allowedUse POST.
422unprocessableValid JSON but not a packable problem — schema violation such as a negative dimension. Quota is not consumed.
429too many requestsPer-key rate limit hit. Honour Retry-After.
503unavailableDeployment not fully configured (database or billing).

Items that physically cannot be placed are not an error: they come back in unpacked with a reason, so a partial order still ships.

Quotas and rate limits

PlanPacks / monthRequests / minOverage
Free060hard cap
Trial25060hard cap
Starter1,000120billed
Growth50,000600billed
Scale500,0003,000billed

Every response carries Packfit-Quota-Limit, Packfit-Quota-Used, Packfit-Quota-Remaining and RateLimit-* headers, so a client can self-throttle without a second request.

Determinism and privacy

The endpoint is stateless: your dimensions, SKUs and carton data are computed in memory and discarded with the response. We persist only a monthly request counter — see the privacy policy.