Selz API

This version is in Beta, there may be issues we're still working out.

Getting Started

The Selz API is organized around REST. Our API has predictable, resource-oriented URLs, and uses HTTP response codes to indicate API errors. We use built-in HTTP features, like HTTP authentication and HTTP verbs, which are understood by off-the-shelf HTTP clients.

Every response, including errors, returns JSON. We use OAuth2 for our API authorization.

The API documentation is generated programmatically, meaning that the API docs are kept accurate and up to date. Everything that can be done via the API will be reflected in the documentation.

To find out which properties and values can be included when making a POST, PUT, GET, or UPDATE call on an object via the API, check the API Reference. There are examples reflecting all possible ways to interact with that object.

Endpoints

All API requests are made to https://api.selz.com/ and all requests are served over HTTPS. The current version is v1.

JSON

All responses, including errors, return JSON. Also our POST and PUT requests require a JSON object for the body. There are more specific examples provided in the Resources.

{
  "id": "526087bd316c9af61145dd87",  
  "first_name": "John",
  "last_name": "Doe",
  "email": "johndoe@selz.com",
  "company": "Selz Inc"    
}

Versioning

At present v1 is the current and only version for TransferWise API.

Date format

All dates in the API use UTC and are strings in the ISO 8601 "combined date and time representation" format:

2016-06-15T15:50:38Z

Rate Limiting

Requests are limited to 200 requests per minute per account. All API keys associated with the same account count against the minute rate limit. Response code 429 is returned when the throttle limit has been reached. A "Retry-After" header is returned indicating how many seconds to wait until retry. Applicable to all requests

Authentication

Selz uses OAuth 2 for API authentication and authorization. Calls done in behalf of the user require the access token.

Authenticate your account when using the API by including your secret Access Token in the request. You can manage your Access Token in the Developer settings of Selz. Your Access Token carry many privileges, so be sure to keep them secret!

User Authentication

This type uses an access token for a specific user and app pair, in order to operate on that user's account, to the extent allowed by that app's permission.

Example:

curl --request GET \
  --url 'https://api.selz.com/customers?limit=10" \
  --header 'accept: application/json' \
  --header 'authorization: Bearer YOUR ACCESS TOKEN HERE'

OAuth 2

1. Start

Execute this link in the browser address bar:

https://api.selz.com/oauth/authorize?response_type=code&client_id=sampleid&redirect_uri=https://example.com

Replace sampleid with your client ID and example.com with your designated redirect URI.

2. Log in / sign up

A new page will open where user must log in with his Selz details or sign-up if using Selz for the first time.

3. Get the authorization code

After a login/signup, an authorization code is generated and appended to the redirected URI, e.g.

https://www.example.com/?code=[CODE]

4. Get the token

To exchange an authorization code for the token, replace client_id:client_secret with your credentials and replace [CODE] with the generated authorization code, also update client_id and redirect_uri values accordingly:

curl \
-u 'client_id:client_secret' \
-d 'grant_type=authorization_code' \
-d 'client_id=sampleid' \
-d 'code=[CODE]' \
-d 'redirect_uri=https://example.com' \
'https://api.selz.com/oauth/connect/token'

Your request will be followed by a response that inculdes access token and a refresh token:

{
  "access_token":"2354k235h432h5j432hkl54h2",
  "token_type":"bearer",
  "refresh_token":"314312jkl432j1k4321j4321432j1",
  "expires_in": 43199,
  "scope":"api_all"
}

All set! Your token is now ready to use. Add your token in the request header for the endpoints that require it (see example with each request).

5. Swapping a token

In order to maintain uninterrupted connection, you can request a new access token whenever it is close to expiring. You can do this by providing the refresh token:

curl \
'https://api.selz.com/oauth/connect/token' \
-u 'client_id:client_secret' \
-d 'grant_type=refresh_token' \
-d 'refresh_token=[your refresh token]'

...and get the new access token in response:

{
  "access_token":"fds78fdf89ds8a7fd09sa",
  "token_type":"bearer",
  "refresh_token":"j4kl325kl4325kl423jkl5432",
  "expires_in":43199,
  "scope":"transfers"
}

Errors

Selz uses conventional HTTP response codes to indicate the success or failure of an API request. In general, codes in the 2xx range indicate success, codes in the 4xx range indicate an error that failed given the information provided (e.g., a required parameter was omitted), and codes in the 5xx range indicate an error with Selz's servers.

HTTP Status code summary

Status code Description
200 - OK Everything worked as expected.
201 - Created The request to POST or PUT a record was successful.
204 - Deleted The request to DELETE a record was successful
400 - Bad request The request was unacceptable, often due to missing a required parameter.
401 - Unauthorized No valid Access Token provided.
404 - Not Found The requested resource doesn't exist.
429 - Too Many Requests Too many requests hit the API too quickly.
50x - Server Errors Something went wrong on Selz's end.

Example of error

{
  "type": "invalid_request_error",  
  "errors": {
    "email": [
      "Email field is not a valid email address."
    ]
  }
}

Pagination

All top-level API resources have support for bulk fetches via "list" API methods.

For instance you can list charges, list customers, and list orders. These list API methods share a common structure, taking at least these three parameters: limit, starting_after, and ending_before. Selz utilizes cursor-based pagination via the starting_after and ending_before parameters. Both take an existing object ID value. The ending_before parameter returns objects created before the named object, in descending chronological order. The starting_after parameter returns objects created after the named object, in ascending chronological order. If both parameters are provided, only ending_before is used.

