Documentation

Orders and fulfillment

Order lifecycle endpoints for staff and customer-side tracking.

An order is created in new state by /storefront/checkout-cart. From there, every state transition is a separate POST endpoint. The endpoints are deliberately verb-named (process, ship, deliver) rather than a single PATCH — the server runs side effects (inventory, notifications, automation triggers) per transition.

Lifecycle

new → processing → shipped → delivered → completed
                    ↘ on-hold ↗
                    ↘ cancelled (terminal)
                    ↘ refunded (terminal)

Each transition runs a hook chain:

StateWhat happens
newCreated by checkout. Customer email queued. Inventory reserved.
processingPicked / packed. Inventory still reserved.
shippedTracking number stored. Inventory decremented. Customer notified.
deliveredCarrier confirmed. Triggers review-request automation.
completedManual close (post-return-window). Locks the order.
on-holdOperations pause; reason recorded. Reversible.
cancelledInventory released. Refund the payment separately if captured.
refundedMoney returned. Order frozen.

Move an order through the lifecycle

All staff-side transitions require a JWT with the User principal and a role that has order-write permissions.

Process

POST/storefront/order/process/:orderNumberJWT
curl -X POST https://appengine.appmint.io/storefront/order/process/ORD-2026-0042 \
  -H "orgid: my-org" -H "Authorization: Bearer <jwt>" \
  -H "Content-Type: application/json" \
  -d '{ "note": "Picked, ready to pack" }'

Ship

POST/storefront/order/ship/:orderNumberJWT
FieldTypeDescription
carrier*string

ups, fedex, usps, dhl, etc.

tracking*string

The tracking number from the carrier label.

servicestring

ground, priority, 2-day.

costnumber

Actual shipping cost (for reconciliation).

itemsArray<{sku, quantity}>

For partial shipments. Omit to ship the whole order.

notestring
await fetch(`/api/storefront/order/ship/${orderNumber}`, {
  method: 'POST',
  headers: { 'Content-Type': 'application/json', orgid: ORG_ID, Authorization: `Bearer ${jwt}` },
  body: JSON.stringify({
    carrier: 'ups',
    tracking: '1Z999AA10123456784',
    service: 'ground',
  }),
});

Partial shipments are first-class: ship some line items now, ship the rest later. The order stays in processing until everything is out the door, then flips to shipped.

Deliver

POST/storefront/order/deliver/:orderNumberJWT

Marks the order delivered. Carrier webhooks (when a logistics integration is connected) fire this automatically; you can also POST it manually for offline channels.

Complete

POST/storefront/order/complete/:orderNumberJWT

Closes the order after the return window. After complete, only refund can change the state.

Hold and release

POST/storefront/order/hold/:orderNumberJWT
POST/storefront/order/release/:orderNumberJWT

Hold accepts { reason: string } and is required. Release moves the order back to its prior state. Both transitions log to the activity feed.

Cancel and refund

POST/storefront/order/cancel/:orderNumberJWT
POST/storefront/order/refund/:orderNumberJWT

Cancel before the order ships; refund after. Refund body: { amount?, items?, reason }. amount defaults to the order total; pass items + amount for partial refunds. Refund posts back to the original payment gateway via the Connect module.

Update order info

POST/storefront/order/update/:orderNumberJWT

For non-state changes — addresses, notes, tags. Useful when the customer emails to fix a typo in the shipping address before the order ships.

{
  "shippingAddress": { "street1": "456 New St", "city": "Brooklyn", "state": "NY", "zip": "11201", "country": "US" },
  "internalNotes": "Customer called to update address",
  "tags": ["address-corrected"]
}

Send order welcome email

GET/storefront/order/send-welcome/:orderNumberJWT

Re-fires the order-confirmation email. Handy after fixing a delivery issue.

Customer-side tracking

The customer doesn't have staff JWT — these endpoints are designed to work from a public order-lookup page or a customer-account portal.

GET/storefront/order/email/:email/:orderNumberNo auth
GET/storefront/order/get/:author/:orderNumberNo auth
GET/storefront/orders/get/:authorNo auth

The first variant matches by email + order number — the receipt has both, and the combination is unguessable in practice. The second uses the customer's SK (:author); call it from an authenticated account page where the SK is known. The third lists all orders for a customer SK — guard it with auth on the front end.

// base-app/src/app/account/orders/page.tsx (server component)
const orders = await storefrontAPI.getOrders(customerSk);

Shipment tracking

GET/shipping/track/:trackingNumberNo auth
GET/shipping/order/:orderNumber/statusNo auth

The tracking endpoint pulls live status from the carrier integration. The order/status variant aggregates: which line items shipped, which are pending, the latest scan event for each tracking number.

{
  "orderNumber": "ORD-2026-0042",
  "overallStatus": "in_transit",
  "shipped": [
    { "sku": "SNK-AIR-1-9-BLK", "quantity": 1, "trackingNumber": "1Z999AA10123456784", "carrier": "ups", "status": "in_transit", "shipDate": "2026-04-22T15:00:00Z" }
  ],
  "pending": [],
  "shipments": [
    { "id": "shp-001", "tracking": "1Z999AA10123456784", "carrier": "ups", "status": "in_transit", "shipDate": "2026-04-22T15:00:00Z" }
  ]
}

Subscriptions

GET/storefront/subscriptions/get/:author/:subscriptionid?No auth
POST/storefront/update-subscriptionJWT

Subscriptions live alongside orders. Each successful billing cycle creates a child order linked to the subscription. The update-subscription endpoint accepts { subscriptionId, action: 'pause' | 'resume' | 'cancel' | 'change-plan', priceId?, quantity? }.

Order remove

GET/storefront/order/remove/:author/:orderNumber?JWT

Hard-deletes a test order. Don't expose to staff UI — used for cleanup scripts only. Roles(User) enforced.

Workflows

POST/storefront/workflows/order-managementJWT
GET/storefront/workflows/order-management/:name?JWT

Order workflows are reusable Automation flows tied to order events (order.shipped, order.delivered). Use them to wire up review-request emails, NPS surveys, or replenishment-reminder schedules without writing code. The Automation module documentation covers the trigger payload shape.