General

Requests

All requests must be made over HTTPS. All data is sent and received as JSON.

When making requests, you receive data as JSON by properly setting the Accept header to application/json, as shown in the example below:

1
2
3
$ curl https://yoursite.desk.com/api/v2/cases/:id \
    -u email:password \
    -H 'Accept: application/json'

When creating or modifying resources, you need to send the data as JSON by properly setting the Content-Type header to application/json, as shown below:

1
2
3
4
5
6
$ curl https://yoursite.desk.com/api/v2/cases \
    -u email:password \
    -X POST \
    -H 'Content-Type: application/json' \
    -H 'Accept: application/json' \
    -d '{"subject":"Test"}'

Responses

All responses will be JSON representations of the requested resource(s) or error message(s).

Single resources will always be returned as a top level object:

1
2
3
{
  "field": "value"
}

Collections of resources will always be returned as a top level array of objects:

1
2
3
4
5
6
7
8
[
  {
    "field": "value"
  },
  {
    "field": "value"
  }
]

Custom Response Headers

Currently, all responses return the following custom headers:

Name Description
X-AppVersion Desk.com app version in the format "major.minor"
X-Rate-Limit-Limit maximum number of requests per minute to the endpoint
X-Rate-Limit-Remaining available requests remaining in the current window
X-Rate-Limit-Reset seconds remaining until the next window begins

Empty Fields

Fields that have no value will always at least return nil rather than not existing in the response.

Timestamps

Timestamps will always be returned in UTC using the standard ISO 8601 format: YYYY-MM-DDTHH:MM:SSZ


Identifiers

A Word on Object Identifiers

Our API was designed in accordance with RESTful principles and HAL which include using a full URI as an "identifier" (which we provide as the self data in the _links attribute). The intention, according to HAL, is to keep that URI the sole means of identifying the resource.

We have received a number of requests to expose the number at the end of the self link in entities, usually for convenience, as its own id field at the root of the object. We are beginning to comply with this request. Please note that the value of this id field may change in the future for reasons including scalability or concurrency with other systems, and is thus provided here only for convenience; yet the URI (including the original number) is expected to be supported long-term to return the resource in question.

By storing the entire self link on your systems, your code should only need to know how to follow HTTP 301 Redirects (and/or update your stored identifier data with the new URI), potentially greatly reducing the amount of work during such a change.

Twitter went through this problem in 2009 with the Twitpocalypse, which broke a number of their client apps at the time. Other API providers have also had to make similar changes to their resource identification design as they have grown.

Examples of development assumptions on your end that might break under these circumstances include: saving just the numeric ID into a 32 bit integer field in your database schema, or having a regular expression in your code that assumes the ID will always be purely base-10 numeric (and short). Stay away from those kinds of assumptions in the systems you build to integrate with ours and we can hopefully minimize future labor.


Rate Limits

Overview

Rate limiting is implemented on a site-wide basis, irrespective of the method of authentication. The current threshold is 60 requests per minute times the number of full-time agents and admins on your site, up to a maximum of 300 requests per minute. For example, a site with 1 admin and 1 agent would have a rate limit of 120 requests per minute. This is limited in 1 minute windows.

Response Headers

All responses include headers with status info about rate limiting.

Name Description
X-Rate-Limit-Limit maximum number of requests per minute to the endpoint
X-Rate-Limit-Remaining available requests remaining in the current window
X-Rate-Limit-Reset seconds remaining until the next window begins

Hitting the Limit

If your application hits the rate limit, an HTTP 429 will be returned with this body:

1
2
3
{
  "message": "Too Many Requests"
}

And these headers, assuming it is 40 seconds into the current window:

1
2
3
4
5
{
  "X-Rate-Limit-Limit": 60,
  "X-Rate-Limit-Remaining": 0,
  "X-Rate-Limit-Reset": 20
}

When the limit is reached, your application should stop making requests until X-Rate-Limit-Reset seconds have elapsed.

