SugarCRM Cookbook - The School of REST - Part 1

Post originally written by sugarmajed.

Welcome to the School of REST - our first installment in the Sugar 7 Cookbook Series! We've always believed that DATA belongs to you and it should always be accessible to you! With Sugar 7 we've added a completely revamped REST API. Today we are going to cover all the basics of pushing data into and getting data out of your SugarCRM instance. For each of the API calls we'll show how to do it in curl, a generic HTTP Request, as well as showing you the response. Also, before we get started you can always hit /rest/v10/help for detailed documentation on all the APIs.

In this Guide we will be creating an Account "Burger Palace" and relating it to a Contact "Bob Burger". We will then show you the ins and outs of Creating and Updating Records, Filtering Lists, Creating Relationships, Filtering on Relationships, Marking Records as Favorites, and Deleting Records.

This guide is broken into 3 sections.

Part 1 - Connecting, Creating Records, and Filtering Lists

Part 2 - Retrieving, Updating, and Relating Records 

Part 3  - Filtering on Relationships, Favorites, and Deleting Records 

You may want to download PostMan for Chrome to follow along interactively from  http://www.getpostman.com/

You can download Our Cookbook1 PostMan collection from  https://gist.github.com/mitani/f39e8d94df9fbda4d97d

Step 1. Connecting/Authenticating

Sugar 7 uses two-legged OAuth2 for authentication. You simply need to do a POST to /rest/v10/oauth2/token with the following params:

grant_typeStringType of request. Available grant types are "password" and "refresh_token".
client_idStringThe client_id of "sugar" will automatically create an OAuth Key in the system and can be used for "password" authentication. The client_id of "support_portal" will create an OAuth Key if the portal system is enabled and will allow for portal authentication. Other client_id's can be created by the administrator in the OAuthKeys section in the Administration section and can be used in the future for additional grant types, if the client secret is filled in, it will be checked to validate use of the client id.
client_secretStringThe clients secret key.
usernameStringThe username of the user authenticating to the system.
passwordStringThe plaintext password the user authenticating to the system.
platformStringDefaults to "base" allows you to have custom meta-data per platform

So, first we are going to login using a grant_type of "password".

CURL.sh

curl -X POST -H Cache-Control:no-cache -H Postman-Token:a6ae8f76-442f-06cf-1072-db18b0ad29a3 -d '{ "grant_type":"password", "client_id":"sugar", "client_secret":"", "username":"username", "password":"password", "platform":"base" }' http://server/pro720/rest/v10/oauth2/token


POST token.http

POST /pro720/rest/v10/oauth2/token HTTP/1.1
Host: Server
Cache-Control: no-cache
Postman-Token: 83a606e1-26f6-ff21-dd5d-f25f6cb735eb

{ "grant_type":"password", "client_id":"sugar", "client_secret":"", "username":"username", "password":"password", "platform":"base" }

response.json

{
"access_token": "5ee48ec7-023e-ecff-5184-530bd0358868",
"expires_in": 3600,
"token_type": "bearer",
"scope": null,
"refresh_token": "5f197357-0167-f7a6-7912-530bd03275b6",
"refresh_expires_in": 1209600,
"download_token": "5f531625-e301-e3ea-1b11-530bd098be41"
}

Once you get the response you'll need to hold onto the access_token and the refresh_token. Anytime the access_token is invalidated, you'll want to make another request to the token endpoint with a grant_type of "refresh_token". Store just the refresh_token in long term storage - not the username and password.

For all the following requests we are going to pass in a header of OAuth-Token: {{your access token here}}

2. Create an Account - Burger Palace

Now that we have the access_token we can start having some fun! Let's create a new customer - "Burger Palace" as an Account.

Being that everything is RESTful creating a new record is just a POST away. In this case we just do a POST to /rest/v10/Accounts.

For this example we will be posting an id as part of the record. You do not need to do this as SugarCRM will automatically generate an id for your record.

CURL Accounts.sh

