REST API

This guide covers the inner working of Spree’s RESTful API. It assumes a basic understanding of the principles of REST. More specifically, the guide will provide information on the following:

  • The various resources available through the API
  • How to make a basic API call
  • Specific examples on how to access certain resources
  • Tools and other suggestions for working with the API

1 Overview

The REST API is designed to give developers a convenient way to access data contained within Spree. With a standard read/write interface to store data, it is now very simple to write third party applications (ex. iPhone) that can talk to Spree. It is also possible to build sophisticated middleware applications that can serve as a bridge between Spree and a warehouse or inventory system.

2 Available Resources

The API currently only supports a limited number of resources. The current list reflects the needs of paying clients who funded the development of the API. The list will be expanded soon to cover additional resources. Adding more resources is simply a matter of making the time for testing and investigating possible security implications.

Spree currently supports RESTful access to the following resources:

  • Orders
  • LineItems
  • Shipments
  • InventoryUnits
  • Products
  • Countries
  • States

3 JSON Data

Developers communicate with the Spree API using the JSON data format. Requests for data are communicated in the standard manner using the HTTP protocol.

For example, the request GET /api/orders/R664048424.json will produce output similar to the following:

{
  "order": {
    "number": "R664048424",
    "completed_at": "2010-01-11T02:07:43-05:00",
    "adjustment_total": 11.37,
    "created_at": "2010-01-11T02:07:43-05:00",
    "shipments": [
      {
        "number": "1284318100104",
        "created_at": "2010-01-11T12:07:40-05:00",
        "shipping_method": {
          "name": "UPS Ground",
          "created_at": "2010-01-11T12:07:30-05:00",
          "updated_at": "2010-01-11T12:07:30-05:00",
          "zone_id": 2,
          "id": 574015644
        },
        "shipped_at": null,
        "cost": 5.0,
        "updated_at": "2010-01-11T12:07:40-05:00",
        "tracking": null,
        "order_id": 1035625630,
        "id": 1002961742,
        "address_id": 607887544,
        "shipping_method_id": 574015644,
        "address": {
          "city": "North Serenahaven",
          "address1": "6957 Alyce Wells",
          "created_at": "2010-01-11T12:07:39-05:00",
          "address2": "Apt. 741",
          "zipcode": "16804",
          "updated_at": "2010-01-11T12:07:39-05:00",
          "country_id": 214,
          "state_name": null,
          "lastname": "Nikolaus",
          "id": 607887544,
          "phone": "502-510-7481 x3973",
          "firstname": "Vallie",
          "state_id": 889445952,
          "alternative_phone": null
        },
        "state": "pending"
      }
    ],
    "updated_at": "2010-01-11T12:07:43-05:00",
    "token": null,
    "total": 153.68,
    "item_total": 142.31,
    "id": 1035625630,
    "bill_address": {
      "city": "Goyettebury",
      "address1": "628 Eleanora Ferry",
      "created_at": "2010-01-11T12:07:39-05:00",
      "address2": "Apt. 655",
      "zipcode": "16804",
      "updated_at": "2010-01-11T12:07:39-05:00",
      "country_id": 214,
      "state_name": null,
      "lastname": "Hammes",
      "id": 1046447419,
      "phone": "1-160-338-6820",
      "firstname": "Kathlyn",
      "state_id": 889445952,
      "alternative_phone": null
    },
    "user_id": 607882993,
    "line_items": [
      {
        "price": 22.99,
        "variant": {
          "price": 22.99,
          "count_on_hand": 10,
          "product_id": 723959550,
          "is_master": true,
          "depth": null,
          "deleted_at": null,
          "cost_price": 21.0,
          "weight": null,
          "id": 504220342,
          "sku": "ROR-00012",
          "height": null,
          "width": null
        },
        "created_at": "2010-01-11T12:07:30-05:00",
        "quantity": 1,
        "updated_at": "2010-01-11T12:07:30-05:00",
        "order_id": 1035625630,
        "id": 648459260,
        "variant_id": 504220342
      },
      {
        "price": 15.99,
        "variant": {
          "price": 15.99,
          "count_on_hand": 10,
          "product_id": 459084718,
          "is_master": true,
          "depth": null,
          "deleted_at": null,
          "cost_price": 13.0,
          "weight": null,
          "id": 215054540,
          "sku": "ROR-00011",
          "height": null,
          "width": null
        },
        "created_at": "2010-01-11T12:07:30-05:00",
        "quantity": 2,
        "updated_at": "2010-01-11T12:07:30-05:00",
        "order_id": 1035625630,
        "id": 995042986,
        "variant_id": 215054540
      }
    ],
    "state": "new",
    "credit_total": 0
  }
}

