Introduction & Overview

StackOne’s webhook system enables real-time event notifications between your application and integrated services. Instead of constantly polling for updates, webhooks provide an efficient way to receive immediate notifications when important events occur.

What are Webhooks?

are automated HTTP callbacks that allow real-time communication between systems:
  1. Your application exposes an HTTPS endpoint to receive event notifications
  2. StackOne sends HTTP POST requests with event data to your endpoint
  3. Your application processes these events and responds accordingly

Benefits of Using Webhooks

  • Real-time Updates: Receive instant notifications about changes
  • Reduced API Load: Eliminate the need for constant polling
  • Efficient Processing: Handle events as they happen
  • Automated Workflows: Trigger actions based on events
  • Resource Optimization: Save resources by avoiding unnecessary API calls

Getting Started with Webhooks

Quick Start Guide

  1. Navigate to StackOne Webhooks Dashboard
  2. Click “Add Webhook”
  3. Enter your endpoint URL
  4. Select desired events
  5. Save and copy your signing secret
  6. Implement signature verification in your application
  7. Start receiving events!

Webhook Dashboard Overview

The webhook dashboard at https://app.stackone.com/webhooks provides comprehensive webhook management:
  • View & Configure: Manage webhook settings and configurations
  • Monitor Health: Track webhook delivery status and performance
  • Security: Access signing secrets for request verification
  • Event Management: Add or modify event subscriptions

Prerequisites

Before setting up webhooks, ensure you have:
  1. A publicly accessible HTTPS endpoint to receive webhooks
  2. Ability to verify webhook signatures (recommended)
  3. Proper error handling for webhook payloads
  4. Rate limiting consideration for high-volume events

Setting up a new webhook

  1. After selecting a StackOne project, you will see the Webhooks section in the left-hand-side menu.
  2. Create and configure a new webhook by clicking the Add Webhook button
  3. Input in the Webhook URL field a valid URL that StackOne should send a request to whenever one of the previously selected event occurs.
  4. Select the events that your webhook should be triggered on, programmatic native, manual native, or synthetic events, the availability of which depends on the provider you’re connecting to.
  5. Programmatic native events: are events generated by the underlying provider which are then mapped to StackOne unified resource events. Once subscribed to a provider’s programmatic native event, our system will programmatically create an event subscription in the provider’s system for each currently linked account for that provider, and to any future linked account of that provider.
  6. Manual native events: are events generated by the underlying provider which are then mapped to StackOne unified resource events. Unlike the programmatic events, manual events are not automatically subscribed to by our system. You will need to manually create an event subscription for each currently linked account for that provider, and to any future linked account of that provider. We’ll provide you with a link to the URL the provider will send the event to, as well as a secret to authenticate the request.
  7. Synthetic Events: For these events, StackOne periodically polls the underlying provider’s records every hour and compares the stored hashes against the hashes of the mapped records received from the provider. Thanks to the hashing process, no PII is stored. The polling time can be configured based on your needs.
  8. Finally, click on the Save Webhook button. You should now see your newly configured Webhook on the webhooks page.
  9. Take note of the webhook secret displayed in the webhook listing page as it will allow you to verify the authenticity of events coming from StackOne, we’ll use this secret to sign the webhook request based on the payload of the event.
    1. As a relatively less secure but simpler alternative, you can also verify 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.

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 a programmatic native, manual native, or synthetic event for one or more providers. Anything in orange represents 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 programmatic native webhook subscription for each linked account, as well as the status of the synthetic events execution.

Disabling a webhook

You can disable a webhook by clicking the Disable button in the webhook listing page. This will stop the emission of events to the webhook URL.

Enabling a webhook

You can enable a disabled webhook by clicking the Enable button in the webhook listing page. This will resume the emission of events to the webhook URL

Deleting a webhook

You can delete a webhook by clicking the Delete button in the webhook listing page. This will remove the webhook from the webhook listing page and stop the emission of events to the webhook URL. This will also clean up the relevant programmatically created subscriptions for the linked accounts in the underlying provider’s system.
Deleting a webhook is irreversible.

How StackOne Synthetic Webhooks Work

How StackOne Native Webhooks Work

Event subscriptions lifecycle details

  • When a webhook is created, StackOne will programmatically create an event subscription in the underlying provider’s system for each currently linked account for that provider, and to any future linked account of that provider.
  • When a webhook is deleted, StackOne will clean up the relevant programmatically created subscriptions for the linked accounts in the underlying provider’s system.
  • When a webhook is disabled, StackOne will stop the emission of events to the webhook URL, but the event subscriptions in the underlying provider’s system will remain active.
  • When a webhook is enabled, StackOne will resume the emission of events to the webhook URL, and the event subscriptions in the underlying provider’s system will remain active.

event list and associated record_type

