NAV Navbar
shell python haskell
  • Introduction
  • Orders
  • Offers
  • Returns
  • Sandbox Testing
  • Errors
  • Transporters
  • import json
    import requests
    
    {-# LANGUAGE OverloadedStrings #-}
    
    import           Control.Lens         ((&), (.~), (^.))
    import           Data.Aeson           (object, toJSON, (.=))
    import qualified Data.ByteString      as SBS
    import qualified Data.ByteString.Lazy as LBS
    import           Data.Monoid          ((<>))
    import           Network.Wreq         (Options, Response, defaults, getWith,
                                           header, postWith, responseBody)
    
    

    Introduction

    Welcome to the Channable API v1.

    This version of the Channable API allows you to:

    Details

    Key Value
    Api version v1
    Protocol HTTPS
    Hostname api.channable.com
    Common Path /v1/companies/:company_id/projects/:project_id

    Taxonomy

    Term Description
    Platform A webshop platform, such as WooComerce, Lightspeed, etc.
    Marketplace A place where you can sell your products, such as Google, Amazon, eBay, etc. Sometimes referred to as channel.
    Order A purchase of any products made by a client.
    Offer A selling proposal made by a seller over any class of products. It includes price, stock and other seller-dependent values.

    Rate Limiting

    The Channable API has a rate limiting policy to which you must comply to.
    It is defined on a per-company basis, meaning that generating new API tokens will not circumvent our rate-limiting.

    Frequency Burst
    Max 2 requests per second Burst of 100 requests

    Note: We use the Leaky Bucket algorithm for rate-limiting.

    Getting started

    To get started you need to:

    Find your project ids

    To find the project ids you are interested in:

    1. Log in to the Channable app
    2. Select a project
    3. Open the project Settings
    4. Copy the Project id value

    Generate an Api Token for your company

    To both find your Channable company_id and to generate a new API token for your company, go to app.channable.com and sign in.

    On the main page hover your mouse over your account name and a drop down menu will show, as seen on the screenshot:

    From here you can see your company id, as well as generate your api token:

    Notes:

    How to include the token in your requests:

    # Headers method: Authorization: Bearer
    curl "api_endpoint_here"
      -H "Authorization: Bearer your_api_token"
    
    # URL method: ?access_token parameter
    curl "api_endpoint_here?access_token=your_api_token"
    
    apiToken :: ByteString
    apiToken = your_api_token
    
    reqOptions :: Options
    reqOptions = defaults & header "Authorization" .~ ["Bearer " <> apiToken]
    
    api_token = <api_token>
    headers = {'Authorization': "Bearer {}".format(api_token)}
    

    Using the API Token

    You must include your API token in every request you make to Channable.
    The token can be included either in the:

    After generating your API token you are ready to try out the endpoints and build your integration using the Channable Sandbox. More information on how to start testing using the Channable Sandbox can be found here: Testing with the Channable Sandbox.

    Orders

    Get all orders

    Request

    $ curl 'https://api.channable.com/v1/companies/123/projects/456/orders?offset=50&limit=1' \
        -H 'authorization: Bearer api_key'
    
    getAllOrders =
       getWith reqOptions $ "https://api.channable.com/v1/companies/123/projects/456/orders?offset=50&limit1"
         where reqOptions = defaults & header "Authorization" .~ ["Bearer " <> apiToken]
    
    def get_all_orders():
      return requests.get("https://api.channable.com/v1/companies/123/projects/456/orders?offset=50&limit=1",
                          headers={'Authorization': "Bearer {}".format(api_token)},
                         ).json()
    
    

    Response

    {
      "total": 1,
      "error_count": 0,
      "orders": [
        {
          "channel_id": "123",  # The order id of the marketplace
          "channel_name": "bol",
          "created": "2017-08-02T14:31:48",
          "data": {
            "billing": {
              "address1": "Billingstraat 1",
              "address2": "Onder de brievenbus huisnummer 1 extra adres info",
              "address_supplement": "Onder de brievenbus huisnummer 1 extra adres info",
              "city": "Amsterdam",
              "company": "Bol.com",
              "country_code": "NL",
              "email": "dontemail@me.net",
              "first_name": "Jans",
              "house_number": 1,
              "house_number_ext": "",
              "last_name": "Janssen",
              "middle_name": "",
              "region": "",
              "street": "Billingstraat",
              "zip_code": "5000 ZZ"
            },
            "customer": {
              "company": "Bol.com",
              "email": "dontemail@me.net",
              "first_name": "Jans",
              "gender": "male",
              "last_name": "Janssen",
              "middle_name": "",
              "mobile": "",
              "phone": "0201234567"
            },
            "extra": {
              "comment": "Bol.com order id: 123",
              "memo": "Order from Channable \n Bol.com order id: 123\n Customer receipt: https:\/\/www.bol.com\/sdd\/orders\/downloadallpackageslips.html"
            },
            "price": {
              "commission": 1.5,
              "currency": "EUR",
              "payment_method": "bol",
              "shipping": 0,
              "subtotal": 123.45,
              "total": 123.45,
              "transaction_fee": 0
            },
            "products": [
              {
                "commission": 1.5,
                "delivery_period": "2017-08-02+02:00",
                "ean": "9789062387410",
                # The official identifier of the product, depending on the marketplace. For example GTIN, ISBN or in the case of Amazon the ASIN.
                "article_number":  "9789062387410",
                "id": "11693020",
                "price": 61.725,
                "quantity": 2,
                "reference_code": "123",
                "shipping": 0,
                "title": "Harry Potter"
              }
            ],
            "shipping": {
              "address1": "Shipmentstraat 42 bis",
              "address2": "",
              "address_supplement": "3 hoog achter extra adres info",
              "city": "Amsterdam",
              "company": "The Company",
              "country_code": "NL",
              "email": "nospam4me@myaccount.com",
              "first_name": "Jan",
              "house_number": 42,
              "house_number_ext": "bis",
              "last_name": "Janssen",
              "middle_name": "",
              "region": "",
              "street": "Shipmentstraat",
              "zip_code": "1000 AA"
            },
            "delivery_request": {
              "method": "Shipping method",
              "carrier": "Shipping carrier",
              "promise": "Shipping promise"
            }
          },
          "error": false,
          "fulfillment": {},
          "id": 299623,
          "modified": "2017-08-10T18:08:13.699449",
          "platform_id": "299623",
          "platform_name": "channable",
          "project_id": 6496,
          "status_paid": "paid",  # This is always paid, because the marketplace handles payments
          "status_shipped": "not_shipped"
        }
      ]
    }
    

    This endpoint retrieves all orders for a given project of your company, sorted from most recent to oldest. Optionally, you can pass query parameters to customize the query and response content.
    Note that the order data itself (like address information) can not be modified.

    Request

    Method GET
    Endpoint /orders
    Headers none
    Query parameters see below

    Query parameters

    Parameter Type Default Description
    offset Int 0 Defines the starting position, in which position 0 resembles the most recent order.
    limit Int 15, max = 100 The number of orders to retrieve after offset.
    search String empty Search term used to filter the orders list.
    errors Bool False Choose to only retrieve orders with errors.
    Note: true, True, and 1 evaluate to True, everything else is considered False.
    status String empty Search on shipment status, possible values: not_shipped, shipped, cancelled, waiting, pending_shipment, pending_cancellation.
    start_date* String empty Search for orders with a creation date after this date, in format YYYY-MM-DD
    end_date* String empty Search for orders with a creation date before this date, in format YYYY-MM-DD
    last_modified_after* String empty Search for orders that have been modified date after this date, in format YYYY-MM-DD
    last_modified_before* String empty Search for orders that have been modified date before this date, in format YYYY-MM-DD
    Timezones*

    When using the start_date and end_date parameters please keep in mind that the created date of the order has a CET timezone. The same holds true for the last_modified_after and last_modified_before, here the timestamp used to denote the last change also has a CET timezone.

    Example

    Suppose the following scenario for a given project of yours:

    Request parameters Response
    none {"orders": [o4, o3, o2, o1], "error_count": 1, "total": 4}
    ?errors=true {"orders": [o3], "error_count": 1, "total": 1}
    ?limit=2 {"orders": [o4, o3], "error_count": 1, "total": 2}
    ?offset=1 {"orders": [o3, o2, o1], "error_count": 1, "total": 3}
    ?offset=4 {"orders": [], "error_count": 1, "total": 0}
    ?offset=1&limit=1 {"orders": [o3], "error_count": 1, "total": 1}
    ?offset=1&limit=2 {"orders": [o3, o2], "error_count": 1, "total": 2}
    ?offset=1&limit=2&errors=true {"orders": [o3], "error_count": 1, "total": 1}
    ?search=t {"orders": [o4, o1], "error_count": 0, "total": 2}
    ?search=t&errors=true {"orders": [], "error_count": 1, "total": 0}
    ?search=tE {"orders": [o3], "error_count": 1, "total": 1}
    ?search=tE&errors=true {"orders": [o3], "error_count": 1, "total": 1}
    ?offset=1&limit=2&search=t&errors=true {"orders": [], "error_count": 1, "total": 0}
    ?status=not_shipped {"orders": [o4], "error_count": 1, "total": 1}
    ?start_date=2018-01-01 {"orders": [o4, o3, o2], "error_count": 1, "total": 3}
    ?start_date=2018-01-01&end_date=2018-02-28 {"orders": [o3, o2], "error_count": 1, "total": 2}
    ?last_modified_after=2018-02-29 {"orders": [o4], "error_count": 1, "total": 1}

    1: This assumes that none of the orders have been modified since creation.

    Response

    The response body is a JSON object containing the following fields:

    You can see at your right a real example of such return value.

    Get an order by id

    Request

    $ curl 'https://api.channable.com/v1/companies/123/projects/456/orders/7' \
        -H 'authorization: Bearer api_key'
    
    getOrderById :: Int -> IO (Response ByteString)
    getOrderById id =
      getWith reqOptions $ "https://api.channable.com/v1/companies/123/projects/456/orders/" <> show id
        where reqOptions = defaults & header "Authorization" .~ ["Bearer " <> apiToken]
    
    
    def get_order_by_id():
      return requests.get("https://api.channable.com/v1/companies/123/projects/456/orders/7",
                          headers={'Authorization': "Bearer {}".format(api_token)},
                         ).json()
    

    Response

    {
      "order": {
        "channel_id": "123",
        "channel_name": "bol",
        "created": "2017-08-02T14:31:48",
        "data": {
          "billing": {
            "address1": "Billingstraat 1",
            "address2": "Onder de brievenbus huisnummer 1 extra adres info",
            "address_supplement": "Onder de brievenbus huisnummer 1 extra adres info",
            "city": "Amsterdam",
            "company": "Bol.com",
            "country_code": "NL",
            "email": "dontemail@me.net",
            "first_name": "Jans",
            "house_number": 1,
            "house_number_ext": "",
            "last_name": "Janssen",
            "middle_name": "",
            "region": "",
            "street": "Billingstraat",
            "zip_code": "5000 ZZ"
          },
          "customer": {
            "company": "Bol.com",
            "email": "dontemail@me.net",
            "first_name": "Jans",
            "gender": "male",
            "last_name": "Janssen",
            "middle_name": "",
            "mobile": "",
            "phone": "0201234567"
          },
          "extra": {
            "comment": "Bol.com order id: 123",
            "memo": "Order from Channable \n Bol.com order id: 123\n Customer receipt: https:\/\/www.bol.com\/sdd\/orders\/downloadallpackageslips.html"
          },
          "price": {
            "commission": 1.5,
            "currency": "EUR",
            "payment_method": "bol",
            "shipping": 0,
            "subtotal": 123.45,
            "total": 123.45,
            "transaction_fee": 0
          },
          "products": [
            {
              "commission": 1.5,
              "delivery_period": "2017-08-02+02:00",
              "ean": "9789062387410",
              "id": "11693020",
              "price": 61.725,
              "quantity": 2,
              "reference_code": "123",
              "shipping": 0,
              "title": "Harry Potter"
            }
          ],
          "shipping": {
            "address1": "Shipmentstraat 42 bis",
            "address2": "",
            "address_supplement": "3 hoog achter extra adres info",
            "city": "Amsterdam",
            "company": "The Company",
            "country_code": "NL",
            "email": "nospam4me@myaccount.com",
            "first_name": "Jan",
            "house_number": 42,
            "house_number_ext": "bis",
            "last_name": "Janssen",
            "middle_name": "",
            "region": "",
            "street": "Shipmentstraat",
            "zip_code": "1000 AA"
          },
          "delivery_request": {
            "method": "Shipping method",
            "carrier": "Shipping carrier",
            "promise": "Shipping promise"
          }
        },
        "error": false,
        "fulfillment": {},
        "id": 299623,
        "modified": "2017-08-10T18:08:13.699449",
        "platform_id": "299623",
        "platform_name": "channable",
        "project_id": 6496,
        "status_paid": "paid",  # This is always paid, because the marketplace handles payments
        "status_shipped": "shipped"
      },
      "events": [
        {
          "created": "2016-10-20T10:55:08.507355",
          "id": 1234545,
          "message": "Sent shipment update to Amazon",
          "modified": "2016-10-20T10:55:08.507378",
          "order_id": 299623,
          "project_id": 91919,
          "status": "info"
        },
        {
          "created": "2016-10-20T10:54:53.074700",
          "id": 2370337,
          "message": "Changed shipping status: not_shipped -> shipped",
          "modified": "2016-10-20T10:54:53.074705",
          "order_id": 299623,
          "project_id": 91919,
          "status": "info"
        },
        {
          "created": "2016-10-19T10:54:15.008544",
          "id": 1407404,
          "message": "Channable order processed: 299623",
          "modified": "2016-10-19T10:54:15.008551",
          "order_id": 299623,
          "project_id": 91919,
          "status": "info"
        }
      ]
    }
    

    This endpoint retrieves an order, and its events, based on the order_id.

    Request

    Method GET
    Endpoint /orders/:order_id
    Headers none
    Query parameters none

    Response

    The response body is a JSON object containing the following fields:

    You can see at your right a real example of such return value.

    Understanding the address fields

    There are two ways of storing the address information for order billing and shipping:

    1. Using the fields street, house_number, house_number_ext, and address_supplement
    2. Using the fields address1 and address2

    The first is more used in the Netherlands, where an address requires a house number, while the second version is more used abroad. We support both cases, and, in the example to the right, you see how to translate one into the other.

    Understanding the price fields

    An order contains a list of products purchased by a customer.
    The product.quantity stores the quantity of products of a given kind were purchased. The product.price is per product, not product.quantity * price_per_unit. The order.price.total is the sum of all purchased products' price, shipping costs, and other applicable fees.

    Shipment update

    Request

    $ curl 'https://api.channable.com/v1/companies/123/projects/456/orders/7/shipment' \
        -H 'authorization: Bearer api_key' \
        -H 'content-type: application/json' \
        -X POST -d '{"tracking_code": "3S93829038223232", "transporter": "DHL"}'
    
    updateOrderShipment :: Int -> IO (Response ByteString)
    updateOrderShipment id =
      postWith reqOptions ("https://api.channable.com/v1/companies/123/projects/456/orders/" <> show id <> "/shipment") reqBody
        where reqBody = object ["tracking_code" .= ("3S93829038223232" :: String)]
    
    def update_order_shipment():
      request_body = json.dumps({'tracking_code': '3S93829038223232', 'transporter': 'DHL'})
      return requests.post("https://api.channable.com/v1/companies/123/projects/456/orders/7/shipment",
                           headers={'Authorization': "Bearer {}".format(api_token), 'Content-Type': 'application/json'},
                           data=request_body
                          )
    

    This endpoint allows you to update an order shipment status.
    We will then propagate the update to the involved marketplaces.

    Request

    Method POST
    Endpoint /orders/:order_id/shipment
    Headers Content-Type: application/json
    Query parameters none

    Request body scheme

    {
        "type": "object",
        "properties": {
            "tracking_code": {"type": ["string", "null"]},
            "transporter": {"type": ["string", "null"]},
            "return_tracking_code": {"type": ["string", "null"]},
            "return_transporter": {"type": ["string", "null"]},
        }
    }
    

    Body

    We expect a JSON body containing the following optional fields:

    Field Expected Type
    tracking_code String or null
    transporter String or null
    return_tracking_code String or null
    return_transporter String or null

    Note: return_tracking_code and return_transporter are only applicable for Otto orders. Supplying these fields for other orders will not have any effect.

    Response

    Response

    {
      "status": "success",
      "message": "Processed 1 shipment"
    }
    

    The response body is a JSON object containing the following fields:

    Field Type Value
    status String "success"
    message String "Processed 1 shipment"

    NB! As of Monday June 28 09:00 the shipments endpoint will no longer immediately forward the shipment update to the marketplace. Instead shipment updates will be grouped within Channable and sent in bulk at a regular interval every 15 minutes. This change will ensure that Channable properly utilizes the rate limits of the marketplace APIs and send your updates in bulk wherever possible.

    From then on the response message will be "Shipment update queued", and the order will get the pending_shipment status. Because we will queue the shipment update internally we will also not provide any marketplace specific feedback in this response. To check that the shipment has successfully been forwarded to the marketplace you can do a get request on /orders 15 minutes later to verify that the order status is no longer pending_shipment but instead shipped.

    Cancellation update

    Request

    $ curl 'https://api.channable.com/v1/companies/123/projects/456/orders/7/cancel' \
        -H 'authorization: Bearer api_key' \
        -H 'content-type: application/json' \
        -X POST
    
    updateOrderShipment :: Int -> IO (Response ByteString)
    updateOrderShipment id =
      postWith reqOptions ("https://api.channable.com/v1/companies/123/projects/456/orders/" <> show id <> "/cancel")
    
    def update_order_shipment():
      return requests.post("https://api.channable.com/v1/companies/123/projects/456/orders/7/cancel",
                           headers={'Authorization': "Bearer {}".format(api_token), 'Content-Type': 'application/json'},
                          )
    

    This endpoint allows you to update an order status and set it to cancelled. We will then propagate the update to the involved marketplaces.
    Note that this only covers the case where the seller wants to cancel an order. If the buyer cancels an order, the marketplace requires the seller to manually confirm this cancellation in the marketplace seller account.

    Request

    Method POST
    Endpoint /orders/:order_id/cancel
    Headers Content-Type: application/json
    Query parameters none

    Response

    Response

    {
      "status": "success",
      "message": "Processed 1 cancellation"
    }
    

    The response body is a JSON object containing the following fields:

    Field Type Value
    status String "success"
    message String "Processed 1 cancellation"

    NB! As of Monday June 28 09:00 the cancel endpoint will no longer immediately forward the cancellation update to the marketplace. Instead cancellation updates will be grouped within Channable and sent in bulk at a regular interval every 15 minutes. This change will ensure that Channable properly utilizes the rate limits of the marketplace APIs and send your updates in bulk wherever possible.

    From then on the response message will be "Cancellation update queued", and the order will get the pending_cancellation status. Because we will queue the cancellation update internally we will also not provide any marketplace specific feedback in this response. To check that the cancellation has successfully been forwarded to the marketplace you can do a get request on /orders 15 minutes later to verify that the order status is no longer pending_cancellation but instead cancelled.

    Offers

    Get all offers

    Request

    $ curl 'https://api.channable.com/v1/companies/123/projects/456/offers?limit=3' \
        -H 'authorization: Bearer api_key'
    
    getAllOffers :: IO ()
    getAllOffers =
      getWith reqOptions ("https://api.channable.com/v1/companies/123/projects/456/offers?limit=3")
        where reqOptions = defaults & header "Authorization" .~ ["Bearer " <> apiToken]
    
    def get_all_offers():
      return requests.get("https://api.channable.com/v1/companies/123/projects/456/offers",
                          headers={'Authorization': "Bearer {}".format(api_token)},
                         ).json()
    

    Response

    {
        "offers": [
            {
                "stock": 9,
                "created": "2017-09-18T18:42:22.082110",
                "price": "16.50",
                "id": 13,
                "client_id": "28996383",
                "available": true,
                "stock_tracking": true,
                "project_id": 6496,
                "modified": "2017-09-18T18:42:22.082116",
                "title": "A Deeper Understanding",
                "gtin": "57492975777038"
            },
            {
                "stock": 9,
                "created": "2017-09-18T18:42:22.076744",
                "price": "9.99",
                "id": 12,
                "client_id": "28996382",
                "available": true,
                "stock_tracking": true,
                "project_id": 6496,
                "modified": "2017-09-18T18:42:22.076755",
                "title": "Medicine Music",
                "gtin": "8459297524058"
            },
            {
                "stock": 9,
                "created": "2017-09-18T18:06:04.649664",
                "price": "12.99",
                "id": 11,
                "client_id": "28996383",
                "available": true,
                "stock_tracking": true,
                "project_id": 6496,
                "modified": "2017-09-18T18:06:04.649671",
                "title": "Smoke Ring for my Halo",
                "gtin": "635964745195"
            }
        ],
        "total": 9
    }
    

    This endpoint retrieves all offers for a given project of your company, sorted from most recent to oldest. Offers are the product updates that you posted. These are found under "Orders" > "Product updates" in the Channable tool.
    Optionally, you can pass query parameters to customize the query and response content.

    Request

    Method GET
    Endpoint /offers
    Headers none
    Query parameters see below

    Query parameters

    Parameter Type Default Description
    offset Int 0 Defines the starting position
    limit Int 15, max = 100 The number of offers to retrieve after offset.
    search String empty Search term used to filter the offers list.
    start_date String empty Search for offers with a creation date after this date, in format YYYY-MM-DD
    end_date String empty Search for offers with a creation date before this date, in format YYYY-MM-DD

    Example

    Suppose the following scenario for a given project of yours:

    Request parameters Response
    none {"offers": [o4, o3, o2, o1], "total": 4}
    ?limit=2 {"offers": [o4, o3], "total": 2}
    ?offset=4 {"offers": [], "total": 0}
    ?offset=1 {"offers": [o3, o2, o1], "total": 3}
    ?offset=1&limit=1 {"offers": [o3], "total": 1}
    ?offset=1&limit=2 {"offers": [o3, o2], "total": 2}
    ?search=t {"offers": [o4, o1], "total": 2}
    ?search=tE {"offers": [o3], "total": 1}
    ?start_date=2018-01-01 {"offers": [o4, o3, o2], "total": 3}
    ?start_date=2018-01-01&end_date=2018-02-28 {"offers": [o3, o2], "total": 2}
    ?last_modified_after=2018-02-29 {"offers": [o4], "total": 1}

    Response

    The response body is a JSON object containing the following fields:

    You can see at your right a real example of such return value.

    Update project offers

    Request

    $ curl 'https://api.channable.com/v1/companies/123/projects/456/offers' \
      -H 'authorization: Bearer api_key' \
      -H 'content-type: application/json' \
      -X POST -d '[{"id": "123", "title": "abc", "price": 123.45, "stock": 9}]'
    
    updateOffersStock =
      postWith reqOptions "https://api.channable.com/v1/companies/123/projects/456/offers" requestBody
        where
          reqOptions = defaults & header "Authorization" .~ ["Bearer " <> apiToken]
          requestBody = toJSON [object [ "id" .= ("123" :: String)
                                           , "title".= ("abc" :: String)
                                           , "price" .= (123.45 :: Double)
                                           , "stock" .= (9 :: Int)
                                           ]
                                   ]
    
    def update_offers_stock():
      request_body = json.dumps([{"id": "123",
                                  "title": "abc",
                                  "price": 123.45,
                                  "stock": 9
                                }])
      return requests.post("https://api.channable.com/v1/companies/123/projects/456/offers",
                           headers={'Authorization': "Bearer {}".format(api_token), 'Content-Type': 'application/json'},
                           data=request_body
                          ).json()
    

    This endpoint allows you to update the offers' stock of a selected project.
    We will then propagate the update to all the marketplaces of the targeted project.

    Request

    Method POST
    Endpoint offers
    Headers Content-Type: application/json
    Query parameters none
    Limit 50 offers per request

    Body

    Request body JSON schema

    {
      "type": "array",
      "items": {
        "type": "object",
        "required": ["id", "price", "stock", "title"],
        "properties": {
          "id": {"oneOf": [{"type": "string"}, {"type": "number"}]},
          "title": {"type": "string"},
          "price": {"type": "number", "minLength": 1},
          "discount_price": {"oneOf": [{"type": "number"}, {"type": "null"}]},
          "stock": {"type": "number"},
          "gtin": {"type": "string"}
        }
      }
    }
    

    The request body is expected to be a list of offers (max. 50 per request) with the following fields and types specification:

    Field Type Required
    id String / number
    title String
    price number
    discount_price number
    stock number
    gtin String

    Response

    Response

    {
        "status": "success",
        "message": "1 offers sent",
        "content": [
            {
                "id": "123",
                "status": "success",
                "message": "OK"
            }
        ]
    }
    

    The response body is a JSON object containing the following fields:

    Field Value
    status success
    message <number_of> offers sent (e.g., "3 offers sent")
    content list containing the result of updating each offer, per marketplace.

    Returns

    Get all returns

    Request

    $ curl 'https://api.channable.com/v1/companies/123/projects/456/returns?limit=1' \
        -H 'authorization: Bearer api_key'
    
    getAllOffers :: IO ()
    getAllOffers =
      getWith reqOptions ("https://api.channable.com/v1/companies/123/projects/456/returns?limit=1")
        where reqOptions = defaults & header "Authorization" .~ ["Bearer " <> apiToken]
    
    def get_all_offers():
      return requests.get("https://api.channable.com/v1/companies/123/projects/456/returns?limit=1",
                          headers={'Authorization': "Bearer {}".format(api_token)},
                         ).json()
    

    Response

    {
        "returns": [
            {
                "status": "new",
                "channel_name": "bol",
                "channel_id": "61284922",
                "channable_id": 151,
                "data": {
                    "item": {
                        "id": "11694321",
                        "order_id": "4522232111",
                        "gtin": "0884500642113",
                        "title": "Nike Air Force 1 Winter Premium GS Flax Pack",
                        "quantity": 1,
                        "reason": "Anders, namelijk:",
                        "comment": "De schoenen vielen te groot."
                    },
                    "customer": {
                        "gender": "male",
                        "first_name": "Jans",
                        "last_name": "Van Janssen",
                        "email": "2ixee2337ca74m23423uu@verkopen.bol.com"
                    },
                    "address": {
                        "first_name": "Jans",
                        "last_name": "Van Janssen",
                        "email": "2ixee2337ca74m23423uu@verkopen.bol.com",
                        "street": "Teststraat",
                        "house_number": 12,
                        "address1": "Teststraat 12",
                        "adderss2": "",
                        "city": "Utrecht",
                        "country_code": "NL",
                        "zip_code": "1234 XZ"
                    }
                }
            }
        ],
        "total": 9
    }
    

    This endpoint retrieves all returns for a given project of your company, sorted from most recent to oldest.
    Optionally, you can pass query parameters to customize the query and response content.

    Request

    Method GET
    Endpoint /returns
    Headers none
    Query parameters see below

    Query parameters

    Parameter Type Default Description
    offset Int 0 Defines the starting position
    limit Int 15, max = 100 The number of returns to retrieve after offset.
    search String empty Search term used to filter the returns list.
    start_date String empty Search for returns with a creation date after this date, in format YYYY-MM-DD
    end_date String empty Search for returns with a creation date before this date, in format YYYY-MM-DD
    last_modified_after String empty Search for returns that have been modified date after this date, in format YYYY-MM-DD
    last_modified_before String empty Search for returns that have been modified date before this date, in format YYYY-MM-DD

    Example

    Suppose the following scenario for a given project of yours:

    Request parameters Response
    none {"returns": [o4, o3, o2, o1], "total": 4}
    ?limit=2 {"returns": [o4, o3], "total": 2}
    ?offset=4 {"returns": [], "total": 0}
    ?offset=1 {"returns": [o3, o2, o1], "total": 3}
    ?offset=1&limit=1 {"returns": [o3], "total": 1}
    ?offset=1&limit=2 {"returns": [o3, o2], "total": 2}
    ?search=t {"returns": [o4, o1], "total": 2}
    ?search=tE {"returns": [o3], "total": 1}
    ?start_date=2018-01-01 {"returns": [o4, o3, o2], "total": 3}
    ?start_date=2018-01-01&end_date=2018-02-28 {"returns": [o3, o2], "total": 2}
    ?last_modified_after=2018-02-29 {"returns": [o4], "total": 1}

    Return status update

    Request

    $ curl 'https://api.channable.com/v1/companies/123/projects/456/returns/7/status' \
        -H 'authorization: Bearer api_key' \
        -H 'content-type: application/json' \
        -X POST -d '{"status": "accepted"}'
    
    updateReturnStatus:: Int -> IO (Response ByteString)
    updateReturnStatus id =
      postWith reqOptions ("https://api.channable.com/v1/companies/123/projects/456/returns/" <> show id <> "/status") reqBody
        where reqBody = object ["status" .= ("accepted" :: String)]
    
    def update_return_status():
      request_body = json.dumps({'status': 'accepted'})
      return requests.post("https://api.channable.com/v1/companies/123/projects/456/returns/7/status",
                           headers={'Authorization': "Bearer {}".format(api_token), 'Content-Type': 'application/json'},
                           data=request_body
                          )
    

    This endpoint allows you to update a return status. We will then propagate the update to the involved marketplaces.
    Depending of the return status the end customer does or does not get their money back.

    Request

    Method POST
    Endpoint /returns/:return_id/status
    Headers Content-Type: application/json
    Query parameters none

    The return_id is the Channable id of the return.

    Request body scheme

    {
        "type": "object",
        "required": ["status"],
        "properties": {
            "status": {"type": "string", "minLength": 1, "enum": ["accepted", "rejected", "repaired", "keeps", "exchanged", "cancelled"]},
        }
    }
    

    Body

    We expect a JSON body containing the required shipment information:

    Field Type Required
    status String

    Possible values for status

    Value Description
    accepted Accept the return, the end customer gets their money back.
    rejected Reject the return, the end customer does not get their money back.
    repaired The product is repaired and send back to the customer, they do not get their money back.
    keeps The customer keeps the product but does get their money back.
    exchanged A different product is sent to the customer, they do not get their money back.
    cancelled The return is cancelled, the end customer does not get their money back.

    Response

    Response

    {
      "status": "success",
      "message": "Processed 1 return status update"
    }
    

    The response body is a JSON object containing the following fields:

    Field Type Value
    status String "success"
    message String "Processed 1 return status updates"

    Sandbox Testing

    The channable api sandbox lets you test your order integration with the channable api in a safe environment without sending requests to the marketplaces you want to connect to. All data is mocked but the structure of the data and api calls needed to manage your orders are the same no matter which marketplace you end up connecting with.

    Setup

    To setup a test environment with the Channable Sandbox you must first make a connection with the Sandbox. This can be done in the same way as all other conenctions within Channable by going to user_name -> connections and clicking on "+ Add new connection".

    Select "Channable Sandbox", give the connection a label and click on the blue "Create" button. No further information is needed.

    Once you have made a connection, navigate to the project you want to use and click on "Setup" -> "Setup orders" > "+ Setup orders".

    From here make an order connection between the "Channable API" and the "Channable Sandbox" and activate the order connection. You are now ready to start testing.

    Orders

    To create a test order, navigate to "Setup" -> "Setup orders" -> "Settings" of the Channable Sandbox order connection. From here click on the gear in the top right and "Send test order".

    In the popup fill in an item id and click the blue "Send test order" button. A test order has now been created and you can use all request methods described in "Orders" above to interact with it.

    Shipments/Cancellations

    Once one or more testorders have been created, use the request methods described in "Shipment update" and "Cancellation update" under "Orders" above to mark an order as "shipped" and to give it track & trace information or to mark the order as "cancelled".

    Offers

    To test sending offer updates, that is updating the stock value on all marketplaces that you have an order connection to, you can use the request methods described in "Offers" above. You do not need to have created a test order to test this as you can update the stock value of any product in your store, also products that you have not received an order for through Channable.

    Returns

    Returns come from the marketplaces you will connect to. In order to test this, navigate to "Setup" -> "Setup orders" -> "Settings" of the Channable Sandbox order connection. From here click on the gear in the top right and "Send test return".

    In the popup fill in a Channable order id and click the blue "Send test return" button. A test return has now been created for the order id you filled in. You can now use all request methods described in "Returns" above to handle the return.

    When you are done testing

    Once you are satisfied with your Channable api setup and want to go live, contact our support department and they will help you setup the order connections.

    Errors

    The response body in this case will be:

    {
      "status": "error",
      "message": "explanation of what went wrong"
    }
    

    The exception to the rule is the 401, whose body is:

    {
      "error": "explanation of what went wrong",
      "data": null
    }
    

    The Channable API uses the following error codes:

    Code Meaning Reason
    400 Bad Request The server cannot or will not process the request due to something that is perceived to be a client error (e.g., malformed request syntax, invalid request message framing, or deceptive request routing)
    401 Unauthorized You are using an invalid or already revoked API token.
    403 Forbidden You are not allowed to access the resources your request targets.
    404 Not Found The targeted resources could not be found.
    405 Method Not Allowed You tried to access an endpoint with an invalid HTTP method.
    406 Not Acceptable You requested a format that isn't json.
    409 Conflict Your request could not be completed due to a conflict with the current state of the target resource.
    422 Unprocessable Entity We could understand the content type of the request entity, but we are unable to process the contained instructions.
    429 Too Many Requests You have sent too many requests in a given amount of time, thus exceeding our rate-limiting policy. Please try again later.
    500 Internal Server Error We had a problem with our server. Please try again later.
    503 Service Unavailable We are temporarily offline for maintenance. Please try again later.

    Transporters

    You should always use one of the standardized transporter codes when submitting an order shipment. There are a few ways to figure out which standardized transporter codes Channable supports. If your transporter is not supported already, please contact our support department so we can start supporting it.

    All standardized Transporter codes

    Request

    $ curl 'https://api.channable.com/v1/transporters/all'
    
    getAllTransporters =
       getWith reqOptions $ "https://api.channable.com/v1/transporters/all"
        where reqOptions = defaults
    
    def get_all_transporters():
        return requests.get(url='https://api.channable.com/v1/transporters/all').json()
    

    Response

    {
        "transporters": [
            {"transporter_code": "POSTNL", "transporter_label": "PostNL"},
            {"transporter_code": "OTHER", "transporter_label": "Other"},
            ...
        ]
    }
    

    This endpoint allows you to see all standardized transporter codes that Channable supports. We will ensure that the code is converted into the code the marketplace supports when marking your orders as shipped.

    Ensure to always use the value from the transporter_code field when sending a shipment update.

    Request

    Method GET
    Endpoint /transporters/all
    Headers none
    Query parameters none

    All transporters supported on a specific marketplace

    Request

    $ curl 'https://api.channable.com/v1/transporters/channel/bol'
    
    getMarketplaceTransporters =
       getWith reqOptions $ "https://api.channable.com/v1/transporters/channel/bol"
         where reqOptions = defaults
    
    def get_marketplace_transporters():
        return requests.get(url='https://api.channable.com/v1/transporters/channel/bol').json()
    

    Response

    {
        "transporters": [
            {"country": "ALL", "transporter_label": "PostNL"},
            {"country": "BE", "transporter_label": "FedEx Nederland"},
            {"country": "ALL", "transporter_label": "FedEx Belgiƫ"},
            ...
        ]
    }
    

    This endpoint allows you to see all standardized transporter codes that Channable supports for a given marketplace. Even if your desired transporter code is not supported by the marketplace you can still submit it in a shipment update. Channable will then use the marketplace code for the OTHER standardized transporter code.

    When determining which marketplace transporter to use Channable will look at the shipment country. If there is a country specific marketplace transporter then Channable will use that. If not we will use the transporter associated with the ALL country. It is common that a marketplace does not have many country specific transporters, but just have a single transporter that it wants us to use for all countries.

    The marketplace name must be one of:

    Request

    Method GET
    Endpoint /transporters/channel/{marketplace_name}
    Headers none
    Query parameters none
    URL parameter Marketplace name from the list above

    All marketplaces that are supported by a given transporter

    Request

    $ curl 'https://api.channable.com/v1/transporters/transporter/FEDEX'
    
    getMarketplaceTransporters =
       getWith reqOptions $ "https://api.channable.com/v1/transporters/transporter/FEDEX"
         where reqOptions = defaults
    
    def get_marketplace_transporters():
        return requests.get(url='https://api.channable.com/v1/transporters/transporter/FEDEX').json()
    

    Response

    {
        "channels": {
            "amazon": [
                {"country": "JP", "label": "FedEx Japan"},
                {"country": "ALL", "label": "FedEx"},
                ...
            ],
            "bol": [
                {"country": "ALL", "label": "FedEx Nederland"},
                {"country": "BE", "label": "FedEx Belgiƫ"},
            ],
            ...
        }
    }
    

    This endpoint allows you to see all marketplaces that are supported for a given standardized transporter code. Even if your desired marketplace is not supported by the standardized transporter you can still submit it in a shipment update. Channable will then use the marketplace code for the OTHER standardized transporter code.

    Request

    Method GET
    Endpoint /transporters/transporter/{transporter_code}
    Headers none
    Query parameters none
    URL parameter Transporter code from the /transporters/all endpoint