Submit Fulfillment Order to Provider
POST/api/fulfillment-orders/:fulfillmentOrder/submit
Transitions request_status from unsubmitted → submitting and stamps submitted_at to the moment of hand-off (NOT when the carrier confirms — see Stage 2 for the carrier-callback flow that flips submitting → submitted/accepted).
orders:writeGrant this scope to your token under Settings → Developer → Personal Access Tokens.
Mutability gate. Allowed ONLY when status=open AND request_status=unsubmitted. Violations return 409.
Request body (all fields optional). Submission acts on the WHOLE FO — there are no per-line quantities (split the FO first to dispatch part of it).
acknowledge_open_channel_fulfillment(boolean, default false) — double-fulfillment guardrail (F4a). When an still-open sales-channel (Shopify) fulfillment order may yet ship some of this FO's units, an omitted/false submit is refused with 409 carryingrequires_acknowledgement: trueand acoveragepreview. Resubmit with thistrueto dispatch natively anyway.requested_integration_instance_id(integer|null) — per-FO shipping-provider override; must be a SHIPPING_PROVIDER instance.requested_fulfillment_type(string|null, onlymanual) — force Manual dispatch (no integration instance).requested_shipping_method_id(integer|null) /requested_shipping_method(string|null) — requested carrier service.requested_ship_date(date|null),requested_metadata(object|null) — optional ship intent bridged onto the SalesOrderFulfillment (notes,fulfillment_sequence,signature_required,veracore_*,isInsured,instructionsForWarehouse).lot_allocations(array|null) — per-line FEFO override for lot-tracked lines:[{ sales_order_line_id, lots: [{ fifo_layer_id, quantity }] }]. Re-validated at dispatch; stale picks fall back to FEFO. Omit for automatic FEFO/FIFO.serials(array|null) — serials picked for serialized lines:[{ sales_order_line_id, serials: ["SN-1", ...] }]. Stored on the fulfillment order line and re-checked when the shipment is recorded: a picked serial that has since left stock at the FO's warehouse is dropped, and units without a usable serial take the oldest serials in stock. Serials reported by the provider win. A non-empty list must hold exactly one serial per unit still to ship, with no repeats, and under Require exact match only serials available at the FO's warehouse (422 onserials.N.serials). A line not on this FO returns 422 onserials.N.sales_order_line_id. Omit to let the shipment pick the serials; a re-submit without it clears an earlier pick.
The "Allow Sales Orders with unfulfillable quantity to be dispatched (partially)" setting does not gate this endpoint: submitting a single fulfillment order is a deliberate per-order dispatch, so it proceeds even when the sales order still has unfulfillable (backordered) quantity. That setting applies to automatic dispatch and to the bulk-submit endpoint only.
On an order that mixes shipped and Click & Collect items, requested_shipping_method_id may not switch the fulfillment order between shipping and pickup — change its shipping method first (409).
Each line includes requires_pallet_delivery (bool): the buyer chose pallet (freight/LTL) delivery for that line, so it should ship by freight, not parcel.
A 409 is also returned when the sales channel has already fulfilled units this fulfillment order would ship (for example, the order was marked fulfilled directly in the channel). Those lines report channel_covered: true with channel_already_fulfilled_quantity greater than 0. Re-submit with acknowledge_open_channel_fulfillment: true only if the units are genuinely still owed.
Required permission: fulfillment_orders.submit (Fulfillment Orders → Submit & sync) and fulfillment_orders.index (Fulfillment Orders → View). A token also needs its orders scope; the owner needs these role permissions.
Request
Responses
- 200
- 401
- 403
- 404
- 409
- 422
- 429
OK
Response Headers
Unauthenticated — the bearer token is missing, revoked, expired, or malformed. Never retry automatically; fix the credential. See the Errors guide.
Forbidden
Response Headers
Not found — no record with the given identifier (or the route does not exist). Verify the ID before retrying.
Conflict
Response Headers
Validation failed — the body is a field → messages map (Laravel shape) or the platform envelope with a stable machine-readable code. Fix the payload and resubmit.
Rate limited — platform limit is 1,000 requests/min; individual tokens may carry lower limits. Honor the Retry-After header before retrying. See the Rate Limits guide.