4 Making an API Call

4.1 Authentication

You will need an authentication token to access the API. These keys can be generated on the user edit screen within the admin interface. When using the authentication token, you will not need your password. Spree now uses HTTP Basic Authentication, and most implementations assume that you’ll want to have a password, if you are required or prompted for one just pass a dummy like “X”.

curl -u bb51d44e:x http://example.com/api/orders.json

The token allows the request to assume the same level of permissions as the actual user to whom the token belongs.

The admin role is fairly “powerful” by default. Consider creating a new role and assign a more limited set of permissions. This way you can create a user and corresponding token that has more narrow permissions (ex. read only access to orders.)

4.2 HTTP Methods

Your requests must use the correct HTTP method.

  • GET for listing and viewing individual resources
curl -u V8WPYgRdSZN1mSQG17sK:x \
http://example.com/api/orders.json
curl -u V8WPYgRdSZN1mSQG17sK:x \
http://example.com/api/orders/R123123.json
  • POST for creating resources
curl -X POST -u DBBgEG6ko38ChCSA6ZJM:x \
-d '{"product":{"name":"foo","description":"foo","price":"12"}}' \
http://localhost:3000/api/products.json \
-H "Content-Type:application/json"
  • PUT for updating existing resources
curl -X PUT -u V8WPYgRdSZN1mSQG17sK:x \
-d '{"shipment":{"shipping_...},...}' \
http://localhost:3000/api/orders/R123123/shipments.json \
-H "Content-Type:application/json"

4.3 Other Supported Formats

Currently the API supports only the JSON format.

Supporting the XML format should not be difficult since Rails supports it out of the box. Volunteers who are willing to provide a patch (and tests) for XML support are encouraged to do so.

4.4 Searching

All list actions support filtering using Search Logic parameters. For example, to view all shipments that are ready to ship and that were created since the date 2010-01-01:

GET api/shipments.json?search[state]=ready_to_ship\
&search[created_at_greater_than]=2010-01-01

Use —globoff when passing SL params through Curl to avoid syntax issues.

For more details on parameters syntax please see the Search Logic README.

4.5 Creating and Updating Resources

Parameters should be supplied in the request body as a JSON encoded hash of attributes.

Successfully creating a resource will result in a 201 Created response code while updating will result in 200 OK. If creating or updating a resource fails the result will be a 500 Server Error response. You can then consult the Rails logs to see more details on why the request resulted in a failure.

5 API Reference

5.1 Orders

Orders support the following operations

  • Index
  • Show

The following examples demonstrate the API using Order#id. You can also perform the same operations using Order#number.

5.1.1 List Orders

Request:

GET /api/orders.json

Response:

[
  { order: { ... } },
  { order: { ... } },
  ...
]
5.1.2 View Order

Request:

GET /api/orders/{order_id}.json

Response:

{
  order: {
    ...
  }
}
5.1.3 Line Items for an Order

Request:

GET /api/orders/{order_id}/line_items.json

Response:

[
  { line_item: { ... } },
  { line_item: { ... } },
  ...
]
5.1.4 Create a Line Item on an Order

Request:

POST /api/orders/{order_id}/line_items.json
...

Response:

HTTP Status: 201 Created
Location: http://example.com/api/orders/{order_id}/line_items/{new_line_item_id}.json
5.1.5 Update a Line Item

Request:

PUT /api/orders/{order_id}/line_items/{line_item_id}.json
...

