REST API overview

All data is sent and received as JSON over HTTPS. All endpoints are relative to the base URL:


Every request must include an Accept header (see Versioning below) and a Content-Type header. Most requests must include an Authorization header (see Authenticating requests below):

Accept: application/vnd.layer+json; version=3.0
Content-Type: application/json
Authorization: Layer session-token="YOUR-SESSION-TOKEN"


The API is versioned via an Accept header:

Accept: application/vnd.layer+json; version=3.0

You must explicitly require a version via the header. This allows us to make changes in future versions, and gives you time to update, without breaking your app in the meantime. An error is returned if you fail to request a specific version of the API:

Invalid header | Status: 406 (Not Acceptable)

  "id": "invalid_header",
  "code": 107,
  "message": "Invalid Accept header; must be of form application/vnd.layer+json; version=x.y",
  "url": "",
  "data": {
    "header": "Accept"


Many resource objects contain properties postfixed with _url, which indicate the correct URL for performing an operation upon that resource. For example, the Conversation object contains a message_url property with the current URL for getting Message objects in that Conversation.

You can also get a list of important URLs by making a GET request to the root endpoint:



curl -i -X GET


Status: 200 (OK)

The Link header in the response will contain important URLs:

link: <>; rel=nonces,
      <>; rel=sessions,
      <>; rel=conversations,
      <>; rel=content

The possible rel values are:

  • nonces: The URL to request a nonce for authentication
  • conversations: The URL to load the first page of conversations
  • content: The URL for uploading large content, such as images and video
  • websocket: The URL to establish a WebSocket connection

Requests overview

To protect Layer servers from abuse, we enforce rate limiting. Limits are set to be relatively forgiving. Exceeding limits will return a Rate Limiting Error (Status 429 (Too Many Requests)), along with an empty response body.

Authenticating requests

Unless otherwise noted, every request made using the REST API must be authenticated with a session token. See Authentication for details on getting a session token.

Session tokens will expire after some time (by default, this interval is 5 minutes for staging apps and 30 days for production apps). You will need to acquire a new session token once a previous one expires.

If you send a request without a session token, or with an expired session token, the request will fail:

Authentication required | Status: 401 (Unauthorized)

  "id": "authentication_required",
  "code": 4,
  "message": "The session token is no longer valid because it has expired.",
  "url": "",
  "data": {
    "nonce": "38a9b5a41725ec2bbb51ce43328b671731496f1f"

Retrying requests and deduplication

Requests can fail for a number of reasons, both client-side and server-side. To avoid exposing your users to bugs or lost data, you should always be prepared to retry requests. We recommend an exponential backoff strategy until the request is successful.

However, retrying requests may lead to the possibility of creating duplicate resources (for example, if the client goes offline after sending a request but before receiving a successful response). As a result, we provide a way to deduplicate creation requests by assigning an id to your creation request:

// Illustrative example
var request = {
    id: generateUUID(),
    field1: value1,
    field2: value2

execute(request, function(err, result) {
    if (err) delayAndRetryWithSameID(request);

The server will not re-execute the request if the ID has already been used. For a REST request, the server will instead respond with an HTTP status 409 (Conflict) and an error in the response body:

  "id": "id_in_use",
  "code": 111,
  "message": "The requested Message already exists",
  "url": "",
  "data": Message|Conversation

For a WebSocket request, a WebSocket packet will be returned indicating that the request was unsuccessful. The packet will contain the above JSON in the body.

If you receive such an error, it’s safe to retry the request using the same UUID.

Providing an id is optional. If you do provide an id, it will become the ID of the resource you created. If you do not provide an id, we will generate an ID for the resource.