Skip to main content

Connect your custom app to Supliful using Shopify Admin API

This article provides a guide on how to use Shopify’s Admin API as a headless backend to send orders from your custom app or storefront to Supliful for automated fulfillment. Your customers never interact with Shopify.

S
Written by Supliful Support
Updated this week

Who should use this?

  • Brands with custom mobile apps, proprietary storefronts, or enterprise platforms

  • Teams who want automated Supliful fulfillment without a public Shopify storefront

Before you start (requirements)

  • Supliful:

    • Active subscription: $29/month (or $23/month annually)

    • Valid payment method on file for product base costs and shipping

    • Products customized in Supliful and published to Shopify via Publish to Shopify

  • Shopify:

    • Active store (Basic plan works; Plus increases API rate limits)

    • Supliful app installed and connected

    • Shipping: Supliful Fulfillment location with shipping zones/rates

    • Checkout setting: Automatic fulfillment for paid orders

  • Your application:

    • Server-side backend (required for secure API calls)

    • Secure storage for Shopify Admin API token

    • Webhook endpoints for order/fulfillment updates

    • Database mapping internal order IDs to Shopify order IDs

Step 1: Install and connect Supliful in Shopify

  1. In Shopify admin, install the Supliful app and complete the connection to your Supliful account.

  2. In Products, confirm each Supliful product shows Inventory will be stocked at: Supliful Fulfillment.

Important: Only publish products from Supliful to Shopify using Publish to Shopify. Manually created Shopify products will not fulfill.

Step 2: Configure automatic fulfillment and shipping

  1. Go to Settings > Checkout.

  2. Under Order processing, select Automatically fulfill the order’s line items.

  3. Go to Settings > Shipping and delivery.

  4. In the Supliful Fulfillment section, configure shipping zones and rates.

Step 3: Create a Shopify custom app and GraphQL API access

  1. In Shopify admin: Settings > Apps and sales channels > Develop apps > Create an app.

  2. Configure Admin API scopes:

    • read_products, write_products,

    • read_orders, write_orders,

    • read_customers, write_customers

    • read_fulfillments, write_fulfillments (optional if you mark fulfillments)

  3. Install the app to generate credentials.

  4. Copy the Admin API access token and store it securely (environment variables or a secrets manager).

  5. Note your GraphQL Admin API endpoint: https://your-store.myshopify.com/admin/api/2025-07/graphql.json

References:

Caution: Do not expose access tokens in client-side code. Use server-side calls only.

Verify API connectivity

Test your GraphQL connection with a simple shop query:

query {
shop {
name
email
primaryDomain {
url
}
}
}

Example using curl:

curl -X POST \
"https://your-store.myshopify.com/admin/api/2025-07/graphql.json" \
-H "X-Shopify-Access-Token: your_access_token_here" \
-H "Content-Type: application/json" \
-d '{
"query": "{ shop { name email primaryDomain { url } } }"
}'

Step 4: Sync products from Shopify to your app

  1. Use the GraphQL Admin API to fetch products and variants.

  2. Store: product ID, variant IDs, title/description, images, pricing, weight, SKU, and fulfillment service information.

  3. Identify Supliful products by checking if variants are assigned to a fulfillment service (not merchant-managed).

  4. Schedule a daily or weekly sync and subscribe to product webhooks (PRODUCTS_CREATE, PRODUCTS_UPDATE, PRODUCTS_DELETE).

Note: Map Shopify product/variant IDs to your internal product IDs for ordering.

Example GraphQL query for products

query GetProducts($first: Int!) {
products(first: $first) {
edges {
node {
id
title
description
images(first: 5) {
edges {
node {
url
altText
}
}
}
variants(first: 10) {
edges {
node {
id
title
price
sku
weight
weightUnit
inventoryItem {
id
tracked
}
}
}
}
}
}
pageInfo {
hasNextPage
endCursor
}
}
}

Variables:

{
"first": 250
}

Example using curl:

curl -X POST \
"https://your-store.myshopify.com/admin/api/2025-07/graphql.json" \
-H "X-Shopify-Access-Token: your_access_token_here" \
-H "Content-Type: application/json" \
-d '{
"query": "query GetProducts($first: Int!) { products(first: $first) { edges { node { id title description variants(first: 10) { edges { node { id title price sku weight weightUnit } } } } } pageInfo { hasNextPage endCursor } } }",
"variables": {
"first": 250
}
}'

Note: Use pagination with pageInfo.hasNextPage and pageInfo.endCursor to fetch all products if you have more than 250.

