NAV
Shell

Introduction

Distributors, wholesalers and retailers use Stock2Shop to manage inventory and orders from their ERP/Warehouse.

If you are developer wanting to code some customisation into your existing workflow or someone wanting to integrate with one of our existing merchants, read on!

There are many use cases for connecting to our API, see our tutorials for further examples.

Using the API

Our API is a JSON based RESTful web service.

In order to keep transactions secure requests need to be authenticated. We use token based authentication.

For each request you send, you must include a token=xyz query parameter. To obtain a token, first authenticate and then pass this token back on future requests.

The token is ephemeral, meaning it could expire, in which case you should authenticate again. Keep this in mind when designing your application.

Base URLs

Stock2Shop has 2 API’s.

https://app.stock2shop.com/v1/{end point}
https://api.stock2shop.com/v2-queue/{end point}

The former is our original monolith based API that performs mainly CRUD based operations. The later uses our microservices architecture and is normally used for asynchronous operations which involve a queue and worker processing.

Rate Limits

Stock2Shop may rate limit requests per client. If you receive:-

429 Too Many Requests

you have been rate limited.

We recommend not more than 1 request per second.

User Roles

Users have different roles on Stock2Shop. Roles limit which endpoints you can access.

Our roles are:-

Role Meaning
none If there is no role assigned you are an admin user
trade B2B trade store user
read_only Readonly to Customers, Products, Orders and other
product_editor Read and write to Products (excluding product meta)
product_meta_editor Read and write to Products and Product Meta
customer_read_only Read Customers

When you authenticate you will see your users role returned. If you are an Admin user, you can create additional users (assigned to specific roles) to access the API. It is best to do this through our console, see below.

Console / B2B Trade Store

A good example of how this API can be used is seen in on our Console and B2B Store

You can login to either one of these systems via your browser with the appropriate user role. Once logged in, open your browsers inspector and monitor the xhr requests sent.

Users

POST /v1/users/authenticate

Example

curl -X POST \
    -H 'Content-Type: application/json' \
    -d '{
          "system_user_auth": {
            "username": "barry",
            "password": "*****"
          }
        }' \
    https://app.stock2shop.com/v1/users/authenticate?format=json

Example Response

{
  "system_user": {
    "id": 1,
    "created": "2015-03-03T07:30:06+0000",
    "name": "Bob",
    "surname": "Seger",
    "email": "bob@example.com",
    "username": "bob",
    "modified": "2020-12-29T13:46:44+0000",
    "client_id": 1,
    "active": 1,
    "admin": 0,
    "token": "xxx",
    "client_name": "Cleitn Name",
    "fulfillment_services": [],
    "channels": [],
    "sources": [],
    "customers": [],
    "roles": [
      {
        "id": 1,
        "description": "product_meta_editor"
      }
    ],
    "role_description": "product_meta_editor"
  }
}

Authenticate a user to retrieve a token.

HTTP Request

POST https://app.stock2shop.com/v1/users/authenticate

Parameters

Parameter type Description
body body system_user_auth json object

GET /v1/users/valid_token

Example

curl -X GET \
    -H 'Content-Type: application/json' \
    https://app.stock2shop.com/v1/users/valid_token/xxx?format=json

Example Response

{
  "system_user": {
    "id": 1,
    "created": "2015-03-03T07:30:06+0000",
    "name": "Bob",
    "surname": "Seager",
    "email": "bob@example.com",
    "username": "bob",
    "modified": "2021-01-05T08:07:57+0000",
    "client_id": 1,
    "active": 1,
    "admin": 0,
    "token": "xxx"
  }
}

Check to see if a token is valid.

HTTP Request

GET https://app.stock2shop.com/v1/users/valid_token/{token}

Parameters

Parameter type Description
token url path token for authentication

Sources

GET /v1/sources

Example

curl -X GET \
    -H 'Content-Type: application/json' \
    https://app.stock2shop.com/v1/sources?format=json$token=xxx

Example Response

{
  "system_sources": [
    {
      "id": 318,
      "created": "2017-07-25T10:32:46+0000",
      "description": "Example",
      "sync_token": "0",
      "modified": "2021-08-27T13:57:29+0000",
      "client_id": 123,
      "type": "apifact_sapone",
      "active": 1,
      "source_id": null
    }
  ]
}

