JSON API

JSON API is a specification for how a client should request that resources be fetched or modified, and how a server should respond to those requests.

JSON API is designed to minimize both the number of requests and the amount of data transmitted between clients and servers. This efficiency is achieved without compromising readability, flexibility, or discoverability.

JSON API requires use of the JSON API media type application/vnd.api+json for exchanging data.

Validating the messages

To validate messages, you can retrieve the JSON Schema file:

GET /api/v8/schema

Content Negotiation

Clients MUST send all JSON API data in request documents with the header Content-Type: application/vnd.api+json without any media type parameters.

Clients that include the JSON API media type in their Accept header MUST specify the media type there at least once without any media type parameters.

Clients MUST ignore any parameters for the application/vnd.api+json media type received in the Content-Type header of response documents.

Example
$ch = curl_init();
$header = array(
   'Content-type: application/vnd.api+json',
   'Accept: application/vnd.api+json',
);
$url = 'https://path-to-instance/api/v8/modules/meta/list';
curl_setopt($ch, CURLOPT_URL, url);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');;
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
$output = curl_exec($ch);

Handling Errors

The JSON API specifies the errors object to send messages back to the user. SuiteCRM API errors are exceptions which have been thrown. Errors are logged in suitecrm.log and posted back to the client application.

SuiteCRM log
Tue Sep 26 13:51:17 2017 [697][1][FATAL] [ERROR] Code: [8006] Status: [409] Message: [SuiteCRM] [API] [Conflict] ["id" already exist] "11c48ede-e9c6-6c15-fa37-59b66a8dbeab" Detail: ["Unable create record, because it already exists."] Source: [/data/attributes/id]
Response
{
 "errors": [
   {
     "code": 8006,
     "title": "[SuiteCRM] [API] [Conflict] [\"id\" already exist] \"11c48ede-e9c6-6c15-fa37-59b66a8dbeab\"",
     "detail": "Unable create record, because it already exists.",
     "source": {
       "pointer": "/data/attributes/id"
     },
     "status": 409
   }
 ]
}

Modules API

SuiteCRM offers access to modules resources.

Available Modules

To get a list of modules you can use the following url:

GET https://path-to-instance/api/v8/modules/meta/list

List available modules

To get a list of records use the following url:

