API - The Basics Follow

 

Connecting to Salpo CRM

Before you begin, you will need:

 

Requests

All requests must be made to the Salpo CRM HTTPS URL and must have the following headers: accept header, content-type header and authorization header.

The accept header is used to specify the version of the API you are trying to access. The content-type header is used to describe the nature of the data within the body of the response. The authorization header is required to authenticate the user and gain access to protected resources (you will need an API key for this).

A useful tool when testing your requests is Postman (a Chrome extension). This allows you to make API requests without any programming.

A typical Salpo CRM API GET request would look like the following:

 


For this example we are using Guzzle PHP HTTP client, however, you can use any HTTP client. We strongly recommend you use a library to communicate with the API rather than PHP's built in cURL functions.

<?php
...
$client = new GuzzleHttp\Client();
$res = $client->get(
'https://[subdomain].lhlive.com/contacts',
array(
'auth' => array('[Salpo CRM Username]', '[API Key]'),
'headers' => array(
'Accept' => 'application/vnd.Salpo CRM.v1.hal+json',
'Content-Type' => 'application/json'
)
)
);

echo $res->getBody();      // {"_links": {"...
var_export($res->json()); // Outputs the JSON decoded data
...


   
curl -H 'Accept: application/vnd.Salpo CRM.v1.hal+json' \ -H 'Content-Type: application/json' \
-u 'username:API Key' \
https://[subdomain].lhlive.com/contacts

 

A typical response to the API GET request above would like the following:

    {
"_links": {
"previous":{
"href": "https://[subdomain].lhlive.com/contacts..."
}
"next": {
"href": "https://[subdomain].lhlive.com/contacts..."
},
"first": {
"href": "https://[subdomain].lhlive.com/contacts..."
},
"last": {
"href": "https://[subdomain].lhlive.com/contacts..."
}
},
"_embedded": {
"contacts": [
{
"_links": {
"self": {
"href": "https://[subdomain].lhlive.com/contacts/1"
},
...
},
"type": "individual",
"name": "John Smith",
"firstName": "John",
"lastName": "Smith",
...
},
...
]
}
}

 

Accept Header

You can use different versions of the API for each request. You can find what versions are available for each request within the API documentation. Requests are returned in HAL+JSON format, so you always include the header requesting the results as JSON:

    Accept: application/vnd.Salpo CRM.v1.hal+json

Whilst we do not recommend it, you can use the latest version of the API in the request by using latest instead:

    Accept: application/vnd.Salpo CRM.latest.hal+json

Note: Using latest can lead to problems if the API function is changed or edited.

 

Content-Type Header

When sending data to Salpo CRM in a POST or PUT request, your request must specify the content type of your data:

    Content-Type: application/json

If your JSON request is invalid, the API will respond with a status code 400 Bad Request. This commonly occurs when ampersands are not correctly encoded or there are validation errors in the text of your request. Please inspect the body of the response for more details regarding the error.

 

Authorization Header

To connect to Salpo CRM’s API, you will need to do the following:

1. Generate an API key. You can find this by clicking on your user account in Salpo CRM and selecting API Tokens. Create a new key by clicking Add API Token. Add a name and clicking Create. The next window displayed contains the API key.

Note: You will only see this key once, so please copy it before closing this window.

2. Use the API key. Salpo CRM uses HTTP Basic Authentication—your API key is securely encrypted by the SSL channel because all requests are made to the HTTPS URL.

In most programming languages, the API key can be specified in the authentication section of request. If your language requires a username and password, enter the Salpo CRM username for the API username and the API key for the password.

 

General Patterns

API requests and responses are made using general patterns. These consist of:

Pattern HTTP Method(s) Description
List GET Retrieve a paginated list of objects
Get GET Get a single object using the unique identifier of the object
Add POST Add a new object
Edit PUT Modify an existing object using the unique identifier
Delete DELETE Delete an existing object using the unique identifier
Undelete PUT Restore a previously deleted object using the unique identifier
Merge POST Merge two objects together using the unique identifier of both objects
Search GET Search for objects whilst specifying a query string
Bulk GET,DELETE Perform bulk actions on a filtered list of objects

 

Some examples of General Patterns:

GET

    <?php
...
$client = new GuzzleHttp\Client();
$res = $client->get(
'https://[subdomain].lhlive.com/contacts/1',
array(
'auth' => array('[Salpo CRM Username]', '[API Key]'),
'headers' => array(
'Accept' => 'application/vnd.Salpo CRM.v1.hal+json',
'Content-Type' => 'application/json'
)
)
);

echo $res->getBody();      // {"_links": {"...
var_export($res->json()); // Outputs the JSON decoded data
...
   curl -XGET -H 'Accept: application/vnd.Salpo CRM.v1.hal+json' \
     -H 'Content-Type: application/json' \
-u 'username:API Key' \ https://[subdomain].lhlive.com/contacts/1

 

 

ADD

    <?php