List all sources.

If source_id is not null, then this is a linked source.

HTTP Request

POST https://app.stock2shop.com/v1/sources

Parameters

Parameter type Description
token query param token for authentication

Channels

GET /v1/channels

Example

curl -X GET \
    -H 'Content-Type: application/json' \
    https://app.stock2shop.com/v1/channels?format=json$token=xxx

Example Response

{
  "system_channels": [
    {
      "id": 123,
      "created": "2015-08-08T21:32:46+0000",
      "description": "My Sales Channel",
      "modified": "2020-05-06T09:53:30+0000",
      "client_id": 456,
      "active": 1,
      "type": "shopify",
      "price_tier": null,
      "qty_availability": null
    }
  ]
}

List all channels.

HTTP Request

POST https://app.stock2shop.com/v1/channels

Parameters

Parameter type Description
token query param token for authentication

Products

Products are made up of one or more variants. A variant is the unique SKU (sometimes called barcode or GTIN) found in the ERP.

Products are usually associated with marketing related meta data, such as images, categories, titles and so on. Whereas a variant is associated with warehouses, pricing and is the unit which is ordered, invoiced and shipped. For more information on this relationship see here

We use different identifiers for products and variants.

Identifier Meaning
product.id Stock2Shop’s unique identifier
product.variant.id Stock2Shop’s unique identifier
product.source_product_code Unique identifier for the product on the ERP
product.variant.source_variant_code Unique identifier for the variant on the ERP
product.variant.sku This is normally the same as source_variant_code but can be different depending on your ERP.
product.channels.channel_product_code If the product is linke to a channel, this is the unique identifier on the channel

If the product/variant has not been synced with channels and sources, the corresponding identifiers will be missing.

POST /v1/products/elastic_search

Example

curl -X POST \
    -H 'Content-Type: application/json' \
    -d '{
          "query": {
            "bool": {
              "filter": [
                {
                  "range": {
                    "modified": {
                      "gt": "2021-08-27 08:23:04"
                    }
                  }
                }
              ]
            }
          },
          "size": 1,
          "from": 0,
          "sort": {
            "modified": {
              "order": "desc"
            }
          }
        }' \
    https://app.stock2shop.com/v1/products/elastic_search?token=xxx

Example Response

