Create Fulfillment
curl --request POST \
--url https://api.launchmystore.io/api/v1/orders/{order_id}/fulfillments.json \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"status": "<string>",
"tracking_number": "<string>",
"tracking_company": "<string>",
"tracking_url": "<string>",
"service_id": "<string>",
"line_items": [
{
"id": "<string>",
"quantity": 123
}
],
"notify_customer": true
}
'import requests
url = "https://api.launchmystore.io/api/v1/orders/{order_id}/fulfillments.json"
payload = {
"status": "<string>",
"tracking_number": "<string>",
"tracking_company": "<string>",
"tracking_url": "<string>",
"service_id": "<string>",
"line_items": [
{
"id": "<string>",
"quantity": 123
}
],
"notify_customer": True
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
status: '<string>',
tracking_number: '<string>',
tracking_company: '<string>',
tracking_url: '<string>',
service_id: '<string>',
line_items: [{id: '<string>', quantity: 123}],
notify_customer: true
})
};
fetch('https://api.launchmystore.io/api/v1/orders/{order_id}/fulfillments.json', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.launchmystore.io/api/v1/orders/{order_id}/fulfillments.json",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'status' => '<string>',
'tracking_number' => '<string>',
'tracking_company' => '<string>',
'tracking_url' => '<string>',
'service_id' => '<string>',
'line_items' => [
[
'id' => '<string>',
'quantity' => 123
]
],
'notify_customer' => true
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.launchmystore.io/api/v1/orders/{order_id}/fulfillments.json"
payload := strings.NewReader("{\n \"status\": \"<string>\",\n \"tracking_number\": \"<string>\",\n \"tracking_company\": \"<string>\",\n \"tracking_url\": \"<string>\",\n \"service_id\": \"<string>\",\n \"line_items\": [\n {\n \"id\": \"<string>\",\n \"quantity\": 123\n }\n ],\n \"notify_customer\": true\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.launchmystore.io/api/v1/orders/{order_id}/fulfillments.json")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"status\": \"<string>\",\n \"tracking_number\": \"<string>\",\n \"tracking_company\": \"<string>\",\n \"tracking_url\": \"<string>\",\n \"service_id\": \"<string>\",\n \"line_items\": [\n {\n \"id\": \"<string>\",\n \"quantity\": 123\n }\n ],\n \"notify_customer\": true\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.launchmystore.io/api/v1/orders/{order_id}/fulfillments.json")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"status\": \"<string>\",\n \"tracking_number\": \"<string>\",\n \"tracking_company\": \"<string>\",\n \"tracking_url\": \"<string>\",\n \"service_id\": \"<string>\",\n \"line_items\": [\n {\n \"id\": \"<string>\",\n \"quantity\": 123\n }\n ],\n \"notify_customer\": true\n}"
response = http.request(request)
puts response.read_body{
"status": 201,
"state": "success",
"data": {
"fulfillment": {
"fulfillmentId": "ful_new456",
"orderId": "ord_xyz789",
"status": "success",
"trackingNumber": "1Z999AA10123456784",
"trackingCompany": "UPS",
"trackingUrl": "https://ups.com/track?num=1Z999AA10123456784",
"serviceId": "fs_abc123",
"createdAt": "2024-01-20T14:30:00Z"
}
}
}
Fulfillments
Create Fulfillment
Create a fulfillment for order items and automatically mark the order as shipped
POST
/
api
/
v1
/
orders
/
{order_id}
/
fulfillments.json
Create Fulfillment
curl --request POST \
--url https://api.launchmystore.io/api/v1/orders/{order_id}/fulfillments.json \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"status": "<string>",
"tracking_number": "<string>",
"tracking_company": "<string>",
"tracking_url": "<string>",
"service_id": "<string>",
"line_items": [
{
"id": "<string>",
"quantity": 123
}
],
"notify_customer": true
}
'import requests
url = "https://api.launchmystore.io/api/v1/orders/{order_id}/fulfillments.json"
payload = {
"status": "<string>",
"tracking_number": "<string>",
"tracking_company": "<string>",
"tracking_url": "<string>",
"service_id": "<string>",
"line_items": [
{
"id": "<string>",
"quantity": 123
}
],
"notify_customer": True
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
status: '<string>',
tracking_number: '<string>',
tracking_company: '<string>',
tracking_url: '<string>',
service_id: '<string>',
line_items: [{id: '<string>', quantity: 123}],
notify_customer: true
})
};
fetch('https://api.launchmystore.io/api/v1/orders/{order_id}/fulfillments.json', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.launchmystore.io/api/v1/orders/{order_id}/fulfillments.json",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'status' => '<string>',
'tracking_number' => '<string>',
'tracking_company' => '<string>',
'tracking_url' => '<string>',
'service_id' => '<string>',
'line_items' => [
[
'id' => '<string>',
'quantity' => 123
]
],
'notify_customer' => true
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.launchmystore.io/api/v1/orders/{order_id}/fulfillments.json"
payload := strings.NewReader("{\n \"status\": \"<string>\",\n \"tracking_number\": \"<string>\",\n \"tracking_company\": \"<string>\",\n \"tracking_url\": \"<string>\",\n \"service_id\": \"<string>\",\n \"line_items\": [\n {\n \"id\": \"<string>\",\n \"quantity\": 123\n }\n ],\n \"notify_customer\": true\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.launchmystore.io/api/v1/orders/{order_id}/fulfillments.json")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"status\": \"<string>\",\n \"tracking_number\": \"<string>\",\n \"tracking_company\": \"<string>\",\n \"tracking_url\": \"<string>\",\n \"service_id\": \"<string>\",\n \"line_items\": [\n {\n \"id\": \"<string>\",\n \"quantity\": 123\n }\n ],\n \"notify_customer\": true\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.launchmystore.io/api/v1/orders/{order_id}/fulfillments.json")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"status\": \"<string>\",\n \"tracking_number\": \"<string>\",\n \"tracking_company\": \"<string>\",\n \"tracking_url\": \"<string>\",\n \"service_id\": \"<string>\",\n \"line_items\": [\n {\n \"id\": \"<string>\",\n \"quantity\": 123\n }\n ],\n \"notify_customer\": true\n}"
response = http.request(request)
puts response.read_body{
"status": 201,
"state": "success",
"data": {
"fulfillment": {
"fulfillmentId": "ful_new456",
"orderId": "ord_xyz789",
"status": "success",
"trackingNumber": "1Z999AA10123456784",
"trackingCompany": "UPS",
"trackingUrl": "https://ups.com/track?num=1Z999AA10123456784",
"serviceId": "fs_abc123",
"createdAt": "2024-01-20T14:30:00Z"
}
}
}
Creates a fulfillment record on the order and, when
The order lifecycle is:
Omitting
status is a
terminal success state, automatically bumps order.status to
shipped (or delivered). Shipping apps don’t need a second call to
update the order — one POST writes the tracking number, courier, and
status in a single request.
This endpoint is the write-back half of the live-rate flow. For
the full lifecycle (quote → checkout pick → orders/create webhook →
push to carrier → call this endpoint with the AWB), see
Live Rate Providers and
Build a shipping app.
When the order status changes
Each Fulfillment row represents one shipment. After every write, the platform recomputes coverage by summinglineItems[].quantity across
all success-state Fulfillment rows for the order and updates
order.status accordingly:
| Coverage | order.status becomes |
|---|---|
| 0% — no success fulfillments yet | unchanged |
| > 0% but < 100% — some packages out | partial |
| 100% — every ordered quantity shipped | shipped (or delivered if the fulfillment status is delivered) |
pending → confirmed → paid → partial → shipped → delivered
(canceled / abandoned as terminal off-ramps)
order.status is only mutated from pending, confirmed, paid, or
partial. Manual overrides (shipped set by the merchant) and
terminal states (canceled/delivered) are never trampled.
One exception: a fulfillment with status delivered also moves an order
that is already shipped to delivered. Without it a delivered parcel sat at
shipped forever, since shipped is otherwise not a mutable state — carrier
tracking could never complete the order.
Multi-package orders
For orders that ship in multiple packages, POST one fulfillment per shipment and include only the items in that package inline_items. The platform tracks coverage per line_item.id:
# Shipment 1 — 2 of the 5 items go out today
curl -X POST '.../orders/ord_xyz789/fulfillments.json' \
-d '{
"status": "success",
"tracking_number": "AWB-PKG-A",
"tracking_company": "DHL",
"tracking_url": "https://dhl.com/track/AWB-PKG-A",
"line_items": [
{ "id": "li_001", "quantity": 1 },
{ "id": "li_002", "quantity": 1 }
]
}'
# → order.status flips from "paid" → "partial"
# Shipment 2 — remaining 3 items ship a day later
curl -X POST '.../orders/ord_xyz789/fulfillments.json' \
-d '{
"status": "success",
"tracking_number": "AWB-PKG-B",
"tracking_company": "FedEx",
"tracking_url": "https://fedex.com/track/AWB-PKG-B",
"line_items": [
{ "id": "li_003", "quantity": 1 },
{ "id": "li_004", "quantity": 1 },
{ "id": "li_005", "quantity": 1 }
]
}'
# → order.status flips from "partial" → "shipped"
line_items is the back-compat shortcut for single-shipment
orders: the fulfillment is treated as covering everything, so a single
POST flips order.status straight to shipped (skipping partial).
Path Parameters
string
required
The order ID
Body Parameters
string
default:"pending"
Initial fulfillment status. Set to
success to immediately mark the
shipment as out the door — this is what bumps order.status to
shipped. Allowed: pending, open, success, shipped,
delivered, cancelled, error, failure.shipped and delivered are terminal carrier states, typically written
later from tracking rather than at creation. Prefer
update for progressing an existing
shipment — posting a second fulfillment for the same parcel leaves the
order showing the same tracking number twice.string
Carrier tracking number (e.g. AWB code).
string
Carrier name (e.g.
"UPS", "FedEx", "USPS", "DHL",
"Shiprocket").string
Public, shopper-facing tracking URL. Must be a valid URL.
string
Optional. The ID of the fulfillment service that produced this
shipment, returned by Create Fulfillment Service.
Caching this on a shop metafield after registration avoids a
list-call on every push.
array
boolean
default:"true"
Send shipment notification email to the customer.
Side effects
When the call succeeds:- A fulfillment record is created on the order.
- If
statustriggers a status flip (see table above),order.statusis updated in the same request. - A
fulfillments/createwebhook is dispatched to every subscriber on the store (3-retry exponential backoff: 1m / 5m / 15m).
Required scope
write_orders
Request example
curl -X POST 'https://api.launchmystore.io/api/v1/orders/ord_xyz789/fulfillments.json' \
-H 'Authorization: Bearer <oauth_access_token>' \
-H 'Content-Type: application/json' \
-d '{
"tracking_number": "1Z999AA10123456784",
"tracking_company": "UPS",
"tracking_url": "https://ups.com/track?num=1Z999AA10123456784",
"status": "success",
"service_id": "fs_abc123"
}'
Response
{
"status": 201,
"state": "success",
"data": {
"fulfillment": {
"fulfillmentId": "ful_new456",
"orderId": "ord_xyz789",
"status": "success",
"trackingNumber": "1Z999AA10123456784",
"trackingCompany": "UPS",
"trackingUrl": "https://ups.com/track?num=1Z999AA10123456784",
"serviceId": "fs_abc123",
"createdAt": "2024-01-20T14:30:00Z"
}
}
}
order.status is not returned in this response — the order row is
updated as a side effect. Re-fetch the order via
Get Order if you need the new status in
the same flow.