Getting Started
We have designed the API to follow GraphQL conventions: fields are named in camelCase, types are named in PascalCase and enums are named in PascalCase with values in ALL CAPS. Shopify's own GraphQL APIs also follow this same pattern.
Compare this with our RESTful APIs, which adopt snake_case for fields in both requests and responses.
The GraphQL endpoint
Unlike the REST Storefront API, which has a variety of endpoints, the GraphQL Admin API has a single endpoint:
POST https://submarine.discolabs.com/admin/api/v1/graphql.json
Authentication
In order to secure communication between API clients and the Submarine GraphQL API, both the clients and server will interact via HTTPS and will implement the widely adopted Hash-based Message Authentication Code (HMAC) authentication method. Using this methodology ensures data integrity, validates request origin and prevents replay requests.
In brief, HMAC authentication relies on the server issuing a unique API client ID and a unique shared secret to each client. The API client ID is used to identify the client from which a request originates and may also be used to scope access to API features. The shared secret is a key available to both the client and server and is used by the client to sign the request and by the server to validate the authenticity of said request.
The client should use the secret to build a request signature, which should be sent with the API client ID as an Authorization
request header:
Authorization: hmacauth {api_client_id}:{signature}
The signature is simply a base64-encoded version of the request payload, signed using HMAC-SHA256. An example Ruby implementation:
calculated_hmac = OpenSSL::HMAC.digest('sha256', 'shared-secret', request_body_as_string)
Base64.strict_encode64(calculated_hmac)
Invalid requests will return a 401 error:
{
"errors": [
{
"message": "Could not authenticate request",
"extensions": {
"code": "InvalidSignatureError"
}
}
]
}
Query the GraphQL Admin API
You can access the GraphQL Admin API endpoint using cURL or any other HTTP client.
Example query
Retrieving details of an order.
Request:
{
order(id: "gid://submarine/Order/1835878973539") {
customer {
email
}
id
lineItems {
quantity
sku
}
name
totalPrice {
amount
currencyCode
}
}
}
Response:
{
"data": {
"order": {
"customer": {
"email": "[email protected]",
},
"id": "gid://submarine/Order/1835878973539",
"lineItems": [
{
"quantity": 1,
"sku": "10745A"
},
{
"quantity": 2,
"sku": "90001A"
}
],
"name": "#34043",
"totalPrice": {
"amount": 9.9,
"currencyCode": "AUD"
}
}
}
}
Bad requests
Errors caused by malformed or invalid requests will usually be returned in the
userErrors
field of the response payload. Higher-level errors, such as issues with authentication, will be surfaced in a top-levelerrors
key.
Example mutation: orderCalculate
Calculating an order.
Request:
mutation orderCalculate($input: CalculatedOrderInput!) {
orderCalculate(input: $input) {
calculatedOrder {
availableShippingRates {
handle
price {
amount
currencyCode
}
title
}
customer {
email
}
customerPaymentMethods {
default
id
sourceId
type
}
}
userErrors {
field
messages
}
}
}
Input:
{
"input": {
"customerId": "gid://shopify/Customer/754119508038",
"lineItems": [
{
"quantity": 2,
"variantId": "gid://shopify/ProductVariant/18390473867334"
}
],
"shippingAddress": {
"first_name": "David",
"last_name": "Bird",
"company": "Disco Labs",
"address1": "36-38 Gipps Street",
"city": "Collingwood",
"countryCode": "AU",
"provinceCode": "VIC",
"zip": "3066"
}
}
}
Good response:
{
"data": {
"orderCalculate": {
"calculatedOrder": {
"availableShippingRates": [
{
"handle": "shopify-Free%20shipping-0.00",
"price": {
"amount": 0.0,
"currencyCode": "AUD"
},
"title": "Free shipping"
},
{
"handle": "shopify-Standard%20Shipping-10.00",
"price": {
"amount": 10.0,
"currencyCode": "AUD"
},
"title": "Standard Shipping"
}
],
"customer": {
"email": "[email protected]"
},
"customerPaymentMethods": [
{
"default": false,
"id": "gid://submarine/CustomerPaymentMethod/90836",
"sourceId": "350297",
"type": "invoice"
},
{
"default": true,
"id": "gid://submarine/CustomerPaymentMethod/90837",
"sourceId": "350300",
"type": "card"
}
]
},
"userErrors": []
}
}
}
Bad response:
{
"data": {
"orderCalculate": {
"calculatedOrder": null,
"userErrors": [
{
"field": "customerId",
"messages": [
"Cannot find customer: 754119508038"
]
}
]
}
}
}
Example mutation: orderCreate
Using the output of the orderCalculate
mutation to create a Shopify order.
Request:
mutation orderCreate($input: OrderInput!) {
orderCreate(input: $input) {
order {
createdAt
name
shippingLine {
price {
amount
currencyCode
}
title
}
shopifyId
totalPrice {
amount
currencyCode
}
}
userErrors {
field
messages
}
}
}
Input:
{
"input": {
"billingAddress": {
"first_name": "David",
"last_name": "Bird",
"company": "Disco Labs",
"address1": "36-38 Gipps Street",
"city": "Collingwood",
"countryCode": "AU",
"provinceCode": "VIC",
"zip": "3066"
},
"customerId": "gid://shopify/Customer/754119508038",
"lineItems": [
{
"quantity": 2,
"variantId": "gid://shopify/ProductVariant/18390473867334"
}
],
"paymentMethodId": "gid://submarine/CustomerPaymentMethod/90837",
"shippingAddress": {
"first_name": "David",
"last_name": "Bird",
"company": "Disco Labs",
"address1": "36-38 Gipps Street",
"city": "Collingwood",
"countryCode": "AU",
"provinceCode": "VIC",
"zip": "3066"
},
"shippingLine": {
"shippingRateHandle": "shopify-Standard%20Shipping-10.00"
}
}
}
Good response:
{
"data": {
"orderCreate": {
"order": {
"createdAt": "2020-07-09T21:37:12Z",
"id": "gid://submarine/Order/2227720257606",
"name": "#1117",
"shippingLine": {
"price": {
"amount": 10.0,
"currencyCode": "AUD"
},
"title": "Standard Shipping"
},
"shopifyId": "gid://shopify/Order/2227720257606",
"totalPrice": {
"amount": 41.9,
"currencyCode": "AUD"
}
},
"userErrors": []
}
}
}
Bad response:
{
"data": {
"orderCalculate": {
"calculatedOrder": null,
"userErrors": [
{
"field": "paymentMethodId",
"messages": [
"Cannot find default payment method"
]
}
]
}
}
}
Example mutation: draftOrderComplete
Specifying an existing draft order to complete and the payment method to complete it with.
Request:
mutation draftOrderComplete($input: CompletableDraftOrderInput!) {
draftOrderComplete(input: $input) {
completedDraftOrder {
orderId
orderName
processorReference
submarineReference
}
userErrors {
field
messages
}
}
}
Input:
{
"input": {
"draftOrderId": "gid://shopify/DraftOrder/3847825202",
"paymentMethodId": "gid://submarine/AuthorizedPaymentMethod/90837"
}
}
Good response:
{
"data": {
"draftOrderComplete": {
"completedDraftOrder": {
"orderId": "gid://shopify/Order/2227720257606",
"orderName": "#49274",
"processorReference": "ch_1DGJF3JqrrHX7MBOBXWbkO6q",
"submarineReference": "e55ef486-aa71-4e48-ae7f-e40a27cd0761"
},
"userErrors": []
}
}
}
Bad response (error with input):
{
"data": {
"draftOrderComplete": {
"orderId": null,
"orderName": null,
"processorReference": null,
"submarineReference": null,
"userErrors": [
{
"field": "paymentMethodId",
"messages": [
"Cannot find specified payment method"
]
}
]
}
}
}
Bad response (payment failed):
{
"data": {
"draftOrderComplete": {
"orderId": null,
"orderName": null,
"processorReference": null,
"submarineReference": null,
"userErrors": [
{
"field": "paymentMethodId",
"messages": [
"FAILED: Your card was declined. [4566185e-b79f-45e7-8432-e17f456ecb19]"
]
}
]
}
}
}
Bad response (payment rate limited):
{
"data": {
"draftOrderComplete": {
"orderId": null,
"orderName": null,
"processorReference": null,
"submarineReference": null,
"userErrors": [
{
"field": "paymentMethodId",
"messages": [
"FAILED: Too many requests."
]
}
]
}
}
}
Bad response (payment succeeded but draft order completion failed):
{
"data": {
"draftOrderComplete": {
"orderId": null,
"orderName": null,
"processorReference": "ch_1DGJF3JqrrHX7MBOBXWbkO6q",
"submarineReference": "e55ef486-aa71-4e48-ae7f-e40a27cd0761",
"userErrors": [
{
"field": "draftOrderId",
"messages": [
"Could not complete draft order"
]
}
]
}
}
}
Updated over 1 year ago