{
  "took": 3,
  "timed_out": false,
  "_shards": {
    "total": 5,
    "successful": 5,
    "skipped": 0,
    "failed": 0
  },
  "hits": {
    "total": 27,
    "max_score": null,
    "hits": [
      {
        "_index": "products--------2020-09-02-08-31-08",
        "_type": "_doc",
        "_id": "62777",
        "_score": null,
        "_source": {
        },
        "sort": [
          1609835104000
        ]
      }
    ]
  },
  "request": {
    "url": "https:\/\/vpc-stock2shop-647vemf3m3u22fhny7f7dqjs4m.eu-west-1.es.amazonaws.com\/products\/_search",
    "method": "POST",
    "body": {
      "aggs": {},
      "query": {
        "bool": {
          "filter": [
            {
              "range": {
                "modified": {
                  "gt": "2018-07-16 08:23:04"
                }
              }
            },
            {
              "term": {
                "client_id": 21
              }
            },
            {
              "term": {
                "active": true
              }
            }
          ]
        }
      },
      "size": 2,
      "from": 0,
      "sort": {
        "modified": {
          "unmapped_type": "date",
          "order": "desc"
        }
      }
    }
  },
  "system_products": [
    {
      "id": 62777,
      "created": "2015-03-03 12:37:51",
      "active": true,
      "source_product_code": "CDROM",
      "modified": "2021-01-05 08:25:04",
      "client_id": 21,
      "source_id": 57,
      "body_html": "<p>No one uses CDs in computers anymore. Why is this here? Testing for Wilson.<\/p>",
      "collection": "Storage",
      "title": "CD ROMs",
      "product_type": "Media",
      "vendor": "",
      "hash": "a22d32a40b598f2fcec0f80e23c06c34",
      "channels": [
        {
          "channel_id": "47",
          "channel_product_code": "62777"
        },
        {
          "channel_id": "255",
          "channel_product_code": "51"
        }
      ],
      "tags": "",
      "options": [
        {
          "name": "Selection",
          "position": 1
        }
      ],
      "meta": [
        {
          "id": 244931,
          "created": "2016-02-15 10:08:04",
          "key": "release_date",
          "value": "",
          "modified": "2020-10-28 14:10:47",
          "template_name": null,
          "client_id": 21
        },
        {
          "id": 1068975,
          "created": "2017-02-14 07:16:46",
          "key": "Dimensions",
          "value": "<a href=\"http:\/\/www.stock2shop.com\">test<\/a>",
          "modified": "2020-10-28 14:10:47",
          "template_name": null,
          "client_id": 21
        },
        {
          "id": 3235454,
          "created": "2018-10-21 17:49:08",
          "key": "channel_b2b",
          "value": "TRUE",
          "modified": "2020-10-28 14:10:47",
          "template_name": null,
          "client_id": 21
        }
      ],
      "images": [
        {
          "src_50x50": "https:\/\/s2s-image.s3.amazonaws.com\/thumb\/50x50\/adf8ded854495aedb72edefaaab4209b.jpg",
          "src_160x160": "https:\/\/s2s-image.s3.amazonaws.com\/thumb\/160x160\/adf8ded854495aedb72edefaaab4209b.jpg",
          "id": 271528,
          "created": "2018-06-12 04:55:30",
          "source_image_code": null,
          "storage_code": "adf8ded854495aedb72edefaaab4209b.jpg",
          "src": "https:\/\/s2s-image.s3.amazonaws.com\/adf8ded854495aedb72edefaaab4209b.jpg",
          "modified": "2020-01-30 08:32:19",
          "client_id": 21,
          "product_id": 62777,
          "active": true,
          "hash": "adf8ded854495aedb72edefaaab4209b"
        }
      ],
      "variants": [
        {
          "id": 67841,
          "source_variant_code": "4317647",
          "price": 10,
          "barcode": "",
          "sku": "CD003",
          "qty": 38,
          "grams": 0,
          "inventory_management": true,
          "active": true,
          "product_id": 62777,
          "image_id": 271528,
          "client_id": 21,
          "option1": "Pack of 25",
          "price_tiers": [
            {
              "tier": "Wholesale",
              "price": 28
            },
            {
              "tier": "Was",
              "price": 0
            }
          ],
          "qty_availability": []
        },
        {
          "id": 67842,
          "source_variant_code": "4317716",
          "price": 55,
          "barcode": "",
          "sku": "CD004",
          "qty": 39,
          "grams": 0,
          "inventory_management": true,
          "active": true,
          "product_id": 62777,
          "image_id": 235461,
          "client_id": 21,
          "option1": "Pack of 50",
          "price_tiers": [
            {
              "tier": "Wholesale",
              "price": 50
            },
            {
              "tier": "Was",
              "price": 0
            }
          ],
          "qty_availability": []
        }
      ]
    }
  ],
  "took_s2s": 7
}

Stock2Shop uses Elastic Search to search products.

To build a search query you need to understand how Elastic Search bool queries work

To see the query that is used internally look at the request response property.

The example query on the right shows all products modified since a certain date. It returns only 1 product and sorts the results by date modified descending. The perfect query if you want to page through products getting most recent updates, see our tutorials for examples of this.

Use the data inside of the system_products response property has a complete list of product information as stored on Stock2Shop, not on Elastic Search. This is important, since there may be a delay in indexing product changes to Elastic Search and system_products will give you a live view.

Elastic search aggregate queries can also be performed. This is a powerful tool if you are looking to group properties.

HTTP Request

POST https://app.stock2shop.com/v1/products/elastic_search

Parameters

Parameter type Description
token query param token for authentication
body body json elastic search boolean query

POST /v1/products/queue

Example (minimum required information)

curl -X POST \
    -H 'Content-Type: application/json' \
    -d '{
      "system_products": [
        {
          "source": {
            "source_id": "123",
            "product_active": "true",
            "source_product_code": "ABC",
            "sync_token": "0",
            "fetch_token": "0"
          },
          "product": {
            "title": "Example",
            "variants": {
              "source_variant_code": "ABC",
              "sku": "ABC"
            }
          }
        }
      ]
    }' \
    https://app.stock2shop.com/v1/products/queue?token=xxx&source_id=123

