Request headers

All webhooks payloads are sent to your server with the following headers:

Header Description
layer-webhook-event-type Event type that triggered this request
layer-webhook-signature Computed HMAC hex digest of the body, using the configured secret as the key
layer-webhook-request-id UUID for this request. Will be reused if this request needs to be resent
layer-webhook-id UUID of the webhook itself
user-agent Constant value, currently layer-webhooks/3.0
content-type Constant value, currently application/vnd.layer.webhooks+json; version=3.0

The version parameter in the Content-Type header always reflects the webhooks version used when creating the webhook. Currently, 3.0 is the only available version.

Request body

Every webhook request contains the following fields:

Name Type Description
event.created_at timestamp Time at which the event occurred
event.type string One of the event types
event.id string UUID which can be used for deduplication

Some events will also include actor, which reflects the user responsible for the event. For example, in the case of Message.create, actor is the user who sent the Message. In the case of Message.updated, actor is the user who sent the receipt.

Other fields in the request body are defined by each event type, detailed below.

If you specified config when registering your Webhook, it will be added to every request payload.

Validating payload integrity

Webhooks are created with a secret; this value is used to compute a Hash-based Message Authentication Code (HMAC) of the request body. The HMAC is delivered with the request via the layer-webhook-signature HTTP header.

We recommend checking and validating the HMAC for each request. This protects you from other services from sending unexpected, possibly malicious, payloads to your webhook endpoints.

Validation is done by feeding the secret and the entire request body to a crypto library (such as OpenSSL) that is capable of computing an HmacSHA1 digest. If the request and body are valid, the computed signature will match the header value.

Responding to a webhook

IMPORTANT: Please review the Requirements section before starting your integration with Layer Webhooks.

The server receiving the webhook payload must:

  • Respond with a 2xx status code
  • Respond within 1 second
  • Accept HTTP/1.1 and keep-alive requests

If any of these conditions are not met, the delivery will be considered a failure. Failures will be retried with an exponential backoff delay for up to approximately 30 minutes. Retries will have the same value in the layer-webhook-request-id header.

Common mistake

Your server must be able to deduplicate requests. For example, if your server successfully processes the initial attempt but takes longer than 1 second to respond, we will redeliver the same request. You must be able to handle this and deduplicate/ignore additional requests.

If the request fails to get a successful response after the retry period expires, the webhook will be marked as “inactive”. It may be reactivated via the REST API. The Developer Dashboard indicate why a webhook was deactivated; this information is also available via the status_reason field when retrieving webhook details via the REST API.

Your server does not need to return any content in the response. In fact, we encourage you to respond with 204 (No content).

Event payloads

Webhook request payloads include information about the event and the resource involved.

Message sent

A Message.created event is sent when a new message is sent, either by a user or via the Server API. Sending announcements will not trigger a Message.created event.

{
  "event": {
    "created_at": "2015-09-17T20:46:47.561Z",
    "type": "Message.created",
    "id": "c12f340d-3b62-4cf1-9b93-ef4d754cfe69"
  },
  "actor": BasicIdentity,
  "message": Message,
  "config": {
    "key1": "value1",
    "key2": "value2"
  }
}

Message deleted

A Message.deleted event is sent when a Message is deleted for all participants. Local/per-user deletion will not trigger a webhook event. This event will be triggered for Messages deleted by either users or via the Server API.

{
  "event": {
    "created_at": "2015-09-17T20:46:47.561Z",
    "type": "Message.deleted",
    "id": "c12f340d-3b62-4cf1-9b93-ef4d754cfe69"
  },
  "actor": BasicIdentity,
  "message": Message,
  "config": {
    "key1": "value1",
    "key2": "value2"
  }
}

Conversation created

A Conversation.created event is sent when a new Conversation is created. This will be triggered for conversations created by users or via the Server API.

{
  "event": {
    "created_at": "2015-09-17T20:46:47.561Z",
    "type": "Conversation.created",
    "id": "c12f340d-3b62-4cf1-9b93-ef4d754cfe69"
  },
  "actor": BasicIdentity,
  "conversation": Conversation,
  "config": {
    "key1": "value1",
    "key2": "value2"
  }
}

Conversation deleted

A conversation.deleted event is triggered when a Conversation is deleted for all participants. Local/per-user deletion does not trigger a webhook event.

{
  "event": {
    "created_at": "2015-09-17T20:46:47.561Z",
    "type": "Conversation.deleted",
    "id": "c12f340d-3b62-4cf1-9b93-ef4d754cfe69"
  },
  "actor": BasicIdentity,
  "conversation": Conversation,
  "config": {
    "key1": "value1",
    "key2": "value2"
  }
}

Conversation metadata change

A Conversation.updated event is sent when a Conversation’s data changes, which currently means metadata. This event is triggered by changes made by users or via the Server API.