https://path-to-instance/api/{module}
Example Request:
https://path-to-instance/api/v8/modules/Accounts
Example Response:
{
 "data": [{
   "type": "Accounts",
   "id": "1",
   "attributes": {
     "Name": "JSON API paints my bikeshed!"
   }
 }, {
   "type": "Accounts",
   "id": "2",
   "attributes": {
     "Name": "Rails is Omakase"
   }
 }]
 "links": {
     "self": "https://path-to-instance/api/v8/modules/Accounts?page[limit]=20&page[offset]=0",
     "first": "https://path-to-instance/api/v8/modules/Accounts?page[limit]=20&page[offset]=0",
     "last": null,
     "prev": null,
     "next": null,
  }`
}

Pagination

SuiteCRM offers a offset-based strategy for pagination, a client MAY use the page[offset] and page[limit] parameter to request for pagination. The following keys MUST be used for pagination links:

first

the first page of data

last

the last page of data

prev

the previous page of data

next

the next page of data

Keys MUST either be omitted or have a null value to indicate that a particular link is unavailable.

Sparse fieldset

A client MAY request that an endpoint return only specific fields in the response on a per-type basis by including a fields[TYPE] parameter. The value of the fields parameter MUST be a comma-separated (U+002C COMMA, “,”) list that refers to the name(s) of the fields to be returned.

GET https://path-to-instance/api/v8/modules/Accounts?fields[Accounts]=name,date_created

Sorting

SuiteCRM offers sorting via the sort parameter.

Example
GET https://path-to-instance/api/v8/modules/Accounts?sort=name,-date_created

The attribute is sorted in ascending order, however to sort in descending order you simply add MINUS (-) in front of the attribute name. The value of the sort parameter MUST be a comma-separated (U+002C COMMA, “,”) list.

Filtering

The JSON Api (v1.0.0) does not specify which strategy to use when filtering, instead the filter query parameter is reserved for filtering. So this section attempts to describe the filtering strategy implemented in SuiteCRM.

Pre-made Filters

You can specify a pre-made filter like so

GET /api/v8/modules/\{module}?filter={name}
  • {name} represents the name of the filter

Example use the roi filter and sort by total_revenue in descending order

GET /api/v8/modules/Leads?filter=roi&sort=-total_revenue

Filter by id

If you need to retrieve a set of modules you can use the following call

GET /api/v8/modules/{module}?filter[id]=\{id},...
  • {type} represents represent the module type:

  • {filter} represents the id for the module type

To get a list of accounts with the id of 1, 2, and 3:

GET /api/v8/modules/Accounts?filter[Accounts]=1,2,3

Filter by attributes

GET /api/v8/modules/\{module}?filter[{type}.{attribute}]=[[{operator}]]{comparator},...
  • {type} represents represent the module type:

  • {attribute} represents an attribute name eg. date_modified

  • {operator} represents a [[API-8#Operators|filter operation]]

  • {comparator} represents the attribute value to compare against eg 2017-11-17T11:40:00+00:00

GET /api/v8/modules/{module}?filter[Accounts.date_modified]=[[gt]]2017-11-17T11:40:00+00:00

Filter relationships

GET /api/v8/modules/{module}?filter[{type}.{related type}.{attribute}]=[[{operator}]]{comparator},...
GET /api/v8/modules/Accounts?filter[Accounts.Contacts.date_modified]=[[gt]]2017-11-17T11:40:00+00:00

Filter Middle Table fields

GET /api/v8/modules/{module}?filter[{type}.{related type}.meta.middle_table.{attribute}]=[[{operator}]]{comparator},...
  • {related type} represents the link for the related module type

  • {attribute} represents an attribute name eg. date_modified

  • {operator} represents a [[API-8#Operators|filter operation]]

  • {comparator} represents the attribute value to compare against eg 2017-11-17T11:40:00+00:00

GET /api/v8/modules/Meetings?filter[Meetings.Users.meta.middle_table.accept_status]=[[eq]]Accept

Operators

The operators are represented by string values, so that they are not encoded, plus it will make it easier to extend operations in the future.

Comparators

Operator Name Example

eq

Equal

filter[Accounts.deleted]=[[eq]]1

ne

Not Equal

filter[Accounts.deleted]=[[ne]]0

gt

Greater Than

filter[Accounts.date_modified]=[\[gt]]2017-11-17T11:40:00+00:00

lt

Less Than

filter[Accounts.date_modified]=[[lt]]2017-11-17T11:40:00+00:00

gte

Greater Than or Equal

filter[Accounts.date_modified]=[[gte]]2017-11-17T11:40:00+00:00

lte

Less Than or Equal

filter[Accounts.date_modified]=[[lte]]2017-11-17T11:40:00+00:00

in

In List

filter[Accounts.name]=[[in]]inc,[[in]]ltd

nin

Not In List

filter[Accounts.name]=[[nin]]inc,[[nin]]ltd

Strings

Operator Name Example

li

Like

filter[Accounts.name]=[[li]]sam%

nli

Not Like

filter[Accounts.name]=[[nli]]bob%

This convention was taken from the MongoDb syntax.

Multiple Comparators vs Multiple Filters

When you need to create multiple conditions on a field, Due the length limitation of a url, it is recommended that you use the multiple comparator notation.

Comparators separated by ","
GET /api/v8/modules/Accounts?filter[Contacts.date_modified]=[[gte]]2017-11-17T11:40:00+00:00,[[lte]]2017-11-18T11:40:00+00:00

When you need to filter by multiple fields, you should use the multiple filters notation.

Filters separated by "&"
GET /api/v8/modules/Accounts?filter[Contacts.date_modified]=[[gte]]2017-11-17T11:40:00+00:00&filter[Contacts.last_contacted]=[[lte]]2017-11-18T11:40:00+00:00

List Recently Viewed Records

List recently viewed records of the currently logged in user for all modules

GET /api/v8/modules/\{module}/viewed

List Favourite Records

List favourite records of the currently logged in user for all modules

GET /api/v8/modules/\{module}/favorites

Attribute Definitions

To get the definitions of the attributes / fields. This is useful to get the constraints for each attribute

/api/v8/modules/\{module}/meta/attributes

Get Language Strings

  • Module Strings

    GET /api/v8/modules/{module}/meta/language
  • Application and Drop Down Strings

    GET /api/v8/modules/meta/languages

Module Record

To consume a record from a module, you can do the standard CRUD operations

  • Create

    POST /api/v8/modules/{module}/{id}
  • Retrieve

    GET /api/v8/modules/{module}/{id}
  • Update

    PATCH /api/v8/modules/{module}/{id}
  • Delete

    DELETE /api/v8/modules/{module}/{id}

To consume a related module records, you can do the standard relationship fetching and updating operations:

  • Create

    POST /api/v8/modules/{module}/{id}/relationships/{link}
  • Read

    GET /api/v8/modules/{module}/{id}/relationships/{link}
  • Update

    PATCH /api/v8/modules/{module}/{id}/relationships/{link}
  • Delete

    DELETE /api/v8/modules/{module}/{id}/relationships/{link}

Inclusion of fields

The filter only controls how the data is filtered. How the data is presented is handled separately. You can use the fields query parameter to select the fields, or use the include query parameter to select to include the related types you wish to be in the response.

Files

To create notes and documents, you will need to be able to upload files. This uses fields which have the type set to "file". The SuiteCRM API expects the contents of the file to be assigned to the {fieldname}_file attribute. The contents must be a base64 encoded string. When requesting a note or a document, the API will decode the file in the same request.

Example payload for notes:
{
  "data": {
    "id": "",
    "type": "Notes",
    "attributes": {
      "name": "Test",
      "portal_flag": true,
      "filename": "testFile.txt",
      "filename_file": "U3VpdGVDUk0gaXMgdGhlIGJlc3Q="
    }
  }
}
Example payload for documents:
{
  "data": {
  "id": "",
  "type": "Documents",
  "attributes": {
    "name": "testFile.png",
    "document_name": "testFile.png",
    "active_date": "2017-11-17T11:40:00+00:00",
    "portal_flag": true,
    "revision": "1",
    "filename": "testFile.txt",
    "filename_file": "U3VpdGVDUk0gaXMgdGhlIGJlc3Q="
  }
}

Relationship Fields

To create a meeting, you need to provide the invitees and there accept status.

POST api/v8/modules/Meetings
Payload:
{
  "data": {
    "id": "",
    "type": "Meetings",
    "attributes": {
      "name": "RelationshipsTest",
      "date_start": "2017-11-07T10:55:43+00:00",
      "date_end": "2017-11-07T11:10:43+00:00",
      "duration_hours": "",
      "duration_minutes": 15,
      "assigned_user_id": "1",
      "assigned_user_name": "Administrator"
    },
    "relationships": {
      "users": {
        "data": [
          {
            "id": "1",
            "type": "User",
            "meta": {
              "middle_table": {
                "data": {
                  "id": "",
                  "type": "Link",
                  "attributes": {
                    "accept_status": "accept",
                    "user_id": "1"
                  }
                }
              }
            }
          },
          {
            "id": "seed_max_id",
            "type": "Users",
            "meta": {
              "middle_table": {
                "data": {
                  "id": "",
                  "type": "Link",
                  "attributes": {
                    "accept_status": "none",
                    "user_id": "seed_max_id"
                  }
                }
              }
            }
          },
          {
            "id": "seed_chris_id",
            "type": "Users",
            "meta": {
              "middle_table": {
                "data": {
                  "id": "",
                  "type": "Link",
                  "attributes": {
                    "accept_status": "none",
                    "user_id": "seed_chris_id"
                  }
                }
              }
            }
          }
        ]
      }
    }
  }
}

Please ensure that you include the meta middle table in each link in the relationship otherwise it will set all the middle table fields to the first meta object.

Content is available under GNU Free Documentation License 1.3 or later unless otherwise noted.