General

The API is based on the principals of REST which makes it a RESTful webservice. This means:

Authentication

You need to be authenticated if you wish to make any requests to the API. Each application (either website, mobile application, desktop application) requires an API-key to identify the application against the API. The API key is unique per application and shouldn't be shared.

You need 3 things to authenticate the user with the API:

  • API key
  • Username
  • Password

The API key needs to be sent with in a header named X-ApiKey. The username and password are in the message body in the preferred media type format. There is a 3rd optional body parameter named RememberMe. This will determine if the session is temporary or permanent. The default value is false. This only applies to browser based clients.

Note that the Host header is required with every request.

Example request

POST https://s1-api-tst.fource.nl/api/auth/credentials HTTP/1.1
X-ApiKey: [ApiKey]
Host: https://s1-api-tst.fource.nl.nl
Content-Length: 38
Content-Type: application/json
Accept: application/json
{"UserName":"[UserName]", "Password":"[Password]", "RememberMe": [true|false] }
                        

Example response

HTTP/1.1 200 OK
Cache-Control: private
Content-Type: application/json; charset=utf-8
Server: Microsoft-IIS/8.5
X-Powered-By: ServiceStack/3.971 Win32NT/.NET
Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS
Access-Control-Allow-Headers: X-ApiKey, Content-Type, Authorization
Access-Control-Expose-Headers: Location
Access-Control-Allow-Credentials: true
X-AspNet-Version: 4.0.30319
Set-Cookie: ss-id=P7/JJm75PKnIDw+THfzS; path=/; HttpOnly
Set-Cookie: ss-pid=IwEIOR4NqRKqKHVV8PaG; expires=Sat, 27-May-2034 13:16:04 GMT; path=/; HttpOnly
Date: Tue, 27 May 2014 13:16:06 GMT
Content-Length: 71
{"sessionId":"P7/JJm75PKnIDw+THfzS","authToken": "5e6d7248-668d-4197-819a-fe330b0579de","userName":"[username]","responseStatus":{}}
                        

You will receive a session id when you successfully authenticate. You will need to send this session id as the ss-id cookie with every request. Note that the server also returns several Set-Cookie headers. This will instruct any browser to automatically set those cookies for the current host. Also note that they are HttpOnly which means you cannot access these cookies using javascript.

Session ID cookies are automatically set when the authentication is done within a browser. There will be a ss-id cookie when RememberMe was false and a ss-pid when RememberMe was true

Together with a session id, an authentication token is provided that will allow to open the B2B website without the need to log in. The token can only be used once and is valid for 10 minutes.

Single-sign-on with B2B

We highly recommend building your application on top of the API. However, We are aware this is not viable for all integration partner. The B2B portal can be used in order to communicate with the API.

Start B2B with token

After obtaining an Authentication Token, the B2B web portal should be started using the url: https://s1-tst.fource.nl?auth_token=[tokenId]. The B2B website will then use the same session as the one the initial authentication request started. Be aware that this token expires after 10 minutes, and can only be used once. Also note that some browsers/web components will overwrite cookies across all instances, therefor only 1 session can be active at a given time and using another token will discard the previous session.

Code examples

C# API example

A simple example authenticating and getting the user info. It uses the RestSharp nuget package.

The flow from the code example:

  • Authenticate the web browser control with a POST authenticate request using credentials and API key
  • The response with the cookie will be set in the web browser control
  • Read the sessionid from the response
  • Load https://s1-tst.fource.nl and be logged in
  • Communicate with the API with the sessionid in the same session as the user in the B2B website
class Program
{
    private static RestClient _client;
    static void Main(string[] args)
    {
        _client = new RestClient("https://s1-api-tst.fource.nl/api");
        var sessionId = Authenticate();
        _client.AddDefaultParameter("ss-id", sessionId, ParameterType.Cookie);
        var account = GetAccount();
        Console.WriteLine(account.DisplayName);
        Console.ReadLine();
    }
    static string Authenticate()
    {
        var request = new RestRequest("auth/credentials", Method.POST);
        request.AddHeader("X-ApiKey", "[ApiKey]");
        request.AddParameter("UserName", "[username]");
        request.AddParameter("Password", "[password]");
        return _client.Execute<AuthenticationResult>(request).Data.SessionId;
    }
    static Account GetAccount()
    {
        var request = new RestRequest("account", Method.GET);
        return _client.Execute<Account>(request).Data;
    }
}
public class AuthenticationResult
{
    public string SessionId { get; set; }
}
public class Account
{
    public string DisplayName { get; set; }
}           
                        