Concurrency Limits for /api/v2/insights3/ calls

The Insights3 endponts have an additional rate limit for concurrency. This additional limit does not use or modify the general API rate-limit response headers listed above. Please see Insights V3 for details.


Authentication

We currently support Basic Authentication (over SSL/TLS) and OAuth 1.0a authentication.

HTTP Basic Auth

Basic authentication is the simplest form of authentication to get you up and running with the Desk.com API. If you only require access to your own account's data, Basic Authentication is an excellent choice.

To use basic authentication, simply use HTTP Basic Auth with your Desk.com email and password as shown in our curl examples:

1
2
3
$ curl https://yoursite.desk.com/api/v2/cases \
    -u email:password \
    -H 'Accept: application/json'

OAuth 1.0a

If you are writing an API application that needs to access other accounts on behalf of their users, you'll need to invest in authenticating with OAuth 1.0a. This gives you the capability to not store emails/passwords of users using your API application.

Click here for an example using the standard ruby OAuth library.


Verbs

GET

Used for retrieving a resource or collection of resource

POST

Used for creating a resource

PATCH

Used for modifying a resource

DELETE

Used for deleting a resource

Legacy Client Support

If your HTTP client is not able to perform PATCH/DELETE requests, you can alternatively perform a POST request using an X-HTTP-Method-Override header to specify the intended HTTP verb:

1
2
3
4
5
6
7
$ curl https://yoursite.desk.com/api/v2/cases/1 \
    -u email:password \
    -H 'Accept: application/json' \
    -H 'Content-Type: application/json' \
    -H 'X-HTTP-Method-Override: PATCH' \
    -X POST \
    -d '{ "subject":"Updated" }'

Response Codes

Success

  • 200 Success: request was successful
  • 201 Created: resource successfully created, includes Location header giving the location of the new resource
  • 202 Accepted: request was accepted, but requires processing
  • 204 No Content: request was successful, but nothing to return to the client

Redirection

  • 304 Not Modified: resource has not been modified since the ETag provided in the if-none-match header

Error

  • 400 Bad Request: generic errors, such as not being able to parse the submitted JSON
  • 401 Unauthorized: request requires authentication and authorization fails
  • 404 Not Found: requested resource was not found
  • 403 Forbidden: authenticated user does not have authorization to access the requested resource
  • 405 Method Not Allowed: incorrect HTTP verb used to perform the requested action
  • 406 Not Acceptable: request used an unacceptable Accept header (currently only support application/json)
  • 409 Conflict: request could not be processed due to a conflict with another request (e.g., request contained duplicate parameters or case was locked by another user when attempting to update it)
  • 415 Unsupported Media Type: requested POST/PATCH requires a Content-Type of application/json
  • 422 Unprocessable Entity: requested modification or creation of a resource failed due to validation errors Specific application error codes are detailed under Validation Codes
  • 429 Too Many Requests: your application is currently Rate Limited
  • 501 Not Implemented: the request involved a data type which is not currently implemented

ETag Caching

Several endpoints support caching with ETags. Any request made to one of these endpoints which returns a 304 Not Modified will not count against your rate limit. They're free!

Endpoints with ETag Support

Resource Actions
Labels LIST
Groups LIST
Macros LIST
Users LIST
Case Replies LIST

The example responses below show only the headers. If the data on the server has changed, a 200 will be returned along with the entire response body. If the data on the server has not changed, a 304 will be returned with an empty response body, signifying that your application has up-to-date data.

Request without an ETag

1
2
3
$ curl https://yoursite.desk.com/api/v2/groups \
    -u email:password \
    -I

Example Response Headers

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
HTTP/1.1 200 OK
Date: Fri, 24 May 2013 15:00:10 GMT
Content-Type: application/json; charset=utf-8
Connection: keep-alive
X-Rate-Limit-Remaining: 59
X-Rate-Limit-Limit: 60
X-Rate-Limit-Reset: 50
X-AppVersion: 120.1
ETag: "1369407549"
X-Frame-Options: SAMEORIGIN
X-UA-Compatible: IE=Edge
Cache-Control: no-cache, private
X-Runtime: 0.323047
X-Rack-Cache: miss

