...
This guide introduces how webhooks work, including how to configure a webhook for your app.
Also known as “Service Hooks”.
In this article
Table of Contents |
---|
...
Your organization must be an active Scope customer.
Your organization must have provisioned at least one Scopear.com admin
User
account.You must have previously authenticated with the Scope GraphQL API.
Use cases
Common webhook use cases include the following:
...
Info |
---|
If a valid query is supplied with a webhook subscription, it will be executed during the delivery of each subsequent webhook notification, and the result will replace the default JSON payload. See customizing the payload for more information. |
What does a webhook look like?
After you subscribe to a webhook topic, Scope sends a webhook notification as an HTTP POST to the specified callback url each time an event occurs for the specified topic.
By way of example, the default scenario_session/created
webhook notification includes the following:
Headers
X-Scope-Topic:
scenario_session/created
X-Scope-Topic-Version:
1.1
X-Scope-API-Version:
3
X-Scope-Domain:
scopear.com
X-Scope-Webhook-Type:
node
X-Scope-Hmac-Sha256:
XWmrwMey6OsLMeiZKwP4FppHH3cmAiiJJAweH5Jo4bM=
X-Scope-Webhook-Id:
b54557e4-bdd9-4b37-8a5f-bf7d70bcd043
Info |
---|
If your organization is equipped with an “on-prem” install of the Scope System, your domain value is likely to be different than the example value above. |
Where:
X-Scope-Topic
(&X-Scope-Topic-Version
) specify thetopic
that triggered the webhookX-Scope-API-Version
specifies the version of the Scope GraphQL API that was used to serialize the webhook event payloadX-Scope-Domain
specifies the system that sent the webhookX-Scope-Hmac-Sha256
is used to verify the authenticity of webhooksX-Scope-Webhook-Id
is used to troubleshootX-Scope-Webhook-Type
specifies the type of payload being sent
Info |
---|
Currently, all Scope webhook notifications include a value of |
Body
Code Block | ||
---|---|---|
| ||
{
"data": {
"id": "t5V3S3X8Trt/weWu0A2YHb0cRFJfcRA2rXT3vAGTz1A=",
"url": "https://cms.scopear.com/api/v3/graphql/object/t5V3S3X8Trt/weWu0A2YHb0cRFJfcRA2rXT3vAGTz1A="
}
} |
Where:
id
is a globally unique descriptor that can be used to query the Scope GraphQL API for more information.url
is a reference to a simple REST representation of the object that can be accessed by HTTP GET.
Note |
---|
The url provided should only be used as a fallback if you are unable to leverage GraphQL to gather additional information. REST endpoints are only capable of delivering 1st-class attributes for any given resource (e.g. they can provide a User’s |
How can I get more information?
By default, Scope delivers minimalistic payloads in order to encourage integrators to query the Scope GraphQL API for the specific information that they require.
GraphQL is a powerful tool that allows integrators to ask for exactly what they need.
See Using Global Node Ids and Exploring the Graph to learn how to use the Scope GraphQL API.
Info |
---|
Most relationships (aka “Connections”) are bidirectional in the Scope GraphQL API. This means that given a node in the graph, you can usually to navigate “up” to find parent-like objects (such as |
GraphQL Examples
Given the following webhook:
Code Block | ||
---|---|---|
| ||
{
"id": "OiSSdfK1Su4gwG4zAi2InavDB9eRE9aOnGHYRMG5dqY=",
"url": "https://cms.scopear.com/api/v3/graphql/object/w4Aa1bjiiNSt1StG11n40b4dYU8lAigrCalO8r6cYwYchxAsWytj-F6wojhI_CsN"
} |
An integrator could answer the question “what Scenario is associated with this ScenarioSession?” by querying the Scope GraphQL API as follows:
Code Block | ||
---|---|---|
| ||
query {
node(id: "OiSSdfK1Su4gwG4zAi2InavDB9eRE9aOnGHYRMG5dqY=") {
... on ScenarioSession {
scenario {
id
}
}
}
} |
Or, an integrator could answer the question “what ScenarioSessionEvents are associated with this ScenarioSession?” by querying the Scope GraphQL API as follows:
...
language | graphql |
---|
...
Webhook Topics
Created, updated, and deleted topics are available for every GraphQL Object Type that implements the Node Interface (e.g. scenario_session/created
, scenario_session/updated
, and scenario_session/deleted
).
Note |
---|
Although updated and deleted topics are available for all Node types, not all Node types can be updated or deleted. This means that you might not ever receive a webhook notification for a topic that you have subscribed to. |
Additionally, the following special use topics are available:
call/completed
call_recording/completed
scenario_session/paused
scenario_session/resumed
scenario_session/completed
scenario_session_part/completed
scenario_session_step/completed
Creating A Webhook Subscription
Creating a webhook subscription can be done in three (or four) steps using our new interface.
Step One: Navigate to the Webhook page
To begin, click on the Webhooks link under the Develop Tools dropdown menu on the left side navigation.
...
Step Two: Click on Create Webhook
Clicking on the Webhooks link will bring you to the Webhooks index which lists all the current webhooks created for your organization. Next, click on the Create Webhook button on the far right side of this page.
...
Step Three: Fill out Mandatory Fields
...
The Create Webhook button will cause a slide-out form to appear from the right side with several fields. The fields marked with an asterisk ( * ) are mandatory fields. Next to each field should also be a question mark ( ? ) that will give some more info on what each field is.
Name
Give your webhook an informative name by filling in the NAME field.
For our example, the NAME field is set to “Test”:
...
Topic
A webhook needs an event topic. Select a topic from the TOPIC drop-down list. There are several topics to choose from.
For our example, the topic will be set to the user creation event. That means, this webhook will be sent out every time a user is created in the system:
...
Content Type
As described earlier, a webhook notification request will have its content type set to JSON by default. However, you can change this to XML by selecting it from the CONTENT TYPE drop-down list.
For our example, the webhook will be JSON:
...
Callback URL
A webhook notification needs to target a URL. If you want to test out a webhook, you can use webhook.site for the CALLBACK URL endpoint to test with.
For our example, the CALLBACK URL is set to one obtained from webhook.site:
...
Tip |
---|
At this point, you are all done with your webhook! You can go to the bottom of the form to save your webhook. You can always edit it in the future. |
...
Info |
---|
You can test out your webhook right away. Check out the section on Testing Webhooks. If you want to customize the webhook further, proceed to Step Four. |
Step Four: Fill out Optional Fields
There are several optional fields that can be used to customize your webhook and provide some additional security.
Integration
If you have any Integrations created, you can associate your webhook to an existing integration. For more information about Integrations, click here.
For our example, we will leave INTEGRATION blank:
...
Shared Secret
To add some security to your Webhook, you can supply a secret key that will be used to generate a base64-encoded X-Scope-Hmac-SHA256 header. For more information about how to use this, checkout Verifying Webhooks.
For our example, we will input a simple SHARED SECRET:
...
Query
The query field is super useful. By default, only the ID and the URL will be returned for the node that had been affected:
Default
Code Block | ||
---|---|---|
| ||
{
"data": {
"id": "t5V3S3X8Trt/weWu0A2YHb0cRFJfcRA2rXT3vAGTz1A=",
"url": "https://cms.scopear.com/api/v3/graphql/object/t5V3S3X8Trt/weWu0A2YHb0cRFJfcRA2rXT3vAGTz1A="
}
} |
What it does is that it allows for more data to be added to the webhook by allowing a GraphQL query to be ran and the data returned from the query will be used instead. When a query
is supplied with a webhook subscription, it will be executed by the server each time that it prepares a notification for delivery, and the result of that execution will replace the default JSON payload.
For example, let’s make the user/created
webhook that we just created return the name of the user:
...
$nodeId (of type ID
) and $nodeType (of type String
) are supplied by the server as input parameters when executing the query that you provide.
Now, the webhook will be returning different data:
Code Block |
---|
{ "data": { "node": { "name": "Alain Bloch" } id } } } } } |
Customizing the payload
In lieu of querying for additional data after receiving a webhook, integrators can supply a valid GraphQL query, in advance, while subscribing to a webhook.
When a query
is supplied with a webhook subscription, it will be executed by the server each time that it prepares a notification for delivery, and the result of that execution will replace the default JSON payload.
This feature saves integrators from a round trip with the server and reduces the requirements for building integrations (i.e. integrators that supply queries in advance can more easily use platforms like Zapier).
Info |
---|
Subscription queries are executed with the context of the user & organization that created the subscription; as if the user had authenticated and executed the query via the API. |
By way of example, to customize the payload for a scenario_session/completed
webhook subscription, you can supply a query argument when creating or updating the subscription:
Code Block | ||
---|---|---|
| ||
mutation {
createWebhookSubscription(
input: {
webhookSubscription: {
topic: { key: "scenario_session/completed" }
callbackUrl: "!!!REPLACE ME WITH PRODUCTION VALUE!!!"
sharedSecret: "!!!REPLACE ME WITH PRODUCTION VALUE!!!"
query: """
query ($nodeId: ID!) {
node(id: $nodeId) {
... on ScenarioSession {
startedAt
endedAt
duration
idleDuration
numberOfStepsPossible
numberOfStepsViewed
percentOfStepsViewed
state
externalData
scenarioRelease {
author {
id
name
}
}
}
}
}
"""
}
}
) {
errors {
fullMessage
message
path
type
}
webhookSubscription {
id
}
}
} |
$nodeId (of type ID
) and $nodeType (of type String
) are supplied by the server as input parameters when executing the query that you provide.
See Sample Queries for more complicated query examples.
List of available webhook topics
Created, updated, and deleted topics are available for every GraphQL Object Type that implements the Node Interface (e.g. scenario_session/created
, scenario_session/updated
, and scenario_session/deleted
).
Note |
---|
Although updated and deleted topics are available for all Node types, not all Node types can be updated or deleted. This means that you might not ever receive a webhook notification for a topic that you have subscribed to. |
Additionally, the following special use topics are available:
call/completed
call_recording/completed
scenario_session/paused
scenario_session/resumed
scenario_session/completed
scenario_session_part/completed
scenario_session_step/completed
Alternatively, you can retrieve the list of currently available topics by executing the following query against the Scope GraphQL API:
Code Block | ||
---|---|---|
| ||
query {
webhookTopics {
nodes {
key
version
disabledAt
}
}
} |
A topic is disabled if the disable_at field is not null.
Configuring your app
To receive webhooks, register a HTTPS endpoint with your app to act as a webhook receiver.
Then, subscribe to a webhook topic and supply the endpoint registered in the previous step as the callbackUrl
input value. Scope will immediately begin sending your app webhook notifications whenever events occur that pertain to the specified topic.
Webhook subscriptions are scoped to the user and organization that they're registered with.
This means that: a) other users & organizations can't view, modify, or delete the subscriptions you create; and b) subscriptions registered for other organizations will not be triggered for events that pertain to your organization’s data.
Subscribing to a webhook topic
You can subscribe to a webhook topic by executing the createWebhookSubscription mutation in GraphQL.
The following example shows how to subscribe for the scenario_session/created
webhook topic (via the Scope GraphQL API).
When multiple versions are available for a given topic, each is likely to be triggered by different circumstances or behave differently in some way. If you do not supply a topic version (or ID) when executing the mutation, the latest available topic version will be used for the topic key that you supply.
GraphQL Query
Code Block | ||
---|---|---|
| ||
mutation {
createWebhookSubscription(
webhookSubscription: {
topic: { key: 'scenario_session/created' }
callbackUrl: "https://my-custom-application.com/hooks/scopear"
sharedSecret: "SOME SUPER SECRET STRING"
}
) {
errors {
field
message
}
webhookSubscription {
id
}
}
} |
JSON response
Code Block | ||
---|---|---|
| ||
{
"data": {
"createWebhookSubscription": {
"errors": [],
"webhookSubscription": {
"id": "wMtvO+6DUTQZomQK8MP0Ppsip6hiuTe1TIihz7iadkk="
}
}
}
} |
Testing webhooks
When testing webhooks, you can run a local server or use a publicly available service such as Beeceptor. If you decide to run a server locally, then you need to make it publicly available using a service such as Pagekite or ngrok. The following URLS do not work as endpoints for webhooks:
...
Localhost
...
}
} |
See Sample Queries for more complicated query examples.
Info |
---|
Queries are executed with the context of the user & organization that created the subscription; as if the user had authenticated and executed the query via the API. |
Data Validation Template
Suppose you want to validate the data and make a decision on whether to send the webhook request or not. In this field, we can use the Liquid template language to make a conditional statement. This uses the default data or the data generated from the Query field discussed above.
Continuing from our last example, let’s only send a webhook notification if the user’s name is not “Alain Bloch”:
...
Note |
---|
Make sure that the Data Validation Template returns either a true or false value. |
Template
Suppose you want to customize the request body being sent because the default request body isn’t accepted by your webhook callback url. Here we can use the Liquid template language to further customize the body.
For our example, let’s remove the outer data and node objects that the actual data is nested within. Instead, we will return the actual data:
...
Now, the webhook request will return just the name:
Code Block |
---|
{
"name": "Alain Bloch"
} |
Error Handling Template
By default, if a webhook notification returns a unsuccessful response (anything but a 200 HTTP status code), it will add an error under the Error tab on the webhook. However, sometimes an API will return a successful response but have an error message within the response body. With the Error Handling Template, you can use the Liquid template language to add some conditional statement. Anything returned will be treated as an error. Return nothing if the response is successful.
For example, let’s assume that the API we are interacting with will return a successful response but with an error message inside the response:
Code Block |
---|
{
"error": "User already exists"
} |
In this case, we want to examine the response body for an error and return that message:
...
Info |
---|
The body of the response is accessible with the body variable. The headers of the response are accessible with the headers variable. |
Webhook Overview
After creating your webhook, you can click on the webhook in the list of webhooks and be brought to the Webhook Overview page. This shows all the details of the webhook:
...
Webhook Errors
Any errors that the webhook subscription encounters when it tries to deliver a notification will be displayed under the Error tabs on the webhook page.
...
Clicking on the link will bring you to the list of errors for that webhook:
...
Clicking on an error will bring you to the Error Details page. This contains useful information about the webhook error that can be used for debugging purposes:
...
Removing an Error
If you feel that the error is resolved, you can delete the error by clicking on the box next to the error on the list page and then deleting it.
...
Testing Webhooks
If you want to test your webhook out, there is an easy way to do it! On the Webhook form, there is a toggle that can be switched on. This toggle will use the last node created for that topic as the example node to be used for delivery. This can have some unexpected results since you might not know what the last node is. Nonetheless, it is a good way to test if the Webhook is working.
...
Note |
---|
When using the test toggle, the data validation template will not be used. |
Disabling A Webhook
If your webhook isn’t ready or is erroring, you can always disable it (rather than delete it). Use the edit form, and on the bottom of the form is an Enabled webhook toggle. Turn this off to disable the webhook:
...
Webhook Anatomy
After you Create A Webhook Subscription, Scope sends a webhook notification as an HTTP POST to the specified callback url each time an event occurs for the specified topic.
A webhook notification can be broken down into X Headers, Content Type, and Request Body.
Headers
The headers of a webhook are very specific:
X-Scope-Topic: Specify the topic that triggered the webhook
X-Scope-Topic-Version: Specify the version of the topic because topics might mean different things in future versions.
X-Scope-API-Version: Specifies the version of the Scope GraphQL API that was used to serialize the webhook event payload.
X-Scope-Domain: Specifies the system that sent the webhook.
X-Scope-Hmac-Sha256: Used to verify the authenticity of webhooks.
X-Scope-Webhook-Id: The record ID of the webhook.
X-Scope-Webhook-Type: Specifies the type of payload being sent
X-Scope-Webhook-Job-Id: Used for troubleshooting the webhook request.
X-Scope-Time: UTC Timestamp on when this webhook was sent.
Content-Type: By default, a webhook will send its payload in a JSON format, and the content-type will be set to application/json in the headers of the request. However, when Creating A Webhook, a webhook can also be specified to be other formats.
Info |
---|
Currently, all Scope webhook notifications include a value of |
Example
Code Block |
---|
X-Scope-Topic: scenario_session/created
X-Scope-Topic-Version: 1.1
X-Scope-API-Version: 3
X-Scope-Domain: scopear.com
X-Scope-Webhook-Type: node
X-Scope-Hmac-Sha256: XWmrwMey6OsLMeiZKwP4FppHH3cmAiiJJAweH5Jo4bM=
X-Scope-Webhook-Id: b54557e4-bdd9-4b37-8a5f-bf7d70bcd043
content-type: application/json |
Info |
---|
If your organization is equipped with an “on-prem” install of the Scope System, your domain value is likely to be different than the example value above. |
Request Body
The request body of the webhook notification has default but it can be customized. Check out Creating A Webhook for information about to customize the request body.
Default Request Body
id
is a globally unique descriptor that can be used to query the Scope GraphQL API for more information.url
is a reference to a simple REST representation of the object that can be accessed by HTTP GET.
Note |
---|
The url provided should only be used as a fallback if you are unable to leverage GraphQL to gather additional information. REST endpoints are only capable of delivering 1st-class attributes for any given resource (e.g. they can provide a User’s |
Example
Code Block | ||
---|---|---|
| ||
{
"data": {
"id": "t5V3S3X8Trt/weWu0A2YHb0cRFJfcRA2rXT3vAGTz1A=",
"url": "https://cms.scopear.com/api/v3/graphql/object/t5V3S3X8Trt/weWu0A2YHb0cRFJfcRA2rXT3vAGTz1A="
}
} |
Retrieving more data
By default, Scope delivers minimalistic payloads in order to encourage integrators to query the Scope GraphQL API for the specific information that they require.
GraphQL is a powerful tool that allows integrators to ask for exactly what they need.
See Using Global Node Ids and Exploring the Graph to learn how to use the Scope GraphQL API.
Info |
---|
Most relationships (aka “Connections”) are bidirectional in the Scope GraphQL API. This means that given a node in the graph, you can usually to navigate “up” to find parent-like objects (such as |
GraphQL Examples
Given the following webhook default data response:
Code Block | ||
---|---|---|
| ||
{
"id": "OiSSdfK1Su4gwG4zAi2InavDB9eRE9aOnGHYRMG5dqY=",
"url": "https://cms.scopear.com/api/v3/graphql/object/w4Aa1bjiiNSt1StG11n40b4dYU8lAigrCalO8r6cYwYchxAsWytj-F6wojhI_CsN"
} |
You could answer the question “What Scenario is associated with this ScenarioSession?” by using the following GraphQL API query:
Code Block | ||
---|---|---|
| ||
query {
node(id: "OiSSdfK1Su4gwG4zAi2InavDB9eRE9aOnGHYRMG5dqY=") {
... on ScenarioSession {
scenario {
id
}
}
}
} |
Or, you could answer the question “What ScenarioSessionEvents are associated with this ScenarioSession?” by querying the Scope GraphQL API as follows:
Code Block | ||
---|---|---|
| ||
query {
node(id: "OiSSdfK1Su4gwG4zAi2InavDB9eRE9aOnGHYRMG5dqY=") {
... on ScenarioSession {
scenarioSessionEvents {
nodes {
id
}
}
}
}
} |
Configuring your app
To receive webhooks, register a HTTPS endpoint with your app to act as a webhook receiver.
Then, subscribe to a webhook topic and supply the endpoint registered in the previous step as the callbackUrl
input value. Scope will immediately begin sending your app webhook notifications whenever events occur that pertain to the specified topic.
Webhook subscriptions are scoped to the user and organization that they're registered with.
This means that: a) other users & organizations can't view, modify, or delete the subscriptions you create; and b) subscriptions registered for other organizations will not be triggered for events that pertain to your organization’s data.
Creating an endpoint for webhooks
...
To avoid timeouts and errors, consider deferring app processing until after the webhook response has been successfully sent.
Verifying webhooks
Each webhook request includes a base64-encoded X-Scope-Hmac-SHA256 header, which is generated using the shared secret you provide when creating a webhook subscription along with the data sent in the request.
...