Core Concepts
FractalPack models the end-to-end fulfillment workflow from catalog items through packed shipments. This guide explains each entity and how they relate.
Entity lifecycle
Items (catalog) Containers (box/pallet library)
│ │
▼ ▼
Orders ──────────────► Pack ◄── Rules (business logic)
│ │
▼ ▼
Shipments Pack Results
│
▼
Fulfillment Plans (parcel vs LTL)
│
▼
Wave Plans (pick optimization)
Items
Items represent SKUs in your catalog with physical properties. Create items via POST /api/v1/items or manage them through CSV/bulk import.
{
"id": "SKU-A100",
"name": "Widget Pro",
"length": 10,
"width": 8,
"height": 6,
"weight": 2.5,
"category": "electronics",
"tags": ["fragile", "battery"]
}
Key properties:
- Dimensions (length, width, height) in inches and weight in pounds
- Tags drive rule evaluation (e.g.,
fragiletriggers separation rules) - Quantity on pack requests specifies how many units to pack
- VoidSpace for hollow items (nesting optimization)
- RollProperties for rollable materials (fabric, film, cable)
- Packaging levels (case pack quantity, Ti/Hi) for pallet optimization
Containers
Containers define the boxes, pallets, and equipment available for packing. Manage them via POST /api/v1/containers or load them into your Container Master for automatic use.
Boxes (fixed-size)
{
"id": "BOX-MED",
"containerType": "Box",
"innerLength": 18,
"innerWidth": 14,
"innerHeight": 12,
"weightMaxGross": 50,
"baseCost": 1.25
}
Dynamic containers
Variable-size containers (e.g., cut-to-fit boxes) with min/max dimension ranges:
{
"id": "DYNBOX-1",
"minLength": 6, "maxLength": 24,
"minWidth": 6, "maxWidth": 18,
"minHeight": 4, "maxHeight": 16,
"maxWeight": 50
}
Pallets
{
"id": "PLT-48x40",
"length": 48,
"width": 40,
"maxHeight": 60,
"palletWeight": 45,
"maxWeight": 2500,
"platformHeight": 6,
"maxStackWeight": 1200
}
Equipment (trailers)
{
"id": "TRAILER-53",
"length": 636,
"width": 98,
"height": 108,
"tareWeight": 15000,
"maxWeight": 45000,
"equipmentType": "DryVan"
}
Container Master fallback
When a pack request omits containers, FractalPack automatically loads your organization's Container Master -- all active boxes, pallets, and equipment. An explicit empty array ("containers": []) opts out of this fallback.
Orders
Orders represent customer orders with line items. Each line references a SKU with a quantity.
{
"sourceOrderId": "ORD-2026-1234",
"lines": [
{ "sku": "SKU-A100", "quantityOrdered": 3 },
{ "sku": "SKU-B200", "quantityOrdered": 1 }
]
}
Order statuses: pending -> processing -> packed -> shipped -> cancelled
Shipments
Shipments are packable units created from orders. Creating a shipment resolves order line SKUs against your Item Master to get physical dimensions, then prepares the items for packing.
curl -X POST https://api.fractalpack.com/api/v1/shipments \
-H "X-Api-Key: fpk_test_..." \
-H "Content-Type: application/json" \
-d '{ "orderId": "ORD-2026-1234" }'
Shipment lifecycle: created -> optimized -> rated -> ready -> shipped / cancelled
Key operations:
- Optimize (
POST /shipments/{id}/optimize) -- run the packing algorithm - Rate (
POST /shipments/{id}/rate) -- get carrier shipping rates - Ready (
POST /shipments/{id}/ready) -- mark as ready to ship
Pack
The core algorithm endpoint. Send items and containers, get back a 3D packing solution.
POST /api/v1/pack
Options:
- algorithm --
fpFast(default, speed-optimized) orfpLayer(best fit, slower) orliquidFill(volume-fill estimator) - allowMultipleBoxes -- pack across multiple containers (default
true); set tofalsefor per-container cartonization fitness mode - optimizeFor --
volume(default) orcost(uses rate tables to minimize shipping cost) - loadingMode --
standardorpalletized - separationGroups -- keep tagged item groups in separate containers
- affinityGroups -- pin items to specific containers
See the Quickstart for a complete pack request example.
Rules, Policies, and Selectors
Rules vs Policies vs Selectors
Rule — the unit of business logic. A rule defines a single constraint or preference: "keep hazmat and food in separate boxes," "never use a container heavier than 50 lb for fragile items." Rules are reusable — the same rule can belong to multiple policies.
PackingPolicy — a named bundle of rules plus an embedded Selector. A policy is the thing you attach context to. Instead of saying "this rule always fires," you say "this policy (and its rules) fires when the selector matches." Each organization gets an Org Default policy on day one that contains all existing rules and uses a wildcard selector — so your rules behave identically to before, with no changes required.
Selector — the activation predicate embedded on a policy. It declares when the policy should apply. A selector has up to four axes: container level (Box, Pallet, Equipment), shipping mode (Parcel, Ltl, Ftl), ship-to customer ID, and site/warehouse ID. A null or empty axis is a wildcard — it matches everything.
How activation works: When a pack request arrives, FractalPack evaluates every enabled policy's selector against the current context. All matching policies activate (union — order doesn't matter). Their rule ID lists are merged and deduplicated, and then that rule set is handed to the rules engine.
Request context: { level: "Box", mode: "Ltl", site: "WH-EAST" }
Policy A selector: { levels: ["Box"], modes: null } → matches (Box ✓, mode wildcard ✓)
Policy B selector: { levels: ["Pallet"] } → no match (level mismatch)
Policy C selector: { sites: ["WH-EAST", "WH-WEST"] } → matches (site ✓, other axes wildcard ✓)
Activated rule set: union of Policy A rules + Policy C rules (deduplicated)
Conflict resolution by rule kind:
- Hard constraints (IncompatibilityGroup, BoxSetFilter, CarrierPackagingRule): all constraints from all activated policies apply. Set-valued constraints — exclusion/inclusion container lists and separation groups — compose by union, so every contributing policy's constraint takes effect. Scalar overrides such as CarrierPackagingRule dimension and weight caps take the strictest value across all active rules (lowest cap wins).
- Soft preferences (ObjectiveWeightModifier): contributions from all activated policies sum additively into the cost objective.
- ConsolidationProfile: first-wins. When two policies contribute conflicting ConsolidationProfile rules, the first one encountered wins. Avoid overlap by design; the AI assistant warns when it detects this pattern.
V1 selector axis limitation: The customers and sites axes match against raw IDs (CustomerInfo.Id and PackRequest.SiteId) — no master-data join is performed. Full master entities are a separate planned effort. See the packing policies recipe for details.
Rules
Rules inject business logic into the packing process without changing your API calls. They are evaluated server-side before every pack operation.
Common rule types:
- BoxSetFilter — include or exclude specific containers based on conditions (item count, weight, tags)
- IncompatibilityGroup — separate items into compatibility groups (e.g., hazmat classes that cannot share a box)
- ItemGrouping — pin items matching patterns to specific containers (affinity)
- CarrierPackagingRule — apply carrier-specific dimension/weight caps
- ConsolidationProfile — control how orders are consolidated (grouping level, mixed-order settings)
- DestinationOverride — override container selection or routing based on destination
- OrderPreprocessing — transform or annotate order data before packing begins
Rules have an optional advisory applicableLevels field (Box, Pallet, Equipment). This tag is used for UI hints, lint warnings, and AI explainability — it is not enforced at evaluation time. To scope a rule to a specific container level, attach it to a policy whose selector targets that level.
Managing rules via the API
# List all rules
curl https://api.fractalpack.com/api/v1/rules \
-H "X-Api-Key: fpk_test_your_api_key"
# Create a rule
curl -X POST https://api.fractalpack.com/api/v1/rules \
-H "X-Api-Key: fpk_test_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"name": "Exclude refrigerated containers for dry goods",
"ruleType": "BoxSetFilter",
"enabled": true,
"priority": 10,
"conditions": {
"operator": "and",
"conditions": [
{ "field": "tags", "operator": "contains", "value": "dry-goods" }
]
},
"action": {
"filterType": "Exclude",
"containerIds": ["BOX-REFRIGERATED-1", "BOX-REFRIGERATED-2"]
}
}'
# Update a rule
curl -X PUT https://api.fractalpack.com/api/v1/rules/rule_abc123 \
-H "X-Api-Key: fpk_test_your_api_key" \
-H "Content-Type: application/json" \
-d '{ "enabled": false }'
# Delete a rule
curl -X DELETE https://api.fractalpack.com/api/v1/rules/rule_abc123 \
-H "X-Api-Key: fpk_test_your_api_key"
Per-request rule selection
By default every globally Enabled rule is evaluated automatically on every pack
request. To restrict which rules apply for a single request, pass a ruleIds
whitelist on the POST /v1/pack (or POST /v1/fulfillment-plans) body:
# Apply only the listed rules; everything else is skipped for this request.
curl -X POST https://api.fractalpack.com/api/v1/pack \
-H "X-Api-Key: fpk_test_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"items": [...],
"containers": [...],
"ruleIds": ["rule_abc123", "rule_def456"]
}'
The whitelist is intersected with each rule's global enabled flag, so a rule
that is enabled: false stays skipped even when listed. Pass ruleIds: [] to
disable rules entirely for this one request. Omitting the field falls back to
the historical "all enabled rules apply" behavior — your existing integrations
need no changes.
The Load Planner's "Rules" sidebar card uses this same field under the hood —
the user's per-plan checkbox state is sent as ruleIds on each Optimize click.
Inspecting which rules fired
When rules fire during a pack request, the response includes a firedRules array:
{
"firedRules": [
{
"ruleId": "rule_abc123",
"ruleName": "Fragile Separation",
"ruleType": "IncompatibilityGroup",
"summary": "Separated items into 2 compatibility groups"
}
]
}
Consolidation
Consolidation combines multiple orders heading to the same destination into fewer shipments, reducing shipping costs.
- Evaluate (
POST /api/v1/consolidation/evaluate) -- analyze orders for consolidation opportunities - Create group (
POST /api/v1/consolidation/groups) -- group orders together - Pack group (
POST /api/v1/consolidation/groups/{id}/pack) -- pack the consolidated group
Consolidation profiles control constraints: whether mixed orders can share a carton, consolidation level (carton vs pallet), and more.
Fulfillment Plans
Fulfillment plans evaluate the best shipping strategy for an order. The system compares parcel (small package) vs LTL (less-than-truckload) options and returns cost breakdowns for each.
curl -X POST https://api.fractalpack.com/api/v1/fulfillment-plans \
-H "X-Api-Key: fpk_test_..." \
-H "Content-Type: application/json" \
-d '{ "orderIds": ["ORD-2026-1234"] }'
Response includes multiple options with cost comparisons. Select an option via POST /fulfillment-plans/{id}/select to create shipments automatically.
Wave Planning
Wave planning optimizes warehouse pick paths across multiple orders. Given a set of orders and warehouse layout, the algorithm groups orders into waves and sequences pick locations to minimize travel distance.
curl -X POST https://api.fractalpack.com/api/v1/wave-plan \
-H "X-Api-Key: fpk_test_..." \
-H "Content-Type: application/json" \
-d '{
"orders": [...],
"maxOrdersPerWave": 20,
"algorithm": "nearest-neighbor-2opt"
}'
Available algorithms: nearest-neighbor-2opt (default, higher quality) and nearest-neighbor (faster).
Stable identifiers
Some API values have permanent aliases to preserve backward compatibility. Sending the alias in a request works exactly the same as sending the canonical value — the alias is never echoed back.
| Canonical value | Permanent alias | Notes |
|---|---|---|
fpLayer |
ebAfit |
Packing algorithm. Changed in PR 2 (2026-04). Requests using "ebAfit" continue to work indefinitely. |
When you send algorithm: "ebAfit", the API resolves it to fpLayer and the response always echoes "fpLayer". If you have dashboards or alerting filters on "ebAfit", update them to "fpLayer" — the alias is accepted on input but never appears in output or telemetry.