Note that the ETag, which includes quotes, must be sent to the server with the quotes included.

Request with a current ETag

1
2
3
4
$ curl https://yoursite.desk.com/api/v2/groups \
    -u email:password \
    -H "if-none-match: \"1369407549\""
    -I

Example Response Headers

1
2
3
HTTP/1.1 304 Not Modified
Date: Fri, 24 May 2013 15:05:42 GMT
Connection: keep-alive

Validation Codes

A 422 Unprocessable Entity response occurs when there were errors validating a resource. An error body will contain details about each field that had errors, using application specific validation error codes.

Example Response

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
{
  "message": "Validation Failed",
  "errors": {
    "priority": [
      "inclusion"
    ],
    "status": [
      "existence"
    ]
  }
}

Codes

  • blank: value has not been set and is required
  • existence: value does not exist
  • taken: value has already been taken and must be unique
  • inclusion: value is not an available choice
  • exclusion: value is reserved
  • too_short: string value is too short
  • too_long: string value is too long
  • invalid: value is invalid, please see documentation for resource specifics

Note: All timestamps must be after 1970-01-01 00:00:00 UTC


Relationships

Related resources are linked and embedded using the HAL specification.

Resource Links

Resource links allow your client to follow links between resources instead of needing to hardcode urls.

Every resource has a link to itself, denoted by the self link. The type of resource linked is noted in a link's class attribute.

As an example, here is a response for a case with a link to the customer the case belongs to (leaving additional fields out for clarity):

Example Response

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
{
  "subject": "Some case",
  "_links": {
    "self": {
      "href": "/api/v2/cases/1",
      "class": "case"
    },
    "customer": {
      "href": "/api/v2/customers/1",
      "class": "customer"
    }
  }
}

Embedding Resources

Related resources are linked and embedded using the HAL specification.

Embedding is useful when you need to retrieve a particular resource or collection of resources along with the related resource(s).

Not every relationship can be embedded, and you can only specify embedded relationships on the top level resource or collection of resources. To see exactly which relationships can be optionally embedded, please refer to the documentation for the particular resource.

Here is how you would request a case and embed the related customer:

Request

1
2
3
$ curl https://yoursite.desk.com/api/v2/cases/1?embed=customer \
    -u email:password \
    -H 'Accept: application/json'

Response with Embedded Resource

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
{
  "subject": "Some case",
  "_links": {
    "self": {
      "href": "/api/v2/cases/1",
      "class": "case"
    },
    "customer": {
      "href": "/api/v2/customers/1",
      "class": "customer"
    }
  },
  "_embedded": {
    "customer": {
      "first_name": "John",
      "last_name": "Doe",
      "_links": {
        "self": {
          "href": "/api/v2/customers/1",
          "class": "customer"
        }
      }
    }
  }
}

Pagination

Requests to collections of resources will return a page of 50 resources by default. You can request up to 100 entries per page by using the per_page parameter. By default, the first page is returned unless specified with the page parameter. You can follow links to different pages using _links and access the resulting resources under the _embedded entries. Some endpoints allow for a higher per_page value. Please see specific resources for details.

Some endpoints have a max allowed per_page value. These endpoints currently include Case list, Company Case list, Customer Case list, Filter Case list, Case search, Customer list, Customer search, Company list, Company search, and Article search. Please see the linked pages for more details.

Fields

field name description
total_entries the total number of entries available
page the page number of the returned page

Links

rel class description
self page this page of entries
first page the first page of entries
last page the last page of entries
previous page the previous page of entries
next page the next page of entries

Embedded Resources

rel description
entries the collection of resources

Example Response

