# Webhooks

This reference explains:

* What webhooks are
* The benefit of webhooks
* The different use cases for webhooks
* How to configure webhooks
* Which webhook events you can consume

### Overview

You can think of a webhook as a type of **Application Programming Interface (API)** driven by events rather than requests. So, a webhook allows one system to send data to another when a particular event occurs. Unlike APIs, where one system requests another to receive data.

Examples of webhook events are:

* A track is started
* A challenge is started
* A track is completed

With APIs, you frequently need to send requests to check if data is updated or if new data is available. With webhooks, you receive updated or new data automatically. And only when they occur. Therefore, webhooks are more efficient than APIs.

### Instruqt and webhooks

Instruqt facilitates webhooks, which allows you to integrate your system with Instruqt. For example, Instruqt can send event data to your system when events occur like:

* Sandbox created
* Track started
* Challenge started

And the sent event data can contain fields like:

* `track_slug`
* `timestamp`
* `participant_id`

### Use cases

Common use cases for applying Instruqt webhooks are:

* **LMS integration**\
  By processing Instruqt event data in your LMS, you can track your learners' progress in achieving their learning goals.
* **CRM integration**\
  By processing Instruqt event data in your CRM, you can track your potential customers' progress in experimenting with your software.

## Configure webhooks

You configure a webhook by setting an endpoint for the webhook. An endpoint is a URL at your end where Instruqt delivers event data.

### Add an endpoint

{% tabs %}
{% tab title="🌐 Web UI" %}