C# iframe example

A simple example authenticating and getting the cart from a windows forms application while letting the user use the B2B website.

download Zip

Javascript example

This example uses the jQuery library to make the ajax calls.

$(function () {
    $.ajax({
        url: 'http://s1-api-tst.fource.nl/api/auth/credentials',
        type: 'POST',
        accept: 'application/json',
        contentType: 'application/json',
        dataType: 'json',
        cache: false,
        data: JSON.stringify({
            UserName: '[username]',
            Password: '[password]',
            RememberMe: [true|false]
        }),
        beforeSend: function (xhr) {
            // you only need to do this when authenticating
            xhr.setRequestHeader('X-ApiKey', '[api key]');
        },
        success: function (result) {
            // authentication was succesful and the browser should have handeld the ss-(p)id cookies.
        },
        error: function (xhr) {
            // an error occured
        }
    });
});
                        

Sessions

Every user that authenticates gets assigned a new session. The session contains the information about the products in the cart and the products the user has placed in the RMA order. There are two scenario's where the session id might change:

  1. The users places an (RMA) order. Everything about an order is tied to a session. So you will get a new session id in case the user would like to place another (RMA) order.
  2. A session expires. The current session lifetime scope is about twenty minutes. Reason for this is that products which are placed in the cart, will be reserved (based on the quantity) in order to guarantee the product is still in stock when the order is placed. These reservations are released when the session expires. When a request is made with an expired session id, The API will provide a new session id.
Both scenario's are handled automatically by the browser because the server sends back a Set-Cookie header with the new session id. Other clients will need to look for the Set-Cookie headers manually in every response.

There are some rare cases where the session will become corrupted. This will result in a forced logout. You will get a HTTP response with status code 401 and broken-session as the message.

Dates

All dates have the following format: /Date(1400000000000)/. The number within the parentheses is an integer value representing the number of milliseconds since 1 January 1970 00:00:00 UTC (Unix Epoch). This representation can be parsed with JavaScript using var date = eval('new Date(1400000000000)') or for example using the moment.js library like: moment('/Date(1400000000000)/'). One could use a regex to extract the milliseconds (\d+).

CORS

CORS is enabled, browser based clients will be able to make requests to the API. However, there are some limitations. For example; older browsers will not be able to send PUT or DELETE request in a CORS context.

Error handling

Errors that are being thrown by the application result in a response with a HTTP status code and optionally an error message. Here you can find a general list with HTTP status codes.

Every invalid request will result in HTTP status code with 400 and a message describing why the request is invalid. These error-messages should be handeld by your application to enhance user experience.

All requests that are being issued without a session id or with an invalid session id will result in HTTP status code 401. You will need to retreive a new session id by authenticating again.

Any internal problems will result in HTTP status code 500. This usually means something is seriously wrong on our site and you should contact us.

We use alot of 3rd party online services to complement our own service. The 504 HTTP status code will be returned when one of these services are offline or they are performing unexpected behaviour.

It's possible to create a permanent session when logging in. After a few days of inactivity the session will be reset. The API will send the 205 HTTP status code to notify that this has happend. When this occurs the cart will be emptied. The request can be retried in order to receive it's actual result.

Error messages