curl -X POST -H OAuth-Token:9126cdb4-0c4e-4796-1a4f-530bccd48060 -H Cache-Control:no-cache -H Postman-Token:cb3331fa-7e62-1f11-9fae-cb9731bc3500 -d '{ "id":"demo_burger_palace", "name":"Burger Palace", "account_type":"Customer", "description":"My Example Account", "email":[{"email_address":"burgers@example.com"}] }' http://server/pro720/rest/v10/Accounts


POST Accounts.http

POST /pro720/rest/v10/Accounts HTTP/1.1
Host: server
OAuth-Token: 9126cdb4-0c4e-4796-1a4f-530bccd48060
Cache-Control: no-cache
Postman-Token: 90329f95-f2a9-fd87-c4cf-421fdd9f24ae

{ "id":"demo_burger_palace", "name":"Burger Palace", "account_type":"Customer", "description":"My Example Account", "email":[{"email_address":"burgers@example.com"}] }

Response.json

{
"id": "demo_burger_palace",
"name": "Burger Palace",
"date_entered": "2014-02-25T10:51:51-08:00",
"date_modified": "2014-02-25T10:51:51-08:00",
"modified_user_id": "1",
"modified_by_name": "Administrator",
"created_by": "1",
"created_by_name": "Administrator",
"doc_owner": "",
"user_favorites": "",
"description": "My Example Account",
"deleted": false,
"assigned_user_id": "",
"assigned_user_name": "",
"team_count": "",
"team_name": [
        {
"id": 1,
"name": "Global",
"name_2": "",
"primary": true
        }
    ],
"email": [
        {
"email_address": "burgers@example.com",
"invalid_email": false,
"opt_out": false,
"primary_address": false,
"reply_to_address": false
        }
    ],
"email1": "burgers@example.com",
"email2": "",
"invalid_email": false,
"email_opt_out": false,
"facebook": "",
"twitter": "",
"googleplus": "",
"account_type": "Customer",
"industry": "",
"annual_revenue": "",
"phone_fax": "",
"billing_address_street": "",
"billing_address_street_2": "",
"billing_address_street_3": "",
"billing_address_street_4": "",
"billing_address_city": "",
"billing_address_state": "",
"billing_address_postalcode": "",
"billing_address_country": "",
"rating": "",
"phone_office": "",
"phone_alternate": "",
"website": "",
"ownership": "",
"employees": "",
"ticker_symbol": "",
"shipping_address_street": "",
"shipping_address_street_2": "",
"shipping_address_street_3": "",
"shipping_address_street_4": "",
"shipping_address_city": "",
"shipping_address_state": "",
"shipping_address_postalcode": "",
"shipping_address_country": "",
"parent_id": "",
"sic_code": "",
"duns_num": "",
"parent_name": "",
"campaign_id": "",
"campaign_name": "",
"my_favorite": false,
"_acl": {
"fields": {}
    },
"following": true,
"_module": "Accounts"
}

And of course you can add any additional fields that you would like to set.

3. Retrieve a list of Accounts 

Great, now that we have records in our system let's fetch them back.  Being RESTful you just need to do a GET request to /rest/v10/Accounts. Don't forget to pass in the OAuth-Token header.  To speed things up be specific about the fields that you want. Let's just fetch the name, account type, and description by passing in fields=name,account_type,description. We'll pass in max_num to specify that we only want 3 records at a time. By default if max_num isn't set it will return 20 records. In this case the request will look like /rest/v10/Accounts?fields=name,account_type,description&max_num=3

CURL Accounts.sh

curl -X GET -H OAuth-Token:df488865-4a70-6d88-036c-530ce62453d0 -H Cache-Control:no-cache -H Postman-Token:19a88a98-b2bf-da03-a1f9-ab22730bd3e2 http://server/pro720/rest/v10/Accounts?fields=name,account_type,description&max_num=3


GET Accounts.http

GET /pro720/rest/v10/Accounts?fields=name%2Caccount_type%2Cdescription&max_num=3 HTTP/1.1
Host: server
OAuth-Token: df488865-4a70-6d88-036c-530ce62453d0
Cache-Control: no-cache
Postman-Token: 74200fe5-a926-a5d4-d07d-d899bb1a573e


