Ok… where the begin… Why is it IMPOSSIBLE to test fulfillment API code??
I have spent over 5 hours trying to fulfill test orders in my software using API.
I have managed to get everything to work except fulfillments.
I have:
- Set the right API permissions to do fulfillment
- Enabled test mode and bogus gateway to test order
- Tried both legacy mode and new mode
- Tried specific versions of API
** I have also tried many combinations outside of the above to get a result i can fulfill an order with.
I continuously get 404 not found (using newer API version) or 406 status code using the older legacy API.
This led me down the path of creating inventory and assigning products to it.
Raising several internal orders (none of which fulfill)
Changing the order creation settings to not auto fulfill so that it doesnt have a fulfillment cancelled flag.
Basically everything I possibly can to fulfill a single test order and it just keeps failing. both myself and ChatGPT are all out of ideas here.
See the FULL code here, it is in vb . NET, I have omitted personal information from any further text:
Public Async Function CreateFulfillment(orderId As String,
trackingNumber As String,
trackingCompany As String,
trackingUrl As String) As Task(Of Boolean)
Using client As New HttpClient()
client.DefaultRequestHeaders.Accept.Clear()
client.DefaultRequestHeaders.Accept.Add(New MediaTypeWithQualityHeaderValue(“application/json”))
client.DefaultRequestHeaders.Add(“X-Shopify-Access-Token”, API)
client.DefaultRequestHeaders.UserAgent.ParseAdd(“Mozilla/5.0 (WixMate)”)
Try
'---------------------------------------------------------
’ STEP 1: Try modern Fulfillment Orders API first
'---------------------------------------------------------
Dim fulfillOrderUrl As String = $“{BaseUrl}fulfillment_orders.json?order_id={orderId}”
Dim fulfillOrderResp As HttpResponseMessage = Await client.GetAsync(fulfillOrderUrl)
Dim fulfillOrderBody As String = Await fulfillOrderResp.Content.ReadAsStringAsync()
StrCatch = "Normal way" & vbCrLf & "=========" & vbCrLf & vbCrLf & "URL: " & fulfillOrderUrl & vbCrLf & vbCrLf
StrCatch &= $"fulfillorderresp: {fulfillOrderResp.ToString}" & vbCrLf & vbCrLf
StrCatch &= $"fulfillorderresp status code: {fulfillOrderResp.StatusCode}" & vbCrLf & vbCrLf
StrCatch &= $"fulfillorderBody: {fulfillOrderBody.ToString}" & vbCrLf & vbCrLf
Clipboard.SetText(StrCatch)
If fulfillOrderResp.IsSuccessStatusCode Then
Dim fulfillOrderJson As JObject = JObject.Parse(fulfillOrderBody)
Dim fulfillmentOrders = fulfillOrderJson("fulfillment_orders")
If fulfillmentOrders Is Nothing OrElse Not fulfillmentOrders.Any() Then
Debug.WriteLine("No fulfillment orders found for this order (empty array). Falling back to legacy API.")
Return Await CreateLegacyFulfillment(client, orderId, trackingNumber, trackingCompany, trackingUrl)
End If
' Use the first fulfillment order
Dim fulfillmentOrderId As String = fulfillmentOrders(0)("id").ToString()
Dim url As String = $"{BaseUrl}admin/api/2024-10/fulfillment_orders/{fulfillmentOrderId}/fulfillments.json"
Dim payload As New JObject(
New JProperty("fulfillment",
New JObject(
New JProperty("message", "Order shipped"),
New JProperty("notify_customer", True),
New JProperty("tracking_info",
New JObject(
New JProperty("number", trackingNumber),
New JProperty("company", trackingCompany),
New JProperty("url", trackingUrl)
)
)
)
)
)
Dim content As New StringContent(payload.ToString(), Encoding.UTF8, "application/json")
Dim response As HttpResponseMessage = Await client.PostAsync(url, content)
Dim body As String = Await response.Content.ReadAsStringAsync()
Debug.WriteLine($"[FulfillmentOrders POST] Status {CInt(response.StatusCode)} - {body}")
If Not response.IsSuccessStatusCode Then
Throw New ApplicationException($"Shopify fulfillment failed (HTTP {CInt(response.StatusCode)}): {body}")
End If
Return True
ElseIf fulfillOrderResp.StatusCode = HttpStatusCode.NotFound Then
Debug.WriteLine("No fulfillment orders exist for this order (404). Falling back to legacy API.")
Return Await CreateLegacyFulfillment(client, orderId, trackingNumber, trackingCompany, trackingUrl)
Else
Throw New ApplicationException($"Failed to fetch fulfillment orders: {fulfillOrderResp.StatusCode} {fulfillOrderBody}")
End If
Catch ex As Exception
Throw New ApplicationException($“Error fulfilling Shopify order {orderId}:{vbCrLf}{ex.Message}”, ex)
End Try
End Using
End Function
Dim StrCatch As String = “”
Private Async Function CreateLegacyFulfillment(client As HttpClient,
orderId As String,
trackingNumber As String,
trackingCompany As String,
trackingUrl As String) As Task(Of Boolean)
Try
Dim url As String = $“{BaseUrl}orders/{orderId}/fulfillments.json”
Dim payload As New JObject(
New JProperty(“fulfillment”,
New JObject(
New JProperty(“notify_customer”, True),
New JProperty(“tracking_number”, trackingNumber),
New JProperty(“tracking_company”, trackingCompany),
New JProperty(“tracking_url”, trackingUrl)
)
)
)
Dim content As New StringContent(payload.ToString(), Encoding.UTF8, “application/json”)
Dim response As HttpResponseMessage = Await client.PostAsync(url, content)
Dim body As String = Await response.Content.ReadAsStringAsync()
StrCatch &= “Legacy way” & vbCrLf & “=========” & vbCrLf & vbCrLf & "URL: " & url & vbCrLf & vbCrLf
StrCatch &= "Status code | Response: " & response.ToString & “|” & CInt(response.StatusCode) & vbCrLf & vbCrLf
StrCatch &= "Body: " & body
Clipboard.SetText(StrCatch)
Debug.WriteLine($“[LegacyFulfillment] Status {CInt(response.StatusCode)} - {body}”)
If Not response.IsSuccessStatusCode Then
Throw New ApplicationException($“Legacy fulfillment failed (HTTP {CInt(response.StatusCode)}): {body}”)
End If
Return True
Catch ex As Exception
Throw New ApplicationException($“Error fulfilling order (legacy path): {vbCrLf}{ex.Message}”, ex)
End Try
End Function
Here are the results of trying to fulfill an order that was NOT manual but made via the website in test mode with the bogus payment provider setup. I will share below the concatenated string collecting data about the fulfillment process and how it went:
Normal way
URL: https://xxx.myshopify.com/admin/fulfillment_orders.json?order_id=5808026484771
fulfillorderresp: StatusCode: 404, ReasonPhrase: ‘Not Found’, Version: 1.1, Content: System.Net.Http.StreamContent, Headers:
{
Transfer-Encoding: chunked
Connection: keep-alive
x-sorting-hat-podid: 34
x-sorting-hat-shopid: 19773111
shopify-web-runtime: web
cross-origin-opener-policy: noopener-allow-popups
vary: Accept-Encoding
referrer-policy: origin-when-cross-origin
x-frame-options: DENY
x-shopid: 19773111
x-shardid: 34
x-stats-apiclientid: 1900478
x-stats-apipermissionid: 10524426275
x-shopify-api-version: 2024-10
x-shopify-api-version-warning: About Shopify API versioning
x-shopify-shop-api-call-limit: 1/40
strict-transport-security: max-age=63072000; includeSubDomains; preload
x-request-id: 11d031d2-b739-4c6f-8908-72773e66221f-1761081768
server-timing: upstream_processing;dur=35, upstream_verdict_flag_enabled;dur=0.334;desc=“count”, upstream__y;desc=“73bca28b-830f-4984-8132-3a0d318c96d3”, upstream__s;desc=“4bac29d2-1e65-4263-ba42-67436a76f16a”
server-timing: socket;dur=0;desc=“Socket ready”, headers;dur=48;desc=“Headers”, reused;desc=“Socket reused”
server-timing: cfRequestDuration;dur=342.999935
content-security-policy: default-src ‘self’ data: blob: ‘unsafe-inline’ ‘unsafe-eval’ https://* shopify-pos://; block-all-mixed-content; child-src ‘self’ https:// shopify-pos://; connect-src ‘self’ wss:// https://*; frame-ancestors ‘none’; img-src ‘self’ data: blob: https:; script-src https://cdn.shopify.com https://cdn.shopifycdn.net https://checkout.pci.shopifyinc.com https://checkout.pci.shopifyinc.com/build/75a428d/card_fields.js https://api.stripe.com https://mpsnare.iesnare.com https://appcenter.intuit.com https://www.paypal.com https://js.braintreegateway.com https://c.paypal.com https://maps.googleapis.com https://www.google-analytics.com https://v.shopify.com ‘self’ ‘unsafe-inline’ ‘unsafe-eval’; upgrade-insecure-requests; report-uri /csp-report?source%5Baction%5D=error_404&source%5Bapp%5D=Shopify&source%5Bcontroller%5D=admin%2Ferrors&source%5Bsection%5D=admin_api&source%5Buuid%5D=11d031d2-b739-4c6f-8908-72773e66221f-1761081768; report-to shopify-csp
x-content-type-options: nosniff
x-download-options: noopen
x-permitted-cross-domain-policies: none
x-xss-protection: 1; mode=block
reporting-endpoints: shopify-csp=“/csp-report?source%5Baction%5D=error_404&source%5Bapp%5D=Shopify&source%5Bcontroller%5D=admin%2Ferrors&source%5Bsection%5D=admin_api&source%5Buuid%5D=11d031d2-b739-4c6f-8908-72773e66221f-1761081768”
x-dc: gcp-us-central1,gcp-us-central1,gcp-us-central1,gcp-us-central1,gcp-us-central1
Date: Tue, 21 Oct 2025 21:22:49 GMT
Set-Cookie: koa.sid=QhrSmSPLW66EatLuuRa4widmaxPk_RxU; path=/admin; expires=Wed, 22 Oct 2025 21:22:49 GMT; samesite=lax; secure; httponly
Set-Cookie: koa.sid.sig=Co1gStwez2xSwjM73zN_Nl2g04Q; path=/admin; expires=Wed, 22 Oct 2025 21:22:49 GMT; samesite=lax; secure; httponly
Alt-Svc: h3=“:443”; ma=86400
cf-cache-status: DYNAMIC
Report-To: {“endpoints”:[{“url”:“https://a.nel.cloudflare.com/report/v4?s=m0aAhwiNCc4EqWD9tgSgqe91E5B9FXxf4zm8NiOtqmVVt0ulUvzMhVq4zSxUnJJ%2FepCJi9txFZ1pn5K715Rl08AonXszod7W%2FB%2FAfZI%2BXFMFYkl%2FdeQ1bItjy%2FuArfazS0ia9UUKGQP1vFTvcns%3D”}],“group”:“cf-nel”,“max_age”:604800}
NEL: {“success_fraction”:0.01,“report_to”:“cf-nel”,“max_age”:604800}
Server: cloudflare
CF-RAY: 9923cfff2fb0d713-BNE
Content-Type: application/json; charset=utf-8
}
fulfillorderresp status code: NotFound
fulfillorderBody: {“errors”:“Not Found”}
Legacy way
URL: https://xxx.myshopify.com/admin/orders/5808026484771/fulfillments.json
Status code | Response: StatusCode: 406, ReasonPhrase: ‘Not Acceptable’, Version: 1.1, Content: System.Net.Http.StreamContent, Headers:
{
Connection: keep-alive
x-sorting-hat-podid: 34
x-sorting-hat-shopid: 19773111
shopify-web-runtime: web
cross-origin-opener-policy: noopener-allow-popups
referrer-policy: origin-when-cross-origin
x-frame-options: DENY
x-shopid: 19773111
x-shardid: 34
x-stats-apiclientid: 1900478
x-stats-apipermissionid: 10524426275
x-shopify-api-version: 2024-10
x-shopify-api-version-warning: About Shopify API versioning
x-shopify-shop-api-call-limit: 1/40
strict-transport-security: max-age=63072000; includeSubDomains; preload
x-request-id: 1655f260-24f2-472c-b185-c9160ab75432-1761081769
server-timing: upstream_processing;dur=26, upstream_verdict_flag_enabled;dur=0.223;desc=“count”, upstream__y;desc=“482e1c9d-b204-41cb-9ba4-1f0c4afa898d”, upstream__s;desc=“cea19428-24d7-4a66-b09b-f7eb586fbc94”
server-timing: socket;dur=0;desc=“Socket ready”, headers;dur=37;desc=“Headers”, reused;desc=“Socket reused”
server-timing: cfRequestDuration;dur=328.000069
content-security-policy: default-src ‘self’ data: blob: ‘unsafe-inline’ ‘unsafe-eval’ https://* shopify-pos://; block-all-mixed-content; child-src ‘self’ https:// shopify-pos://; connect-src ‘self’ wss:// https://*; frame-ancestors ‘none’; img-src ‘self’ data: blob: https:; script-src https://cdn.shopify.com https://cdn.shopifycdn.net https://checkout.pci.shopifyinc.com https://checkout.pci.shopifyinc.com/build/75a428d/card_fields.js https://api.stripe.com https://mpsnare.iesnare.com https://appcenter.intuit.com https://www.paypal.com https://js.braintreegateway.com https://c.paypal.com https://maps.googleapis.com https://www.google-analytics.com https://v.shopify.com ‘self’ ‘unsafe-inline’ ‘unsafe-eval’; upgrade-insecure-requests; report-uri /csp-report?source%5Baction%5D=error_404&source%5Bapp%5D=Shopify&source%5Bcontroller%5D=admin%2Ferrors&source%5Bsection%5D=admin_api&source%5Buuid%5D=1655f260-24f2-472c-b185-c9160ab75432-1761081769; report-to shopify-csp
x-content-type-options: nosniff
x-download-options: noopen
x-permitted-cross-domain-policies: none
x-xss-protection: 1; mode=block
reporting-endpoints: shopify-csp=“/csp-report?source%5Baction%5D=error_404&source%5Bapp%5D=Shopify&source%5Bcontroller%5D=admin%2Ferrors&source%5Bsection%5D=admin_api&source%5Buuid%5D=1655f260-24f2-472c-b185-c9160ab75432-1761081769”
x-dc: gcp-us-central1,gcp-us-central1,gcp-us-central1,gcp-us-central1,gcp-us-central1
Alt-Svc: h3=“:443”; ma=86400
cf-cache-status: DYNAMIC
Report-To: {“endpoints”:[{“url”:“https://a.nel.cloudflare.com/report/v4?s=ZjE4QYJPhwEjzM1WFQnoYz7khwWMcfic2Olfts2ZTdU%2FR35p5Li4LVCDuMijZYHRocG8szV6OkRFoDrysGcmK5iCdCwGuj3%2FR45wuab9kB%2FGBOdfzWLUxylR9JnuUU9NIp3TFEkYkzTkyegnycM%3D”}],“group”:“cf-nel”,“max_age”:604800}
NEL: {“success_fraction”:0.01,“report_to”:“cf-nel”,“max_age”:604800}
CF-RAY: 9923d0016d5fd713-BNE
Date: Tue, 21 Oct 2025 21:22:49 GMT
Server: cloudflare
Content-Length: 0
Content-Type: application/json
}|406
Body:
Can someone please, please, please, explain how anyone, ever, in the history of Shopify has managed to fulfill a single order using API that wasn’t an actual 100% legitimate purchase made via the store. Test mode and other orders do not work.
ALSO, WARNING TO OTHERS READING THIS!!!
After enabling test mode, I am now struck dealing with support to remove test mode as it it has wiped all other payments methods somehow and now the entire store is stuck in test mode. Hopefully this rep i’m on hold to can fix it soon.