Name Description
api-key-not-found Cannot create application for the api. Input was invalid.
auth-invalid-api-key The provided Api key is unknown or invalid.
auth-invalid-username-password The username and/or password do not match.
backorder-routeid-invalid A backorder delivery route needs to be provided.
bad-action-accu-retour This product should be added to the retour order instead of the cart.
bad-action-diesel-retour This product should be added to the retour order instead of the cart.
bad-action-tire-retour This product should be added to the retour order instead of the cart.
broken-session The session was corrupted. A forced logout has been performed.
carid-is-required The car id is required.
cart-is-locked The cart is locked for changes.
cart-invalid-quantity The provided quantity is invalid. The quantity should 1 or higher.
cart-unknown-product The provided product id is unknown or invalid. Product id's should be 9 characters long.
could-not-set-account-setting Unable to set property, could not parse value or unknown setting name.
cart-customerorderreferencenumber-empty The custom order reference number should not be empty.
debtor-not-found The requested debtor was not found
defered-routeid-invalid A defered delivery route needs to be provided.
cart-deliveryroute-empty A delivery route needs to be provided.
direct-routeid-invalid A direct delivery route needs to be provided.
file-to-large The file is to large.
insufficient-permissions-accu-retour The user is not allowed to return car batteries.
insufficient-permissions-auto-data The user is not allowed to use autodata.
insufficient-permissions-create-news The user is not allowed to create news items.
insufficient-permissions-create-notification The user is not allowed to create notifications.
insufficient-permissions-delete-news The user is not allowed to delete news items.
insufficient-permissions-delete-notification The user is not allowed to delete notifications.
insufficient-permissions-diesel-retour The user is not allowed to request a 'diesel injector test'
insufficient-permissions-retours The user is not allowed to use retours.
insufficient-permissions-tire-center The user is not allowed to perform this tire center action.
insufficient-permissions-tire-retour The user is not allowed to return tires.
invalid-carid The provided car id is not valid.
invalid-categoryid The provided category id is invalid.
invalid-date The given date is not valid
invalid-input-date-format The provided date was is invalid.
invalid-delivery-address-id The selected delivery address is invalid.
invalid-end-date The provided date was is invalid.
invalid-file-extension Invalid file extension
invalid-gaik-submission The gaik submission is not complete. The order cannot be processed.
invalid-input-on-create-application Cannot create application for the api. Input was invalid.
invalid-interval-ids The provided internal idid'ss are invalid
required-length-for-{0}-is-{1} Indicates that the given parameter does not match it's length requirement.
invalid-license-plate The provided licenseplate is valid. This wikipedia page lists all valid formats
invalid-parameter-value An invalid parameter value. Parameter types can be found in the api reference.
invalid-productid The provided product id is invalid. Product id's should be 9 characters long.
invalid-retourid The provided route id is invalid.
invalid-vin Invalid VIN
{0}-is-invalid Indicates that the given parameter value is invalid.
{0}-is-required Indicates that the given parameter is required.
{0}-is-unknown Indicates that the given parameter value is unknown.
items-on-route-do-not-match The quantity of items do not match the quantity of items in the cart
max-quantity-per-route-overflow Cannot update the retour item. The maximum quantity per route has been reached.
missing-required-parameter Missing required parameter. Refer to the api reference for a full overview of parameters.
mpm-down The mpm service we are depending on is responding in an unexpected manner
news-id-already-exists The provided news item id already exists.
no-catalogue-found
no-productid A product id has to be provided.
no-productids One or more product id's have to be provided
not-found The requested resource was not found
notificationid-already-exists The provided notification id already exists.
cart-paymentMethod-empty The payment method should not be empty.
cart-orderwithoutitemsincart-invalid The order could not be placed because the cart was empty.
reset-session The session was reset. Retry the request for actual results.
retour-invalid-deposit-amount The deposit amount needs to be larger then 0 when it's a deposit retour.
retour-invalid-quantity Adding a retour with negative quantity is not allowed.
retour-no-retourid The retour item id is required.
retour-no-orderid Only products that belong to a certain order id and order line id can be returned.
retour-no-orderlineid Only products order-line-id can be returned.
retour-no-productid The product id has to be provided in order to add it to the retours
retour-not-retourable The product cannot be retoured
routeid-expired The chosen route is expired. Make sure to update the routes before trying to place an order.
routeid-invalid The chosen route is expired. Make sure to update the routes before trying to place an order.
products-search-invalid-categoryid-or-genericarticleids No category id or generic article ids were provided while searching for products for a car
invalid-searchterm-null-empty-or-whitespace The searchterm cannot be null or empty.
shipping-unknown The chosen route is expired. Make sure to update the routes before trying to place an order.
tire-center-process-error Could not procces tireCenter request, relogging might fix this
too-many-results-refine-the-search The search resulted with more then 200 results. The user should refine his search.
unknown-carid The provided car id does not exist.
cartitem-unknown The provided cart item id does currently not exist.

History

April 30 2014 WithoutPriceStock is now deprecated

The WithoutPriceStock request parameter is now deprecated. From now on will the prices and stock quantities always be retrieved because the performance impact is minimal. We've replaced the flag with another flag named WithoutRouteInfo. Route info will not be loaded if this flag is set to true. This will save you about 3 seconds for each product in the response. We will remove this flag on June 30 2014.

