StackOne Webhooks
Describes how to use the StackOne webhooks functionality
Overview
StackOne allows configuring multiple Webhooks to establish alerts' communication between your application and StackOne servers. After creating a webhook, you will need to test Webhooks by specifying:
– example payload
– your customized webhook integration code
Webhook Listing
Navigate to the webhook section by going to https://app.stackone.com/webhooks. On that section you can:
- view any configured webhook and clickthrough to update their configuration
- copy the signing secret to use in your codebase accordingly
- create a new webhook
Setting up a new webhook
-
After selecting a StackOne project, you will see the
Webhooks
section in the left-hand-side menu. -
Create and configure a new webhook by clicking the
Add Webhook
button -
Input in the
Webhook URL
field a valid URL that StackOne should send a request to whenever one of the previously selected event occurs. -
Select the events that your webhook should be triggered on (native or synthetic events)
- Native events: are events generated by the underlying provider which are then associated with a StackOne unified event/resource, parsed and forwarded to the configured webhook URL. Subscribing to native event will, once the configuration saved, programmatically create an event subscription for each current and future linked account of the selected provider
- Synthetic Events: StackOne periodically polls the underlying provider records every hour and comparing stored hashes. The poll time can be adapted based on your needs and no PII is stored as part of this process.
-
Finally, click on the
Save Webhook
button. You should now see your newly configured Webhook on thewebhooks
page. -
Take note of the webhook secret displayed in the webhook listing page as it will allow you to authenticate more securely webhooks coming from StackOne
- As a relatively less secure but simpler alternative, you can also authenticate events sent by StackOne by adding a custom query parameter to your webhook URL eg
?token=unique_secret_token
and verify this parameter's presence when receiving the request.
- As a relatively less secure but simpler alternative, you can also authenticate events sent by StackOne by adding a custom query parameter to your webhook URL eg
Overview Tab
The overview tab shows you at a glance the number of events sent to the configured webhook URL within the timeframe selected on the right-hand side (whether these events were successfully sent or if the request to the configured webhook URL failed)
Events Tab
Account Events
Account Events are events originating from StackOne related to a linked account connection and not to any underlying resource (eg: employee or candidates). Account events can be useful for your application/server to be notified of any new Accounts being linked (account.created
event) or updated (account.updated
) allowing you to trigger relevant workflow on your end (eg. syncing data from the underlying tenant).
Unified Events
The Events tab of the webhook configuration allows you to view and update the unified events this webhook is subscribed to. Any "green" in the table indicates that the webhook is currently subscribed to native or synthetic event for one or more underlying provider. Anything in orange represent an event subscription that has been modified but not yet saved.
Health Tab
The health tab of the new webhook allows you to see at a glance the status of each underlying native webhook subscription for each linked account
event
list and associated record_type
event
list and associated record_type
Category | Resource | Events |
---|---|---|
Accounts | Accounts | account.created , account.updated , account.deleted |
HRIS | Employees | hris_employees.created , hris_employees.updated , hris_employees.deleted |
Employments | hris_employments.created , hris_employments.updated , hris_employments.deleted | |
ATS | Assessments | ats_assessments.created , ats_assessments.updated , ats_assessments.deleted |
Candidates | ats_candidates.created , ats_candidates.updated , ats_candidates.deleted | |
Applications | ats_applications.created , ats_applications.updated , ats_applications.deleted | |
Interviews | ats_interviews.created , ats_interviews.updated , ats_interviews.deleted | |
Jobs | ats_jobs.created , ats_jobs.updated , ats_jobs.deleted | |
Job Postings | ats_job_postings.created , ats_job_postings.updated , ats_job_postings.deleted | |
Lists | ats_lists.created , ats_lists.updated , ats_lists.deleted | |
Users | ats_users.created , ats_users.updated , ats_users.deleted | |
CRM | Accounts | crm_accounts.created , crm_accounts.updated , crm_accounts.deleted |
Contacts | crm_contacts.created , crm_contacts.updated , crm_contacts.deleted | |
LMS | Assignments | lms_assignments.created , lms_assignments.updated , lms_assignments.deleted |
Completions | lms_completions.created , lms_completions.updated , lms_completions.deleted | |
Content | lms_content.created , lms_content.updated , lms_content.deleted | |
Courses | lms_courses.created , lms_courses.updated , lms_courses.deleted | |
Users | lms_users.created , lms_users.updated , lms_users.deleted |
Note:
The webhook settings such as
URL
andEvents
can be updated at any point after creating the webhook by returning to the Webhooks page and clicking on the webhook you would like to update.
You can also delete the webhook if it's no longer needed (irreversible action).
Testing Webhooks
Example Payload
Accounts-based webhooks payload will follow the following interface:
{
project_id: string, // the ID of the StackOne project
event: 'account.updated' | 'account.created' | 'account.deleted' | 'hris_employees.updated', 'hris_employees.created',
event_date: 'string', // date as ISO string (eg: '2023-07-03T01:55:47.217Z')
record_type: 'account',
record_id: string | number, // In the case of accounts-based events - this will be the account ID
sent_at: 'string', // date as ISO string (eg: '2023-07-03T01:55:47.217Z')
account_id: string, // This will always be the Account ID
origin_owner_id?: string, // unique indentifier used to track customers or organizations
origin_owner_name?: string, // human-readable name associated with Origin Owner ID
origin_username?: string, // username responsible for connecting the tool
setup_information?: object, // extra information used to setup the account
}
Account event (account.updated) example
{
"project_id": "acmeinc-27270",
"account_id": "41220118784113397989",
"event": "account.updated",
"event_date": "2023-11-01T13:10:31.408Z",
"record_type": "account",
"record_id": "41220118784113397989",
"sent_at": "2023-11-01T13:10:31.418Z",
"origin_owner_id": "unique-org-identifier",
"origin_owner_name": "Test organization",
"origin_username": "sample-user",
"setup_information": {
"domain": "companytest"
}
}
Employee Synthetic Event (employee.updated) example
{
"project_id": "acmeinc-27270",
"account_id": "41260118784113327989",
"event": "hris_employees.updated",
"event_date": "2023-11-01T15:09:22.961Z",
"record_type": "employees",
"record_id": "3223323715753018140",
"sent_at": "2023-11-01T15:09:22.978Z"
}
Job Postings Native Event (job_postings.updated) example
{
"project_id": "dev-56501", // StackOne Project ID
"account_id": "44250964874717348401", // StackOne Linked Account ID
"event": "ats-job_postings.updated", // Unified Event name
"event_date": "2024-11-13T17:01:16.0007", // Datetime string of when the event was received by StackOne from the provider
"record_type": "ats_job_postings", // Unified Resource Type
"record_id": "cxIQam9iX2lkOjEzMmM3ZWRjLWNmMTktNDdjMy1hODdlLTUxMzU3NDI4ZDVmZA", // Stackone Identifier for the resource
"record_remote_id": "132c7edc-cf19-47c3-a87e-51357428d5fd", // Original (remote) Identifier for the resource
"sent_at": "2024-11-1317:01:16.844Z", // Timestamp of when the event was sent to the configured webhook URL
// original event data including original payload
"raw_event": {
"headers": {
"smartrecruiters-timestamp": "1731517276",
"content-type": "application/json"
},
"body": {
"job_id": "18dedcd5-2509-4363-a565-7b63b2ablaf1",
"job_ad_id": "132c7edc-cf19-47c3-a87e-51357428d5fd"
}
}
}
A x-stackone-signature
header will be added to all webhook requests, e.g.,'x-stackone-signature': 'jqzLV78Lkg5RhZgx6nRA1FLIYBkqoclBdRrNizjlZcU'
. This header will contain a hmac sha256
hash of the given payload using the Signature Secret
previously given as a secret key during hashing.
3rd party Webhook generators:
You can easily test the account-based webhooks by using a third-party webhook creation tool such as https://webhook.site or https://typedwebhook.tools/ or by setting-up a proxy to your localhost via ngrok.
StackOne is not affiliated with these websites.
Sample Webhook consumer code
// index.js
import express from 'express';
import bodyParser from 'body-parser';
import { createHmac } from 'crypto';
const app = express();
// Replace with the signing secret string displayed on the Stackone webhooks details page
const STACKONE_WEBHOOK_SIGNATURE =
'S-gQTtwraolKVsY88OvQNZ-TlGrHw9mvHKp-ls8Co9CkOADQc1VSeCZ_2hDK6oWuKUd_4qxjRLzoEGYA9JVCNg';
/**
* The function checks if a given signature is valid by comparing it with the payload hash using a signing secret.
* @param signature - The `signature` parameter is the signature value that you want to validate. It is typically a string
* representing the cryptographic signature of the `payload` parameter.
* @param payload - The payload is the data that needs to be signed. It can be any string or object that you want to verify
* the integrity of.
* @param signingSecret - The `signingSecret` parameter is a secret key used for generating the signature. It is a string
* value that should be kept confidential and known only to the parties involved in the signature verification process.
* @returns a boolean value indicating whether the calculated hash is equal to the provided signature.
*/
function isSignatureValid(signature, payload, signingSecret) {
const hash = createHmac('sha256', signingSecret)
.update(payload)
.digest('base64url');
return hash === signature;
}
const jsonParser = bodyParser.json();
app.post('/integrations/webhook/accounts', jsonParser, (req, res) => {
const signature = req.headers['x-stackone-signature'];
if (
!signature ||
!isSignatureValid(
req.headers['x-stackone-signature'],
JSON.stringify(req.body),
STACKONE_WEBHOOK_SIGNATURE,
)
) {
return res.status(401).send('Unauthorized');
}
// Do something with the payload
console.log(req.body);
// The webhook URL should return a 200 upon successful processing of the event. The response does not need to include anything else.
return res.status(200).send(`Thank you for the account webhook event ❤️`);
});
console.log('listening on port 3333');
app.listen(3333);
// This sample server can be run via `node index.js`
Signing Secret Rotation
You can rotate the webhook signing secret by following this process
-
Create a new signing secret by clicking on the existing secret to view and click with the
Add Signing Secret
buttonThe new signing secret will be flagged as inactive and requests sent to your webhook URL will only be signed with the active signing secret
-
In StackOne: Copy this new (currently inactive) signing secret value
-
In your own codebase:update the verification logic to verify the request with both the current secret and the new one copied in the previous step.
-
In StackOne: Activate the new webhook signing secret by clicking on it and confirming the activation. Once activated all requests sent to your webhook URL by StackOne will be signed using this new secret.
-
In StackOne: Once you've verified that new requests are being verified as expected, you may delete the old secret. You will not be able to retrieve it or activate it again once deleted.
-
In your own codebase: remove the logic responsible for verifying the request with the now inactive / deleted signing secret
Updated 5 days ago