Arguments

Argument Description
limit A limit on the number of objects to be returned, between 1 and 100.
starting_after A cursor for use in pagination. starting_after is an object ID that defines your place in the list. For instance, if you make a list request and receive 100 objects, ending with obj_foo, your subsequent call can include starting_after=obj_foo in order to fetch the next page of the list.
ending_before A cursor for use in pagination. ending_before is an object ID that defines your place in the list. For instance, if you make a list request and receive 100 objects, starting with obj_bar, your subsequent call can include ending_before=obj_bar in order to fetch the previous page of the list.

Response

data An array containing the actual response elements, paginated by any request parameters.
has_more Whether or not there are more elements available after this set. If **false**, this set comprises the end of the list.

Example Response

{
  "limit": 10,
  "has_more": false,
  "data": [
    {
      "id": "583ccd51131bbc2518346a4a",
      "created_time": "2016-11-29T00:35:29.052Z",
      "first_name": "John",
      "last_name": "Doe",
      "email": "support+1@selz.com",
      "company": null,
      "delivery_address": null,
      "billing_address": null
    },
    {
      "id": "58201857131bbd130c0b6ff8",
      "created_time": "2016-11-07T05:59:51.754Z",
      "first_name": "matthew",
      "last_name": "kempe",
      "email": "support+2@selz.com",
      "company": null,
      "delivery_address": null,
      "billing_address": null
    }    
  ]
}

Webhooks

Webhooks are a way for web apps to get real-time notifications of some operations in Selz. Once you register a URI to receive webhooks, Selz will send an HTTP request to that URI every time there's a change for any of your app's registered users.

Available events

Type Description
blog_post_created Occurs whenever a blog post is created
blog_post_updated Occurs whenever a blog post is updated
blog_post_deleted Occurs whenever a blog post is deleted
category_created Occurs whenever a category is created
category_updated Occurs whenever a category is updated
category_deleted Occurs whenever a category is deleted
customer_created Occurs whenever a customer is created
customer_updated Occurs whenever a customer is updated
customer_deleted Occurs whenever a customer is deleted
discount_created Occurs whenever a discount is created
discount_updated Occurs whenever a discount is updated
discount_deleted Occurs whenever a discount is deleted
order_payment_succeeded Occurs whenever an order payment succeeded
order_completed Occurs whenever an order payment is completed
product_created Occurs whenever a product is created
product_updated Occurs whenever a product is updated
product_deleted Occurs whenever a product is deleted
product_soldout Occurs whenever a product is sold out
product_variant_soldout Occurs whenever a product variant is sold out

Securing Webhooks

Webhooks can be verified by calculating a digital signature.

Each Webhook request includes a HTTP_X_SELZ_SIGNATURE header along with the data sent in the request. To verify that the request came from Selz, compute the HMAC digest according to the following algorithm and compare it to the value in the HTTP_X_SELZ_SIGNATURE header. If they match, you can be sure that the Webhook was sent from Selz and the data has not been compromised.

PHP Example

<?php
$json = json_decode(file_get_contents('php://input'), TRUE);
$message = $json["timestamp"].$json["token"];

if(!ValidateSignature($message)){
    header("HTTP/1.1 400 BAD REQUEST");
    die();
}

// Do something with webhook payload
$orderId = $json["data"]["id"];

header("HTTP/1.1 200 OK");
die();

function CreateSignature($message) {
    $key = 'your_webhook_verification_key';

    $aMessage = iconv(iconv_get_encoding("internal_encoding"), "ASCII", $message);
    $aKey = iconv(iconv_get_encoding("internal_encoding"), "ASCII", $key);

    $sig = hash_hmac('sha256', $aMessage, $aKey, true);
    return base64_encode($sig);
}

function ValidateSignature($message){
    $signature = $_SERVER["HTTP_X_SELZ_SIGNATURE"];
    $signatureToCompare = CreateSignature($message);

    return ($signature == $signatureToCompare);
}
?>

Node.js Example

var crypto = require('crypto');

app.post('/webhook', function (req, res) {
    // https://github.com/visionmedia/express/wiki/Migrating-from-3.x-to-4.x
    // express 3.x: app.use(express.bodyParser());
    // express 4.x: app.use(bodyParser());
    var json = req.body;
    res.send(200);

    var signature = req.headers['x-selz-signature'];
    var message = json["timestamp"] + json["token"];


    if (validateSignature(message, signature)) {
        console.log('OK');
    } else {
        console.log('ERROR');    
    }
});


function createSignature(message) {
    var key = 'your_webhook_verification_key';

    return crypto.createHmac('sha256', key).update(message, 'ascii').digest('base64');
}

function validateSignature(message, signature) {
    var signatureToCompare = createSignature(message);

    return (signature == signatureToCompare);
}

Best practices

Always respond to webhooks quickly

To make sure you can always respond within ten seconds, you should always do your work on a separate thread (as in the simple example above) or asynchronously using a queue.

Manage concurrency

When a user makes a number of changes in rapid succession, your app is likely to receive multiple notifications for the same user at roughly the same time. If you're not careful about how you manage concurrency, your app can end up processing the same changes for the same user more than once. For some applications, this is not a serious issue. Work that can be repeated without changing the outcome is called idempotent. If your app's actions are always idempotent, you don't need to worry much about concurrency. Unfortunately, not every app can be made idempotent easily. For example, suppose you have an app that sends email every time a certain file is changed. To avoid sending duplicate emails, you need to make sure that your app never processes the same user on multiple threads/processes at the same time.