Example response of the second page of users, showing 2 users per page with a total of 110 users:

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
{
  "total_entries": 110,
  "page": 2,
  "_links": {
    "self": {
      "href": "/api/v2/users?page=2&per_page=2",
      "class": "page"
    },
    "first": {
      "href": "/api/v2/users?page=1&per_page=2",
      "class": "page"
    },
    "last": {
      "href": "/api/v2/users?page=55&per_page=2",
      "class": "page"
    },
    "next": {
      "href": "/api/v2/users?page=3&per_page=2",
      "class": "page"
    },
    "previous": {
      "href": "/api/v2/users?page=55&per_page=2",
      "class": "page"
    }
  },
  "_embedded": {
    "entries": [
      {
        "id": 1,
        "name": "John Doe",
        "public_name": "John Doe",
        "email": "john@acme.com",
        "level": "agent",
        "created_at": "2016-08-18T22:29:27Z",
        "updated_at": "2017-08-11T22:29:27Z",
        "current_login_at": "2017-08-17T22:29:27Z",
        "last_login_at": "2017-08-11T22:29:27Z",
        "avatar": "http://www.gravatar.com/avatar/3904fac733nf92bfbf54d71c9bbc18cd?default=404&rating=PG&size=50",
        "_links": {
          "self": {
            "href": "/api/v2/users/1",
            "class": "user"
          },
          "preferences": {
            "href": "/api/v2/users/1/preferences",
            "class": "user_preference"
          },
          "macros": {
            "href": "/api/v2/users/1/macros",
            "class": "macro"
          },
          "filters": {
            "href": "/api/v2/users/1/filters",
            "class": "filter"
          },
          "integration_urls": {
            "href": "/api/v2/users/1/integration_urls",
            "class": "integration_url"
          },
          "groups": {
            "href": "/api/v2/users/1/groups",
            "class": "group"
          },
          "searches": {
            "href": "/api/v2/users/1/searches",
            "class": "search"
          }
        }
      },
      {
        "id": 1,
        "name": "John Doe",
        "public_name": "John Doe",
        "email": "john@acme.com",
        "level": "agent",
        "created_at": "2016-08-18T22:29:27Z",
        "updated_at": "2017-08-11T22:29:27Z",
        "current_login_at": "2017-08-17T22:29:27Z",
        "last_login_at": "2017-08-11T22:29:27Z",
        "avatar": "http://www.gravatar.com/avatar/3904fac733nf92bfbf54d71c9bbc18cd?default=404&rating=PG&size=50",
        "_links": {
          "self": {
            "href": "/api/v2/users/1",
            "class": "user"
          },
          "preferences": {
            "href": "/api/v2/users/1/preferences",
            "class": "user_preference"
          },
          "macros": {
            "href": "/api/v2/users/1/macros",
            "class": "macro"
          },
          "filters": {
            "href": "/api/v2/users/1/filters",
            "class": "filter"
          },
          "integration_urls": {
            "href": "/api/v2/users/1/integration_urls",
            "class": "integration_url"
          },
          "groups": {
            "href": "/api/v2/users/1/groups",
            "class": "group"
          },
          "searches": {
            "href": "/api/v2/users/1/searches",
            "class": "search"
          }
        }
      }
    ]
  }
}

Field Selection

The fields included in a response can be limited by providing the comma separated fields param in the request. _links will be returned with all responses. See each endpoint for a list of available fields.

Example Curl Request

1
2
3
$ curl https://yoursite.desk.com/api/v2/cases/:id\?fields\=subject,status \
    -u email:password \
    -H 'Accept: application/json'

Example Response

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
{
  "subject": "Welcome",
  "status": "new",
  "_links": {
    "self": {
      "href": "/api/v2/cases/1",
      "class": "case"
    },
    "customer": {
      "href": "/api/v2/customers/1",
      "class": "customer"
    },
    "assigned_user": {
      "href": "/api/v2/users/2",
      "class": "user"
    },
    "assigned_group": {
      "href": "/api/v2/groups/1",
      "class": "group"
    },
    "locked_by": null
  }
}