Response.json

{
"next_offset": 3,
"records": [
        {
"id": "demo_burger_palace",
"name": "Burger Palace",
"date_modified": "2014-02-25T10:51:51-08:00",
"description": "My Example Account",
"account_type": "Customer",
"_acl": {
"fields": {}
            },
"_module": "Accounts"
        },
        {
"id": "ec125a78-f202-250b-e75e-530ca4d7d1d0",
"name": "Ink Conglomerate Inc",
"date_modified": "2014-02-25T06:11:00-08:00",
"description": "",
"account_type": "Customer",
"_acl": {
"fields": {}
            },
"_module": "Accounts"
        },
        {
"id": "ebc56ea4-ef13-b024-c07d-530ca40c97e4",
"name": "Constrata Trust LLC",
"date_modified": "2014-02-25T06:11:00-08:00",
"description": "",
"account_type": "Customer",
"_acl": {
"fields": {}
            },
"_module": "Accounts"
        }
    ]
}

Excellent! If we wanted to paginate we could just add the offset parameter to the request. That is the number of records you want to skip in the list before returning anything. So passing an offset of 3 would get us to the next page of records.

4. Filter a list of Accounts by Name 

Now, let's filter our list for records that have the name "Burger Palace". To do this we just need to do a GET request to /rest/v10/Accounts and to pass in the filter parameter. In this case the request would look like /rest/v10/Accounts?filter[0][name]=Burger Palace&fields=name,account_type,description

CURL Accounts Filter.sh

curl -X GET -H OAuth-Token:34b05aba-e8be-0db8-5fbe-530d19787689 -H Cache-Control:no-cache -H Postman-Token:347571b3-c873-f846-b469-c002810f4538 http://server/pro720/rest/v10/Accounts?filter[0][name]=Burger Palace&fields=name,account_type,description,email

GET Accounts Filter.http

GET /pro720/rest/v10/Accounts?filter%5B0%5D%5Bname%5D=Burger%20Palace&fields=name%2Caccount_type%2Cdescription%2Cemail HTTP/1.1
Host: server
OAuth-Token: 34b05aba-e8be-0db8-5fbe-530d19787689
Cache-Control: no-cache
Postman-Token: 548a67ec-ebfe-fa37-51a3-698783eaff16


Response.json

{
"next_offset": -1,
"records": [
        {
"id": "demo_burger_palace",
"name": "Burger Palace",
"date_modified": "2014-02-25T14:32:25-08:00",
"description": "My Example Account",
"email": [
                {
"email_address": "burgers@example.com",
"invalid_email": false,
"opt_out": false,
"primary_address": false,
"reply_to_address": false
                }
            ],
"account_type": "Customer",
"_acl": {
"fields": {}
            },
"_module": "Accounts"
        }
    ]
}

You'll notice in the response that the next_offset in the response is set to -1 which means that there are no more records in the collection.

5. Filter a list of Accounts by Names starting with "B" and a specific email address

Now, let's pass in multiple filter conditions and let's use some of the filter operations.

Sugar 7 supports the following filter operations

$equalsPerforms an exact match on that field.
$not_equalsMatches on non-matching values.
$startsMatches on anything that starts with the value.
$inFinds anything where field matches one of the values as specified as an array.
$not_inFinds anything where field does not match any of the values as specified as an array.
$is_nullChecks if the field is null. This operation does not need a value specified.
$not_nullChecks if the field is not null. This operation does not need a value specified.
$ltMatches when the field is less than the value.
$lteMatches when the field is less than or equal to the value.
$gtMatches when the field is greater than the value.
$gteMatches when the field is greater than or equal to the value.

So to filter for Account names starting with the letter "B" we'll use the $starts operator. So instead of "filter[0][name]=Burger Palace" we can use filter[0][name][$starts]=B.