Example Response

{
  "system_products": {
    "sync_product": 1
  }
}

Example (all fields)

curl -X POST \
    -H 'Content-Type: application/json' \
    -d '{
      "system_products": [
        {
          "source": {
            "source_id": "{source_id}",
            "product_active": "true",
            "source_product_code": "ABC",
            "sync_token": "0",
            "fetch_token": "0"
          },
          "product": {
            "options": [
              {
                "name": "Size",
                "position": "1"
              }
            ],
            "body_html": "",
            "collection": "",
            "product_type": "",
            "tags": "",
            "title": "Example",
            "vendor": "",
            "variants": {
              "source_variant_code": "ABC-XXL",
              "sku": "ABC-XXL",
              "barcode": "",
              "qty": 9,
              "qty_availability": [
                {
                  "description": "Warehouse X",
                  "qty": 10
                }
              ],
              "price": 100.00,
              "price_tiers": [
                {
                  "tier": "Price List X",
                  "price": 80.00
                }
              ],
              "inventory_management": "true",
              "grams": 0,
              "option1": "XXL",
              "option2": null,
              "option3": null
            },
            "meta": [
              {
                "key": "foo",
                "value": "bar"
              }
            ]
          }
        }
      ]
    }' \
    https://app.stock2shop.com/v1/products/queue?token=xxx&source_id=123

Example Response

{
  "system_products": {
    "sync_product": 1
  }
}

Queue products for processing.

Products are added to a queue for subsequent processing. The endpoint will return the number of products added to the queue.

HTTP Request

POST https://app.stock2shop.com/v1/products/queue

Parameters

Parameter type Description
token query param token for authentication
body body json elastic search boolean query

Images

Images are linked to products and can optionally be linked to variants. When an image is created on Stock2Shop, various thumbnail versions are created.

Images are store on AWS S3 with the following url format:-

And for thumbnails:-

There is a maximum size of 2mb per image. We support .jpg and .png image types.

Below are the important ID’s associated with an image.

Identifier Meaning
image.image_id Unique identifier for the image on the ERP (if it originates from the ERP)
image.product_id Stock2Shop internal ID for product.
source.source_id Unique identifier for the source
source.source_product_code Unique identifier for the product on the ERP
source.source_variant_code Unique identifier for the variant on the ERP

POST /v1/images/queue

The image property should be the base64 encoded text value of the image.

Example Basic

curl -X POST \
    -H 'Content-Type: application/json' \
    -d '{
          "system_images": [
            {
              "source": {
                "source_id": 57,
                "source_product_code": "keyboard",
                "source_variant_code": "4191161"
              },
              "image": {
                "image_id": "a",
                "action": "I",
                "image": "iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4"
              }
            }
          ]
        }' \
    https://app.stock2shop.com/v1/images/queue?token=xxx

Example Response

{
  "system_images": {
    "sync_image": 1
  }
}

Example with Meta

curl -X POST \
    -H 'Content-Type: application/json' \
    -d '{
          "system_images": [
            {
              "source": {
                "source_id": 57,
                "source_product_code": "keyboard",
                "source_variant_code": "4191161"
              },
              "image": {
                "image_id": "a",
                "action": "I",
                "image": "iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4",
                "meta": {
                  "main_image": "true"
                }
              }
            }
          ]
        }' \
    https://app.stock2shop.com/v1/images/queue?token=xxx

Example Response

{
  "system_images": {
    "sync_image": 1
  }
}

Queue images for processing.

It is important to understand that the queue in which products and images are processed is FIFO (first in first out). This means if you are adding a new product with some images, you first need to queue the product for insertion, followed by the image with the appropriate source_product_code.

If the product fails to be created for whatever reason, the image will not be added and you will need to requeue the image.

The endpoint will return the number of images added to the queue.

HTTP Request

POST https://app.stock2shop.com/v1/products/queue

Parameters

Parameter type Description
token query param token for authentication
body body json payload, see examples

Orders

Orders usually originate from a sales channe (ecommerce platform, marketplace or our B2B trade store). They are then saved to Stock2Shop and processed to the ERP, if required.

We use different identifiers for orders.