Step 5: Create or retrieve customer records

Before creating orders, it's recommended to create customer records in Shopify or retrieve existing ones. This enables:

  • Using customerToAssociate in orderCreate (Step 6, Option A) to skip the separate orderCustomerSet mutation

  • Proper customer data management and order history

  • Customer account features and loyalty programs

Create a new customer

mutation CreateCustomer($input: CustomerInput!) {
customerCreate(input: $input) {
customer {
id
email
firstName
lastName
phone
}
userErrors {
field
message
}
}
}

Variables:

{
"input": {
"email": "[email protected]",
"firstName": "John",
"lastName": "Smith",
"phone": "+15555551234",
"acceptsMarketing": false
}
}

Retrieve existing customer by email

query GetCustomerByEmail($query: String!) {
customers(first: 1, query: $query) {
edges {
node {
id
email
firstName
lastName
phone
}
}
}
}

Variables:

{
"query": "email:[email protected]"
}

Best practice: Check if a customer exists before creating a new one to avoid duplicates. Store the Shopify customer ID in your database mapped to your internal customer ID. Use this customer ID with the customerToAssociate field when creating orders in Step 6.

Step 6: Create orders via GraphQL Admin API

  1. After payment in your app, create a Shopify order using the orderCreate mutation.

  2. Include:

    • lineItems with exact variant ID and quantity

    • Customer email and phone (via customAttributes or customer object)

    • shippingAddress and billingAddress (full details, including phone)

    • Financial status (via transactions or mark as paid)

    • note with your internal order ID

    • Optional: customerToAssociate to link an existing customer (skips separate orderCustomerSet mutation)

References:

Important: Missing customer phone or email will cause Supliful to decline the order.

GraphQL orderCreate mutation

mutation CreateOrder($order: OrderCreateOrderInput!) {
orderCreate(order: $order) {
order {
id
name
totalPriceSet {
shopMoney {
amount
currencyCode
}
}
displayFinancialStatus
displayFulfillmentStatus
customer {
id
email
}
}
userErrors {
field
message
}
}
}

Variables (complete example)

{
"order": {
"lineItems": [
{
"variantId": "gid://shopify/ProductVariant/12345678",
"quantity": 2
}
],
"shippingAddress": {
"firstName": "John",
"lastName": "Smith",
"address1": "123 Main Street",
"address2": "Apt 4B",
"city": "Louisville",
"provinceCode": "KY",
"countryCode": "US",
"zip": "40202",
"phone": "+15555551234"
},
"billingAddress": {
"firstName": "John",
"lastName": "Smith",
"address1": "123 Main Street",
"address2": "Apt 4B",
"city": "Louisville",
"provinceCode": "KY",
"countryCode": "US",
"zip": "40202",
"phone": "+15555551234"
},
"email": "[email protected]",
"phone": "+15555551234",
"note": "Order from mobile app - Order ID: APP-12345",
"financialStatus": "PAID",
"sendReceipt": false,
"sendFulfillmentReceipt": false,
"customAttributes": [
{
"key": "internal_order_id",
"value": "APP-12345"
}
]
}
}

Example using curl

curl -X POST \
"https://your-store.myshopify.com/admin/api/2025-07/graphql.json" \
-H "X-Shopify-Access-Token: your_access_token_here" \
-H "Content-Type: application/json" \
-d '{
"query": "mutation CreateOrder($order: OrderCreateOrderInput!) { orderCreate(order: $order) { order { id name totalPriceSet { shopMoney { amount currencyCode } } displayFinancialStatus displayFulfillmentStatus customer { id email } } userErrors { field message } } }",
"variables": {
"order": {
"lineItems": [
{
"variantId": "gid://shopify/ProductVariant/12345678",
"quantity": 2
}
],
"shippingAddress": {
"firstName": "John",
"lastName": "Smith",
"address1": "123 Main Street",
"address2": "Apt 4B",
"city": "Louisville",
"provinceCode": "KY",
"countryCode": "US",
"zip": "40202",
"phone": "+15555551234"
},
"billingAddress": {
"firstName": "John",
"lastName": "Smith",
"address1": "123 Main Street",
"address2": "Apt 4B",
"city": "Louisville",
"provinceCode": "KY",
"countryCode": "US",
"zip": "40202",
"phone": "+15555551234"
},
"email": "[email protected]",
"phone": "+15555551234",
"note": "Order from mobile app - Order ID: APP-12345",
"financialStatus": "PAID",
"sendReceipt": false,
"sendFulfillmentReceipt": false
}
}
}'