July 15 2014 withoutAutodataInfo is now deprecated

The withoutAutodataInfo request parameter (used by the Get Carinfo requests)is now deprecated. From now on the AutodataCarInfos will always be returned; based on the account permissions. AutodataCarInfo will no longer contain TechnicalInfo, this information can be retrieved by calling car/autodata/{autodataid}/technicalinfo. The flag will be completely removed after April 15 2014.

September 11 2015 cart/placeorder is now deprecated

Replaced by cart/place-order and cart/place-simple-order

Products

When searching products or retrieving products based on cars and categories, you can receive more than just the products. This chapter describes how to interpertate this additional information. For example; a duration property is returned when searching for products, the duration property describes how long the call took, and how long calls to external services took

Filters

Each request for products will return a property called filterOptions. This is an array of filters that can be applied to the list of products. Each filter options consists of

  • A human readable name
  • A unique key
  • A filterType which specifies the type of filter (Multiselect, NumberRange, DateRange)
  • isAttribute which specifies whether the filter is on the properties of a product or applies to the attributes list of the product
  • Values is an array of all the possible values for the filter option. Each value consists of
    • an unique Id
    • human readable value
    • a list of conforming productIds
  • productsWithoutValue specifies how many product have not specified value for this filter option
  • priority specifies the sorting regarding the filter options

Assets

Some request will provide assets to provide the user with additional context, for example, where retrieving products of the category "10000000028" for a car will provide an image with the layout of different parts belonging to that car.

Assets consists of an array of different assets. Each asset consists of:

  • Human readable name
  • An image link imageUrl
  • productAssetMaps, an array defining clickable area's on the image. They can be used to apply filters to the product list. each productAssetMap consists of:
    • groupId, a corresponding value can be found within filter options
    • coordinates, an array defining the corners of a polygon, used to draw an area over the provided image.

Paging

Requesting large amount of products (100+) can cause long response times, therefor it's advisable to make use of the pagination provided in the API. Requests that retrieve a list of products have a skip and take query parameter. Skip 100 and take 10 will return the products #101 to #110.

Backorder Info

Some products are possible to be backorders. When Fource does not have the product in stock. Backorder information can be requested in order for Fource to check external parties and request stock information there. For example, the user needs 5 of product X, Fource has only 3 products in stock. An external party has 4 in stock. When the user tries to put 5 in his cart. Backorder information can be requested for the remaining 2 products.

When requesting backorder information, you'll need to calculate the quantity needed for the backorder yourself. (Total needed quantity - quantity in stock)

Placing an order

You can add products to the cart using POST /cart/items with the specified product and quantity. The quantity can be changed using PUT /cart/items/{id} or be deleted using DELETE /cart/items/{id}. You can clear the cart in it's entirety using DELETE /cart/items.

There are several things the user needs to select so he or she can place an order. A user has to select his preferred payment method. The available payment methods for a user can be received with GET /account.

This can return multiple options depending on the user:

Id Value
0 Op rekening
1 Rembours
2 Contant

A user also has to specify delivery routes. There are a three types of delivery routes: direct, deferred and backorder. The type of delivery depends on the product and the requested quantity. The possible delivery routes can be received with GET /cart/shipping. Be aware that delivery routes can expire and so should be checked again when the user wants to place the order.

There are two additional options available for all route types. The user always has the option to collect the products himself. To send this option when placing an order send "A" as the route id. The user also has the option to let the order be sent later with another delivery. Note you can only send this option when you have received delivery routes. To send this option when placing an order send "N" as the route id.

The user also needs to provide an order reference. This is something that the client can use for it's own history system(s). This is a required field.

There are two ways to place an order. The ‘simple’ version allows you to provide an option indication whether all products should be shipped together, or the products should be shipped individually as soon as possible. The regular version allows for more flexibility in route selection.

Using the ‘simple’ version will require less work on your part. The regular version will require some additional logic on your part.

Simple version

Placing a simple order requires you to provide us with a Delivery option, we will select the appropriate routes for the given delivery option.

Given a Cart with one product with quantity 2 and shipping information on 2 different routes (R24572180800 & R24572181030).