SugarCRM stores email addresses as normalized values. To search for an Account with an email address we need to search using the relationship for email addresses which is email_addresses. Specifically, we want to filter on the field email_address within that relationship. So to do an exact match on a specific email address we pass in  filter[0][email_addresses.email_address]=burgers@example.com Our request will look like /rest/v10/Accounts?filter[0][name][$starts]=B&filter[0][email_addresses.email_address]=burgers@example.com&fields=name,account_type,description,email . You will notice that we also added email to the list of fields that we wish to have returned which will cause it to return a collection of all the email addresses that are associated with this record.

curl -X GET -H OAuth-Token:e2505ba8-fc56-de05-ce9f-530d1bf56227 -H Cache-Control:no-cache -H Postman-Token:cb452a8c-2bb9-ebf6-ae66-a0700b10cdcc http://honey-b/pro720/rest/v10/Accounts?filter[0][name][$starts]=B&filter[0][email_addresses.email_address]=burgers@example.com&fields=name,account_type,description,email
GET /pro720/rest/v10/Accounts?filter%5B0%5D%5Bname%5D%5B%24starts%5D=B&filter%5B0%5D%5Bemail_addresses.email_address%5D=burgers%40example.com&fields=name%2Caccount_type%2Cdescription%2Cemail HTTP/1.1
Host: server
OAuth-Token: e2505ba8-fc56-de05-ce9f-530d1bf56227
Cache-Control: no-cache
Postman-Token: 29bd612a-828d-ad84-7f6a-dd8ba2314956
{
    "next_offset": -1,
    "records": [
        {
            "id": "demo_burger_palace",
            "name": "Burger Palace",
            "date_modified": "2014-02-25T14:32:25-08:00",
            "description": "My Example Account",
            "email": [
                {
                    "email_address": "burgers@example.com",
                    "invalid_email": false,
                    "opt_out": false,
                    "primary_address": false,
                    "reply_to_address": false
                }
            ],
            "account_type": "Customer",
            "_acl": {
                "fields": {}
            },
            "_module": "Accounts"
        }
    ]
}


Continue to Part 2 - Retrieving, Updating, and Relating Records 

  • Comment originally made by Mehul Bhandari.

    how to use refresh_token in request ??
  • Comment originally made by Matthew Marum.

    Name fields on 'person' records like Contacts, Leads, and Users are tricky. They're non-db fields that are calculated from first_name and last_name fields.  Try something like filter[0][$or][0][first_name][$starts]=abc&filter[0][$or][1][last_name][$starts]=abc and you'll see the result you're looking for.
  • Comment originally made by Anthony.

    Can you explain me how to filter in collection. I want to filter user' name . I use the following GET request:

    /rest/v10/Calls/6e8ed887-8250-e570-280c-557e95c10de7/collection/invitees?fields=name&filter[0][name]=abc

    the Reply:

    {

        "records": [

            {

                "id": "seed_chris_id",

                "name": "Chris Olliver",

                "date_modified": "2016-12-08T11:24:52-08:00",

                "_acl": {

                    "fields": {}

                },

                "_module": "Users",

                "_link": "users"

            },

            {

                "id": "e21bd475-0090-2448-082c-523cc7dff4d2",

                "name": "Brandi Steel",

                "date_modified": "2013-09-25T19:35:16-07:00",

                "locked_fields": [],

                "_acl": {

                    "fields": {}

                },

                "_module": "Contacts",

                "_link": "contacts"

            }

        ],

        "next_offset": {

            "contacts": -1,

            "leads": -1,

            "users": -1

        }

    }

    I expected no record will return. it looks like the filter didn't work

  • Comment originally made by testontash.

    Thank you Matthew,

    Yo are great...Actually I was trying the module name with out the package name. Everything is working fine now.

    Thank you,

    -sudeep

  • Comment originally made by Matthew Marum.

    Accessing data in Custom Modules should be same as accessing data in out of the box modules.  The REST API does not differentiate.  Are you certain you are using your correct custom module identifier?  There may be a package prefix you are leaving off (for example, pkg_CustomModule would be your custom module's ID if the package in module builder was "pkg" and the module you created was called "CustomModule").