Response:

HTTP Status: 200 OK
5.1.6 Shipments for an Order

Request:

GET /api/orders/{order_id}/shipments.json

Response:

[
  { shipment: { ... } },
  { shipment: { ... } },
  ...
]
5.1.7 Create a Shipment for an Order

Request:

POST /api/orders/{order_id}/shipments.json
...

Response:

HTTP Status: 201 Created
Location: http://example.com/api/orders/{order_id}/shipments/{new_shipment_id}.json
5.1.8 Fire State Event

Request:

PUT /api/orders/{order_id}/event.json?e={event_name}

Response (success):

HTTP Status: 200 OK

Response (failure):

HTTP Status: 422

5.2 Shipments

5.2.1 List Shipments

Request:

GET /api/shipments.json

Response:

[
  { shipment: { ... } },
  { shipment: { ... } },
  ...
]
5.2.2 View Shipment

Request:

GET /api/shipments/{shipment_id}.json

Response:

{
  shipment: { ... }
}
5.2.3 Update Shipment

Request:

PUT /api/shipments/{shipment_id}.json
...

Response:

HTTP Status: 200 OK
5.2.4 Fire State Event

Request:

PUT /api/shipments/{shipment_id}/event.json?e={event_name}

Response (Success):

HTTP Status: 200 OK

Response (Failure):

HTTP Status: 422

5.3 Inventory Units

5.3.1 List Inventory Units

Inventory units can be accessed by association to order or shipment. They can also be accessed independently.

Request:

GET /inventory_units.json
GET /orders/{order_id}/inventory_units.json
GET /shipments/{shipment_id}/inventory_units.json

Response:

[
  { inventory_unit_: { ... } },
  { inventory_unit: { ... } },
  ...
]
5.3.2 View Inventory Unit

Request:

GET /inventory_units/{inventory_unit_id}.json

Response:

{
  inventory_unit: { ... }
}
5.3.3 Update an Inventory Unit

Request:

PUT /inventory_units/{inventory_unit_id}.json
...

Response:

HTTP Status: 200 OK
5.3.4 Fire State Event

Request:

PUT /inventory_units/{inventory_unit_id}/event.json?e={event_name}
...

Response (success):

HTTP Status: 200 OK

Response (failure):

HTTP Status: 422

6 Tools and Suggestions

6.1 Curl

Users on a Unix-based system may want to use the well known curl library. This is a great way to test out your API calls on the command line. Here are some additional hints for using curl to test your API.

6.1.1 Use Accept Headers

Instead of using the .json suffix for your URL’s you can use -H "Accept:application/json" instead. For example:

curl -u V8WPYgRdSZN1mSQG17sK:x \
http://example.com/api/orders \
-H "Accept:application/json"
6.1.2 Use --globoff to Solve Syntax Issues
curl -u V8WPYgRdSZN1mSQG17sK:x --globoff \
http://example.com/api/orders.json
6.1.3 Use -i Option to Inspect Headers

You may find yourself wanting to know the exact header returned as a result of your call. In this situation use the -i directive.

curl -u V8WPYgRdSZN1mSQG17sK:x -i \
http://example.com/api/orders.json

Use -I option instead if you’re only interested in returning the header information.

6.1.4 Use Content-Type Headers when Submitting JSON

When using POST/PUT to create/update a resource, you should also specify that you are passing JSON with a Content-Type header. This ensures that Rails interprets the params correctly.

-H "Content-Type:application/json"
6.1.5 Use the -d Option to Pass Params

Make sure you are passing parameters properly with curl. For instance, the URL of http://localhost:3000/api/orders/1035625630/event.json?e=cancel can be expressed as follows:

curl -X PUT -u DBBgEG6ko38ChCSA6ZJM:x \
http://localhost:3000/api/orders/1035625630/event.json \
-d 'e=cancel'

The above approach may solve unexplained “HTTP/1.1 411 Length Required” errors when using the REST API with curl and webrick.

This project is maintained by a core team of developers and is freely available for commercial use under the terms of the New BSD License.