Placing an order with option:

  • AsSoonAsPossible: This will set the routes in such a way that the items will be delivered as soon as possible. So 1 will be delivered on route R24572180800 and the other one on route R24572181030
  • AsSingleDelivery: This will set the routes in such a way that the items will be delivered as one package, since one of the items is not available on route R24572180800, both items will be delivered on route R24572181030
  • AsNotUrgent: This will let us decide when it is most convenient to ship.
  • AsPickUp: Will not be shipped.

The boolean ComplyWithDeliveryTimePreference can be used to specify whether delivery time preference should be honoured when selecting the routes. A user can configure the times they would (not) like to receive shipments. If for example a user has set that no routes should be selected on Saturdays and Sundays, then we will not select routes on Saturday or Sunday, but instead select the first route on Monday.

Regular version

Placing a regular order requires you to build a model representing the cartitems on a specific route. To do this you’ll need to know which routes are available. And which cart items can be placed on what route.

Generally the flow can be described as:

  1. Add product(s) to cart.
  2. Get the shipping information for each cart item (included in the create cart item response).
  3. Request cart routes from first to last time within the shipping information.
  4. Create a model for items on selected routes taking into account the shipping information
  5. Place order.
  6. If the request fails, the provided routes might have expired, so go back to step 2.

For example: This request places an order containing one product with quantity 1 on route R24572180800.

{
  "CustomerOrderReferenceNumber": "32-HL-BK",
  "PaymentMethod": "1",  
  "ItemsOnRoutes": [
    {
      "CartItemId": "001",
      "RouteId": "R24572180800",
      "Quantity": "1"
    }
  ]
}

This request places an order containing one product with quantity 2 on routes R24572180800 and R24572181030

{
  "CustomerOrderReferenceNumber": "32-HL-BK",
  "PaymentMethod": "1",  
  "ItemsOnRoutes": [
    {
      "CartItemId": "001",
      "RouteId": "R24572180800",
      "Quantity": "1"
    },
    {
      "CartItemId": "001",
      "RouteId": "R24572181030",
      "Quantity": "1"
    }
  ]
}

Each modification to the cart will also provide you with shipping information regarding the modified cart item. Note the delivery date, this cart item could be shipped on every cart route equal to or later than the specified delivery date.

"shipments": [
        {
          "shipmentId": "Direct",
          "shipmentLocation": "HOOFDMAGAZIJN HOENSBROEK",
          "deliveryDate": "/Date(1436853600000-0000)/",
          "quantity": 1
        }
]

To get a list of possible routes the resource GET /cart/routes should be called. This will return a list of possible routes for the logged in user. For this example is the first route equal to the delivery date of the cart item shipping information, therefore it’s a valid route for this cart item.

 {
  "routes": [
    {      "id": "R24572180800",      "when": "/Date(1436853600000-0000)/"    },
    {      "id": "R24572180915",      "when": "/Date(1436858100000-0000)/"    },
    {      "id": "R24572181045",      "when": "/Date(1436863500000-0000)/"    },
    {      "id": "R24572181200",      "when": "/Date(1436868000000-0000)/"    },
    {      "id": "R24572181330",      "when": "/Date(1436873400000-0000)/"    },
    {      "id": "R24572181500",      "when": "/Date(1436878800000-0000)/"    },
    {      "id": "R24572190800",      "when": "/Date(1436940000000-0000)/"    },
    {      "id": "R24572190915",      "when": "/Date(1436944500000-0000)/"    },
    {      "id": "R24572191045",      "when": "/Date(1436949900000-0000)/"    },
    {      "id": "R24572191200",      "when": "/Date(1436954400000-0000)/"    },
    {      "id": "R24572191330",      "when": "/Date(1436959800000-0000)/"    },
    {      "id": "R24572191500",      "when": "/Date(1436965200000-0000)/"    }
  ],
  "urgentMessages": []
}

Urgent messages

The admins can place an urgent message to notify the users. We provide these messages in every request. Even if the actual request fails. Every request contains a deserialized message containing it's actual result and the collection with urgent messages. Example:

{
   "products":[
      ...
   ],
   "urgentMessages":[
      {
         "id":1,
         "startDate":"\/Date(1403853780000-0000)\/",
         "level":"Error",
         "title":"Problemen bij TecDoc",
         "message":"Beste klant, <br>TecDoc Heeft momenteel een technische storing. Onze excuses voor de overlast."
      }
   ]
}
                

The level property can have one of these 3 values: Info, Warning or Error. This reflects the importance of that message and allows a developer to style the message accordingly. The message can contain html elements.