API documentation

Webhooks

Use webhooks to trigger requests to your own application when feedback and unsubscribe events occur.

Webhook structure

Webhooks are always sent as an HTTP POST request to the webhook URLs you’ve configured via your Webhooks integration. The details of the activity that triggered the webhook are sent as the body of the HTTP request in JSON format. Every webhook will include an event_type property that describes what kind of activity triggered the event.

Event types

We currently support sending webhook notifications for the following event types:

Type Description
survey_response.created This occurs the moment a person provides a score. It can take a few minutes to write a comment, so it is unlikely one will be present in this initial payload.
survey_response.updated This occurs any time the score or comment on a survey response is changed. Responses usually only contain a score when they are first created, so most of the time comments will only appear in a survey_response.updated event.
unsubscribe.created This occurs the moment a previously active person unsubcribes from a survey that you sent.

Example request body for a survey_response object

{
  "event_type": "survey_response.updated",
  "event_id": "b8d057c59327541d7ec2104c0a9a255ad1997fb00831b9c6bbf09561e6d5cbd0",
  "event_data": {
    "id": "5435",
    "person": {
      "id": "5975",
      "email": "charlie_gravis@example.com",
      "name": "Charlie Gravis",
      "created_at": 1732186784
    },
    "survey_type": "nps",
    "score": 9,
    "comment": "Your service is fast and flawless!",
    "permalink": "https://app.delighted.com/r/5pFDpmlyC8GUc5oxU6USto5VonSKAqOa",
    "created_at": 1732186844,
    "updated_at": 1732187444,
    "person_properties": null,
    "notes": [],
    "tags": [],
    "additional_answers": [
      {
        "id": "4",
        "value": {
          "free_response": null,
          "scale": null,
          "select_one": null,
          "select_many": [
            {
              "id": "DxmjNj",
              "text": "Convenience"
            },
            {
              "id": "uNQIig",
              "text": "Friendly staff"
            }
          ]
        },
        "question": {
          "id": "enum_z9EvYV",
          "type": "select_many",
          "text": "What specifically did you enjoy about your experience with us?"
        }
      },
      {
        "id": "5",
        "value": {
          "free_response": null,
          "scale": null,
          "select_one": {
            "id": "tp0Q4K",
            "text": "Yes"
          },
          "select_many": null
        },
        "question": {
          "id": "enum_caUUdY",
          "type": "select_one",
          "text": "Have you purchased from us in the past?"
        }
      },
      {
        "id": "6",
        "value": {
          "free_response": null,
          "scale": 4,
          "select_one": null,
          "select_many": null
        },
        "question": {
          "id": "integer_buwb3w",
          "type": "scale",
          "text": "How often do you shop at one of our stores?"
        }
      },
      {
        "id": "7",
        "value": {
          "free_response": "A friend recommended you to me!",
          "scale": null,
          "select_one": null,
          "select_many": null
        },
        "question": {
          "id": "text_BJPEm0",
          "type": "free_response",
          "text": "How did you hear about us?"
        }
      }
    ]
  }
}

Example request body for an unsubscribe object

{
  "event_type": "unsubscribe.created",
  "event_id": "eb148ac882fadd6c48acfdff638646d939f76204df2283c3f5e2e94cb88a030a",
  "event_data": {
    "person_id": "5428",
    "email": "joseph@johnston.name",
    "name": "Joseph Alexander",
    "unsubscribed_at": 1732186844
  }
}

Webhook request body parameters

Name Type Description
event_type String This describes what kind of event triggered this webhook. It is composed of an object type and an action, separated by a period: survey_response.created.
event_id String The event_id can be used to deduplicate notifications that may be delivered more than once when it’s hard for us to be sure that earlier attempts succeeded. You can ensure that you don’t unnecessarily process requests multiple times by logging each event_id as you process it and checking each event_id is new to ensure you haven’t already processsed it.
event_data Map/Dictionary

This describes the details of the object that triggered the webhook.

See the example request bodies above for the details you can expect based on the event’s object type.

For more information on the contents of the person_properties, see the Sending to people section of the API guide for more details.

Example request headers

Name Example Value
Content-Type application/json
User-Agent Delighted Webhook Notifier
X-Delighted-API-Version v1
X-Delighted-Webhook-Signature
sha256=5ccf874cdc518138f08f0e5d0ba6f8b4fd1d7a78372d0b858571204b99c0b094

Responding to webhooks

You should respond with an HTTP status code in the 200 range to indicate that you successfully received the webhook. Any response with a status code outside the 200-299 range, including a redirect, will be treated as a failure, and we will try to deliver the webhook again multiple times over several days.

Signature verification

Every webhook includes an X-Delighted-Webhook-Signature header that can optionally be used to verify that the request did indeed come from us. The value of this header is composed of two parts, separated by an equal sign: the signature algorithm, and the signature itself.

To validate the signature, you should take the exact HTTP request body as originally transmitted (before decoding the JSON), use your private Delighted API key, and generate an HMAC digest over the request body, using the signature algorithm specified in the header.

Here’s an example in Ruby:

def webhook_signature_valid?(request)
  private_api_key = ENV['DELIGHTED_API_KEY']
  signature_header = request.headers['X-Delighted-Webhook-Signature']
  algorithm, received_signature = signature_header.split('=', 2)
  if algorithm != 'sha256'
    raise Exception, "Invalid algorithm"
  end
  expected_signature = OpenSSL::HMAC.hexdigest(
    OpenSSL::Digest.new(algorithm), private_api_key, request.body.read
  )
  return SecurityUtils.secure_compare(received_signature, expected_signature)
end

Data security

We recommend that your webhooks use HTTPS URLs so that the customer information delivered to you via webhooks is encrypted while traveling over the internet.