CategoryResourceEvents
AccountsAccountsaccount.created, account.updated, account.deleted
HRISEmployeeshris_employees.created, hris_employees.updated, hris_employees.deleted
Employmentshris_employments.created, hris_employments.updated, hris_employments.deleted
ATSAssessmentsats_assessments.created, ats_assessments.updated, ats_assessments.deleted
Candidatesats_candidates.created, ats_candidates.updated, ats_candidates.deleted
Applicationsats_applications.created, ats_applications.updated, ats_applications.deleted
Interviewsats_interviews.created, ats_interviews.updated, ats_interviews.deleted
Jobsats_jobs.created, ats_jobs.updated, ats_jobs.deleted
Job Postingsats_job_postings.created, ats_job_postings.updated, ats_job_postings.deleted
Listsats_lists.created, ats_lists.updated, ats_lists.deleted
Usersats_users.created, ats_users.updated, ats_users.deleted
CRMAccountscrm_accounts.created, crm_accounts.updated, crm_accounts.deleted
Contactscrm_contacts.created, crm_contacts.updated, crm_contacts.deleted
LMSAssignmentslms_assignments.created, lms_assignments.updated, lms_assignments.deleted
Completionslms_completions.created, lms_completions.updated, lms_completions.deleted
Contentlms_content.created, lms_content.updated, lms_content.deleted
Courseslms_courses.created, lms_courses.updated, lms_courses.deleted
Userslms_users.created, lms_users.updated, lms_users.deleted
Note:The webhook settings such as URL and Events 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).

Event payload

Account Events

Accounts-based webhooks 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
  provider: string, // The integration provider the account is connected to
  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 updated 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",
  "provider": "integration_key",
  "origin_owner_id": "unique-org-identifier",
  "origin_owner_name": "Test organization",
  "origin_username": "sample-user",
  "setup_information": {
    "domain": "companytest"
  }
}

Unified Events

Unified events are events for a unified resource type, such as an employee, a job, a candidate, etc. Synthetic events will not contain the raw_event field due to the nature of the hashing process.
Note:Synthetic events will not contain the raw_event or event_data fields due to the nature of the hashing process. Native events will contain the raw_event field, containing the original event payload received from the provider, as well as event_data which contains as much mapped data as possible from the original event payload.
{
  project_id: string, // the ID of the StackOne project
  event: string, // the event name
  event_date: string, // date as ISO string (eg: '2023-07-03T01:55:47.217Z')
  record_type: string, // the type of the resource
  record_id: string | number, // the ID of the resource
  sent_at: string, // date as ISO string (eg: '2023-07-03T01:55:47.217Z')
  raw_event?: object, // native events only - the original event payload received from the provider
  event_data?: object, // native events only - the mapped data from the original event payload
}

Synthetic event 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"
}

Native event 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"
    }
  }
}

Webhook signature verification

We’ll use the signature secret we mentioned in the previous section to verify the authenticity of the webhook request. To do so we’ll send a x-stackone-signature header. This header will contain an 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://typedwebhook.tools/ or by setting-up a proxy to your localhost via ngrok.StackOne is not affiliated with these websites.

Triggering a webhook event manually for testing purposes

In case you want to verify that your webhook is working as expected, you can trigger a webhook event manually by using an API key from the API keys section of the project you’re working on, getting the webhook ID from the webhook details URL, and calling the webhook events endpoint like so:
curl --location 'https://api.stackone.com/webhooks/123/events' \
--header 'Content-Type: application/json' \
--header 'x-account-id: 41961660184810031685' \
--header 'Authorization: Basic <base 64 encoded API key>:' \
--data '{
    "webhook_ids": [3],
    "events": [

    { "event": "hris_employees.created", "record_id": "123456" }]
}'

Sample Webhook consumer code

An example of a webhook consumer code in Node.js with signature verification can be found below:
// 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
  1. Create a new signing secret by clicking on the existing secret to view and click with the Add Signing Secret button
    The new signing secret will be flagged as inactive and requests sent to your webhook URL will only be signed with the active signing secret
  2. In StackOne: Copy this new (currently inactive) signing secret value
  3. 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.
  4. 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.
  5. 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.
  6. In your own codebase: remove the logic responsible for verifying the request with the now inactive / deleted signing secret

Troubleshooting & Monitoring

Common Issues and Solutions

  1. Webhook Not Receiving Events
    • Verify webhook is enabled in dashboard
    • Check endpoint URL is accessible
    • Review event subscriptions
    • Inspect webhook health tab
  2. Signature Verification Failures
    • Confirm correct signing secret usage
    • Check for payload modifications
    • Verify signature calculation method
    • Ensure correct encoding (base64url)
  3. Missing Events
    • Check webhook health metrics
    • Review retry status
    • Verify event subscriptions
    • Consider synthetic events for reliability
    • Add logging to your webhook consumer code when an event is received in your application
  4. Performance Issues
    • Implement async processing
    • Optimize response time
    • Consider rate limiting
    • Monitor resource usage

Production Checklist

  • Implement proper error handling
  • Set up monitoring and alerting
  • Configure retry policies
  • Document recovery procedures
  • Test failure scenarios
  • Monitor resource usage
  • Regular security reviews
Keep your webhook endpoints efficient and reliable:
  • Process events asynchronously
  • Implement proper error handling
  • Monitor webhook health regularly
  • Maintain security best practices