{
  "event": {
    "created_at": "2015-09-17T20:46:47.561Z",
    "type": "Conversation.updated",
    "id": "c12f340d-3b62-4cf1-9b93-ef4d754cfe69"
  },
  "conversation": Conversation,
  "changes": [{
    "operation": "set",
    "property": "metadata.title",
    "value": "foo",
    "from": "bar"
  }, {
    "operation": "delete",
    "property": "metadata.foobar"
  }],
  "config": {
    "key1": "value1",
    "key2": "value2"
  }
}

Conversation participant added

A Participation.created event is sent whenever participants are added to a conversation. This event is triggered by both user actions and via the Server API.

{
  "event": {
    "created_at": "2015-09-17T20:46:47.561Z",
    "type": "Participation.created",
    "id": "c12f340d-3b62-4cf1-9b93-ef4d754cfe69"
  },
  "conversation": Conversation,
  "changes": [{
    "operation": "add",
    "property": "participants",
    "value": BasicIdentity
  }],
  "config": {
    "key1": "value1",
    "key2": "value2"
  }
}

Conversation participant removed

A Participation.deleted event is sent whenever participants are added to or removed from a conversation. This event is triggered by both user actions and via the Server API.

{
  "event": {
    "created_at": "2015-09-17T20:46:47.561Z",
    "type": "Participation.deleted",
    "id": "c12f340d-3b62-4cf1-9b93-ef4d754cfe69"
  },
  "conversation": Conversation,
  "changes": [{
    "operation": "remove",
    "property": "participants",
    "value": BasicIdentity
  }],
  "config": {
    "key1": "value1",
    "key2": "value2"
  }
}

Message Receipts

A Receipt.created event is sent whenever messages are marked as delivered or read for a user. The receipt object shows the type of receipt and the range of message positions affected. When from=to, this was an individual message receipt.

{
  "event": {
    "created_at": "2015-09-17T20:46:47.561Z",
    "type": "Receipt.created",
    "id": "c12f340d-3b62-4cf1-9b93-ef4d754cfe69"
  },
  "actor": BasicIdentity,
  "conversation": Conversation,
  "receipt": {
    "type": "Read",
    "positions": {
      "from": 120709792,
      "to": 189794791
    }
  },
  "config": {
    "key1": "value1",
    "key2": "value2"
  }
}

Message Parts Replaced on a Message

The replacement of all Message parts is emitted as an update to the Message entity.

{
    "event": {
        "id": "c12f340d-3b62-4cf1-9b93-ef4d754cfe69",
        "created_at": "2015-09-17T20:46:47.561Z",
        "type": "Message.updated"
    },
    "actor": BasicIdentity,
    "message": Message,
    "changes": [{
        "operation": "set",
        "property": "parts",
        "value": [ MessagePart, MessagePart ],
        "from": [ MessagePart ]
    }],
    "config": {
        "key1": "value1",
        "key2": "value2"
    }
}

Message Part Appended to a Message

{
    "event": {
        "id": "c12f340d-3b62-4cf1-9b93-ef4d754cfe69",
        "created_at": "2015-09-17T20:46:47.561Z",
        "type": "MessagePart.created"
    },
    "actor": BasicIdentity,
    "message": Message,
    "changes": [{
        "operation": "add",
        "property": "parts",
        "id": "layer:///message/f3cc7b32-3c92-11e4-baad-164230d1df67/parts/0ee1a14c-a18c-44a1-88da-e7c351093681",
        "value": MessagePart
    }],
    "config": {
        "key1": "value1",
        "key2": "value2"
    }
}

Message Part Updated

{
    "event": {
        "id": "c12f340d-3b62-4cf1-9b93-ef4d754cfe69",
        "created_at": "2015-09-17T20:46:47.561Z",
        "type": "MessagePart.updated"
    },
    "actor": BasicIdentity,
    "message": Message,
    "part": MessagePart,
    "changes": [
        {
            "operation": "set",
            "property": "body",
            "value": "Not funny at all!"
            "from": "This is funny!"
        },
        {
            "operation": "set",
            "property": "mime_type",
            "value": "text/plain",
            "from": "text/html"
        }
    ],
    "config": {
        "key1": "value1",
        "key2": "value2"
    }
}

Message Part Deleted

{
    "event": {
        "id": "c12f340d-3b62-4cf1-9b93-ef4d754cfe69",
        "created_at": "2015-09-17T20:46:47.561Z",
        "type": "MessagePart.delete"
    },
    "actor": BasicIdentity,
    "message": Message,
    "changes": [{
        "operation": "remove",
        "property": "parts",
        "id": "layer:///message/f3cc7b32-3c92-11e4-baad-164230d1df67/parts/0ee1a14c-a18c-44a1-88da-e7c351093681",
        "value": MessagePart
    }],
    "config": {
        "key1": "value1",
        "key2": "value2"
    }
}