Identifier Meaning
order.id Stock2Shop’s unique identifier
order.channel_order_code The channels unique identifier
orders.sources.source_order_code Unique identifier for the ERP. It is possible to have multiple sources in the case of split orders.

If the order has not been synced with channels / sources, the corresponding identifiers will be missing.

POST /v1/orders/elasticsearch

Example

curl -X POST \
    -H 'Content-Type: application/json' \
    -d '{
      "query": {
        "bool": {
          "must": [
            {
              "multi_match": {
                "fields": [
                  "id.autocomplete",
                  "channel_order_code",
                  "sources.source_order_code",
                  "customer.first_name",
                  "customer.last_name",
                  "customer.email",
                  "customer.channel_customer_code"
                ],
                "query": "abc",
                "operator": "or"
              }
            }
          ],
          "must_not": [],
          "filter": []
        }
      },
      "from": 0,
      "size": "10",
      "sort": {
        "_score": {
          "order": "desc"
        }
      }
    }' \
    https://app.stock2shop.com/v1/orders/elastic_search?token=xxx

Example Response

{
  "took": 123,
  "timed_out": false,
  "_shards": {
    "total": 5,
    "successful": 5,
    "skipped": 0,
    "failed": 0
  },
  "hits": {
    "total": 10000,
    "max_score": 10.32759,
    "hits": []
  },
  "request": {
    "url": "https:\/\/vpc-stock2shop-647vemf3m3u22fhny7f7dqjs4m.eu-west-1.es.amazonaws.com\/orders\/_search",
    "method": "POST",
    "body": {
      "aggs": {},
      "query": {
        "bool": {
          "must": [
            {
              "multi_match": {
                "fields": [
                  "id.autocomplete",
                  "channel_order_code",
                  "sources.source_order_code",
                  "customer.first_name",
                  "customer.last_name",
                  "customer.email",
                  "customer.channel_customer_code"
                ],
                "query": "abc",
                "operator": "or"
              }
            }
          ],
          "must_not": [],
          "filter": [
            {
              "term": {
                "client_id": 91
              }
            }
          ]
        }
      },
      "from": 0,
      "size": "10",
      "sort": {
        "_score": {
          "order": "desc"
        }
      }
    }
  },
  "system_orders": [
    {
      "id": 933302,
      "created": "2020-06-08 14:03:48",
      "channel_order_code": "0027795",
      "modified": "2020-06-08 14:04:08",
      "channel_id": 191,
      "client_id": 91,
      "customer_id": 495995,
      "notes": "Send Soon",
      "status": "ordered",
      "total_discount": 0,
      "line_item_sub_total": 0,
      "line_item_tax": 0,
      "shipping_sub_total": 0,
      "shipping_tax": 0,
      "sub_total": 119.23,
      "tax": 17.88,
      "total": 137.11,
      "customer": {
        "channel_customer_code": "5ba71121b3",
        "email": "abc@example.com",
        "first_name": "Roger",
        "last_name": "Waters"
      },
      "sources": [
        {
          "source_id": "318",
          "source_order_code": "3360213"
        }
      ],
      "line_items": [
        {
          "id": 3715344,
          "grams": 0,
          "price": 119.23,
          "sku": "3397006838",
          "total_discount": 0,
          "qty": 1,
          "variant_id": 620602,
          "source_id": 318,
          "tax_lines": [
            {
              "rate": "15"
            }
          ],
          "sub_total": 119.23,
          "tax": 17.88,
          "tax_per_unit": 17.88,
          "total": 137.11,
          "sub_total_display": "119.23",
          "tax_display": "17.88",
          "tax_per_unit_display": "17.88",
          "total_display": "137.11",
          "price_display": "119.23",
          "total_discount_display": "0.00"
        }
      ],
      "meta": [
        {
          "key": "customer_reference",
          "value": "PO 123"
        }
      ],
      "shipping_address": {
        "address1": "Sunny Str",
        "address2": null,
        "city": null,
        "company": "Apple",
        "country": "ZA",
        "country_code": "ZA",
        "first_name": "Roger",
        "last_name": "Waters",
        "phone": "123456789",
        "province": null,
        "province_code": null,
        "zip": "1234"
      },
      "billing_address": {
        "address1": "Sunny Str",
        "address2": null,
        "city": null,
        "company": "Apple",
        "country": "ZA",
        "country_code": "ZA",
        "first_name": "Roger",
        "last_name": "Waters",
        "phone": "123456789",
        "province": null,
        "province_code": null,
        "zip": "1234"
      },
      "shipping_total": 0,
      "sub_total_display": "119.23",
      "shipping_total_display": "0.00",
      "shipping_tax_display": "0.00",
      "tax_display": "17.88",
      "total_display": "137.11",
      "total_discount_display": "0.00"
    }
  ],
  "took_s2s": 51
}

