Authentication
# With shell, you can just pass the correct header with each request
$ curl "api_endpoint_here" \
-H "X-ApiKey: YOUR_API_KEY"
CM Commerce provides three types of Authentication, each with its own use case. These are:
- API Key: For store owners or someone affiliated with a store owner (typically a developer). This is the fastest way to get access to a store’s data and actions;
- OAuth: For Partner Apps that wish to authenticate with their users' accounts in CM Commerce. This gives access to CM Commerce on behalf of one or more users.
- HMAC: For Partner Apps only. Used for accessing the Partner App API.
API Key Authentication
All paths should prefixed with https://commerce.campaignmonitor.com/api/v1 and will need the following HTTP header:
X-ApiKey: YOUR_API_KEY
OAuth
CM Commerce implements the Authorization Code Grant of the OAuth v2.0 standard, with PoP Tokens (Proof of Possession).
The standard is followed to spec, except for these cases:
- Access Tokens do not expire.
responseType
is not a required query parameter.accessType
is not a required query parameter.state
is required and must have a minimum length.
In general terms, the OAuth process involves a Partner App getting authorization from the User to access their account. When authorized, an Access Token must be requested from CM Commerce. This token can then be included in requests to CM Commerce to Authenticate & Authorize the App to access a User’s account.
This section has the following structure:
Terminology
- User: Someone who owns an account in CM Commerce, and on whose behalf the API is used;
- Partner App: An app, authorized by CM Commerce, that can access a User’s API when authorized by that User;
- Access Token: A 30 character string that, when included in an API request, Authenticates its associated User and Authorizes access according to it associated scopes.
- Scope: A string that represents permission to access a resource. Listed here.
- PoP Token: Proof of Possession Token. The only type of Access Token issued by CM Commerce.
- Code: A short-lived 60 character string that can be exchanged for an Access Token.
- Client Secret: A 60 character string that acts as password when Authenticating a Partner App and as symmetric key when signing a PoP Token.
- JWS: JSON Web Signature
Issuing Access Token
CM Commerce uses the Authorization Code Grant flow from OAuth 2.0 to issue Access Tokens. This involves three requests:
- Redirecting the User to CM Commerce, where they can authorize the Partner App
- CM Commerce redirects the User back to the Partner App, including a
code
- The Partner App exchanges the
code
for anaccess_token
These three steps are described in detail below.
Step 1: Get User Authorization
# EXAMPLE REQUEST
$ curl "https://commerce.campaignmonitor.com/oauth/authorize" \
-G \
-X GET \
-d client_id=57b5aa3b046abfb053d80b52 \
-d redirect_uri=https%3A%2F%2Fwww.partner-app.com%2Fcallback \
-d scope=read_webhook%20write_newsletter_email \
-d state=2034590283
To begin the OAuth flow, the User is redirected to CM Commerce’s authorize endpoint:
https://commerce.campaignmonitor.com/oauth/authorize
Including the following parameters in the URL query string:
Parameter | Details |
---|---|
client_id: | string |
The Partner App’s ID. Should be provided by CM Commerce. | |
redirect_uri: | string |
Where the User will be redirected. Must match the value saved for the Partner App. | |
scope: | string, optional |
The scopes that the App wishes to access on behalf of the User. Space separated values. | |
state: | string |
A random string, at least 10 characters long, that is unique. This will be passed on to redirect_uri when the User accepts or rejects the authorization, and should be validated to be the same. |
The User will be required to log-in to CM Commerce as needed.
If all required parameters are present and valid, the User can then review what permissions (scopes) are requested by which App. Conversely, any missing or invalid parameters will instead show an error page with a description of each problem.
At this point the User can Authorize or Reject the Authorization Request. When rejecting, the User will be redirected to redirect_uri
with the following parameters in the URL query string:
Parameter | Details |
---|---|
error: | string |
As per spec, will have the value “access_denied”. | |
error_description: | string |
Will be “The user refused to authorize your App.”. | |
state: | string |
Will be the same state value sent on the first request. |
This ends the OAuth flow prematurely.
Conversely, when the User authorizes the App, an authorization code
is generated that is bound to that User, the requested scopes and redirect_uri
. The User is then redirected to redirect_uri
with the code
in the query params, and the same state
that was received in the request, starting step 2.
Step 2: Receive Authorization Code
# EXAMPLE REQUEST
$ curl "https://www.partner-app.com/callback" \
-G \
-X GET \
-d code=f85b6db85f79310aee955bf104caa2dfcb50e6a565f8f8837f44c6ded306 \
-d state=2034590283
At this point the User has authorized the Partner App to access their account on their behalf. They will be redirected to redirect_uri
and the URL query will include the following parameters:
Parameter | Details |
---|---|
code: | string |
A 60 character hex string. This code represents the user’s authorization and must be used to get an Access Token in step 3. It is single-use and short-lived, expiring in 3 minutes. | |
state: | string |
Must be the same value sent in the first request. |
The state
param’s value should be validated. It is exactly the same value as was sent in the first step. This ensures that the User is coming from the original request, and that the code
isn’t from an earlier request intercepted by an attacker.
The code
can now be used to request an Access Token from CM Commerce.
Step 3: Exchange Code for Access Token
# EXAMPLE REQUEST
$ curl "https://commerce.campaignmonitor.com/oauth/access-token" \
-H "Authorization: Basic NTdiNWFhM2IwNDZhYmZiMDUzZDgwYjUyOndhdGNodS1sb29raW4tYXQ=" \
-H "Content-Type: application/x-www-form-urlencoded" \
-X POST \
-d code=f85b6db85f79310aee955bf104caa2dfcb50e6a565f8f8837f44c6ded306 \
-d redirect_uri=https%3A%2F%2Fwww.partner-app.com%2Fcallback
EXAMPLE RESPONSE
{
"access_token": "0aee955bf104caa2dfcb50e6a565f8",
"token_type": "pop"
}
To issue an access_token
, the received code
must be POST
ed to CM Commerce.
This request must use HTTPS and be Authenticated using Basic Access Authentication using the App’s ID as the username and the App’s Client Secret as password. Both are provided by CM Commerce.
The POST body must have the application/x-www-form-urlencoded
MIME type and the following parameters:
Parameter | Details |
---|---|
code: | string |
The 60 character code received in Step 2 of the flow. | |
redirect_uri: | string |
The Partner App’s redirect URI. It must be the exact same value as used to generate the code . |
If the App is successfully authenticated, the redirect_uri
matches the App and the code
is valid, CM Commerce will return a new access_token
in JSON:
Parameter | Details |
---|---|
access_token: | string |
A 30 character token bound to the User and requested Scopes. Must be kept secret. | |
token_type: | string |
Indicate’s the token’s type. Is “pop”. |
This access_token
can then be used to authorize any client when accessing the User’s data.
Authorizing Requests
As mentioned above, CM Commerce issues PoP Access Tokens. These add a layer of security on top of Bearer tokens, requiring Partner Apps to sign every request using their Client Secret.
Reading the Tokens draft & JWS RFC will provide deeper understanding of what’s going on, but to get started quickly we’d recommend using a lib for the language being used. Some examples:
- JavaScript ->
npm install jws
- Ruby ->
gem install jwt
If the language used isn’t listed, get in touch with us and we’ll help out finding an appropriate alternative.
Generating the PoP Token
# Ruby
require 'jwt'
client_secret = 'partner-app-client-secret'
payload = { at: 'pop-access-token', ts: Time.now.to_i }
pop_token = JWT.encode payload, client_secret, 'HS256'
# > "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJhdCI6IjEyMzQ1NiIsInRzIjoxMjM0NTY3OH0.J6DUAy9TopLOfIJHFbY2BNDankWID9ZvJ-ylHoV_a6k"
// JavaScript
const jws = require('jws');
const clientSecret = 'partner-app-client-secret';
const header = { typ: 'JWT', alg: 'HS256' };
const payload = { at: 'pop-access-token', ts: Math.floor(Date.now() / 1000) };
const popToken = jws.sign({ header, payload, secret: clientSecret });
// > 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdCI6IjEyMzQ1NiIsInRzIjoxMjM0NTY3OH0.Pocf1CzRha25nwCfoZynProYLcV1UE5SlcRGa3qzZXo'
As a first step, the Access Token received from CM Commerce must be included in an Object (Hash for Ruby) at the key at
. In that object, the current Unix time must be defined at the key ts
. This timestamp is validated by CM Commerce to be within 3 minutes of current time, in an effort to prevent header reuse by attackers.
This object is then signed with HMAC, generating 3 Base64 encoded strings, separated by a period. This is the PoP Token.
We support three HMAC algorithms, HS256
, HS384
and HS512
.
# Example authorized request
$ curl https://commerce.campaignmonitor.com/api/v1 \
-H "Authorization: PoP eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdCI6IjEyMzQ1NiIsInRzIjoxMjM0NTY3OH0.Pocf1CzRha25nwCfoZynProYLcV1UE5SlcRGa3qzZXo
The PoP Token can then be included in a request’s Authorization header, prefixed with the string “PoP”.
The server then decodes the PoP Token and validates the signature to both Authenticate the Partner App and Authorize its access to a User’s account through the included Access Token.
Scopes
Below is a table of the recognized scopes. Scopes that are prefixed with write_
provide read & write access, while those with the read_
prefix provide access to read-only endpoints.
read_webhook
/write_webhook
: These allow accessing the registered webhooks for a User’s account. Withwrite_
one can also register new webhooks & endpoints.read_newsletter_template
: Provides access to a User’s Newsletter Templates. This includes contents and sending stats.read_newsletter_email
: Provides access to the list of emails associated with a sent Newsletter Template. Includes action timestamps (sentAt, openedAt, etc.) and email address info.read_async_job
: Provides access to Async Jobs, their statuses and results.read_segments
/write_segments
: Access to Customer Segments.read_customers
/write_customers
: Allows reading customer and subscriber data. Write scope allows unsubscribing emails from lists.read_customer_list
/write_customer_list
: Provides access to customer lists.
HMAC
# Ruby
require 'jwt'
client_secret = 'partner-app-client-secret'
payload = { iss: '57b5aa3b046abfb053d80b52' }
pop_token = JWT.encode payload, client_secret, 'HS256'
# > "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiI1N2I1YWEzYjA0NmFiZmIwNTNkODBiNTIifQ.sxd8uG4EkeIXHsIIVELrfGTIZcaTFE9a9YY-8HGHuOQ"
// Javascript
const jws = require('jws');
const clientSecret = 'partner-app-client-secret';
const header = { typ: 'JWT', alg: 'HS256' };
const payload = { iss: '57b5aa3b046abfb053d80b52' };
const popToken = jws.sign({ header, payload, secret: clientSecret });
// > 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiI1N2I1YWEzYjA0NmFiZmIwNTNkODBiNTIifQ.sxd8uG4EkeIXHsIIVELrfGTIZcaTFE9a9YY-8HGHuOQ'
# Example authorized request
$ curl https://commerce.campaignmonitor.com/api/v1/partners/ \
-H "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiI1N2I1YWEzYjA0NmFiZmIwNTNkODBiNTIifQ.sxd8uG4EkeIXHsIIVELrfGTIZcaTFE9a9YY-8HGHuOQ
This authentication type is used exclusively when accessing the Partner Apps API because it authenticates a Partner only, not a User. It is used on signed JSON Web Tokens with a JSON Web Signature.
The token must have an iss
entry set to the Partner App’s ID. Then, it must be signed with the Client Secret and added to the Authorization header of each request, using the Bearer
schema.
All requests to the Partner Apps API must be authorized this way.
Coupons
Services related to retreiving coupons using the Coupons API
All Coupons
# DEFINITION
GET https://commerce.campaignmonitor.com/api/v1/coupons
# EXAMPLE REQUEST
$ curl "https://commerce.campaignmonitor.com/api/v1/coupons" \
-H "X-ApiKey: YOUR_API_KEY"
EXAMPLE RESPONSE
{
"meta":{
"_links":{
"next":"...",
"prev":"...",
"last":"...",
"first":"..."
}
},
"data":[
{
"upsellType":"coupon",
"active":true,
"title":"Lorem ipsum",
"freeShipping":true,
"amount":"10",
"couponCode":"55G2-DHM0-50NN"
},
{
"upsellType":"coupon",
"active":true,
"title":"Lorem ipsum",
"freeShipping":true,
"amount":"10",
"couponCode":"54G2-DHM0-50NL"
}
]
}
https://commerce.campaignmonitor.com/api/v1/coupons
Get a list of all coupons
Get a coupon
# DEFINITION
GET https://commerce.campaignmonitor.com/api/v1/coupons/1a2b3c4d
# EXAMPLE REQUEST
$ curl "https://commerce.campaignmonitor.com/api/v1/coupons/1a2b3c4d" \
-H "X-ApiKey: YOUR_API_KEY"
EXAMPLE RESPONSE
{
"upsellType":"coupon",
"active":true,
"title":"Lorem ipsum",
"freeShipping":true,
"amount":"10",
"couponCode":"55G2-DHM0-50NN"
}
https://commerce.campaignmonitor.com/api/v1/coupons/{COUPON_ID}
A single coupon with all of its details
Arguments
Argument | Details |
---|---|
id: | string, required |
String based id of the coupon supplied in the response from the send of a receipt. |
Delete a coupon
# DEFINITION
DELETE https://commerce.campaignmonitor.com/api/v1/coupons/1a2b3c4d
# EXAMPLE REQUEST
$ curl "https://commerce.campaignmonitor.com/api/v1/coupons/1a2b3c4d" \
-H "X-ApiKey: YOUR_API_KEY" \
-X DELETE
https://commerce.campaignmonitor.com/api/v1/coupons/{COUPON_ID}
Arguments
Argument | Details |
---|---|
id: | string, required |
String based id of the coupon supplied in the response from the send of a receipt. |
Mark coupon as used
# DEFINITION
PUT https://commerce.campaignmonitor.com/api/v1/coupons/1a2b3c4d/use
# EXAMPLE REQUEST
$ curl "https://commerce.campaignmonitor.com/api/v1/coupons/1a2b3c4d/use" \
-H "X-ApiKey: YOUR_API_KEY" \
-X PUT
-d '{
"reference": "1234",
"amount": 123,
"currency": "USD"
}'
EXAMPLE RESPONSE
{
"upsellType":"coupon",
"active":true,
"title":"Lorem ipsum",
"freeShipping":true,
"amount":"10",
"couponCode":"55G2-DHM0-50NN",
"usage": {
"usedAt": "1410788324",
"order": {
"reference": "1234",
"amount": 123,
"currency": "USD"
}
}
}
https://commerce.campaignmonitor.com/api/v1/coupons/{COUPON_ID}/use
Arguments
Argument | Details |
---|---|
id: | string, required |
String based id of the coupon supplied in the response from the send of a receipt. |
|
reference: | string, required |
String based id of the generated order by the coupon. |
|
amount: | float, required |
Amount of the generated order. | |
currency: | string, required |
Currency of the amount generated. |
Products
Services related to sending product updates using the Products API. These endpoints must be used to send updates about products for our reccommendation engine.
Create product
# EXAMPLE REQUEST (SINGLE PRODUCT)
$ curl "https://commerce.campaignmonitor.com/api/v1/products" \
-H "X-ApiKey: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-X POST \
-d '{
"product_id": "ipa_12345",
"type": "beer",
"brand": "StefanoS",
"title": "Refreshing IPA by StefanoS",
"description": "This IPA is awesome and brought to you by the legendary StefanoS",
"hidden": false,
"url": "https://stefanos.beer/products/ipa_12345",
"tags": [ "ipa", "beer", "refreshing" ],
"images": [
{ "position": 1, "url": "https://stefanos.beer/img/ipa_12345_1.jpg" },
{ "position": 2, "url": "https://stefanos.beer/img/ipa_hej.jpg" }
],
"variants": [
{ "price": 10.0 },
{ "price": 15.0 }
],
"categories": [
{
"category_id": "category_1234",
"title": "IPA category",
"description": "A category of good IPAs",
"url": "https://stefanos.beer/categories/category_1234"
}
]
}'
# EXAMPLE REQUEST (MULTIPLE PRODUCTS)
$ curl "https://commerce.campaignmonitor.com/api/v1/products" \
-H "X-ApiKey: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-X POST \
-d '[{
"product_id": "ipa_12345",
"type": "beer",
"brand": "StefanoS",
"title": "Refreshing IPA by StefanoS",
"description": "This IPA is awesome and brought to you by the legendary StefanoS",
"hidden": false,
"url": "https://stefanos.beer/products/ipa_12345",
"tags": [ "ipa", "beer", "refreshing" ],
"images": [
{ "position": 1, "url": "https://stefanos.beer/img/ipa_12345_1.jpg" },
{ "position": 2, "url": "https://stefanos.beer/img/ipa_hej.jpg" }
],
"variants": [
{ "price": 10.0 },
{ "price": 15.0 }
],
"categories": [
{
"category_id": "category_1234",
"title": "IPA category",
"description": "A category of good IPAs",
"url": "https://stefanos.beer/categories/category_1234"
}
]
},
{
"product_id": "wheat_54321",
"type": "beer",
"brand": "StefanoS",
"title": "Refreshing wheat beer by StefanoS",
"description": "This lovely golden wheat is perfect on a hot Summer day.",
"hidden": false,
"url": "https://stefanos.beer/products/wheat_54321",
"tags": [ "ipa", "beer", "refreshing" ],
"images": [
{ "position": 1, "url": "https://stefanos.beer/img/wheat_54321.jpg" }
],
"variants": [
{ "price": 5.0 }
],
"categories": [
{
"category_id": "category_4321",
"title": "Wheat category",
"description": "A category of good wheat beers",
"url": "https://stefanos.beer/categories/category_4321"
}
]
}]'
Create one or more new products [POST]
POST https://commerce.campaignmonitor.com/api/v1/products
Arguments
Main object (product)
Argument | Details |
---|---|
brand: | string, optional |
The brand or vendor for the product, e.g. ‘Apple’. | |
categories: | category array, optional |
A list of categories that the product belong to. | |
description: | string, optional |
The description of the product, for example ‘A very nice soccer ball, updated for the needs of 2014.’. | |
hidden: | boolean, optional |
Indicates whether the product is currently hidden. | |
images: | image array, optional |
A list of images for the product. | |
product_id: | string, required |
The ID of the product. | |
tags: | string array, optional |
A list of tags for the product. | |
title: | string, optional |
The title of the product, for example ‘Soccer Ball 2014’. | |
type: | string, optional |
The type of the product, e.g. ‘ball’. | |
url: | string, optional |
The full or relative url for the product, for example ‘http://example.com/products/soccer-ball-2014’. | |
variants: | variant array, optional |
A list of product variants. The price of a product is always stored in a variant. |
Category
Argument | Details |
---|---|
category_id: | string, required |
The ID of the category. | |
description: | string, optional |
The description of the category. | |
title: | string, optional |
The title of the category, for example ‘Soccer Balls’ | |
url: | string, optional |
The full or relative url for the category, for example ‘http://example.com/category/soccer-balls’. |
Image
Argument | Details |
---|---|
position: | integer, optional |
The position of the image compared to other images for the same product. | |
url: | string, required |
The url where the image is stored. |
Variant
Argument | Details |
---|---|
price: | float, required |
The price of the variant. |
All products are valid.
Some products are valid and some are invalid. The response specifies which products failed to validate.
Valid products are accepted and processed and should not be re-uploaded.
Update product
# EXAMPLE REQUEST
$ curl "https://commerce.campaignmonitor.com/api/v1/products/{product_id}" \
-H "X-ApiKey: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-X PUT \
-d '{
"product_id": "ipa_12345",
"type": "beer",
"brand": "StefanoS",
"title": "Refreshing IPA by StefanoS",
"description": "This IPA is awesome and brought to you by the legendary StefanoS",
"hidden": false,
"url": "https://stefanos.beer/products/ipa_12345",
"tags": [ "ipa", "beer", "refreshing" ],
"images": [
{ "position": 1, "url": "https://stefanos.beer/img/ipa_12345_1.jpg" },
{ "position": 2, "url": "https://stefanos.beer/img/ipa_hej.jpg" }
],
"variants": [
{ "price": 10.0 },
{ "price": 15.0 }
],
"categories": [
{
"category_id": "category_1234",
"title": "IPA category",
"description": "A category of good IPAs",
"url": "https://stefanos.beer/categories/category_1234"
}
]
}'
Update a product [PUT]
PUT https://commerce.campaignmonitor.com/api/v1/products/{product_id}
Arguments
Delete product
Delete a product [DELETE]
# EXAMPLE REQUEST
$ curl "https://commerce.campaignmonitor.com/api/v1/products/{product_id}" \
-H "X-ApiKey: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-X DELETE'
DELETE https://commerce.campaignmonitor.com/api/v1/products/{product_id}
Product Reviews
Endpoints for getting data on existing Product Reviews from Customers.
List Product Reviews
Returns reviews from the most recently created.
# EXAMPLE REQUEST
$ curl "https://commerce.campaignmonitor.com/api/v1/product-reviews?limit=2&page=2" \
-H "X-ApiKey: YOUR_API_KEY" \
-H "Accept: application/json"
EXAMPLE RESPONSE
{
"data": [
{
"id": "57b5aa3b046abfb053d80b52",
"title": "Awesome!",
"comment": "Taste and texture were all on point! ++ would buy again.",
"rating": 5,
"productId": "123456789",
"customerEmail": "happyguy@email.com",
"archived": false,
"visible": true,
"isTest": false,
"createdAt": "2016-11-30T10:20:00.000Z"
},
{
"id": "57b5aa3b046abfb053d80b59",
"title": "Quality Stuff",
"comment": "Product was great, but shipping took two weeks.",
"rating": 4,
"productId": "65443335",
"customerEmail": "mehdude@email.com",
"archived": false,
"visible": false,
"isTest": false,
"createdAt": "2016-11-20T00:00:00.000Z"
}
],
"meta": {
"page": 2,
"pages": 3,
"total": 5,
"limit": 2,
"prevPage": "https://commerce.campaignmonitor.com/api/v1/product-reviews?page=1&limit=2",
"nextPage": "https://commerce.campaignmonitor.com/api/v1/product-reviews?page=3&limit=2"
}
}
List all Product Reviews [GET]
https://commerce.campaignmonitor.com/api/v1/product-reviews
OAuth Scopes: read_product_reviews, write_product_reviews
Query Parameters
Argument | Details |
---|---|
limit: | number, optional |
How many Reviews to return. Default is 10. | |
page: | number, optional |
Which page of Reviews to return. Default is 1. | |
visible: | bool, optional |
Whether to return only visible (those that may appear in widgets) reviews or invisible. | |
archived: | bool, optional |
Whether to return reviews that have been archived by the user. Default is false . Pass an empty string to disable this filter. |
|
test: | bool, optional |
Whether to return reviews created from test followups. Default is false . Pass an empty string to disable this filter. |
Response Body
Then endpoint returns an object with a data
key that is an Array with limit
Product Reviews in the shop, sorted by created date (recent on top). It also returns a meta
key with paging info.
Each Product Review object includes the following info:
Key | Details |
---|---|
id: | string |
The Product Review’s ID. Currently unused. | |
title: | string, optional |
The Review’s title. | |
comment: | string, optional |
The Review’s long comment. | |
rating: | number, optional |
The Review’s rating, 1 to 5. | |
productId: | string |
The ID of the Product that was reviewed. | |
customerEmail: | string, optional |
The email address of the reviewer. May not exist when anonymous. | |
createdAt: | string, optional |
When the Product Review was created. Is an ISO 8601 encoded date. | |
archived: | bool |
A User may Archive a Product Review as a soft delete. Archived reviews aren’t shown in Widgets. | |
visible: | bool |
Only visible reviews can appear in Widgets. |
|
isTest: | bool |
Whether this Review was created from a test FollowUp Email. |
Abandoned Carts
Services related to managing Abandoned Carts. For the Email Campaigns triggered by Abandoned Carts, go to the next section.
Create
# EXAMPLE REQUEST
$ curl "https://commerce.campaignmonitor.com/api/v1/abandoned-carts" \
-H "X-ApiKey: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
-X POST \
-d '{
"token":"sometoken",
"customer":"some@customer.com",
"currency":"USD",
"amount":"39.99",
"items":[
{
"reference":"rct",
"variant":"rct2",
"description":"CM Commerce subscription",
"quantity":1,
"amount":"39.99",
"attributes":[{ "key": "foo", "value": "bar" }]
}
]
}'
RESPONSE
{}
Create or Update a Cart [POST]
https://commerce.campaignmonitor.com/api/v1/abandoned-carts
Arguments
Argument | Details |
---|---|
token: | string, required |
Cart token or unique identifier | |
customer: | string, required |
Email address of the customer to whom the cart belongs | |
currency: | string, required |
ISO 4217 currency code | |
amount: | number, required |
The total amount for the cart | |
items: | [object], required, see below |
The items inside the cart. |
Items
The items inside the cart. Object contents:
Key | Details | Example |
---|---|---|
reference: | string, optional | c_123 |
variant: | string, optional | c_123_2 |
description: | string, required | CM Commerce Hobby Plan |
quantity: | number, optional | 1 |
amount: | number, required | 29.00 |
A single item’s price. | ||
attributes: | [object], optional | [{ key: “foo”, value: “bar” }] |
Item attributes. See below. |
Attributes
Item attributes:
Key | Details |
---|---|
key: | string, required |
value: | string, required |
Delete an abandoned cart
# DEFINITION
DELETE https://commerce.campaignmonitor.com/api/v1/abandoned-carts/1a2b3c4d
# EXAMPLE REQUEST
$ curl "https://commerce.campaignmonitor.com/api/v1/abandoned-carts/1a2b3c4d" \
-H "X-ApiKey: YOUR_API_KEY" \
-X DELETE
https://commerce.campaignmonitor.com/api/v1/abandoned-carts/{TOKEN}
Arguments
Argument | Details |
---|---|
token: | string, required |
Cart token or unique identifier. |
Abandoned Cart Emails
Endpoints for viewing data of emails triggered by abandoned carts, and their customer interaction stats. For the Abandoned Carts themselves, go to the previous section.
List Abandoned Cart Campaigns
Returns a list of the Abandoned Cart Campaigns in the User’s account.
# EXAMPLE REQUEST
$ curl "https://commerce.campaignmonitor.com/api/v1/abandoned-carts/campaigns \
-H "X-ApiKey: YOUR_API_KEY" \
-H "Accept: application/json"
EXAMPLE RESPONSE
{
"data": [
{
"id": "57b5aa3b046abfb053d80b52",
"title": "Campaign for Potential Customers",
"customerSegmentId": "57b5aa3b046abfb053d80b59",
"enabled": true,
"abandonedCartTemplates": [
{ "id": "57b5aa3b046abfb053d76b79" }
]
},
{
"id": "57b5aa3b046abfb053d80b59",
"title": "Entice Existing Customers",
"enabled": true,
"abandonedCartTemplates": [
{ "id": "57b5aa3b046abfb053d76b81" },
{ "id": "57b5aa3b046abfb053d76b56" }
]
}
]
}
List all Campaigns [GET]
https://commerce.campaignmonitor.com/api/v1/abandoned-carts/campaigns
OAuth Scopes: read_abandoned_cart_campaign, write_abandoned_cart_campaign
Response Body
Then endpoint returns an object with a data
key that is an Array with all the campaigns in the shop.
Each Campaign object includes the following info:
Key | Details |
---|---|
id: | string |
The Campaigns’s ID. Use it when calling single-campaign endpoints. | |
title: | string |
The Campaign’s title, set by the User. | |
customerSegmentId: | string, optional |
The ID of the Campaign’s Customer Segment, if one was selected. | |
enabled | boolean |
Whether the Campaign is enabled, i.e. sending emails to customers. | |
abandonedCartTemplates: | array[object] |
A collection of all the Abandoned Cart Templates that belong to the campaign. Includes only the IDs of the templates. |
View Abandoned Cart Campaign
Returns an Abandoned Cart Campaign in the User’s account.
# EXAMPLE REQUEST
$ curl "https://commerce.campaignmonitor.com/api/v1/abandoned-carts/campaigns/57b5aa3b046abfb053d80b52 \
-H "X-ApiKey: YOUR_API_KEY" \
-H "Accept: application/json"
EXAMPLE RESPONSE
{
"data": {
"id": "57b5aa3b046abfb053d80b52",
"title": "Campaign for Potential Customers",
"customerSegmentId": "57b5aa3b046abfb053d80b59",
"enabled": true,
"abandonedCartTemplates": [
{ "id": "57b5aa3b046abfb053d76b81" },
{ "id": "57b5aa3b046abfb053d76b56" }
]
}
}
Show Single Campaign [GET]
https://commerce.campaignmonitor.com/api/v1/abandoned-carts/campaigns/{CAMPAIGN_ID}
OAuth Scopes: read_abandoned_cart_campaign, write_abandoned_cart_campaign
Response Body
Then endpoint returns an object with a data
key that is the Campaign for the ID. The Campaign has the following info:
Key | Details |
---|---|
id: | string |
The Campaigns’s ID. Use it when calling single-campaign endpoints. | |
title: | string |
The Campaign’s title, set by the User. | |
customerSegmentId: | string, optional |
The ID of the Campaign’s Customer Segment, if one was selected. | |
enabled | boolean |
Whether the Campaign is enabled, i.e. sending emails to customers. | |
abandonedCartTemplates: | array[object] |
A collection of all the Abandoned Cart Templates that belong to the campaign. Includes only the IDs of the templates. |
View Abandoned Cart Template
Returns an Abandoned Cart Template. This contains the basis for the content of each of its emails, as well as the trigger delay for an email to be sent.
# EXAMPLE REQUEST
$ curl "https://commerce.campaignmonitor.com/api/v1/abandoned-carts/templates/57b5aa3b046abfb053d80b52 \
-H "X-ApiKey: YOUR_API_KEY" \
-H "Accept: application/json"
EXAMPLE RESPONSE
{
"data": {
"id": "57b5aa3b046abfb053d80b68",
"campaignId": "57b5aa3b046abfb053d89b69",
"subject": "You forgot some goodies!",
"title": "First Abandoned Cart Email",
"rule": "delay",
"period": 0,
"periodHours": 1,
"contentModules": []
}
}
Show Abandoned Cart Template [GET]
Two possible endpoints:
https://commerce.campaignmonitor.com/api/v1/abandoned-carts/templates/{TEMPLATE_ID}
https://commerce.campaignmonitor.com/api/v1/abandoned-carts/campaigns/{CAMPAIGN_ID}/templates/{TEMPLATE_ID}
OAuth Scopes: read_abandoned_cart_template, write_abandoned_cart_template
Response Body
The response body includes a data
key which contains an object representation of the requested abandoned cart template:
Key | Details |
---|---|
id: | string |
The ID for this email. | |
campaignId: | string |
The ID for this template"s Campaign. | |
subject: | string |
The subject used in the emails triggered by this template. May have user variables (e.g.: {firstName}). | |
title: | string |
The template"s title, not customer-facing. | |
rule: | string |
Rule for triggering an email from this template. Currently only “delay” is supported, which is a delay from a cart being abandoned. | |
period: | number |
How many days must pass after the cart is abandoned to trigger an email from this template. Accumulates with periodHours . |
|
periodHours: | string |
How many hours must pass after the cart is abandoned to trigger an email from this template. Accumulates with period . |
|
contentModules: | array |
An array of the Content Modules in a template. These represent the content (copy, images, discounts, etc.) in a template. More details below. |
Content Modules
{
"contentModules": [
{
"position": 0,
"type": "text",
"context": {
"body": "You left all of this stuff behind!"
}
},
{
"position": 1,
"type": "abandonedCart",
"context": {
"quantity": "Quantity",
"description": "Description",
"unitPrice": "Unit Price",
"total": "Total",
"totalOrder": "Total",
"buttonColor": "#1990FF",
"primaryColor": "#1990FF",
"secondaryColor": "#0076E4",
"mainBackgroundColor": "#f8f8f8",
"actionTitle": "Get All My Stuff!"
}
},
{
"position": 2,
"type": "discountCoupon",
"context": {
"title": "You Stuff, At a Discount!",
"description": "{{firstName}}, there"s no way you"re leaving all that stuff in the cart. Grab this discount and check-out now!",
"actionTitle": "Start Shopping",
"expiresOn": "Expires On",
"primaryColor": "#1990FF",
"amount": 10,
"expiryPeriod": 1,
"emailLimit": false,
"couponType": 2,
"backgroundColor": "#f8f8f8",
"onePerUser": true,
"usageLimit": 1
}
}
]
}
Each module has a position
and a type
. The 0-based position indicates where in the email the module appears (starting from top) and the type defines the kind of content.
The module"s content is in the context
key. This object and its keys depend on the module"s type. These map directly to the content & settings in the Template editor, so we recommend referencing that in case any values are unclear.
View Abandoned Cart Email
Returns an Abandoned Cart Email, along with its action timestamps.
# EXAMPLE REQUEST
$ curl "https://commerce.campaignmonitor.com/api/v1/abandoned-carts/emails/57b5aa3b046abfb053d80b52 \
-H "X-ApiKey: YOUR_API_KEY" \
-H "Accept: application/json"
EXAMPLE RESPONSE
{
"data": {
"id": "57b5aa3b046abfb053d80b68",
"templateId": "57b5aa3b046abfb053d89b69",
"abandonedCartId": "57b5aa3b046abf1253d89b33",
"to": "a@customer.com",
"status": "sent",
"sentAt": "2016-11-20T00:00:00.000Z",
"deliveredAt": "2016-11-20T00:00:15.000Z",
"openedAt": "2016-11-20T01:00:00.000Z",
"clickedAt": "2016-11-20T01:01:10.000Z"
}
}
Show Abandoned Cart Email [GET]
Three possible endpoints:
https://commerce.campaignmonitor.com/api/v1/abandoned-carts/emails/{EMAIL_ID}
https://commerce.campaignmonitor.com/api/v1/abandoned-carts/templates/{TEMPLATE_ID}/emails/{EMAIL_ID}
https://commerce.campaignmonitor.com/api/v1/abandoned-carts/campaigns/{CAMPAIGN_ID}/templates/{TEMPLATE_ID}/emails/{EMAIL_ID}
OAuth Scopes: read_abandoned_cart_email, write_abandoned_cart_email
Response Body
The response body includes a data
key which contains an object representation of the requested abandoned cart email:
Key | Details |
---|---|
id: | string |
The ID for this email. | |
templateId: | string |
The ID for this email’s template. | |
abandonedCartId: | string |
The ID for the cart that triggered this email. | |
to: | string |
The email address to which this email was sent. | |
status: | string |
The email’s status. One of “created”, “rendered”, “rejected” or “sent”. | |
sentAt: | string |
When this email was sent. ISO 8601 encoded date. | |
deliveredAt: | string, optional |
When this email was delivered. ISO 8601 encoded date. Can be missing if not delivered. | |
openedAt: | string, optional |
When this email was last opened. ISO 8601 encoded date. Can be missing if never delivered. | |
clickedAt: | string, optional |
When this email was last clicked. ISO 8601 encoded date. Can be missing if no link was ever clicked. |
Users
Services related to users using the Users API.
Current user
# EXAMPLE REQUEST
$ curl "https://commerce.campaignmonitor.com/api/v1/users/current" \
-H "X-ApiKey: YOUR_API_KEY"
EXAMPLE RESPONSE
{
"publicKey": "abcd",
"name": {
"firstName": "John",
"lastName": "Doe"
}
}
Retrieve user information [GET]
https://commerce.campaignmonitor.com/api/v1/users/current
The current API user.
Uninstall
# EXAMPLE REQUEST
$ curl "https://commerce.campaignmonitor.com/api/v1/users/uninstall" \
-X POST \
-H "X-ApiKey: YOUR_API_KEY"
Uninstall the user/app [POST]
https://commerce.campaignmonitor.com/api/v1/users/uninstall
Tells CM Commerce to uninstall the user associated with the given API key. Among other things, this automatically unsubscribes the user from a premium plan.
Newsletters
Endpoints for getting data on existing Newsletters for a shop, and their emails.
List Newsletters
Returns all the Newsletters created in a shop.
# EXAMPLE REQUEST
$ curl "https://commerce.campaignmonitor.com/api/v1/newsletters?limit=2&page=2" \
-H "X-ApiKey: YOUR_API_KEY" \
-H "Accept: application/json"
EXAMPLE RESPONSE
{
"data": [
{
"id": "57b5aa3b046abfb053d80b59",
"title": "4th of July Sales",
"liveAt": "2017-07-03:00:00.000Z",
"audience": {
"lists": ["marketing", "57b5aa3b046abfb053d80b52"],
"customerSegmentKind": "id",
"customerSegmentId": "57b5aa3b046abfb053d80b57",
"customerSegmentInline": { "criteria": [] }
},
"customerLists": [],
"customerSegments": [],
"status": "scheduled"
},
{
"id": "57b5aa3b046abfb053d80b52",
"title": "Black Friday Announcement",
"liveAt": "2016-11-20T00:00:00.000Z",
"customerLists": ["57b5aa3b046abfb053d80b53"],
"customerSegments": ["57b5aa3b046abfb053d80b57"],
"status": "sent",
"sentAt": "2016-11-20T00:10:15.000Z"
}
],
"meta": {
"page": 2,
"pages": 3,
"total": 5,
"limit": 2,
"prevPage": "https://commerce.campaignmonitor.com/api/v1/newsletters?page=1&limit=2",
"nextPage": "https://commerce.campaignmonitor.com/api/v1/newsletters?page=3&limit=2"
}
}
List all Newsletters [GET]
https://commerce.campaignmonitor.com/api/v1/newsletters
OAuth Scopes: read_newsletter_template, write_newsletter_template
Query Parameters
Argument | Details |
---|---|
limit: | number, optional |
How many Newsletters to return. Default is 10. | |
page: | number, optional |
Which page of Newsletters to return. Default is 1. | |
status: | string, optional |
Filters the Newsletters by status. Can be sent , scheduled , live or draft . |
Response Body
Then endpoint returns an object with a data
key that is an Array with limit
newsletters in the shop. The Newsletters are sorted by sentAt
descending, liveAt
ascending and createdAt
descending. It also returns a meta
key with paging info.
Each newsletter object includes the following info:
Key | Details |
---|---|
id: | string |
The Newsletter’s ID. Use it when calling single-newsletter endpoints. | |
title: | string |
The Newsletter’s title. | |
audience | object |
The Newsletter’s audience. Can be comprised of one or more lists, a segment ID or an inline segment. See below. | |
liveAt: | string, optional |
When the Newsletter is set to go live, if scheduled, or when it was set live, if sent or live. Is an ISO 8601 encoded date. Is omitted if the Newsletter is a draft. | |
status | string |
The Newsletter’s current status. Must be one of “draft”, “scheduled”, “live”, or “sent”. | |
sentAt | string, optional |
When the Newsletter was fully sent, if such is the case. Is an ISO 8601 encoded date. | |
customerLists (deprecated): | string[] |
An array of IDs of Customer Lists who will receive / have received this Newsletter. May be empty. Deprecated in favour of audience . |
|
customerSegments (deprecated): | string[] |
An array of IDs of Customer Segments who will receive / have received this Newsletter. May be empty. Deprecated in favour of audience . |
Audience
The Newsletter’s Audience. A Newsletter can be sent to one or more Customer Lists, with an optional segment that limits the Newsletter to a subset of subscribers on those lists.
Key | Details |
---|---|
lists: | string[] |
List IDs. Can only be empty for “draft” Newsletters. | |
customerSegmentKind: | string, optional |
One of "inline" , "id" or null . |
|
customerSegmentId: | string, optional |
A Customer Segment’s ID. Ignored unless customerSegmentKind is "id" . |
|
customerSegmentInline: | object, optional |
An inline Customer Segment. Ignored unless customerSegmentKind is "inline" . See Customer Segments for structure. |
Newsletter Report
Returns the selected Newsletter and some stats regarding its sending status.
# EXAMPLE REQUEST
$ curl "https://commerce.campaignmonitor.com/api/v1/newsletters/57b5aa3b046abfb053d80b52" \
-H "X-ApiKey: YOUR_API_KEY" \
-H "Accept: application/json"
EXAMPLE RESPONSE
{
"data": {
"id": "57b5aa3b046abfb053d80b52",
"title": "Black Friday Announcement",
"liveAt": "2016-11-20T00:00:00.000Z",
"audience": {
"lists": ["everyone"],
"customerSegmentKind": "inline",
"customerSegmentInline": {
"criteria": [{
"criteriaName": "receipt.sent",
"negate": true,
"filters": []
}]
}
},
"customerLists": [],
"customerSegments": [],
"status": "sent",
"sentAt": "2016-11-20T00:10:15.000Z",
"stats": {
"sends": 500,
"opens": 450,
"uniqueOpens": 300,
"clicks": 99,
"recipientCount": 515
},
"contentModules": []
}
}
Single Newsletter Report [GET]
https://commerce.campaignmonitor.com/api/v1/newsletters/{NEWSLETTER_ID}
OAuth Scopes: read_newsletter_template, write_newsletter_template
Response Body
Then endpoint returns an object with a data
key that the Newsletter object. The included info:
Key | Details |
---|---|
id: | string |
The Newsletter’s ID. Use it when calling single-newsletter endpoints. | |
title: | string |
The Newsletter’s title. | |
audience | object |
The Newsletter’s audience. Can be comprised of one or more lists, a segment ID or an inline segment. See above. | |
liveAt: | string, optional |
When the Newsletter is set to go live, if scheduled, or when it was set live, if sent or live. Is an ISO 8601 encoded date. Is omitted if the Newsletter is a draft. | |
status: | string |
The Newsletter’s current status. Must be one of “draft”, “scheduled”, “live”, or “sent”. | |
sentAt: | string, optional |
When the Newsletter was fully sent, if such is the case. Is an ISO 8601 encoded date. | |
stats: | object |
An object with this Newsletter’s stats. Included keys are described below. | |
contentModules: | array |
An array of the Content Modules in a Newsletter. These represent the content (copy, images, discounts, etc.) in a Newsletter. More details below. | |
customerLists (deprecated): | string[] |
An array of IDs of Customer Lists who will receive / have received this Newsletter. May be empty. Deprecated in favour of audience . |
|
customerSegments (deprecated): | string[] |
An array of IDs of Customer Segments who will receive / have received this Newsletter. May be empty. Deprecated in favour of audience . |
Stats
The stats
key has the following format:
Key | Details |
---|---|
sends: | number |
The total number of emails that were sent for this Newsletter. | |
opens: | number |
The total number of times this Newsletter’s emails have been opened. | |
uniqueOpens: | number |
The number of unique users that have opened this Newsletter’s emails. | |
clicks: | number |
The total number of clicks on this Newsletter’s links. | |
recipientCount: | number |
How many recipients was this Newsletter sent to. May differ from sends if there are sending errors. |
Content Modules
{
"contentModules": [
{
"position": 0,
"type": "text",
"context": {
"body": "Black Friday Sale!"
}
},
{
"position": 1,
"type": "image",
"context": {
"value": "https://www.epic-store.com/black-friday-img.png",
"url": "https://www.epic-store.com/sale",
"alignment": "center"
}
},
{
"position": 2,
"type": "discountCoupon",
"context": {
"title": "Extra Black Friday Discount",
"description": "{{firstName}}, these are the last few hours of our Black Friday sale! As an extra incentive, this is a coupon for extra {{discount}} off your next purchase :)",
"actionTitle": "Start Shopping",
"expiresOn": "Expires On",
"primaryColor": "#1990FF",
"amount": 10,
"expiryPeriod": 1,
"emailLimit": false,
"couponType": 2,
"backgroundColor": "#f8f8f8",
"onePerUser": true,
"usageLimit": 1
}
}
]
}
Each module has a position
and a type
. The 0-based position indicates where in the Newsletter the module appears (starting from top) and the type defines the kind of content.
The module’s content is in the context
key. This object and its keys depend on the module’s type. These map directly to the content & settings in the Newsletter editor, so we recommend referencing that in case any values are unclear.
List Newsletter Emails
A paginated list of all the emails sent for a Newsletter. Will be empty if Newsletter has not been sent yet. Emails are returned sorted by email address, and pagination is done by providing the email address from which to return results.
Note that if the Newsletter is currently live and sending, paginating results will not guarantee that all emails are returned. Wait for the “sent” status if an exhaustive list is required.
# EXAMPLE REQUEST
$ curl "https://commerce.campaignmonitor.com/api/v1/newsletters/57b5aa3b046abfb053d80b52/emails?limit=2" \
-H "X-ApiKey: YOUR_API_KEY" \
-H "Accept: application/json"
EXAMPLE RESPONSE
{
"data": [
{
"id": "57b5aa3b046abfb053d80b60",
"to": "a@customer.com",
"sentAt": "2016-11-20T00:00:00.000Z",
"deliveredAt": "2016-11-20T00:00:15.000Z",
"openedAt": "2016-11-20T01:00:00.000Z",
"clickedAt": "2016-11-20T01:01:10.000Z"
}, {
"id": "57b5aa3b046abfb053d80b60",
"to": "another@customer.com",
"sentAt": "2016-11-20T00:00:01.000Z",
"deliveredAt": "2016-11-20T00:00:17.000Z",
"openedAt": "2016-11-21T10:02:00.000Z"
}
],
"meta": {
"nextPage": "https://commerce.campaignmonitor.com/api/v1/newsletters/57b5aa3b046abfb053d80b52/emails/?from=another%40email.com&limit=2",
"total": 500,
"limit": 2,
"pages": 250
}
}
Newsletter Email List [GET]
https://commerce.campaignmonitor.com/api/v1/newsletters/{NEWSLETTER_ID}/emails
OAuth Scopes: read_newsletter_email, write_newsletter_email
Request Query Params
Key | Details |
---|---|
from: | string, optional |
The email address from which to start paginating records. Must be URI encoded if present. | |
limit: | number, optional |
The maximum number of records to return at once. Very high numbers are discouraged and might result in connection timeouts. |
Response Body
The response body includes a data
key which contains an array of newsletter emails. The emails' format:
Key | Details |
---|---|
id: | string |
The ID for this email. | |
to: | string |
The email address to which this email was sent. | |
sentAt: | string |
When this email was sent. ISO 8601 encoded date. | |
deliveredAt: | string, optional |
When this email was delivered. ISO 8601 encoded date. Can be missing if not delivered. | |
openedAt: | string, optional |
When this email was last opened. ISO 8601 encoded date. Can be missing if never delivered. | |
clickedAt: | string, optional |
When this email was last clicked. ISO 8601 encoded date. Can be missing if no link was ever clicked. |
The response body also includes a meta
key with further pagination information. The keys included are:
Key | Details |
---|---|
total: | number |
The total number of emails for this Newsletter. | |
limit: | number |
The maximum number of records returned in this request. | |
pages: | number |
How many pages need to be requested to go through all emails. Varies with total and limit . |
|
nextPage: | string, optional |
The URL form where the next page of emails can be fetched. Use it to cycle pages. May be omitted if there are no more emails to fetch. |
View Newsletter Email
Returns a Newsletter’s email, along with its text & HTML contents.
# EXAMPLE REQUEST
$ curl "https://commerce.campaignmonitor.com/api/v1/newsletters/57b5aa3b046abfb053d80b52/emails/57b5aa3b046abfb053d80b68" \
-H "X-ApiKey: YOUR_API_KEY" \
-H "Accept: application/json"
EXAMPLE RESPONSE
{
"data": {
"id": "57b5aa3b046abfb053d80b68",
"to": "a@customer.com",
"sentAt": "2016-11-20T00:00:00.000Z",
"deliveredAt": "2016-11-20T00:00:15.000Z",
"openedAt": "2016-11-20T01:00:00.000Z",
"clickedAt": "2016-11-20T01:01:10.000Z",
"rendered": {
"text": "This is an email's text version...",
"html": "<html><body>This is an email's HTML content...</body></html>",
"subject:": "This is the Newsletter's subject."
}
}
}
Show Newsletter Email [GET]
https://commerce.campaignmonitor.com/api/v1/newsletters/{NEWSLETTER_ID}/emails/{EMAIL_ID}
OAuth Scopes: read_newsletter_email, write_newsletter_email
Response Body
The response body includes a data
key which contains an object representation of the requested newsletter email. The rendered
key is included with the email’s contents:
Key | Details |
---|---|
id: | string |
The ID for this email. | |
to: | string |
The email address to which this email was sent. | |
sentAt: | string |
When this email was sent. ISO 8601 encoded date. | |
deliveredAt: | string, optional |
When this email was delivered. ISO 8601 encoded date. Can be missing if not delivered. | |
openedAt: | string, optional |
When this email was last opened. ISO 8601 encoded date. Can be missing if never delivered. | |
clickedAt: | string, optional |
When this email was last clicked. ISO 8601 encoded date. Can be missing if no link was ever clicked. | |
rendered: | object |
The rendered content for this email. Contains keys: text , html and subject |
Export Newsletter Recipients
Starts an Async Job to export a Newsletter’s recipients. Once completed, the job’s result will point to a CSV file with email addresses.
Refer to the Async Jobs documentation on how to access the job’s result.
# EXAMPLE REQUEST
$ curl "https://commerce.campaignmonitor.com/api/v1/newsletters/57b5aa3b046abfb053d80b52/recipients/async-export" \
-H "X-ApiKey: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
-X POST
EXAMPLE RESPONSE
{
"data": {
"id": "57b5aa3b046abfb053d80b68",
"kind": "newsletter-recipients",
"status": "pending"
}
}
Create Export Job [POST]
https://commerce.campaignmonitor.com/api/v1/newsletters/{NEWSLETTER_ID}/recipients/async-export
OAuth Scopes: read_newsletter_email, write_newsletter_email
Response Body
The response body includes a data
key which reflects the created Async Job’s info:
Key | Details |
---|---|
id: | string |
The ID for the created AsyncJob. Use this to identify this export. | |
kind: | string |
The kind of job created. Is always newsletter-recipients on this endpoint. |
|
status: | string |
The job’s status. Always pending . |
Export Newsletter Emails
Starts an Async Job to export a Newsletter’s emails. Once completed, the job’s result will point to a CSV file with email addresses and activity timestamps.
Refer to the Async Jobs documentation on how to access the job’s result.
# EXAMPLE REQUEST
$ curl "https://commerce.campaignmonitor.com/api/v1/newsletters/57b5aa3b046abfb053d80b52/emails/async-export" \
-H "X-ApiKey: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
-X POST
EXAMPLE RESPONSE
{
"data": {
"id": "57b5aa3b046abfb053d80b68",
"kind": "newsletter-emails",
"status": "pending"
}
}
Create Export Job [POST]
https://commerce.campaignmonitor.com/api/v1/newsletters/{NEWSLETTER_ID}/emails/async-export
OAuth Scopes: read_newsletter_email, write_newsletter_email
Response Body
The response body includes a data
key which reflects the created Async Job’s info:
Key | Details |
---|---|
id: | string |
The ID for the created AsyncJob. Use this to identify this export. | |
kind: | string |
The kind of job created. Is always newsletter-emails on this endpoint. |
|
status: | string |
The job’s status. Always pending . |
Customer Lists
Services related to managing Customer Lists (for Newsletters) and subscriptions.
Get Customer Lists
Get all available customer lists.
# EXAMPLE REQUEST
$ curl "https://commerce.campaignmonitor.com/api/v1/customer-lists" \
-H "X-ApiKey: YOUR_API_KEY"
-H "Accept: application/json"
EXAMPLE RESPONSE
[
{
"id": "57b5aa3b046abfb053d80b52",
"title": "Weekly Newsletter Subscribers",
"singleOptIn": false,
"enabled": true,
"subscribable": true,
"subscribers": 3,
"pending": 2,
"unsubscribers": 1
},
{
"id": "57b5aa3b046abfb053d80b53",
"title": "New Products Announcements",
"singleOptIn": true,
"enabled": true,
"subscribable": true,
"subscribers": 3,
"pending": 2,
"unsubscribers": 1
},
{
"id": "marketing",
"title": "Accepted Marketing",
"singleOptIn": true,
"enabled": false,
"subscribable": false,
"subscribers": 0,
"pending": 0,
"unsubscribers": 0
}
]
List all Customer Lists [GET]
https://commerce.campaignmonitor.com/api/v1/customer-lists
Arguments
These are provided via query params.
Key | Details |
---|---|
all: | boolean |
Whether to include lists that are not subscribable in the return (eg: Accepts Marketing) |
Return
The endpoint returns an array with all the Customer Lists in the account. Object keys are:
Key | Details |
---|---|
id: | string |
The Customer List ID. This is used whenever referencing this Customer List in other endpoints. | |
title: | string |
The title picked by the user for this List. Used in user-facing interfaces. | |
singleOptIn: | boolean |
Whether this list is single opt-in. If not, a confirmation email is sent on subscription. | |
enabled: | boolean |
Whether this list is enabled. Always true except for the marketing list where it may be false . |
|
subscribable: | boolean |
Whether subscribers can be added to this list. | |
subscribers: | integer |
The number of subscribers for this list. | |
pending: | integer |
The number of pending subscribers for this list. A pending subscriber used an online form to sign up, but has not confirmed the subscription yet. | |
unsubscribers: | integer |
The number of unsubscribers for this list. |
Create List
Creates a new Customer List.
# EXAMPLE REQUEST
$ curl "https://commerce.campaignmonitor.com/api/v1/customer-lists" \
-H "X-ApiKey: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
-X POST \
-d '{
"title":"An Awesome List",
"singleOptIn":false
}'
RESPONSE
{
"data": {
"id": "57b5aa3b046abfb053d80b52",
"title": "An Awesome List",
"singleOptIn": false,
"enabled": true,
"subscribable": true
}
}
Create a Customer List [POST]
https://commerce.campaignmonitor.com/api/v1/customer-lists
OAuth Scopes: read_customer_list, write_customer_list
Arguments
Key | Details |
---|---|
title: | string |
The title for the new List. Used in user-facing interfaces. | |
singleOptIn: | boolean, optional |
Whether this list is single opt-in. If not, a confirmation email is sent on subscription. |
Return
The endpoint returns an object with a data
key with the created list’s data in it:
Key | Details |
---|---|
id: | string |
The Customer List ID. This is used whenever referencing this Customer List in other endpoints. | |
title: | string |
The title picked by the user for this List. Used in user-facing interfaces. | |
singleOptIn: | boolean |
Whether this list is single opt-in. If not, a confirmation email is sent on subscription. | |
enabled: | boolean |
Whether this list is enabled. Always true except for the marketing list where it may be false . |
|
subscribable: | boolean |
Whether subscribers can be added to this list. |
Update List
Updates an existing Customer List. Updates are applied partially both on PUT and PATCH requests.
# EXAMPLE REQUEST
$ curl "https://commerce.campaignmonitor.com/api/v1/customer-lists/57b5aa3b046abfb053d80b52" \
-H "X-ApiKey: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
-X PATCH \
-d '{
"singleOptIn":true
}'
RESPONSE
{
"data": {
"id": "57b5aa3b046abfb053d80b52",
"title": "An Awesome List",
"singleOptIn": true,
"enabled": true,
"subscribable": true
}
}
Update a Customer List [PATCH|PUT]
https://commerce.campaignmonitor.com/api/v1/customer-lists/{LIST_ID}
OAuth Scopes: read_customer_list, write_customer_list
Arguments
Key | Details |
---|---|
title: | string |
A new title for the List. Used in user-facing interfaces. | |
singleOptIn: | boolean, optional |
Whether this list is single opt-in. |
Return
The endpoint returns an object with a data
key with the updated list’s data in it:
Key | Details |
---|---|
id: | string |
The Customer List ID. This is used whenever referencing this Customer List in other endpoints. | |
title: | string |
The title for this List. Used in user-facing interfaces. | |
singleOptIn: | boolean |
Whether this list is single opt-in. If not, a confirmation email is sent on subscription. | |
enabled: | boolean |
Whether this list is enabled. Always true except for the marketing list where it may be false . |
|
subscribable: | boolean |
Whether subscribers can be added to this list. |
Subscribe to List
Subscribe an email address and optional user details to a specific customer list.
# EXAMPLE REQUEST
$ curl "https://commerce.campaignmonitor.com/api/v1/customer-lists/LIST_ID/subscriptions" \
-H "X-ApiKey: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
-X PUT \
-d '{
"email": "an@email.com",
"name": "John Snow",
"source": "CM Commerce",
"sourceType": "SubscriptionForm",
"sourceId": "some-form-slug",
"optInText": "Subscribe to receive new product updates",
"properties": {
"age": 42,
"happy": true,
"dob": "2001-12-23",
"likes": ["wine", "cinnamon", "umami"]
}
}'
Subscribe an Email to a Customer List [PUT]
https://commerce.campaignmonitor.com/api/v1/customer-lists/LIST_ID/subscriptions
LIST_ID
should be the id
returned in the list response.
Arguments
Argument | Details |
---|---|
email: | string |
The email address to subscribe to this Customer List. Must be a valid email address. | |
name: | string, optional |
The email address owner’s name. Expects first or first and last name. | |
properties: | object, optional |
Additional properties to track for this subscriber. Type inference applies the first time a property is tracked, We support string, number, boolean, array and date types. | |
source: | string, optional |
Where this subscription came from. Should be an App or company name. | |
sourceType: | string, optional |
What type of control did the user interact for subscribing. Could be a form type, page, etc. | |
sourceId: | string, optional |
A more granular ID of where the user subscribed. Could be the ID/slug of a form or subscription page. | |
optInText: | string, optional |
What opt-in text was shown to the subscriber. This is required for GDPR compliance. |
Properties
Property keys can have at most 100 characters in length and must match the regex /^[a-zA-Z0-9-_]+$/
. A maximum of 20 different properties are allowed per account.
The first time a property is tracked (uniqueness is ensured by key), CM Commerce tries to infer its type. The following rules apply:
- If value is
null
, no type is inferenced. - If a value can be cast to a number:
- If it is a whole number and the key ends with “_at” or “At”,
date
is inferred. - otherwise,
number
is inferred.
- If it is a whole number and the key ends with “_at” or “At”,
- If a value can be parsed as an iso8601 date,
date
is inferred. - If a value is an array, its first element is used to infer the type (using these rules).
- Otherwise,
string
is inferred.
List Subscribers
Returns a list’s accepted subscribers, pending subscribers (haven’t opted in yet) and unsubscribers.
# EXAMPLE REQUEST
$ curl "https://commerce.campaignmonitor.com/api/v1/customer-lists/marketing/subscribers" \
-H "X-ApiKey: YOUR_API_KEY"
-H "Accept: application/json"
EXAMPLE RESPONSE
{
"data": [
{
"id": "57b5aa3b046abfb053d80b52",
"email": "test@email.com"
}
],
"meta": {
"pages": 1,
"total": 1,
"limit": 10,
"searchAfter": ["test@email.com"],
"nextPage": "https://commerce.campaignmonitor.com/api/v1/customer-lists/5b10ded082d2653410054ac3/subscribers?limit=10&searchAfter=test%40email.com"
}
}
List all Subscribers to a Customer List [GET]
https://commerce.campaignmonitor.com/api/v1/customer-lists/{LIST_ID}/subscribers
OAuth Scopes: read_customers
Arguments
These are provided via query params.
Key | Details |
---|---|
pending: | boolean |
Changes the return to a list of pending subscribers. | |
unsubscribers: | boolean |
Changes the return to a list of unsubscribers. |
Return
The endpoint returns an object with a data
key with the found subscribers, and a meta
key with pagination info. By default, the returned subscribers are Customer objects with event & stats data.
However, when querying for pending
or unsubscribers
the returned subscribers include only the email
key. Pagination is also different. This is for legacy reasons and is likely to change (to match the default return) in the future.
To ensure your app supports all returns and is future-proof, expect only the email
address in the return, and use nextPage
for pagination.
Key | Details |
---|---|
email: | string |
The email address for the subscriber. Only common field across all queries. |
Mass Unsubscribe
Removes one or more list’s subscribers. There are two versions of this request: one that removes a list of emails, another that removes a whole segment.
# EXAMPLE REQUEST
$ curl "https://commerce.campaignmonitor.com/api/v1/customer-lists/LIST_ID/subscribers" \
-H "X-ApiKey: YOUR_API_KEY" \
-X DELETE \
-G \
-d "emails[]=email1@example.org" \
-d "emails[]=email2@example.org" \
-d "emails[]=email3@example.org"
EXAMPLE RESPONSE
{
"data": {
"unsubscribed": 2,
"failed": 0,
"failedReasons": []
}
}
Unsubscribe via Email List [DELETE]
https://commerce.campaignmonitor.com/api/v1/customer-lists/{LIST_ID}/subscribers
OAuth Scopes: write_customers
Arguments
These are provided via query params.
Key | Details |
---|---|
emails: | string[] |
The list of emails to unsubscribe from the Customer List. |
Return
Key | Details |
---|---|
unsubscribed: | number |
How many emails were successfuly unsubscribed. | |
failed: | number |
How many emails failed to unsubscribe. | |
failedReasons: | string[] |
A list of errors that explain unsubscribe failures. |
# EXAMPLE REQUEST
$ curl "https://commerce.campaignmonitor.com/api/v1/customer-lists/LIST_ID/subscribers" \
-H "X-ApiKey: YOUR_API_KEY" \
-X DELETE \
-G \
-d "segment={\"criteria\":[{\"criteriaName\":\"receipt.sent\",\"negate\":true,\"filters\":[]}]}"
EXAMPLE RESPONSE
{
"data": {
"asyncJobId": "5b10ded082d2653410054ac3"
}
}
Unsubscribe via Customer Segment [DELETE]
https://commerce.campaignmonitor.com/api/v1/customer-lists/{LIST_ID}/subscribers
OAuth Scopes: write_customers
Arguments
These are provided via query params.
Key | Details |
---|---|
segment: | string |
A Customer Segment in JSON format. |
Return
There are two possible return formats: If the number of subscribers to remove is relatively low, they will be removed synchronously with the request and the return will mirror the Unsubscribe via Email List’s. Otherwise, an Async Job is started and it’s ID returned instead inside the data
key.
The following format is returned only with status 202:
Key | Details |
---|---|
asyncJobId: | string |
An Async Job’s ID. |
Customer Segments
Services related to managing Customer Segments.
List Customer Segments
Returns all segments and where they’re used.
# EXAMPLE REQUEST
$ curl "https://commerce.campaignmonitor.com/api/v1/segments" \
-H "X-ApiKey: YOUR_API_KEY"
-H "Accept: application/json"
EXAMPLE RESPONSE
{
"data": [
{
"id": "5ae9aeedc60b7e00fe9f133a",
"name": "\"At Risk\" Repeat Customers",
"editable": false,
"uses": []
},
{
"id": "5ae9aeedc60b7e00fe9f1330",
"name": "First-time Buyers",
"editable": false,
"uses": [
{
"modelName": "ReceiptTemplate",
"models": [
{
"id": "5ae9aeedc60b7e00fe9f1356",
"enabled": false
}
]
}
]
}
]
}
List all Customer Segments [GET]
https://commerce.campaignmonitor.com/api/v1/segments
OAuth Scopes: read_segments
Return
The endpoint returns an object with a data
key that contains all the Customer Segments in the account. Object keys are:
Key | Details |
---|---|
id: | string |
The Customer Segment’s ID. This is used whenever referencing this Customer Segment in other endpoints. | |
name: | string |
The segment’ name. | |
editable: | bool |
Whether this is a default segment (false ) or a custom one (true ) |
|
uses: | object[] |
Where this segment is currently being used, see below. |
Uses
Key | Details |
---|---|
modelName: | string |
Name of the Model where a segment is used, Possible values: "AsyncJob" , "Campaign" , "FollowUpTemplate" , "NewsletterTemplate" , "ReceiptTemplate" . |
|
models: | object[] |
All the objects of modelName type where this segment is used. Format depends on model. |
Export Segment Customers
Starts an Async Job to export a Segment’s customers. Once completed, the job’s result will point ot a CSV file with email addresses and subscription status.
Refer to the Async Jobs documentation on how to access the job’s result.
# EXAMPLE REQUEST
$ curl "https://commerce.campaignmonitor.com/api/v1/segments/57b5aa3b046abfb053d80b52/async-export" \
-H "X-ApiKey: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
-X POST
EXAMPLE RESPONSE
{
"data": {
"id": "57b5aa3b046abfb053d80b68",
"kind": "segments-export",
"status": "pending"
}
}
Create Export Job [POST]
https://commerce.campaignmonitor.com/api/v1/segments/{SEGMENT_ID}/async-export
OAuth Scopes: read_customers, read_segments
Response Body
The response body includes a data
key which reflects the created Async Job’s info:
Key | Details |
---|---|
id: | string |
The ID for the created AsyncJob. Use this to identify this export. | |
kind: | string |
The kind of job created. Is always segments-export on this endpoint. |
|
status: | string |
The job’s status. Always pending . |
Webhooks
Endpoints for subscribing and configuring Webhooks triggered by events on your account.
You should use webhooks to get near real-time updates on events in a shop. When one of the supported events happens (eg: a newsletter email is sent), CM Commerce will POST
some data related to that event to the endpoints registered in webhooks that subscribed to that event’s topic.
When Webhook delivery fails (your endpoint can’t be reached or returns an error status code), we’ll keep trying to re-send the failed Webhooks up to a limited number of tries, in an exponential back-off fashion. Details are described below.
For security purposes, all our webhooks use JSON Web Signatures (JWS). These allow recipients to validate that each webhook originates from CM Commerce. More details on the validation are below.
Subscribable Topics
newsletter-template/sent
: Triggered when all emails for a Newsletter have been sent.newsletter-email/sent
: Triggered when a Newsletter is sent to a customer. These typically arrive in bursts as the Newsletter Template is sending;async-job/completed
: Triggered when an Async Job has been completed (failed or succeeded).abandoned-cart-email/sent
: Triggered when an email was sent for an Abandoned Cart.
Payload
All request bodies contain two keys, meta
and data
. The meta
key contains information about the webhook itself:
Key | Details |
---|---|
topic: | string |
The Webhook’s topic. Useful if you wish to direct all webhooks to the same endpoint. | |
ts: | number |
The timestamp for when the webhook was first sent. This will stay equal for subsequent retries. |
The data
key’s content depends on the Webhook’s topic. The following apply:
newsletter-template/sent
# EXAMPLE WEBHOOK
$ curl "https://partner-app.com/registered/endpoint" \
-H "Authorization: Bearer 'eyJhbGciOiJIUzI1NiIsImtpZCI6IkNsaWVudCBTZWNyZXQiLCJ0eXAiOiJKV1QifQ.eyJqaXQiOiIzOTg1Y2JlMC1lM2JlLTExZTYtYThmMy04NTMzOTYyOGMzNGEiLCJpYXQiOjE0ODU0MzE2ODIsImlzcyI6IkNvbnZlcnNpbyJ9.WZYh7Wylj5vnGRWqrgeMXdeRjIqJc9V30nyEG7QHpvk'" \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
-X POST \
-d '{
"meta": {
"topic": "newsletter-template/sent",
"ts": 1485431966802
},
"data": {
"templateId": "57b5aa3b640abfb053d80a63",
"userId": "67b5aa3c640abfb053d80a63",
"status": "sent",
"title": "Black Friday opening announcement",
"sentAt": "2017-01-26T11:57:26.675Z",
"liveAt": "2017-01-26T11:45:28.888Z"
}
}'
Key | Details |
---|---|
templateId: | string |
The Newsletter Template’s ID. Can be used to reference the template using the API. | |
userId: | string |
The User / Store that this template belongs to. | |
status: | string |
Template status, is “sent”. | |
title: | string |
The Newsletter’s title (store-facing, not necessarily in email content). | |
liveAt: | string |
When the template started sending. This is an ISO 8601 formatted date. | |
sentAt: | string |
When the template finished sending. This is an ISO 8601 formatted date. |
newsletter-email/sent
# EXAMPLE WEBHOOK
$ curl "https://partner-app.com/registered/endpoint" \
-H "Authorization: Bearer 'eyJhbGciOiJIUzI1NiIsImtpZCI6IkNsaWVudCBTZWNyZXQiLCJ0eXAiOiJKV1QifQ.eyJqaXQiOiIzOTg1Y2JlMC1lM2JlLTExZTYtYThmMy04NTMzOTYyOGMzNGEiLCJpYXQiOjE0ODU0MzE2ODIsImlzcyI6IkNvbnZlcnNpbyJ9.WZYh7Wylj5vnGRWqrgeMXdeRjIqJc9V30nyEG7QHpvk'" \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
-X POST \
-d '{
"meta": {
"topic": "newsletter-email/sent",
"ts": 1485431966802
},
"data": {
"emailId": "57b5aa3b046abfb053d80b52",
"templateId": "57b5aa3b640abfb053d80a63",
"userId": "67b5aa3c640abfb053d80a63",
"to": "an@email.com",
"subject": "Black Friday Sale",
"title": "Black Friday opening announcement",
"sentAt": "2017-01-26T11:57:26.675Z"
}
}'
Key | Details |
---|---|
emailId: | string |
The Newsletter Email’s ID. Can be used to reference the email using the API. | |
templateId: | string |
The Newsletter Template’s ID. The template that the email belongs to. | |
userId: | string |
The User / Store that this email belongs to. | |
to: | string |
The email address to whom this email was sent. | |
subject: | string |
The subject of the email | |
title: | string |
The Newsletter’s title (store-facing, not necessarily in email content). | |
sentAt: | string |
When the email was sent. This is an ISO 8601 formatted date. |
async-job/completed
# EXAMPLE WEBHOOK
$ curl "https://partner-app.com/registered/endpoint" \
-H "Authorization: Bearer 'eyJhbGciOiJIUzI1NiIsImtpZCI6IkNsaWVudCBTZWNyZXQiLCJ0eXAiOiJKV1QifQ.eyJqaXQiOiIzOTg1Y2JlMC1lM2JlLTExZTYtYThmMy04NTMzOTYyOGMzNGEiLCJpYXQiOjE0ODU0MzE2ODIsImlzcyI6IkNvbnZlcnNpbyJ9.WZYh7Wylj5vnGRWqrgeMXdeRjIqJc9V30nyEG7QHpvk'" \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
-X POST \
-d '{
"meta": {
"topic": "async-job/completed",
"ts": 1485431966803
},
"data": {
"asyncJobId": "57b5aa3b046abfb053d80b52",
"userId": "57b5aa3b046abbf053d80b64"
"kind": "newsletter-recipients",
"status": "done",
"startedAt": "2017-01-26T11:57:26.675Z",
"completedAt": "2017-01-26T12:02:06.665Z",
"result": "https://receiptful.s3.amazonaws.com/async-jobs/1234567890987654321.csv"
}
}'
Data is the JSON representation of the completed Async Job:
Key | Details |
---|---|
asyncJobId: | string |
The Async Job’s ID. | |
userId: | string |
The user on whose behalf this job is running. | |
kind: | string |
What kind of job it is. | |
status: | string |
The job’s status. Is either “done” or “failed”. | |
startedAt: | string, optional |
When this job started processing. Is an ISO 8601 encoded date. Can be null if it hasn’t started yet. | |
completedAt: | string |
When this job completed. Is an ISO 8601 encoded date. | |
error: | string, optional |
An error message that indicates a problem when processing the job. There is no result if this is present. |
|
result: | string, optional |
The final result from processing this job. What it is depends on job kind . |
abandoned-cart-email/sent
# EXAMPLE WEBHOOK
$ curl "https://partner-app.com/registered/endpoint" \
-H "Authorization: Bearer 'eyJhbGciOiJIUzI1NiIsImtpZCI6IkNsaWVudCBTZWNyZXQiLCJ0eXAiOiJKV1QifQ.eyJqaXQiOiIzOTg1Y2JlMC1lM2JlLTExZTYtYThmMy04NTMzOTYyOGMzNGEiLCJpYXQiOjE0ODU0MzE2ODIsImlzcyI6IkNvbnZlcnNpbyJ9.WZYh7Wylj5vnGRWqrgeMXdeRjIqJc9V30nyEG7QHpvk'" \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
-X POST \
-d '{
"meta": {
"topic": "abandoned-cart-email/sent",
"ts": 1485431966893
},
"data": {
"userId": "57b5aa3b0461234053d80b52"
"emailId": "57b5aa3b046abfb053d80b52",
"templateId": "57b5aa3b046abfb053d80b53",
"campaignId": "57b5aa3b046abfb053d80b56",
"abandonedCartId": "57b5aa3b046abfb053d80b59",
"to": "some@customer.com",
"status": "sent"
"sentAt": "2017-01-26T11:57:26.675Z",
"subject": "You forgot your goodies!"
}
}'
Data is the JSON representation of the sent Abandoned Cart email:
Key | Details |
---|---|
emailId: | string |
The email’s ID. | |
templateId: | string |
The email template’s ID. | |
campaignId: | string |
The Abandoned Cart Campaign’s ID. | |
abandonedCartId: | string |
The ID of the Abandoned Cart that triggered this email. | |
userId: | string |
The User’s ID. | |
to: | string |
The email address of the customer that received this email (also the owner of the Abandoned Cart). | |
status: | string |
The email’s status. Is “sent”. | |
sentAt: | stringl |
When the email was sent. Is an ISO 8601 encoded date. | |
subject: | string |
The email subject. |
Retry Mechanism
Webhooks retry up to 6 times, for a total of 7 attempts. Multiple failures are grouped by endpoint and retried in batches. If 5% or more of a batch of retries fails consecutively, all webhooks in that batch are skipped and an attempt is counted for each.
The retry progression is (in time elapsed from first attempt):
- 10min
- 35min
- 1h 30min
- 4h 20min
- 10h 30min
- 1d 3h
- 3d
After all attempts are through, the webhooks are discarded.
Security Signature
# EXAMPLE WEBHOOK
$ curl "https://partner-app.com/registered/endpoint" \
-H "Authorization: Bearer 'eyJhbGciOiJIUzI1NiIsImtpZCI6IkNsaWVudCBTZWNyZXQiLCJ0eXAiOiJKV1QifQ.eyJqaXQiOiIzOTg1Y2JlMC1lM2JlLTExZTYtYThmMy04NTMzOTYyOGMzNGEiLCJpYXQiOjE0ODU0MzE2ODIsImlzcyI6IkNvbnZlcnNpbyJ9.WZYh7Wylj5vnGRWqrgeMXdeRjIqJc9V30nyEG7QHpvk'" \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
-X POST \
-d '{ ... }'
/* JavaScript */
const jws = require('jws');
const authorization = req.get('Authorization');
// > 'Bearer eyJhbGciOiJIUzI1NiIsImtpZCI6IkNsaWVudCBTZWNyZXQiLCJ0eXAiOiJKV1QifQ.eyJqaXQiOiIzOTg1Y2JlMC1lM2JlLTExZTYtYThmMy04NTMzOTYyOGMzNGEiLCJpYXQiOjE0ODU0MzE2ODIsImlzcyI6IkNvbnZlcnNpbyJ9.WZYh7Wylj5vnGRWqrgeMXdeRjIqJc9V30nyEG7QHpvk'
const [, encodedToken] = authorization.split(' ');
const token = jws.decode(encodedToken);
// > {
// > header: { alg: 'HS256', kid: 'Client Secret', typ: 'JWT' },
// > payload: {
// > jit: '3985cbe0-e3be-11e6-a8f3-85339628c34a',
// > iat: 1485431682,
// > iss: 'Conversio'
// > },
// > signature: 'WZYh7Wylj5vnGRWqrgeMXdeRjIqJc9V30nyEG7QHpvk'
// > }
jws.verify(encodedToken, token.header.alg, 'a-secret');
// > true
## Ruby
require 'JWT'
_, encoded_token = request.headers['Authorization'].split
payload, header = JWT.decode(encoded_token, nil, false)
# > payload = {"jit"=>"3985cbe0-e3be-11e6-a8f3-85339628c34a", "iat"=>1485431682, "iss"=>"Conversio"}
# > header = {"alg"=>"HS256", "kid"=>"Client Secret", "typ"=>"JWT"}
JWT.decode(encoded_token, 'a-secret', true, algorithm: header['alg'], iss: 'Conversio', verify_iss: true, verify_iat: true)
# > [{"jit"=>"3985cbe0-e3be-11e6-a8f3-85339628c34a", "iat"=>1485431682, "iss"=>"Conversio"}, {"alg"=>"HS256", "kid"=>"Client Secret", "typ"=>"JWT"}]
A JSON Web Token (JWT) is signed and encoded into the Authorization header of the Webhook’s request using the Bearer schema. The token’s header includes the following keys:
Key | Details |
---|---|
alg: | string |
Has the value “HS256”. It’s the algorightm used for the HMAC signature. | |
typ: | string |
Has the value “JWT”. Indicates this is a JSON Web Token. | |
kid: | string |
Hints at what secret was used for signing the Token. Is “Client Secret” when the webhook is registered for a Partner App, or “API Key” when the webhook was registered through an API. |
The token’s body itself includes the following keys:
Key | Details |
---|---|
jti: | string |
Has a random, unique value. Two tokens with the same jti could mean an attacker is re-using tokens to fabricate authorized webhooks. |
|
iss: | string |
Has the value “Conversio”. | |
iat: | string |
UNIX time of when the token was generated (when webhook request is sent). |
This token is then serialized using JWS Compact Serialization and included in the Authorization header using the Bearer schema.
Webhooks recipients should validate that every request is properly signed. This acts as guarantee that the webhook is valid and was sent by CM Commerce.
Validating the signatures is a process made simple by programming libraries. The same library used during OAuth can be used here, just follow the examples on the right.
View Webhooks
Returns all Webhooks registered for the current shop. If authorized through OAuth, only those webhooks created by the authorized PartnerApp will be listed.
# EXAMPLE REQUEST
$ curl "https://commerce.campaignmonitor.com/api/v1/webhooks" \
-H "X-ApiKey: YOUR_API_KEY"
EXAMPLE RESPONSE
{
"data": [
{
"id": "57b5aa3b046abfb053d80b52",
"topic": "newsletter-email/sent",
"endpoint": "https://www.endpoint.com/cmcommerce-webhook"
}
]
}
List all Webhooks [GET]
https://commerce.campaignmonitor.com/api/v1/webhooks
OAuth Scopes: read_webhook, write_webhook
Response Body
Then endpoint returns an object with a data
key that is an Array with all the webhooks in the shop. It will limit output to the currently authenticated Partner App webhooks if authorized through OAuth.
Each webhook includes the following info:
Key | Details |
---|---|
id: | string |
The Webhook ID. Use it when calling single-webhook endpoints. | |
topic: | string |
The event that triggers this webhook. | |
endpoint: | string |
Where the webhook’s payload is sent, when triggered. Must be a URI. | |
partnerApp: | string, optional |
The ID of the Partner App that created this webhook. Included only when authorization is made with the API key. |
# EXAMPLE REQUEST
$ curl "https://commerce.campaignmonitor.com/api/v1/webhooks/57b5aa3b046abfb053d80b52" \
-H "X-ApiKey: YOUR_API_KEY"
EXAMPLE RESPONSE
{
"data": {
"id": "57b5aa3b046abfb053d80b52",
"topic": "newsletter-email/sent",
"endpoint": "https://www.endpoint.com/conversio-webhook"
}
}
Show Single Webhook [GET]
https://commerce.campaignmonitor.com/api/v1/webhooks/{WEBHOOK_ID}
OAuth Scopes: read_webhook, write_webhook
Response Body
Then endpoint returns an object with a data
key that is itself an object with the Webhook’s properties. It will include the Partner App if authorized with an API key. The following keys are returned:
Key | Details |
---|---|
id: | string |
The Webhook ID. You used it to call this endpoint. | |
topic: | string |
The event that triggers this webhook. | |
endpoint: | string |
Where the webhook’s payload is sent, when triggered. Must be a URI. | |
partnerApp: | string, optional |
The ID of the Partner App that created this webhook. Included only when authorization is made with the API key. |
Create Webhooks
Create a new Webhook for the currently authenticated shop. If authorized through OAuth, the Webhook will be assigned to the authorized Partner App.
Only one Webhook for a unique topic & endpoint combination can exist for each Shop. Thus, trying to create multiple webhooks for the same topic and endpoint will result in errors.
# EXAMPLE REQUEST
$ curl "https://commerce.campaignmonitor.com/api/v1/webhooks" \
-H "X-ApiKey: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-X POST \
-d '{
"endpoint": "https://www.your-endpoint.com/webhooks",
"topic": "newsletter-email/sent",
}'
EXAMPLE RESPONSE
{
"data": {
"id": "57b5aa3b046abfb053d80b52",
"topic": "newsletter-email/sent",
"endpoint": "https://www.your-endpoint.com/webhooks"
}
}
EXAMPLE ERROR RESPONSE
{
"errors": [
"The chosen topic is invalid"
]
}
Create Webhook [POST]
https://commerce.campaignmonitor.com/api/v1/webhooks
OAuth Scopes: write_webhook
Request Body
Key | Details |
---|---|
topic: | string |
The topic to subscribe with this Webhook. | |
endpoint: | string |
URL where to publish this Webhook’s payload, when triggered. |
Response Body
When successful, the endpoint returns the newly created webhook. The returned data mimics that of the show
endpoint:
Key | Details |
---|---|
id: | string |
The new Webhook’s ID. | |
topic: | string |
The event that triggers this webhook. | |
endpoint: | string |
Where the webhook’s payload is sent, when triggered. Must be a URI. | |
partnerApp: | string, optional |
The ID of the Partner App that created this webhook. Included only when authorization is made with the API key. |
Any errors during creation will be identified by a 400 status code and a JSON object with an errors
Array with string explanations of what went wrong.
Update Webhooks
Update an existing webhook with new data. Can change either the topic or endpoint of an existing webhook.
# EXAMPLE REQUEST
$ curl "https://commerce.campaignmonitor.com/api/v1/webhooks/57b5aa3b046abfb053d80b52" \
-H "X-ApiKey: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-X PATCH \
-d '{
"endpoint": "https://www.your-other-endpoint.com/webhooks"
}'
EXAMPLE RESPONSE
{
"data": {
"id": "57b5aa3b046abfb053d80b52",
"topic": "newsletter-email/sent",
"endpoint": "https://www.your-endpoint.com/webhooks"
}
}
EXAMPLE ERROR RESPONSE
{
"errors": [
"The chosen topic is invalid"
]
}
Update Webhook [PATCH/PUT]
https://commerce.campaignmonitor.com/api/v1/webhooks/{WEBHOOK_ID}
OAuth Scopes: write_webhook
If your client doesn’t support the PATCH
method, PUT
can be used to the same effect.
Request Body
Key | Details |
---|---|
topic: | string, optional |
The topic to subscribe with this Webhook. Can’t be null . |
|
endpoint: | string, optional |
URL where to publish this Webhook’s payload, when triggered. Can’t be null . |
Response Body
When successful, the endpoint returns the newly created webhook. The returned data mimics that of the show
endpoint:
Key | Details |
---|---|
id: | string |
The updated Webhook’s ID. | |
topic: | string |
The event that triggers this webhook. | |
endpoint: | string |
Where the webhook’s payload is sent, when triggered. Must be a URI. | |
partnerApp: | string, optional |
The ID of the Partner App that created this webhook. Included only when authorization is made with the API key. |
Any errors during creation will be identified by a 400 status code and a JSON object with an errors
Array with string explanations of what went wrong.
Delete Webhook
Delete an existing Webhook to stop receiving data at the endpoint.
# EXAMPLE REQUEST
$ curl "https://commerce.campaignmonitor.com/api/v1/webhooks/57b5aa3b046abfb053d80b52" \
-H "X-ApiKey: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-X DELETE
Delete Webhook [DELETE]
https://commerce.campaignmonitor.com/api/v1/webhooks/{WEBHOOK_ID}
OAuth Scopes: write_webhook
Successful requests return nothing.
Async Jobs
An Async Job is created on certain API endpoints and user actions that we expect will take a long time to complete. Generally, when a job is created, you’ll receive a Job ID that you can poll using this API to:
- Check if the job’s completed;
- Get the job’s result; and
- Check the job for errors.
Due to the presumably large size of an Async Job’s results and how they’ll be less relevant as time advances, Async jobs expire 1 week after they’ve finished processing, being automatically removed.
List Async Jobs
Returns all the Async Jobs created for the authenticated shop. When using OAuth, only jobs started by the authenticated app are returned.
# EXAMPLE REQUEST
$ curl "https://commerce.campaignmonitor.com/api/v1/async-jobs" \
-H "X-ApiKey: YOUR_API_KEY"
EXAMPLE RESPONSE
{
"data": [
{
"id": "57b5aa3b046abfb053d80b52",
"kind": "newsletter-recipients",
"status": "done",
"startedAt": "2017-01-03T16:32:27.741Z",
"completedAt": "2017-01-03T16:37:20.417Z",
"result": "https://commerce.campaignmonitor.com/async-jobs/57b5aa3b046abfb053d80b52.csv"
},
{
"id": "57b5aa3b046abfb053d80b54",
"kind": "newsletter-recipients",
"status": "pending"
},
{
"id": "57b5aa3b046abfb053d80b57",
"kind": "newsletter-recipients",
"status": "failed",
"startedAt": "2017-01-02T05:33:22.123Z",
"completedAt": "2017-01-02T05:33:45.412Z",
"error": "Something terrible has happened. Get in touch with support tout de suite!"
}
]
}
List all Jobs [GET]
https://commerce.campaignmonitor.com/api/v1/async-jobs
OAuth Scopes: read_async_job
Response Body
The endpoint returns an object with a data
key that is an Array with all the Async Jobs in the shop. It will limit output to the currently authenticated Partner App webhooks if authorized through OAuth.
Each Async Job includes the following info:
Key | Details |
---|---|
id: | string |
The Async Job ID. Use it when calling single-job endpoints. | |
kind: | string |
What kind of job this is. Currently only supports “newsletters-recipients”. | |
status: | string |
The job’s status. One of “pending”, “done” or “failed”. | |
startedAt: | string, optional |
When this job started processing. Is an ISO 8601 encoded date. Is null if it hasn’t started yet. |
|
completedAt: | string, optional |
When this job completed. Is null if it hasn’t finished yet. There’s a result or error if set. Is an ISO 8601 encoded date. |
|
error: | string, optional |
An error message that indicates a problem when processing the job. There is no result if this is present. |
|
result: | string, optional |
The final result from processing this job. Contents depend on job kind . |
|
partnerApp: | string, optional |
The ID of the Partner App that created this Async Job. Included only when authorization is made with the API key. |
Get Async Job
Returns the requested Async Job. When using OAuth, only jobs started by the authenticated app can be accessed.
# EXAMPLE REQUEST
$ curl "https://commerce.campaignmonitor.com/api/v1/async-jobs/57b5aa3b046abfb053d80b52" \
-H "X-ApiKey: YOUR_API_KEY"
EXAMPLE RESPONSE
{
"data": {
"id": "57b5aa3b046abfb053d80b52",
"kind": "newsletter-recipients",
"status": "done",
"startedAt": "2017-01-03T16:32:27.741Z",
"completedAt": "2017-01-03T16:37:20.417Z",
"result": "https://commerce.campaignmonitor.com/async-jobs/57b5aa3b046abfb053d80b52.csv"
}
}
Get an Async Job [GET]
https://commerce.campaignmonitor.com/api/v1/async-jobs/{ASYNC_JOB_ID}
OAuth Scopes: read_async_job
Response Body
The endpoint returns an object with a data
key. The data
key is an object with the Async Job’s properties. It will include the Partner App if authorized with an API key. The following keys are returned:
Key | Details |
---|---|
id: | string |
The Async Job’s ID. The ID used to call this endpoint. | |
kind: | string |
What kind of job this is. Currently only supports “newsletters-recipients”. | |
status: | string |
The job’s status. One of “pending”, “done” or “failed”. | |
startedAt: | string, optional |
When this job started processing. Is an ISO 8601 encoded date. Is null if it hasn’t started yet. |
|
completedAt: | string, optional |
When this job completed. Is null if it hasn’t finished yet. There’s a result or error if set. Is an ISO 8601 encoded date. |
|
error: | string, optional |
An error message that indicates a problem when processing the job. There is no result if this is present. |
|
result: | string, optional |
The final result from processing this job. Contents depend on job kind . |
|
partnerApp: | string, optional |
The ID of the Partner App that created this Async Job. Included only when authorization is made with the API key. |
Partner Apps API
This API comprises of management endpoints for Partner Apps. Because they’re unrelated to any Store, authentication is done through HMAC.
Get Partner App
Returns the saved info for the authenticated Partner App.
# EXAMPLE REQUEST
$ curl https://commerce.campaignmonitor.com/api/v1/partners/ \
-H "Accept: application/json" \
-H "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiI1N2I1YWEzYjA0NmFiZmIwNTNkODBiNTIifQ.sxd8uG4EkeIXHsIIVELrfGTIZcaTFE9a9YY-8HGHuOQ
EXAMPLE RESPONSE
{
"data": {
"id": "57b5aa3b046abfb053d80b52",
"name": "Epic Forms",
"email": "hello@epicforms.com",
"website": "https://epicforms.com",
"logo": "epicforms-logo.jpg",
"redirectUri": "https://epicforms.com/oauth/auth"
}
}
Show Current Partner App [GET]
Response Body
Returns JSON with a data
key which contains the info for the authenticated Partner App:
Key | Details |
---|---|
id: | string |
The Partner App’s ID. This is also the client_id used in OAuth. |
|
name: | string |
The name of this Partner App. | |
email: | string |
The contact email for this Partner App. | |
website: | string |
The Partner App’s homepage. | |
logo: | string |
The Partner App’s logo, used in the OAuth authorize page. Can be a URL or a data URI. | |
redirectUri: | string |
The redirect URI used during OAuth. This value must match the argument received in the authorize call. |
Update Partner App
Updates the saved info for the authenticated Partner App. Use this to update a logo or an OAuth redirect URI.
# EXAMPLE REQUEST
$ curl https://commerce.campaignmonitor.com/api/v1/partners/ \
-H "Accept: application/json" \
-H "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiI1N2I1YWEzYjA0NmFiZmIwNTNkODBiNTIifQ.sxd8uG4EkeIXHsIIVELrfGTIZcaTFE9a9YY-8HGHuOQ" \
-X PATCH \
-d '{
"name": "A Great Name 2",
"email": "another_contact@partner.com",
"website": "www.rebranded.com",
"logo": "data:image/png;base64,iVBORw0...ggg==",
"redirectUri": "https://www.rebranded.com/oauth/auth"
}'
EXAMPLE RESPONSE
{
"data": {
"name": "A Great Name 2",
"email": "another_contact@partner.com",
"website": "www.rebranded.com",
"logo": "",
"redirectUri": "https://www.rebranded.com/oauth/auth"
}
}
Update Partner App [PATCH]
Only the attributes that are present in the body will be updated. The logo must be a base64 encoded data URI.
Updateable Attributes
Key | Details |
---|---|
name: | string |
The name of this Partner App. | |
email: | string |
The contact email for this Partner App. | |
website: | string |
The Partner App’s homepage. | |
logo: | string |
The Partner App’s logo, used in the OAuth authorize page. Must be a data URI. | |
redirectUri: | string |
The redirect URI used during OAuth. This value must match the argument received in the authorize call. |
Errors
The CM Commerce API uses the following error codes:
Error Code | Meaning |
---|---|
400 | Bad Request – Something required is missing of the request is malformed |
401 | Unauthorized – Your API key is wrong |
403 | Forbidden – The requested resource is hidden for administrators only |
404 | Not Found – The specified endpoint, receipt or coupon could not be found |
405 | Method Not Allowed – You tried to access a resource with an invalid method |
406 | Not Acceptable – You requested a format that isn’t json |
413 | Request Entity Too Large – You sent too much data to us |
429 | Too Many Requests – You’re exceeding the API rate limit |
500 | Internal Server Error – We had a problem with our server. Try again later. |
503 | Service Unavailable – We’re temporarially offline for maintenance. Please try again later. |