If you already have a customer ID from Step 5, you can associate the customer directly in the orderCreate mutation using the customerToAssociate field. This eliminates the need for a separate orderCustomerSet mutation.

Step 7: Assign customer to order

Assign customer to the order using the orderCustomerSet mutation after order creation:

mutation AssignCustomerToOrder($orderId: ID!, $customerId: ID!) {
orderCustomerSet(input: {
orderId: $orderId,
customerId: $customerId
}) {
order {
id
customer {
id
email
firstName
lastName
}
}
userErrors {
field
message
}
}
}

Variables:

{
"orderId": "gid://shopify/Order/1234567890",
"customerId": "gid://shopify/Customer/9876543210"
}

Note: GraphQL uses Global IDs (GIDs) in the format gid://shopify/ResourceType/ID. Convert numeric IDs if needed.

Step 8: Verify automatic routing to Supliful

  1. In Shopify, open a test order and confirm:

    • Line items tied to Supliful Fulfillment

    • Fulfillment request status progresses automatically (with automatic fulfillment enabled)

  2. If you prefer manual approval, submit fulfillment via the Fulfillment Order API after review.

Step 9: Implement webhooks for real-time status

  1. Subscribe to these webhook topics:

    • ORDERS_FULFILLED

    • FULFILLMENTS_CREATE

    • FULFILLMENTS_UPDATE

    • ORDERS_CANCELLED

    • ORDERS_UPDATED

  2. Verify each webhook using X-Shopify-Hmac-SHA256.

  3. Respond 200 within 5 seconds and process payloads asynchronously.

  4. Update your order status with tracking_number, tracking_url, carrier, and timestamps.

  5. Notify customers in-app when shipped.

References:

Create webhook subscriptions via GraphQL

mutation CreateWebhook($topic: WebhookSubscriptionTopic!, $webhookSubscription: WebhookSubscriptionInput!) {
webhookSubscriptionCreate(
topic: $topic
webhookSubscription: $webhookSubscription
) {
webhookSubscription {
id
topic
endpoint {
__typename
... on WebhookHttpEndpoint {
callbackUrl
}
}
}
userErrors {
field
message
}
}
}

Variables:

{
"topic": "ORDERS_FULFILLED",
"webhookSubscription": {
"callbackUrl": "https://your-app.com/webhooks/shopify/order-fulfilled",
"format": "JSON"
}
}

Example using curl

curl -X POST \
"https://your-store.myshopify.com/admin/api/2025-07/graphql.json" \
-H "X-Shopify-Access-Token: your_access_token_here" \
-H "Content-Type: application/json" \
-d '{
"query": "mutation CreateWebhook($topic: WebhookSubscriptionTopic!, $webhookSubscription: WebhookSubscriptionInput!) { webhookSubscriptionCreate(topic: $topic, webhookSubscription: $webhookSubscription) { webhookSubscription { id topic endpoint { __typename ... on WebhookHttpEndpoint { callbackUrl } } } userErrors { field message } } }",
"variables": {
"topic": "ORDERS_FULFILLED",
"webhookSubscription": {
"callbackUrl": "https://your-app.com/webhooks/shopify/order-fulfilled",
"format": "JSON"
}
}
}'

Repeat for each webhook topic you need to subscribe to.

Troubleshooting common issues

  • Order created but not in Supliful:

    • Check automatic fulfillment is enabled.

    • Confirm products show Supliful Fulfillment, not Manual.

    • Verify Supliful app connection.

  • Fulfillment request declined:

    • Read decline note in Shopify order timeline.

    • Frequent causes:

      • Missing phone or email → Update order and resubmit.

      • Invalid address → Add validation in checkout; correct and resubmit.

      • Payment method issue in Supliful → Update card or balance.

      • Product not published from Supliful → Republish using Publish to Shopify.

  • Missing status updates in your app:

    • Confirm webhook subscriptions are active and verified.

    • Check endpoint uptime and 200 responses within 5 seconds.

    • Implement polling as a fallback for orders updated since last webhook.

  • GraphQL-specific errors:

    • Invalid GID format → Ensure IDs use format gid://shopify/ResourceType/ID

    • userErrors in response → Check the userErrors array in mutation responses for detailed error messages

    • Rate limiting → GraphQL uses calculated query costs; see rate limits documentation

Support

Shopify API documentation:

Supliful support

  • Live chat with customer support for subscribers or email

  • Provide Shopify order ID and your internal order ID when escalating

  • Dedicated launch support is included in the Business Plan subscription

Did this answer your question?