Stock2Shop uses Elastic Search to search orders.

To build a search query you need to understand how Elastic Search bool queries work

To see the query that is used internally look at the request response property.

The example query on the right shows a free text search on specific order fields.

Use the data inside of the system_orders response property has a complete list of order information as stored on Stock2Shop, not on Elastic Search. This is important, since there may be a delay in indexing order changes to Elastic Search and system_orders will give you a live view.

Elastic search aggregate queries can also be performed. This is a powerful tool if you are looking to group properties.

HTTP Request

POST https://app.stock2shop.com/v1/products/elasticsearch

Parameters

Parameter type Description
token query param token for authentication
body body json elastic search boolean query

POST /v2-queue/order

Example (minimum required information)

curl -X POST \
    -H 'Content-Type: application/json' \
    -d '{
        "my order payload": {
            "foo": "bar"
        }
    }' \
    https://api.stock2shop.com/v2-queue/order?client_id=456&channel_id=123&token=xxx

Example Response

{
  "message": "order received",
  "queue_messages": [
    {
      "key": "213-980-0/2021-08/27/17/48/04-213285668-sync_order.json",
      "message_id": "3d7b74a0-8b1b-4b06-8d7f-8331cb682705"
    }
  ]
}

Queue orders for processing.

Orders are added to a queue for subsequent processing.

HTTP Request

POST https://api.stock2shop.com/v2-queue/order

Parameters

Parameter type Description
token query param token for authentication
client_id query param Client ID
channel_id query param Channel ID
body body order payload

Tutorials

Consuming Product Feed

Outlines the steps to consume a JSON feed of product data from a Stock2Shop account.

Product data sets could be large. To practically consume the entire feed, you need to page through products records. If you want to continually keep your system up to date, one approach is to store a modified date on your system and ask for products which have been modified since this date. This will mean you do not need to continually download the entire product feed.

This tutorial will show how to do this.

Your application should store the following information:-

Your application should perform the following logic:

Step 1: Check for a valid token

Checking to see if your token is still valid

GET /v1/users/valid_token

If your token is invalid or you don’t have a token then fetch one and save it to your application

POST /v1/users/authenticate

Step 2: Fetch products

POST /v1/products/elasticsearch

Your application should set the modified date it stores to the last modified date found in system_products.

Push products

Outlines steps to send a JSON payload of products to Stock2Shop for processing.

If you want to automatically send product updates to Stock2Shop, follow these instructions.

Step 1: Authenticate

POST /v1/users/authenticate

Step 2: Get sources

A “source” is the place your product data comes from. You need to have a valid source_id in order to push product data. There is a distinction between a linked and master source, read here.

Take note of the id field as this will be used as source_id in subsequent steps.

GET /v1/sources

Step 3: Post Products to Queue

It is recommended to batch many products per request, instead of individually.

To understand the relationship between products, variants and their options read this. If the relationship between the product and variant is not correctly configure, these products will be ignored.

POST /v1/products/queue

Post Orders

Outlines the steps to POST an order to a console in Stock2Shop.

You are going to POST a valid JSON payload to a sales channel. Depending on the sales channel, the payload you post will look different. For example, a Shopify order will follow the format of the shopify webhook and a Woocommerce order will follow the Woocommerce format. You are replicating what the channel would normally send Stock2Shop using the HTTP POST method.

If you have a custom channel setup, then it will require whatever format is agreed on with Stock2Shop.

Step 1: Authenticate

POST /v1/users/authenticate

Step 2: Get Channel & Client ID

You need to know you client_id and your channel_id in order to post the order to a specific channel. Fetch a list of your channels:

GET /v1/channels

Step 3: Post Order

POST /v2-queue/order