...
$client = new GuzzleHttp\Client();
$res = $client->post(
'https://[subdomain].lhlive.com/contacts',
array(
'auth' => array('[Salpo CRM Username]', '[API Key]'),
'headers' => array(
'Accept' => 'application/vnd.Salpo CRM.v1.hal+json',
'Content-Type' => 'application/json'
),
'body' => '{"type": "individual", "firstName": "John", "lastName": "Smith", ...}'
)
);

echo $res->getBody();      // {"_links": {"...
var_export($res->json()); // Outputs the JSON decoded data
...

 

   curl -XPOST -H 'Accept: application/vnd.Salpo CRM.v1.hal+json' \
     -H 'Content-Type: application/json' \
-u 'username:API Key' \ https://[subdomain].lhlive.com/contacts \
-d '{"type": "individual", "firstName": "John", "lastName": "Smith", ...}'

 

Notice we have made a request to https://[subdomain].lhlive.com/contacts and not https://[subdomain].lhlive.com/contacts/1

 

EDIT

    <?php
...
$client = new GuzzleHttp\Client();
$res = $client->put(
'https://[subdomain].lhlive.com/contacts/1',
array(
'auth' => array('[Salpo CRM Username]', '[API Key]'),
'headers' => array(
'Accept' => 'application/vnd.Salpo CRM.v1.hal+json',
'Content-Type' => 'application/json'
),
'body' => '{"firstName": "John", "lastName": "Smith", ...}'
)
);

echo $res->getBody();      // {"_links": {"...
var_export($res->json()); // Outputs the JSON decoded data
...

 

   curl -XPUT -H 'Accept: application/vnd.Salpo CRM.v1.hal+json' \
     -H 'Content-Type: application/json' \
-u 'username:API Key' \ https://[subdomain].lhlive.com/contacts/1 -d '{"firstName": "John", "lastName": "Smith", ...}'

 

Handling Responses

Our API conforms to the HAL+JSON standard. The principles of HAL+JSON are that you can follow the links in the JSON to reach related objects to the one you are viewing. More information can be found here

The following patterns are responses to the request patterns above.

Pattern Responses
List
  • Should support pagination via the _links field
  • Should return HTTP status code 200
  • Should include the total item count as a normal field called totalCount
  • Should use the _embedded field with a key of the collection name for the actual items
  • Each embedded item should have a link to the full resource, an ID and any other useful fields for the list
Get
  • Should return HTTP status code 200 (or 404 if the resource does not exist, or 303 if the resource has been soft deleted)
  • If the resource has been soft deleted it should return a location header of the deleted resource URL
  • Should utilise embedded and linked resources where appropriate
  • Each embedded item should have a link to the full resource, an ID and any other useful fields for the list
  • In the case where there is a single URL for multiple subtypes (e.g. contact details) a type field should be used in the JSON body to determine the class and the rules for the other fields
Add 
  • Should return HTTP status code 201 (or 400 in the case of a validation error)
  • Should return a location header pointing to the newly created resource
  • In the case of a validation error then the response body should be a JSON object showing the validation errors for each field (all fields from the request should be present in the response)
Edit 
  • Should return HTTP status code 204 (or 400 in the case of a validation error)
  • For validation responses, see the add response section
Delete
  • Should return HTTP status code 204 (or 404 if the resource could not be found)
  • If the resource is being soft-deleted the header should be 303 with a location header of the new resource URL.
Undelete 
  • Should return HTTP status code 303 (or 404 if the resource could not be found in the deleted items) with a location header of the undeleted resource URL.
Merge 
  • Should return HTTP status code 201 (or 400 in the case of a validation error)
  • Should return a location header pointing to the primary contact
Search 
  • Should be very similar to the list response but should always denote the entity type in the case that results are mixed
Bulk 
  • Should return a HTTP status code of 202 Accepted if the job has been sent to the background with a location header set to point to the URL of the job status (if one exists)
  • Should return the normal HTTP status code expected for the method in question if no background job is used

 

How to handle decoding JSON

All responses are returned in JSON format and will need to be decoded using a JSON decoder in your chosen programming language.

 

Validation Errors

Validation errors will always be returned with a 400 bad request response code.

You will receive a validation error if there is a problem with your request data usually when adding new data or modify existing data.  

Below is an example of the response of a POST request with missing required firstName and lastName fields:

    {
        type: [ ]
        firstName: {
            errors: [
                "error.individual.firstnameOrLastname.blank"
            ]
        }
        lastName: {
            errors: [
                "error.individual.firstnameOrLastname.blank"
            ]
        }
        customData: [ ]
        emails: [ ]
        telephones: [ ]
        websites: [ ]
        addresses: [ ]
    }

 

General Error

Our error codes use standard HTTP response codes and where relevant will contain additional information in the response body.

The example below shows a 404 response to a GET request for a contact that could not be found.

    [
        {
            logRef: "2014-10-10 11:00:17"
            message: "Contact does not exist"
            _links: { }
        }
    ]

 

Other API considerations

  • URLs should use dash-separated, lowercase paths
  • Properties are always camel case
  • Links are always absolute URLs
  • Parameter IDs in the API documentation are named based on the object being added or modified. For example, /contacts/{contact} instead of {id}.

 

Was this article helpful?
6 out of 6 found this helpful

Comments