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)
API Reference
The documentation for the API has been moved. If you're not automatically redirected, you can click this link to go to the new documentation. The previous documentation is kept below for archival purposes.
Introduction
Welcome to the Channable API v1.
This version of the Channable API allows you to:
- Access orders
- Manage order shipments
- Update the stock of an offer on all marketplaces
- Access returns
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 the project ids you are interested in
- find your Channable company id
- generate an API token for your company
Find your project ids
To find the project ids you are interested in:
- Log in to the Channable app
- Select a project
- Open the project
Settings
- 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:
- Only owners of companies are able to generate an Api Token.
- Channable does not store your tokens. Please store them securely.
- In case you lose or leak your token, generate a new one. The previously generated tokens will be automatically revoked.
- The API Token is on a per-company basis and should be shared within your company. Do not generate a new API Token in order to provide one to another member of your company.
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:
- Request headers:
Authorization: Bearer your_api_token
- Request query string:
?access_token=your_api_token
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,
"transaction_id": "012345678",
"payment_details_iban": "NL012345677890795"
},
"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
, end_date
, last_modified_after
or last_modified_before
parameters please keep in mind that the we will interpret the provided dates to be in UTC timezone.
Currently it is not possible to provide more precise timestamps for these parameters.
The timestamps on the returned orders are compliant with ISO 8601 and have a UTC offset of +00:00
.
Example
Suppose the following scenario for a given project of yours:
- The project contains the orders
o1
,o2
,o3
, ando4
(from oldest to most recent) o3
contains some erroro1
ando4
are searchable by the termt
o3
is searchable by the termtE
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:
orders
: the selected list of orders, sorted on thecreated
timestamp.error_count
: the total number of orders containing errors within the selected project (ignores filters)total
: the total number of orders within the selected project (with filters applied)
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, # sum(p.commission for p in products).
"discount": 0, # sum(p.discounts for p in products).
"currency": "EUR",
"payment_method": "bol",
"shipping": 0, # sum(p.shipping for p in products).
"subtotal": 123.45, # sum(p.price * p.quantity for p in products).
"transaction_fee": 0, # transaction fee associated with payment method, if any.
"total": 123.45, # subtotal + shipping + transaction_fee - discount.
"transaction_id": "012345678",
"payment_details_iban": "NL012345677890795"
},
"products": [
{
"commission": 1.5, # commission, not per unit but for `quantity` units
"discount": 0, # discount per unit.
"delivery_period": "2017-08-02+02:00",
"ean": "9789062387410",
"id": "11693020",
"price": 61.725, # price per unit, including taxes.
"price_tax": 10.725, # per unit tax amount.
"quantity": 2,
"reference_code": "123",
"shipping": 0, # price not per unit but for `quantity` units, including taxes.
"shipping_tax": 0, # shipping price tax amount.
"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:
order
: The selected order informationevents
: The list of events of the selected order
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:
- Using the fields
street
,house_number
,house_number_ext
, andaddress_supplement
- Using the fields
address1
andaddress2
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": "Shipment update queued"
}
The response body is a JSON object containing the following fields:
Field | Type | Value |
---|---|---|
status |
String |
"success" |
message |
String |
"Shipment update queued" |
Shipment updates will be grouped within Channable and sent to the marketplaces in bulk at a
regular interval, every 15 minutes. Because we will queue the shipment update internally we will
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": "Cancellation update queued"
}
The response body is a JSON object containing the following fields:
Field | Type | Value |
---|---|---|
status |
String |
"success" |
message |
String |
"Cancellation update queued" |
Cancellation updates will be grouped within Channable and sent to the marketplaces in bulk at a
regular interval, every 15 minutes. Because we will queue the cancellation update internally we will
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:
- The project contains the offers
o1
,o2
,o3
, ando4
(from oldest to most recent) o1
ando4
are searchable by the termt
o3
is searchable by the termtE
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:
offers
: the selected list of offerstotal
: the total number of offer containing errors within the selected project
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:
- The project contains the returns
o1
,o2
,o3
, ando4
(from oldest to most recent) o1
ando4
are searchable by the termt
o3
is searchable by the termtE
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:
aliexpress
amazon
beslist
bol
cdiscount
channable_sandbox
check24
ebay
fnac
idealo
miinto
mirakl_carrefour
mirakl_conforama
mirakl_darty
mirakl_eprice
mirakl_fonq
mirakl_kleertjes
mirakl_nextail
mirakl_vidaxl
otto
real
spartoo
tobedressed
wish
zalando
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 |