1. Click **Settings** → **Webhooks**.
2. Click **+ Add Endpoint** and enter the URL which exposes your webhook.
3. Select the [events](#events) you want to send to this webhook. Leave empty if you want to send all events to the webhook.
4. Click **Create** to add the endpoint.

{% hint style="warning" %}
Endpoint URLs must be publically accessible.
{% endhint %}
{% endtab %}
{% endtabs %}

{% hint style="info" %}
You can view all events delivered, either successfully or failed, to your configured endpoints on the **Logs** tab. The **Activity** tab shows a chart with the recent delivery attempts.
{% endhint %}

### Edit an endpoint

{% tabs %}
{% tab title="🌐 Web UI" %}

1. Click **Settings** → **Webhooks**.
2. Click the URL of the endpoint you want to edit.
3. Click **Edit** behind one of these items:
   * the *URL*
   * the *Description*
   * the text *Subscribed events*
4. Enter your change.
5. Click **Save** to store your change.
   {% endtab %}
   {% endtabs %}

### Test an endpoint

{% tabs %}
{% tab title="🌐 Web UI" %}

1. Click **Settings** → **Webhooks**.
2. Click the URL of the endpoint you want to test.
3. Click the **Testing** tab.
4. Select the **Send event** of your liking.
5. Click **Send example**.
   {% endtab %}
   {% endtabs %}

### Delete an endpoint

{% tabs %}
{% tab title="🌐 Web UI" %}

1. Click **Settings** → **Webhooks.**
2. Click the URL of the endpoint you want to delete.
3. Click the three-dots in the upper right to open the pop-up menu.
4. Click **Delete** in the pop-up menu.
5. Click **Delete** in the pop-up confirmation.
   {% endtab %}
   {% endtabs %}

{% hint style="warning" %}
**Verifying authenticity**

Because your webhook endpoint is publicly available, anyone can impersonate the Instruqt platform by sending events to your endpoint.

Instruqt signs every webhook and metadata with a unique key for each endpoint to identify impersonated requests. You can use this signature to verify the event's authenticity.

See the [Svix documentation](https://docs.svix.com/receiving/verifying-payloads/how) for how to verify the event's signature.
{% endhint %}

## Events and payload

The data sent about an event is known as the payload. Instruqt payloads are in [JSON](https://www.json.org/json-en.html) format. For example, the `challenge_attempt` event delivers a payload like this:

{% tabs %}
{% tab title="JSON" %}

```javascript
{
  "challenge_id": "dxhrbpuvvia9",
  "completed": true,
  "invite_id": null,
  "organization_id": "instruqt",
  "participant_id": "tncb0s7cdkyu",
  "timestamp": "2021-07-29T16:14:57.31297754Z",
  "track_id": "rzwlxtp92ted",
  "track_slug": "getting-started-with-instruqt",
  "type": "track.challenge_attempted",
  "user_id": "gOGhYjyoPJhINJFOugIgiL4pdzu2"
}
```

{% endtab %}
{% endtabs %}

### Events

Instruqt events are grouped at the following levels:

* Track
* Sandbox
* Review

The following tables show the events per group and the corresponding fields in the payload.

#### Track events:

| Event                        | Description                                                        |
| ---------------------------- | ------------------------------------------------------------------ |
| `track.started`              | A user starts playing a track                                      |
| `track.completed`            | A user completes a track                                           |
| `track.cleaned`              | The platform cleaned the sandbox                                   |
| `track.challenge_attempted`  | A user submits a challenge attempt                                 |
| `track.challenge_setup`      | The platform initialized a challenge by executing the setup script |
| `track.challenge_completed`  | The user completed the challenge                                   |
| `track.challenge_started`    | A user starts a challenge                                          |
| `track.challenge_cleaned`    | The platform cleaned a challenge by executing the cleanup script   |
| `track.skipped_to_challenge` | A user skipped to a challenge                                      |
| `track.stopped`              | A user or the platform stopped a track                             |

**Track payload fields:**

| Key                 | Value                                                            | Type                      |
| ------------------- | ---------------------------------------------------------------- | ------------------------- |
| `track_id`          | The track identifier                                             | `string`                  |
| `track_slug`        | The track URL identifier.                                        | `string`                  |
| `organization_id`   | The identifier of the organization this track belongs to         | `string`                  |
| `timestamp`         | Instant the event occurred                                       | `string`                  |
| `participant_id`    | The identifier of this user's unique play of the track           | `string`                  |
| `invite_id`         | The invite the user claimed to gain access to this track, if any | `string \| undefined`     |
| `user_id`           | The user identifier                                              | `string`                  |
| `mode`              | The current play mode                                            | `platform \| embed \| ci` |
| `custom_parameters` | The custom parameters for this play                              | `object \| undefined`     |

*Additionally, challenge events can contain the following payload fields:*

| Key                | Value                                       |
| ------------------ | ------------------------------------------- |
| `challenge_id`     | The ID of the current challenge.            |
| `challenge_index`  | The index of the current challenge.         |
| `total_challenges` | The total number of challenges for a track. |

#### Sandbox events:

| Event              | Description                                  |
| ------------------ | -------------------------------------------- |
| `sandbox.creating` | The platform starts creating a sandbox       |
| `sandbox.created`  | The platform created a sandbox               |
| `sandbox.failed`   | The platform was unable to setup the sandbox |
| `sandbox.cleaned`  | The platform cleaned the sandbox             |

**Sandbox payload fields:**

| Key                 | Value                     |
| ------------------- | ------------------------- |
| `track_id`          | `string`                  |
| `organization_id`   | `string`                  |
| `timestamp`         | `string`                  |
| `participant_id`    | `string`                  |
| `invite_id`         | `string \| undefined`     |
| `user_id`           | `string`                  |
| `mode`              | `platform \| embed \| ci` |
| `custom_parameters` | `object \| undefined`     |

#### Invite events:

| Event type       | Description              |
| ---------------- | ------------------------ |
| `invite.claimed` | A user claimed an invite |

**Invite payload fields:**

| Key                 | Value                                                 | Type                  |
| ------------------- | ----------------------------------------------------- | --------------------- |
| `invite_id`         | The identifier of the invite                          | `string`              |
| `timestamp`         | When it occurred                                      | `string`              |
| `user_id`           | The identifier of the user claiming the invite        | `string`              |
| `team_id`           | The identifier of the team this invite belongs to     | `string`              |
| `team_slug`         | The url identifier of the team this invite belongs to | `string`              |
| `custom_parameters` | The custom parameters for this invite                 | `object \| undefined` |

#### Review events:

| Event type       | Description                   |
| ---------------- | ----------------------------- |
| `review.created` | A user created a track review |
| `review.updated` | A user updated a track review |

**Review payload fields:**

| Key               | Value                                                    | Type                  |
| ----------------- | -------------------------------------------------------- | --------------------- |
| `review_id`       | The identifier of this review                            | `string`              |
| `track_id`        | The identifier of the reviewed track                     | `string`              |
| `organization_id` | The identifier of the organization this track belongs to | `string`              |
| `user_id`         | The identifier of the user leaving a review              | `string`              |
| `score`           | The score                                                | `int`                 |
| `content`         | The written feedback, if any                             | `string \| undefined` |

<details>

<summary>Advanced: Enriching events</summary>

Webhook events do not contain data that personally identifies learners. If your webhook integration requires personally identifiable information (PII) data, it is possible to enrich events by calling the Instruqt [GraphQL API](https://docs.instruqt.com/settings/platform/api) with the Invite ID.

The following query shows how to fetch the details collected for all claims on a given invite.

```graphql
query {
    trackInvite(inviteID: "invite-id") {
        claims {
            userDetails {
	        firstName
		lastName
		email
            }
        }
    }
}
```

</details>

## Example: Sending events to Zapier

Zapier is a no-code automation platform that can intermediate through webhooks between Instruqt and your system. In a Zapier setup, Instruqt sends the webhook payload to Zapier, and Zapier passes the payload through to your system.

Follow these steps to create a Zap for an Instruqt webhook:

{% tabs %}
{% tab title="Zapier" %}

1. Create a new Zap.
2. Configure the *Webhooks by Zapier* trigger.
3. Select the *Catch Hook* trigger.
4. Copy the **Custom Webhook URL** that Zapier created for the *Catch Hook* trigger.
5. Go to Instruqt and [add an endpoint](#add-an-endpoint) with the copied URL.
6. Start a track in Instruqt.
7. Go back to Zapier and click **Test trigger** to check if Zapier found a request. Zapier should show an Instruqt payload.
8. Configure any *Action in Zapier* to pass through the Instruqt payload to your system.
   {% endtab %}
   {% endtabs %}


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.instruqt.com/settings/platform/webhooks.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
