API Documentation
StorageRoom is a service and platform to manage any kind of content online and to easily integrate that content into one or multiple mobile, web or desktop apps.
The StorageRoom Application Programming Interface (API) has been created to allow you to interact programmatically with StorageRoom. The StorageRoom API returns data in JSON, so that you can easily integrate the content into your application or web site. As a developer you can program a large variety of applications on top of the StorageRoom API. There are no thematic limitations, be creative and start developing for your favorite device or platform by using your programming language of choice.
API Summary
The StorageRoom API is designed in a RESTful fashion and is easy to use. It relies on simple unique tokens for access authorization and for the authentication of your application. All Resources support the JSON and JSONP Format. Open Source Libraries are available and make it even easier to use StorageRoom as the data backend for your application.
Almost all of StorageRoom's functionality can be used by your application. Some of the most important features are:
- Search all your Entries to only get the content you require.
- Create a new Collection that contains further Entries
- Add new Entries (Import)
- Update or delete any Entry
- Load associated Entries through Relationships
- Use Webhooks to respond to Entry modifications
Examples
Collections are the containers for your data. They are very flexible, a Collection might define the following Fields:
Restaurant
| Field | Description | Type |
|---|---|---|
| Name | The name of the restaurant | String |
| Description | A long description | String |
| Stars | A rating (1-5) | Integer |
| Street | Street address | String |
| Postal Code | Postal code | String |
| City | City | String |
| Location | Geo coordinate | Location |
| Tags | A list of many tags | Array |
| Menu | A PDF of the menu | File |
| Logo | Logo including auto-generated user-defined thumbnails | Image |
| Category | Association to a Category in another Collection (1:n) | Association |
| Meta Data | Additional meta data such as the date the Entry was created or last modified. Per default meta data is prefixed with "@", but you can change this. |
Editors will get a simple but powerful interface to manage all those restaurants and you can query this data and display the content in your applications.
Table of Contents
The API documentation was written to be read from top to bottom. The top parts build upon each other and provide a good understanding of the API essentials.
- Architecture: Explains the general architecture of the API and how the RESTful web service is modeled
- Authentication: How to authenticate your application
- Errors: HTTP Response Codes and error messages
- Formats: Description of the various formats (JSON, JSONP)
- Resources: What the various Resources do and what they look like
- Embedded Resources: Explains the nested Embedded Resources
- Search: How to find the Entries you are looking for
- Endpoints: List of exposed Endpoints
- Webhooks: Webhooks send a request to your own server and respond to changes
- Demo: Browse some demo data
- Libraries: Open Source StorageRoom libraries
- Sample Code: Open Source example code
- FAQ: Frequently asked questions
- Support: What to do when you encounter problems
Getting Help
We tried our best to make the usage of our API as simple as possible. But if the API doesn't behave like you think it should, if documentation is missing or incorrect or if you have any questions, comments or suggestions, please read the support section.
Architecture
The StorageRoom API is designed in a RESTful fashion. REST (Representational State Transfer) is a set of design principles that state how Web resources can be described and manipulated via the HTTP protocol and URIs. As REST is leveraging existing Web standards it is very easy to consume RESTful web services with any programming language that can make HTTP requests. REST is an architectural style and not a formal protocol. There is no "official" way to build a RESTful web service. Our implementation focuses on simplicity and applicability.
In the next paragraphs we will briefly describe what the four key REST principles are and how they apply to the StorageRoom API. If you want to find out more about REST you can find links for further reading at the bottom of the page.
1. Every "thing" is a Resource with a unique identifier
All the "things" on StorageRoom are RESTful Resources. Resources are the nouns of the web service, they are holding all the data and they can be created, modified or removed.
All Resources need a unique identifier, so that they can be read or modified. The identifier of a Resource is not only unique within the StorageRoom API, it is globally unique in the Web by using URIs.
All the Resources within the StorageRoom API are identifiable by their full URI. Associations also depend on the full URIs to reference other Entries.
| URI | Description |
|---|---|
| http://api.storageroomapp.com/ |
All the Collections in an Account |
| http://api.storageroomapp.com/ |
All the Entries of a Collection |
| http://api.storageroomapp.com/ |
One specific Entry of a Collection |
Take a special look at the first two URIs, as it is not a single "thing", it is an Array of "things", but the Array is still represented as a Resource with a unique URI. Everything that is worth being identified within the web service has such an URI.
2. Resources are linked together
The Web is built out of pages, and all the pages contain links to other pages. The following is an HTML link to another page: Google's homepage. Resources in a REST web service contain links to other associated Resources, just like all the HTML pages in the Web contain links to other pages.
A link to another child Resource might look like this in the StorageRoom web service:
{
"entry": {
"@type": "Category",
"@version": 3,
"@updated_at": "2010-11-26T11:31:28Z",
"@created_at": "2010-11-26T11:31:28Z",
"@collection_url": "http://api.storageroomapp.com/accounts/:account_id/collections/:collection_id",
"@url": "http://api.storageroomapp.com/accounts/:account_id/collections/:collection_id/entries/:entry_id",
"name": "Music",
}
}
As you can see, the Resource contains a "@collection_url" to the Resource URI of the Collection of the Entry (http://api.storageroomapp.com/accounts/:account_id/collections/:collection_id). Associations also use Resource URIs to link to each other.
In general you should always follow the links within the documents that you receive via the API and not manually specify the appropriate URIs for Resources. This is in analogy to links that you follow on regular web pages instead of updating the address bar every time you want to surf somewhere else.
3. Resources have different Representations
The examples above showed some JSON markup, but JSON is only one kind of representation of a Resource. Each Resource can be accessed in multiple different Representations, as shown in the following table:
| URI | Format |
|---|---|
| http://api.storageroomapp.com/ |
JSON |
| http://api.storageroomapp.com/ |
JSONP |
| http://api.storageroomapp.com/ |
Use HTTP Content-Type header |
All of the above URIs represent the same data, but each time the Resource is shown in a different Format. Behind the curtain, it is always the same Resource. You can select the response format for each request that you make.
There's more information about the different Resource Types in the Formats section.
4. Resources can be accessed with a set of standard methods
Great, I understood the URIs and it is nice to have the data in multiple formats, but how can I create new stuff or delete existing stuff?
There are no new URIs to create, update or delete Resources. The action that is performed on the Resource at the given URI is determined by the HTTP method that is used. So far we only created GET requests. The HTTP protocol defines more HTTP methods/verbs that can be used in a request.
| Method | URI | Request Body | Server Response | Description |
|---|---|---|---|---|
| GET | http://api.storageroomapp.com/ |
empty | Representation of Resource | Get the Representation of the Resource in JSON |
| PUT | http://api.storageroomapp.com/ |
Modified JSON Representation of Resource | Updated JSON Representation of Resource | Update the Resource (change one or multiple attributes) |
| DELETE | http://api.storageroomapp.com/ |
empty | empty | Delete a Resource |
| POST | http://api.storageroomapp.com/ |
JSON Representation of the new Resource | Representation of the new Resource | Create a new Resource |
Change the HTTP method by specifying a request method parameter in your programming language and therefore update, delete or create Resources whenever you want to. Please note that for the first three Requests the action is performed on an individual Resource URI, while the last Request to create a new Resource is sent to the collection URI.
When you want to create or update a Resource you have to send a Representation of the Resource to the web service endpoint in the JSON format. If your Request is accepted the web service will return the Representation of the new or updated Resource with additional attributes (e.g. the time it was created and child associations). If things don't work out the web service will respond with a special error message.
The HTTP methods translate nicely into the basic CRUD database operations:
| HTTP method | Database method | Description |
|---|---|---|
| POST | INSERT | Create |
| GET | SELECT | Read |
| PUT | UPDATE | Update |
| DELETE | DELETE | Delete |
GET Requests are "safe" requests, they never change anything on the server, you cannot break anything by doing a GET Request.
Further Reading
This was a basic explanation of REST and how it applies to StorageRoom. Enough for you to start exploring the StorageRoom API. The websites below will be a good starting point if you want to learn more about REST.
Authentication
Each application that you create contains a set of authentication credentials that you need to pass to our servers to authenticate yourself. A request to an Entry can only be made if the application has the corresponding access rights to the Collection. Access rights are defined through Roles in the web interface. You should keep your credentials secret, especially for Applications that have write-access to Collections.
There are two ways to pass the credentials, they are explained in the following.
Passing a query parameter
Just pass an additional query parameter called "auth_token" on all your requests to authenticate yourself. This comes in handy for situations where it is not possible to set HTTP headers.
curl "http://api.storageroomapp.com/accounts/:account_id/collections/:collection_id/entries?auth_token=YOUR_TOKEN"
Using HTTP Basic Auth
Another option is to use the same set of credentials with HTTP Basic Authentication. Just pass your token as the username and leave the password blank. This option might be easier to set up for all the HTTP requests in your HTTP library. Instead of a blank password it is possible to pass an "X".
curl --basic -u YOUR_TOKEN:X "http://api.storageroomapp.com/accounts/:account_id/collections/:collection_id/entries"
Error Handling and Error Codes
HTTP Status Codes
The HTTP protocol defines certain HTTP Status Codes that are used across the web. The table below provides an overview about the Status Codes used by the StorageRoom API. You can find a full list of all status codes on Wikipedia.
| 2xx Success | ||
|---|---|---|
| 200 | Ok | Everything worked out |
| 201 | New Resource created | A new Resource was created |
| 204 | No Content | Everything worked out, but the server returned no content. |
| 3xx Redirection | ||
| 301 | Moved Permanently | The URL of the Resource changed permanently |
| 303 | Moved Temporarily | The URL of the Resource is temporarily different |
| 4xx Client Error | ||
| 400 | Bad Request | The Request contains bad syntax or cannot be fulfilled, repeating the Request doesn't help |
| 401 | Unauthorized | Authentication is possible, but has failed |
| 402 | Payment Required | Your pricing plan doesn't have this feature enabled |
| 403 | Forbidden | You cannot access this Resource |
| 404 | Not Found | The Resource doesn't exist |
| 405 | Method Not Allowed | You picked the wrong HTTP method |
| 409 | Conflict | The request could not be processed because of an optimistic locking error. |
| 422 | Unprocessable Entity | The Request was valid, but the Resource doesn't pass validation rules |
| 5xx Server Error | ||
| 500 | Internal Server Error | Something bad happened. We get notified automatically on those errors |
| 503 | Server Unavailable | The system is currently down for maintenance |
422 Unprocessable Entity
You wanted to create or update a Resource and received this error. This means, that the Request that you created was valid, but there were validation errors when the server tried to save the Resource. An example for this could be that you provided an invalid value for an attribute. You should show those messages to the user so that she can correct her Entry. You should not look into the elements and parse the error message, as the messages might change and they vary for different languages.
An example for an error message:
{
"error": {
"code": 422,
"message": ["Name can't be empty", "Tags are not in a valid format"],
"@type": "Error"
}
}Requests and Responses
All Requests to the StorageRoom API must be encoded in UTF-8. The server will return an UTF-8 encoded document.
Request Formats and how to select one is further described in the Formats section.
Optionally set the "Accept-Encoding" HTTP Header to "gzip, deflate" and we will return compressed Representations of all Resources. This is a trade-off, it requires more computing power on the client, but saves a lot of bandwidth. Your HTTP library of choice must support compression or you have to decompress the Request body manually.
Save processing power and bandwidth through caching by using HTTP ETags and the If-None-Match header.
Meta Prefix ("@")
You can change the meta data prefix for each request if you find that the "@" prefix is problematic when using your JSON or REST library of choice. An example for such a library is the excellent RestKit (Objective-C). It relies heavily on Apple's Key-Value-Coding (KVC), which doesn't allow the "@" in a key path.
Please be careful when changing the meta prefix and be certain that you use a prefix that doesn't cause collisions with the keys in your Entries. Good examples for alternative meta prefixes would be "meta_", "m_", "_" or "$".
It is possible to change the meta prefix in two ways. You should stick to one way and set this permanently in your code for all requests.
Add a Parameter to the Query String
To quickly change the meta prefix just append a parameter called "meta_prefix":
http://api.storageroomapp.com/accounts/4d13574cba05613d25000004.json?auth_token=DZHpRbsJ7VgFXhybKWmT&preview_api=1&meta_prefix=m_
Test this with our Demo Account
Set an HTTP Header
Another way to change the meta prefix is to send a custom HTTP header called "X-Meta-Prefix" with each request. An example with curl is shown below:
curl "http://api.storageroomapp.com/accounts/4d13574cba05613d25000004.json?auth_token=DZHpRbsJ7VgFXhybKWmT&preview_api=1" -H "X-Meta-Prefix: m_"
Formats
The StorageRoom API understands several different Formats, each useful in a variety of different programming contexts.
- JSON: Easily parsed in many languages, lightweight, can be evaluated in JavaScript.
- JSONP: Same structure as JSON, use it for cross-domain requests in older browsers
XML: On roadmap(Screw that, we will stick to JSON)
Specifying the Format
It is possible to specify the Format in two different ways:
- Append the format to the URI
- Set HTTP Headers
Append the format to the URI
This is especially useful for tools where it is not possible to set your own HTTP Headers.
To quickly view different Formats within your browser you can just surf on the StorageRoom website and click on the "JSON" buttons.
| URI | Format |
|---|---|
| http://api.storageroomapp.com/accounts/:account_id/collections | Default format (JSON) |
| http://api.storageroomapp.com/accounts/:account_id/collections.json | JSON (explicit) |
| http://api.storageroomapp.com/accounts/:account_id/collections.js?callback=test | JSONP |
Please notice that changing the extension just displays another Representation of the Resource. If you "delete" one Representation of the Resource all Representations will be deleted.
The format extensions can only be used to read data. If you want to modify Resources you have to set HTTP Headers as well, otherwise our Request Forgery Protection kicks in and will block your query, resulting in a returned HTML page with an HTTP Status Code of 400 (Bad Request).
Set HTTP Headers
The HTTP protocol uses HTTP Headers to specify which media type is used in a request and which media types are supported by the client for the response.
The StorageRoom web service uses the HTTP Headers to let you pick a Format. This is a translation of our Formats to the media types you should use in HTTP Headers:
| Fromat | Media Type |
|---|---|
| JSON | application/json |
| JSONP | application/javascript |
HTTP Accept Header
The HTTP protocol uses the "Accept" Header to let the client specify which media types it supports and prefers. This can be used to specify the Format of the response without using a format extension. The command line tool curl, which is available and pre-installed on many platforms, can be used to test this.
# this will return the JavaScript Representation curl "http://api.storageroomapp.com/accounts/:account_id/collections/:collection_id/entries/:entry_id?callback=test" -H "Accept: application/javascript"
The server will always respond in the Format that you specify in the HTTP Accept Header. However, the format extension has a higher priority than the Accept Header.
The server will specify the current media type of the response in the HTTP Content-Type Header.
HTTP Content-Type Header
The HTTP Content-Type Header specifies which format the current request or response is in. The server will set the Content-Type header in response to your requests to show which Format is returned.
If you are sending data to the StorageRoom API with a POST or PUT Request you also have to specify the Content-Type of your current request, so that the server knows how to parse the data. If you don't set the Content-Type our Request Forgery Protection kicks in and will block your query, resulting in a returned HTML page with an HTTP Status Code of 400 (Bad Request).
The JSON Format
JSON is a lightweight data format that can be parsed in all programming languages. It can be evaluated directly in JavaScript, making it easy to build widgets or web applications.
This is an example of a Resource Representation in JSON:
{
"entry": {
"@type": "Category",
"@updated_at": "2010-11-26T11:31:28Z",
"@created_at": "2010-11-26T11:31:28Z",
"@collection_url": "http://api.storageroomapp.com/accounts/:account_id/collections/:collection_id",
"@url": "http://api.storageroomapp.com/accounts/:account_id/collections/:collection_id/entries/:entry_id",
"name": "Music",
"field1": 1.1,
"field2": "value",
"parent_category": {
"@type": "Category",
"url": "http://api.storageroomapp.com/accounts/:account_id/collections/:collection_id/entries/:entry_id"
}
}
}
The output above is nicely formatted so that it is easier to read. The JSON output of the StorageRoom API does not contain white-spaces. Use JSON Lint to prettify JSON output, which can be useful for debugging purposes.
All elements that start with an at sign ("@") are meta data that are added by our system. Those keys are readable but cannot be modify directly. It is possible to change this prefix to another string.
The following contains an example in JavaScript. JSON can be parsed in many languages, but as JSON is a subset of JavaScript is very simple to use it in JavaScript.
var jsonString = "..."; // the String contains the content of the JSON example above var entry = JSON.parse(jsonString).entry; alert(entry.name); // returns the name alert(entry["@url"]); // returns the unique URI
JavaScript cross-domain AJAX Requests with CORS
Regular JavaScript AJAX requests are subject to the same origin policy of the browser, which forbids cross-domain AJAX requests. Cross-Origin Resource Sharing (CORS) is a relatively new specification which defines ways for a web server to allow its resources to be accessed by web pages from a different domain.
Our API is configured with CORS to allow cross-domain requests. All of the latest browsers (yes, even Internet Explorer) support CORS, you don't have to do anything to get it to work. Just use regular AJAX requests from your web application to our API.
Check if your browser supports CORS.
CORS with jQuery
jQuery's ajax function facilitates AJAX requests across the different browser implementations. Take a look at the following example on how to make cross-domain AJAX requests with jQuery. More documentation on the $.ajax method can be found in the jQuery Documentation.
$('#jquery-cors-link').click(function() { $.ajax({ url: 'http://api.storageroomapp.com/accounts/:account_id/collections/:collection_id/entries.json', data: { auth_token: 'YOUR_AUTH_TOKEN', // meta_prefix: 'm_' // optionally set the meta prefix per_page: 1 }, dataType: 'json', success: function(data) { var entry = data.array.resources[0]; alert("The Entry was created at: " + entry["@created_at"]); }, error: function() { alert("An error occurred."); } }); return false; });
<a href="#" id="jquery-cors-link">When was the last Entry in Demo account created? (jQuery)</a>
Try it: When was the last Entry in the Demo account created? (CORS)
The JSONP Format
The JSONP Format (JSONP) passes the JSON Response to a JavaScript callback function that you specify in a query parameter in the request. JavaScript code on a page is subject to the same-origin policy of the browser (for an exception see CORS above), so you cannot directly fetch the JSON Representation from our API with AJAX. JSONP allows cross-domain JSON Requests.
All Endpoints that can be directly fetched in the JSON Format can also be requested with JSONP by adding a callback parameter. Your callback function can display the returned JSON on your web page or process it in another way without using a proxy.
Just change the format to JSONP and add a "callback" parameter to the query parameters, which specifies the JavaScript function to use. Optionally set the "_" parameter to a random value to prevent caching, this is what jQuery is doing by default.
JSONP with plain old JavaScript
A simple example in plain JavaScript is following and illustrates how JSONP works:
function outputEntry(data) { var entry = data.array.resources[0]; alert("The Entry was created at: " + entry["@created_at"]); } function getEntryWithJSONP() { var script = document.createElement('script'); var url = "http://api.storageroomapp.com/accounts/:account_id/collections/:collection_id/entries.js?callback=outputEntry&per_page=1"; script.setAttribute('src', url); document.getElementsByTagName('head')[0].appendChild(script); // load the script }
<a href="#" onclick="getEntryWithJSONP(); return false;">When was the last Entry in Demo account created? (Plain JS)</a>
Try it: When was the last Entry in the Demo account created? (Plain JS)
JSONP with jQuery
The jQuery library provides handy methods that abstract JSONP requests. Take a look at the following example on how to make cross-domain JSONP requests with jQuery. More documentation on the $.ajax method can be found in the jQuery Documentation.
$('#jquery-jsonp-link').click(function() { $.ajax({ url: 'http://api.storageroomapp.com/accounts/:account_id/collections/:collection_id/entries.js', data: { auth_token: 'YOUR_AUTH_TOKEN', // meta_prefix: 'm_' // optionally set the meta prefix per_page: 1 }, dataType: 'jsonp', success: function(data) { var entry = data.array.resources[0]; alert("The Entry was created at: " + entry["@created_at"]); }, error: function() { alert("An error occurred."); } }); return false; });
<a href="#" id="jquery-jsonp-link">When was the last Entry in Demo account created? (jQuery)</a>
Try it: When was the last Entry in the Demo account created? (jQuery)
Resources
Resources are all the "things" on StorageRoom that you can access and modify by using the StorageRoom API. Resources consist out of simple data types and other Embedded Resources (compound data types).
This section explains how a Resource is structured in general so that it is easier for you to parse the data. The individual Resource sections contain more information about each Resource and what it is used for.
Simple Data Types
On the lowest level Resources exist out of simple data types.
- Strings
- Integers
- Floating point numbers
- ISO8601 timestamps
- Booleans
Compound Data Types
All Resources in the StorageRoom API are compound data types. They exist of multiple simple data types and/or contain other compound data types. Compound data types always contain a "@type" attribute that describes which data type you are dealing with.
The table below shows all primary Resources that are used in the StorageRoom API.
| Type | Description |
|---|---|
| Account | Describes your Account |
| Collection | Defines the structure of Entries and contains all of those Entries |
| Entry | A piece of content |
| Deleted Entry | Log of deleted Entries. |
| Array | A container for multiple other compound data types. |
Optimistic Locking
The API uses Optimistic Locking to prevent lost updates when two Users edit the same Resource simultaneously.
If you want to update an existing Resource through the API you must send the "@version" key back exactly as it was sent to you from the API, otherwise you will receive an HTTP 409 status code and the Resource won't be updated. If you sent the "@version" key and you still receive this error then the Resource was just updated by another user. You should show an error to the user of your application and reload the Resource.
Creating new Resources
Create a new Resource by POSTing a Representation of the new Resource to the Base URL of the Resource.
The following example shows what to do to create a new Entry in the Guidebook Collection.
Step 1: Where do I have to send a POST request to if I want to create a new Guidebook?
Get the list of all the Collections from http://api.storageroomapp.com/accounts/:account_id/collections
{
"array": {
"@type": "Array",
"@url": "http://api.storageroomapp.com/accounts/:account_id/collections",
"resources": [
{
"@type": "Collection",
"@url": "http://api.storageroomapp.com/accounts/:account_id/collections/:categories_id",
"@created_at": "2010-11-26T11:31:27Z",
"@updated_at": "2010-11-26T11:31:27Z",
"@entries_url": "http://api.storageroomapp.com/accounts/:account_id/collections/:categories_id/entries",
"name": "Categories",
"entry_type": "Category",
"fields": [
...
]
},
{
"@type": "Collection",
"@url": "http://api.storageroomapp.com/accounts/:account_id/collections/:guidebooks_id",
"@created_at": "2010-11-26T11:31:28Z",
"@updated_at": "2010-11-26T11:31:28Z",
"@entries_url": "http://api.storageroomapp.com/accounts/:account_id/collections/:guidebooks_id/entries",
"name": "Guidebooks",
"entry_type": "Guidebook",
"fields": [
...
]
}
]
}
}
The URL to create a new Guidebook would be the @entries_url:
http://api.storageroomapp.com/accounts/:account_id/collections/:guidebooks_id/entries
Step 2: What do I have to POST?
Optional: If you want to associate the Guidebook to a Category find a Category and save the Resource URI of this Category.
You would have to POST the following to the above URL to create a new Guidebook that belongs to a specific Category. Don't add the "category" key if you don't want to add a relationship:
{
"entry": {
"name": "My Guidebook",
"category": {
"url": "http://api.storageroomapp.com/accounts/:account_id/collections/:categories_id/entries/:category_id"
}
}
}
Please note that you can but don't have to include any meta data (keys beginning with the @ sign). They will be added automatically.
As a response to your successful POST request you will receive a representation of the resource including all attributes. Find out more about relationships in the Entry section.
Array
Arrays are important as they are used frequently and contain an array of many Resources. If you make a GET request on the Base URL of a Resource you will get an Array in response. The Array will always have the same structure, but - depending on the endpoint - contain different Resources in the "resources" attribute.
Methods
| Read | Create | Update | Delete |
|---|---|---|---|
| yes | no | no | no |
Attributes
| Name | Type | Description |
|---|---|---|
| resources | array | Contains an array of all the Resources |
| @type | String | Type of the Resource |
| @url | String | URL of this Array |
| @total_resources | Integer | Count of Resources in this Array |
| @pages | Integer | Total amount of pages with the current per_page setting |
| @per_page | Integer | How many Resources were returned per page |
| @next_page_url | String | The URL to the next page of this Array if it exists |
| @previous_page_url | String | The URL to the previous page of this Array if it exists |
Example
{
"array": {
"@type": "Array",
"@url": "http://api.storageroomapp.com/accounts/:account_id/collections",
"@next_page_url": "http://api.storageroomapp.com/accounts/:account_id/collections?page=2",
"@per_page": 10,
"@page": 1,
"@pages": 3,
"@total_resources": 29,
"resources": [
{
"@type": "Collection",
"@url": "http://api.storageroomapp.com/accounts/:account_id/collections/:collection_id"
"@created_at": "2010-11-26T11:31:27Z",
"@updated_at": "2010-11-26T11:31:27Z",
"@entries_url": "http://api.storageroomapp.com/accounts/:account_id/collections/:collection_id/entries",
"name": "Categories",
"fields": [
...
]
},
{
"@type": "Collection",
"@url": "http://api.storageroomapp.com/accounts/:account_id/collections/:collection_id",
"@created_at": "2010-11-26T11:31:28Z",
"@updated_at": "2010-11-26T11:31:28Z",
"@entries_url": "http://api.storageroomapp.com/accounts/:account_id/collections/:collection_id/entries",
"name": "Guidebooks",
"fields": [
...
]
}
]
}
}Account
Accounts are at the root of the hierarchy and provide an entry point to browse the API.
Methods
| Read | Create | Update | Delete |
|---|---|---|---|
| yes | no | no | no |
Attributes
| Name | Type | Description |
|---|---|---|
| name | String | Name of the Account |
| subdomain | String | Subdomain of the Account |
| @type | String | Type of the Resource |
| @url | String | URL of this Account |
| @collections_url | String | URL with an Array of all Collections for this Account |
| @deleted_entries_url | String | URL with an Array of all Deleted Entries for this Account |
| @created_at | String (ISO8601) | When this Account was created |
| @updated_at | String (ISO8601) | When this Account was updated |
Examples
{
"account": {
"@type": "Account",
"@url": "http://api.storageroomapp.com/accounts/:account_id",
"@created_at": "2010-11-26T11:31:24Z",
"@updated_at": "2010-11-26T11:31:49Z",
"@collections_url": "http://api.storageroomapp.com/accounts/:account_id/collections",
"@deleted_entries_url": "http://api.storageroomapp.com/accounts/:account_id/deleted_entries",
"name": "Demo",
"subdomain": "demo"
}
}Collection
Think of Collections as tables in a database. Collections define the structure of your data with Fields and contain all the individual Entries. You can query a Collection and find Entries by many different search criteria.
Methods
| Read | Create | Update | Delete |
|---|---|---|---|
| yes | yes | yes | yes |
Attributes
| Name | Type | Description |
|---|---|---|
| name | String | Name of the Collection |
| entry_type | String | String that is used as the @type attribute for Entries of the Collection |
| fields | array | Array of all the Fields that describe the Collection |
| webhook_definitions | array | Array of all the Webhook Definitions |
| primary_field_identifier | String | The identifier of the main field that is used to display Entries in the interface |
| @updated_at | String (ISO8601) | When the Collection was updated |
| @created_at | String (ISO8601) | When the Collection was created |
| @url | String | URL of this Collection |
| @entries_url | String | URL to an Array of all the Entries |
| @deleted_entries_url | String | URL to an Array of all the Deleted Entries in the Collection |
| @type | String | The type of this Resource |
| @version | Integer | The key used for Optimistic Locking |
Examples
{
"collection": {
"@type": "Collection",
"@url": "http://api.storageroomapp.com/accounts/:account_id/collections/:collection_id"
"@created_at": "2010-11-26T11:31:27Z",
"@updated_at": "2010-11-26T11:31:27Z",
"@entries_url": "http://api.storageroomapp.com/accounts/:account_id/collections/:collection_id/entries",
"@deleted_entries_url": "http://api.storageroomapp.com/accounts/:account_id/deleted_entries?collection_url=:collection_url",
"@version": 1,
"name": "Categories",
"entry_type": "Category",
"primary_field_identifier": "name",
"fields": [
{
"@type": "StringField",
"name": "Name",
"required": true,
"default_value": null,
"include_blank_choice": false,
"input_type": "text_field",
"hint": null,
"identifier": "name"
}
],
"webhook_definitions": []
}
}Entry
Collections define how an Entry looks like. Entries are the actual content. You can add as many Fields as you want to an Entry by updating the Collection. A Field can also be an Association to another Entry.
Methods
| Read | Create | Update | Delete |
|---|---|---|---|
| yes | yes | yes | yes |
Attributes
| Name | Type | Description |
|---|---|---|
| @updated_at | String (ISO8601) | When the Entry was updated |
| @created_at | String (ISO8601) | When the Entry was created |
| @url | String | URL of this Entry |
| @collection_url | String | URL to the Collection of the Entry |
| @type | String | The type of this Resource |
| @version | Integer | The key used for Optimistic Locking |
| ... | Anything | All other keys are defined in the Collection |
Examples
{
"entry": {
"@type": "News",
"@url": "http://api.storageroomapp.com/accounts/:account_id/collections/:collection_id/entries/:entry_id",
"@created_at": "2010-11-26T11:31:28Z",
"@updated_at": "2010-11-26T11:31:28Z",
"@collection_url": "http://api.storageroomapp.com/accounts/:account_id/collections/:collection_id",
"@version": 1,
"title": "Test Title",
"body": "Neque rem maiores molestiae debitis magnam et provident.",
"category": {
"@type": "Category",
"url": "http://api.storageroomapp.com/accounts/:account_id/collections/:categories/entries/:category_id"
}
}
}
Creating new Entries
Entries are created like all other Resources. This is described in the Resources section.
Associations
Frequently relationships between Entries in different Collections are required. Let's say you have a Collection of trivia questions for a game and each TriviaQuestion should be categorized. You could directly add the Category as a simple StringField in the TriviaQuestion Collection, but then you would not be able to add additional information to each Category.
If you want a relationship where a Category holds further information you can create a Category Collection with as many Fields as you want. In the TriviaQuestion Collection you would then add an OneAssociationField or a ManyAssociationField.
To-One Association
This means that each TriviaQuestion belongs to one Category (OneAssociationField). Fetch a Category and all its TriviaQuestions with the Search API or get the Category for a TriviaQuestion that you already loaded from the API. Please note that the "category" key below is a single subdocument with the Resource URI of the associated Category.
{
"entry": {
"question": "Question with *one* Category",
"category": {
"@type": "Category",
"url": "http://api.storageroomapp.com/accounts/:account_id/collections/:categories/entries/:category_id"
},
...
}
}
To-Many Association
This means that each TriviaQuestion belongs to many Categories (ManyAssociationField). Fetch a Category and all its TriviaQuestions with the Search API or get all the Categories for a TriviaQuestion that you already loaded from the API. Please note that the "categories" key below is an array with many subdocuments that contain the Resource URIs of all associated Categories.
{
"entry": {
"question": "Question with *many* Categories",
"categories": [
{
"@type": "Category",
"url": "http://api.storageroomapp.com/accounts/:account_id/collections/:categories/entries/:category1_id"
},
{
"@type": "Category",
"url": "http://api.storageroomapp.com/accounts/:account_id/collections/:categories/entries/:category2_id"
}
]
...
}
}
Creating Associations through the API
To create a To-One or To-Many Association between Entries through an API call you need to transmit the documents as they are shown in the examples above. You always need to send the full Resource URI of the other Entry to create an Association.
File and Image Attachments
Arbitrary Files and Images can be attached to Entries through FileFields and ImageFields. ImageFields also support the generation of images from the source image (e.g. thumbnails or lower resolution artwork) through Image Versions. To following gives instructions on how to upload and remove Files and Images through the API.
1. Uploads
The following request must be made to add or to replace an attached File or Image with the identifier "file" for a "Post" Entry:
{
"entry": {
"name": "Name of the Post",
"file": {
"content_type": "image/png",
"filename": "post.png",
"data": "LARGE_BLOCK_OF_BASE64_ENCODED_DATA"
}
...
}
}
Take the file you want to attach, Base64 encode it and nest it in the POST or PUT request as seen above. If you PUT and don't add any data for the "file" Field then the uploaded File will just stay the same. This avoids multiple unnecessary uploads of the same file if you just want to update some other attributes.
2. Removals
There must be another way to remove Files or Images as they cannot be removed by not including the identifier in the updated Resource representation. To remove a File or Image with the identifier "file" you need to PUT the following:
{
"entry": {
"name": "New Name of the Post",
"file": {
"remove": true
}
...
}
}Deleted Entry
An Entry is removed permanently when you delete it. If you want to synchronize a local database in your app with StorageRoom it can be hard to find out which Entries have been deleted from the remote Collection on StorageRoom.
Each Entry therefore leaves a trace on removal. A new DeletedEntry Resource is created automatically when you delete an Entry.
Methods
| Read | Create | Update | Delete |
|---|---|---|---|
| yes | no | no | no |
Attributes
| Name | Type | Description |
|---|---|---|
| @type | String | The type of this Resource |
| @url | String | URL of this Deleted Entry |
| @account_url | String | URL to the Account of the Entry |
| @collection_url | String | URL to the Collection of the Entry |
| @entry_url | String | URL to the original Entry that was deleted |
| @deleted_at | String (ISO8601) | When the original Entry was deleted |
Examples
{
"@type": "DeletedEntry",
"@url": "http://api.storageroomapp.com/accounts/4e1e9c234250712eba000052/deleted_entries/4e269c0f42507106fc000001",
"@account_url": "http://api.storageroomapp.com/accounts/4e1e9c234250712eba000052",
"@collection_url": "http://api.storageroomapp.com/accounts/4e1e9c234250712eba000052/collections/4e1e9c234250712eba000056",
"@entry_url": "http://api.storageroomapp.com/accounts/4e1e9c234250712eba000052/collections/4e1e9c234250712eba000056/entries/4e269c2742507106fc000004",
"@deleted_at": "2011-07-20T09:13:13Z"
}Webhook Call
Webhook Calls are what StorageRoom will POST to the URL in a Webhook Definition. Parse Webhook Calls on the server and respond to changes to your Entries.
The Ruby Gem, which you can use on your server, supports parsing of Webhook Calls (example).
Attributes
| Name | Type | Description |
|---|---|---|
| @type | String | The type of this Resource |
| @event | String | Type of the event (create, update, delete) |
| @attempts | Integer | How many times have we tried to deliver the webhook call |
| @last_attempt_at | String (ISO8601) | When we tried to deliver the last webhook call |
| @last_status_code | Integer | The HTTP status code your server returned on the last webhook call |
| @created_at | String (ISO8601) | When the webhook call was created |
| @entry | Entry | The JSON representation of the Entry at the time the webhook call was created |
Examples
{
"@type": "WebhookCall",
"@event": "update",
"@attempts": 2,
"@last_attempt_at": "2011-07-15T11:04:49Z",
"@last_status_code": 500,
"@created_at": "2011-07-15T11:04:48Z",
"@entry": {
"@collection_url": "http://api.storageroomapp.com/accounts/4e1e9c234250712eba000052/collections/4e1e9c234250712eba000062",
"@created_at": "2011-07-14T07:35:26Z",
"@trash": false,
"@type": "Announcement",
"@updated_at": "2011-07-15T11:04:48Z",
"@url": "http://api.storageroomapp.com/accounts/4e1e9c234250712eba000052/collections/4e1e9c234250712eba000062/entries/4e1e9c3e4250712eba000093",
"@version": 2,
"text": "changed text"
}
}Embedded Resources
Besides the primary Resources described above there are also embedded Resources. Those Resources don't have their own URL and are always part of one of the primary Resources. The embedded Resources are:
Field
Fields are part of Collections and are used to describe the structure of a "column". They don't have their own URL as they are always part of a Collection.
Field Types
A Field will have different options depending on the chosen Field type. A list of all available Field types follows.
-
Atomic
- BooleanField
- DateField
- TimeField
- FloatField
- IntegerField
- StringField
-
Compound
- FileField
- ImageField
- LocationField
- ArrayField
- JsonField
-
Association
- OneAssociationField (To-One)
- ManyAssociationField (To-Many)
Attributes
| Name | Type | Description |
|---|---|---|
| name | String | Name of the Field |
| identifier | String | Unique identifier of the Field |
| collection_url | String | The associated Collection for Association Fields |
| interface | Boolean | Is this Field shown in the editing interface? |
| required | Boolean | Is this Field mandatory and must be filled out? |
| unique | Boolean | This Field's value must be unique within the Collection |
| regexp | String | The regular expression against the string must match |
| minimum_length | Integer | The minimum length of a String Field |
| maximum_length | Integer | The maximum length of a String Field |
| minimum_number | Float | The minimum value of an Integer or Float Field. |
| maximum_number | Float | The maximum value of an Integer or Float Field. |
| minimum_size | Integer | The minimum size in Megabytes of an uploaded File |
| maximum_size | Integer | The maximum size in Megabytes of an uploaded File |
| included_in | Array | The value must be within the Array. |
| excluded_of | Array | The value is not allowed to be within the Array. |
| default_value | Dependent on Field | What is the default value |
| include_blank_choice | Boolean | Can this Field be left blank? |
| input_type | String | Which form of input to use in the interface |
| hint | String | The hint to display when entering data |
| choices | Array | The hint to display when entering data |
| @type | String | The type of this Field |
| versions | Array | Definitions of type Image Version (ImageField) |
Examples
{
"@type": "StringField",
"name": "Name",
"identifier": "name",
"interface": true,
"required": true,
"default_value": "Unnamed Category",
"include_blank_choice": false,
"input_type": "text_field",
"hint": "The name of the Category"
}Location
Use a LocationField to add one or many locations to your Entries. Locations have a special format and can also be searched with radius and bounding box queries.
Attributes
| Name | Type | Description |
|---|---|---|
| lat | Float | Latitude |
| lng | Float | Longitude |
| @type | String | The type of this Resource |
Examples
{
"@type": "Location",
"lat": 12.3333,
"lng": -33.123
}File
Attach one or multiple files to each Entry and download them from your applications. Examples of files are images, PDFs, MP3s, etc.
Attributes
| Name | Type | Description |
|---|---|---|
| @url | String | URL to download the file |
| @type | String | The type of this Resource |
Examples
{
"@type": "File",
"@url": "http://files.storageroomapp.com/accounts/:account_id/collection/:collection_id/entries/:entry_id/fields/:field_id/:filename"
}Image
The Image Resource provides additional support for automatically generating thumbnail images of your uploaded image through Image Versions.
Attributes
| Name | Type | Description |
|---|---|---|
| @url | String | URL to download the image |
| @processing | Boolean | Are Image Versions currently being processed? |
| @type | String | The type of this Resource |
| @versions | Array | Details for all Image Versions |
Examples
{
"@type": "Image",
"@url": "http://files.storageroomapp.com/accounts/:account_id/collection/:collection_id/entries/:entry_id/fields/:field_id/:filename",
"@processing": false,
"@versions": {
"thumb_small": {
"@url": "http://files.storageroomapp.com/accounts/:account_id/collection/:collection_id/entries/:entry_id/fields/:field_id/:thumb_small_filename"
},
"thumb_large": {
"@url": "http://files.storageroomapp.com/accounts/:account_id/collection/:collection_id/entries/:entry_id/fields/:field_id/:thumb_large_filename"
}
}
}Image Version
Image Versions are embedded within Image Fields and define which thumbnails should be generated from the original source image.
Attributes
| Name | Type | Description |
|---|---|---|
| identifier | String | Identifier for the Image Version |
| format | String | The image format (png, jpg or gif) |
| resize_mode | String | The resize mode (none, fill, fit, relative) |
| height | Integer | Height in pixel for fill/fit resize modes |
| width | Integer | Width in pixel for fill/fit resize modes |
| scale | Integer | Scale in percent for relative resize mode |
Examples
{
"@type": "ImageVersion",
"identifier": "thumbnail",
"format": "png",
"resize_mode": "fit",
"width": 100,
"height": 200
}Webhook Definition
A Webhook Definition is part of a Collection and configures one of many Webhooks that are called when an Entry is modified.
Attributes
| Name | Type | Description |
|---|---|---|
| @type | String | The type of this Resource |
| url | String | URL where POST requests are sent to |
| on_create | Boolean | Execute for new Entries? |
| on_update | Boolean | Execute for updated Entries? |
| on_delete | Boolean | Execute for deleted Entries? |
| api | Boolean | Execute for changes coming through the API |
| web_interface | Boolean | Execute for changes coming through the web interface |
| username | String | Optional HTTP Basic username (not readable) |
| password | String | Optional HTTP Basic password (not readable) |
Examples
{
"@type": "WebhookDefinition"
"url": "http://www.myserver.com/webhooks/storage_room"
"on_create": true
"on_update": true
"on_delete": true
"api": true
"web_interface": false
}Search Entries
Use the API's search functionality to construct your own queries to find Entries with many different filter criteria.
Endpoint
- http://api.storageroomapp.com/accounts/:account_id/collections/:collection_id/entries
Query Options
The following options don't change the results that are returned, but they change how the results are returned to you.
| Name | Values | Description | Example |
|---|---|---|---|
| sort | Identifier of any Field | Return the results sorted by this Field | sort=name |
| order | asc, desc | How to order the sorted Entries | order=desc |
| per_page | Integer | How many results should be returned per page | per_page=20 |
| page | Integer | Which page to fetch | page=3 |
# 1000 Categories sorted by their name in descending order /entries?sort=name&per_page=1000&order=desc
Array Parameters
Sometimes it is necessary to pass multiple values to one operator. Append the "[]" characters to the query key to define it as an array. Repeat the same query key multiple times to pass multiple values.
# All Articles tagged with "tag1" /entries?tags=tag1 # All Articles tagged with "tag1", "tag2" and "tag3" /entries?tags!all[]=tag1&tags!all[]=tag2&tags!all[]=tag3
Filter Options (Operators)
Get a subset of all available Entries with these filter options. You can combine the options in any way you want. Append a matcher to an identifier with the exclamation mark (!).
| Symbol | Name | Description | Example |
|---|---|---|---|
| = | Equals | Only Entries with exactly this value | category=restaurants |
| ne | Not Equal | Only Entries not equal to this value | category!ne=restaurants |
| gt | Greater Than | Only Entries with value greater to this value | price!gt=10.0 |
| gte | Greater Than Equal | Only Entries with value greater or equal to this value | price!gte=10.0 |
| lt | Less Than | Only Entries with value less than this value | price!lt=10.0 |
| lte | Less Than Equal | Only Entries with value less than or equal to this value | price!lte=10.0 |
| in | In | Only Entries where *any* of the values match | tags!in[]=tag1&tags!in[]=tag2 |
| nin | Not In | Only Entries where *none* of the values match | tags!nin[]=tag2&tags!nin[]=tag3 |
| all | All | Only Entries where *all* of the values match | tags!all[]=tag1&tags!all[]=tag2 |
| match | Regular Expression | Find Entries where value matches a regular expression (String Fields) | name!match=/pizzas?/ |
| bbox | Bounding Box | Find Entries where a location is within a bounding box (Location Fields) Order: ((lat1, lng1), (lat2, lng2)) |
location!bbox=((1,1),(4,4)) |
| within | Radius Search | Find Entries where a location is within a specific distance (Location Fields) Order: ((lat, lng), max_distance_in_radians) |
location!within=((1,1), 3) |
| near | Radius Search sorted by distance | Find Entries where a location is within a specific distance (Location Fields) Order: ((lat, lng), max_distance_in_radians) |
location!near=((1,1), 3) |
Examples
# All requests start with: # http://api.storageroomapp.com/accounts/:account_id/collections/:collection_id... # All Products where the price is between 10 and 50 /entries?price!gt=10&price!lt=50 # All Products that have been trashed /entries?@trash=true # All Restaurants around a location /entries?location!within=((49.12,9.07),1) # All Restaurants around a location (ordered by distance) /entries?location!near=((49.12,9.07),1) # All Articles where the description contains the word 'test' /entries?description!match=/\btest\b/ # All Articles with a specific Category (Association) /entries?category.url=:category_url # All Articles tagged with "mobile" (ArrayField) /entries?tags=mobile # All Articles tagged with "mobile" AND "nokia" (ArrayField) /entries?tags!all[]=mobile&tags!all[]=nokia # All Articles tagged with "android" OR "iphone" (ArrayField) /entries?tags!in[]=android&tags!in[]=iphone # All Articles tagged where tags are *not* "android" OR "iphone" (ArrayField) /entries?tags!nin[]=android&tags!nin[]=iphone # All Articles with specific URLs /entries?@url!in[]=article_url_1&@url!in[]=article_url_2&@url!in[]=article_url_3 # All Articles updated since a point of time (synchronization) /entries?@updated_at!gt=2011-07-05T12:23:58
Endpoints
As all the Resources provide further links to other Resources you should not build URLs manually. Just follow the existing links. Building links manually is more effort and might break if we ever change our URL structure.
However, for a small script or widget it might be interesting to know what Endpoints are available.
This is an overview about all the available Endpoints. You should only use the Endpoints that are documented in this list. There might be some other undocumented Endpoints, but those might change without upfront notice.
| Resource | Method | Endpoint | Request | Response | Description |
|---|---|---|---|---|---|
| Account | GET | /accounts/:account_id | Account Representation | Get details about your Account | |
| Collection | GET | /accounts/:account_id/ |
Array of Collections in Account | Get all Collections in your Account | |
| Collection | GET | /accounts/:account_id/ |
Collection Template | Template to create a new Collection | |
| Collection | GET | /accounts/:account_id/ |
Collection Representation | Get details about a Collection | |
| Collection | DELETE | /accounts/:account_id/ |
Delete a Collection and all its Entries | ||
| Collection | PUT | /accounts/:account_id/ |
Collection Representation | Collection Representation | Update a Collection |
| Collection | POST | /accounts/:account_id/ |
Collection Representation | Collection Representation | Create a new Collection |
| Entry | GET | /accounts/:account_id/ |
Array of Entries in Collection | Get all Entries in a Collection | |
| Entry | GET | /accounts/:account_id/ |
Entry Template | Template to create a new Entry | |
| Entry | GET | /accounts/:account_id/ |
Entry Representation | Get details about an Entry | |
| Entry | DELETE | /accounts/:account_id/ |
Trash an Entry | ||
| Entry | PUT | /accounts/:account_id/ |
Entry Representation | Entry Representation | Update an Entry |
| Entry | POST | /accounts/:account_id/ |
Entry Representation | Entry Representation | Create a new Entry |
| Deleted Entry | GET | /accounts/:account_id/ |
Array of Deleted |
Get all Deleted |
|
| Deleted Entry | GET | /accounts/:account_id/ |
Deleted |
Get details about a Deleted |
Webhooks
StorageRoom supports Webhooks, so that a HTTP POST request is performed on a custom URL every time Entries are modified. You get notified about changes to your content when they happen and don't need to poll the API constantly to find out if a change occurred.
Use Webhooks to update certain Fields of an Entry on your server, create push notifications with a 3rd party service or use it to synchronize content with another CMS. The opportunities are endless, limited only by your imagination, as you can do anything you want with the data you receive.
Define up to 5 different Webhooks per Collection in our web interface and specify if you want to get notified on new, updated or deleted Entries. In addition, define if you want the callback to be triggered by all changes or only by changes through the web interface or the API.
For each change StorageRoom will send a POST request with a JSON Payload to your custom URL. Your service should respond with a HTTP Status Code of 200 or 201 when it processed the Webhook successfully. In case of failure StorageRoom will retry the request to your server multiple times after some delay. Your endpoint should be prepared for this and use the timestamps in the POST body, and not rely on the time the POST is executed.
Please be careful when updating content with a Webhook, as this could produce infinite loops, where your Webhook and the StorageRoom servers ping each other about changed content forever. Set the "skip_webhooks" parameter to "1" in the query string to prevent any Webhooks from being executed.
Configure a Postbin as your Webhook URL to test how Webhooks work and check out our Ruby on Rails Webhook Example.
Demo
Take a look at our demo account and browse the API with a read-only API key that we provide.
Use the following URL to start at the Account level:
Or take a direct look at a Collection:
- Restaurants: Shows many of the available Fields in action
- Wallpapers: Shows automatically generated thumbnail images
- Posts: Associations between Entries
Browser Extensions
To browse the JSON-API in your browser use one of the following plugins:
- JSONView for Firefox
- Pretty JSON for Chrome
- JSON Formatter for Safari
Libraries
It is not hard to consume the StorageRoom web service as soon as you have figured out how to use your HTTP library of choice. But it is even easier to use the StorageRoom API if you don't have to take care about stuff like parsing JSON responses yourself.
StorageRoomKit (iOS)
StorageRoomKit is a library for iOS and Mac OS that provides helper methods and classes that make it very simple to use the StorageRoom API.
Feature summary:
- Works with NSObjects and NSManagedObjects
- Supports the GET, POST, PUT and DELETE HTTP methods
- Comes with classes and mappings for all StorageRoom Resources
- Helper methods to generate StorageRoom paths
- ... and many more features that the underlying RestKit provides
You can find the code and usage examples on http://github.com/thriventures/StorageRoomKit.
Ruby Gem
To test our web service in every aspect we developed a Ruby Gem. We released it as an open source tool and everybody can use in their applications. It might also give you some inspiration on how to model a library for another programming language.
Importing content from another CMS or Excel sheet can be done in minutes with the Gem (Example).
Feature summary:
- ActiveRecord/ActiveModel like interface.
- Automatic creation of Entry Classes from a Collection, you don’t have to configure anything
- Supports lazy-loading of associations (e.g. post.category will fetch a category transparently if it has not yet been loaded)
- Supports caching through an identity map, so that Resources don’t have to be loaded multiple times
- File uploads and removals
- Model Callbacks
- Webhook Processing
Find the gem on http://github.com/thriventures/storage_room_gem.
sudo gem install storage_room
# Create an Entry collection = StorageRoom::Collection.find('4ddaf68b4d085d374a000003') Guidebook = collection.entry_class # the class is automagically configured from the Collection entry = Guidebook.new(:name => 'Foo', :price => 1.23) if entry.save puts "Guidebook created" else puts "Guidebook could not be created: #{entry.errors.join(', ')}" end # Search for Entries array = Guidebook.search(:name => 'Bar') puts "Guidebooks with name 'Bar':" array.resources.each do |g| puts "- #{g.name}" end # More examples on Github
JavaScript
It is very easy to use the RESTful JSON-API of our CMS in JavaScript (through cross-domain AJAX requests with CORS or JSONP). There is a plethora of great open-source JavaScript libraries for building web-based desktop & mobile applications that work great with our Content Management System (CMS) and make you even more productive:
Pick your weapon of choice and start building great content-based web applications!
Sample Code
To make using our API even easier we provide some sample code. Feel free to reuse parts of the examples.
iPhone
Restaurants are loaded from the StorageRoom CMS, saved locally with Core Data and displayed in a UITableView and a MKMapView. Details can be seen on a secondary page. The restaurants are cached for offline use until the user manually decides to load the Resources again from the web.
This example is kept simple on purpose and focuses on the downloading and parsing of the JSON data.
The code is on GitHub: https://github.com/thriventures/simple_iphone_example
Android
Restaurants and announcements are loaded from the CMS with the JSON API. They are saved locally in a SQLite database and displayed in an Activity with a ListView and an Activity with a MapView. Details can be accessed in a separate activity. The images in the ListView and in the DetailsView are downloaded in the background and cached locally for the application life-time. Special thanks go to Till Simon for providing the code.
The code is on GitHub: https://github.com/TillSimon/storage_room_android_example
Your code here
Please get in touch with us if you have an open-source project that's based on our API that you wish to see here.
FAQ
Is StorageRoom an application generator that spits out a templated mobile application?
Nope. You develop the app yourself or in cooperation with an agency. We only provide libraries to make your life easier. This is an advantage as you have the freedom to do whatever you want with the content in your app.
Can I use StorageRoom for Offline Content?
Yes. You can use StorageRoom to manage content, and then export the content and deliver it for offline use with your app. It is even possible to do delta-synchronization to keep the content up to date in your applications without releasing a new version.
How do I import content from my existing CMS?
There are so many different data formats that it would be hard for us to provide an universal importer. But content can be imported in a snap with our Ruby Gem.
How do I create relationships between my Entries?
If you have a "Posts" Collection and a "Category" Collection and you want to link each Post to a Category then you should create an AssociationField. You can model one-to-one and one-to-many relationships this way.
I have some content that doesn't fit into your Fields?
Use a JSON Field. You can add any custom deeply nested JSON data to this Field, there just won't be a nice interface to edit this content.
What is an Authenticity Token error?
Our servers have a protection against Cross-site request forgery. The protection is only activated for Requests that were made by a browser. Only the POST/PUT/DELETE HTTP Methods are checked.
If you receive this error in your application you probably forgot to set the correct HTTP Content-Type and Accept Header. See Formats for more information.
How do I use StorageRoom on iOS?
Give our StorageRoomKit library a try.
My JSON library doesn't like the "@meta_data" attributes?
Just change the default meta prefix to something else.
Support
We want your feedback! Did you find a bug or encounter a problem while using the API? Do you have feedback or suggestions that you would like to share? Did you find an error in the API documentation?
Maybe the answer to your question is already posted on our help & discussions page. If not, please post your question there.
If you want to contact us directly, send us an email: team@storageroomapp.com.





