RPM Webhooks

In RPM 12 we will be introducing webhooks, which allow third party systems to get notified when certain events happen in a process in RPM:whenever the event occurs an HTTP POST request is sent to a specific URL with details of what happened and which form was affected.


When setting up a new webhook you need to provide:

  1. Name – identify the webhook
  2. Process – identify the associated process
  3. Url – the url to request when the event happens
  4. Event – the event to track. See the list of valid event names section.
  5. Secret – secret key used to generate a checksum to allow the receiving system to confirm the request is indeed from RPM (do not publish anywhere, should only be known by RPM and the receiving system)

Event names

  • form.start – when a form is created on the associated process
  • form.edit – when a form is edited on the associated process (including archive/unarchive)
  • form.trash – when a form on the associated process is sent to the trash
  • form.restore – when a form on the associated process is restored from the trash
  • action.add – when an action is added to a form (this also triggers when an action is restored from the trash)
  • action.edit – when an action is edited on a form
  • action.trash – when an action is trashed
  • agency.add – when an agency is added (manually or via import)
  • agency.edit – when an agency is edited. Including when adding any of these change: file attachments, notes, supplier exclusion, archive/restore, permissions, staff assignments. It also triggers when the agency is restored from trash.
  • agency.trash – when an agency is trashed
  • rep.add – when a rep is added (during agency creation and when done manually)
  • rep.edit – when a rep is modified manually or via import. Including when any of these change: files, notes, rep is promoted to manager, logon information, contact information and when the rep is restored from the trash
  • rep.trash – when rep is trashed
  • staff.add – when a staff user is added manually or via import
  • staff.edit – when a staff user is edited. Including when any of these change: a proxy is added, login information, staff group membership
  • staff.trash – when a staff user is trashed
  • staff.restore – when a staff user is restored from the trash
  • commrun.open – when a commission run is opened. Including when a new one is started
  • commrun.closed – when a commission run is closed
  • commrun.import – when an commission import finishes
  • commrun.calculate – when commission calculation finishes
  • file.add – when a file is added to a form
  • file.edit – when a file attached to a form is changed (name, note, security and folder)
  • file.trash – when a file attached to a form is trashed
  • file.restore – when a file attached to a form is restored
  • supplier.add – when a new Supplier is added
  • supplier.edit – when any of the following supplier attributes are modified:
    • Name
    • Logo
    • Primary contact
    • Basic fields
    • Bypass codes
    • Notes
    • Files
  • supplier.delete – when a supplier is deleted.
  • customer.add – when a new customer is added.
  • customer.edit – when any of the following customer attributes are modified:
    • Name
    • Website
    • Any Contact information (first name, last name, phones, address, and email)
    • Basic fields
    • Primary location information (address or basic fields)
    • Notes
    • Files
  • customer.trash – when a customer is trashed.
  • customer.restore – when a customer is restored from the trash.

Receiving the HTTP Request

The receiving system will receive an HTTP Request using POST. The following headers are sent with the request:

  • Content-Type – <string> the type of the POST data content (always application/json)
  • X-RPM-Instance – <string> The instance name for the sending RPM instance
  • X-RPM-InstanceID – <int> The instance ID for the sending RPM instance
  • X-RPM-Subscriber – <int> The ID of the subscriber where the event happened
  • X-RPM-Signature – <string> checksum signature to allow receiving system to validate the request
  • User-Agent – RPM-Webhook
  • Host – IP address of the RPM instance that generated the request
  • Content-Length – Length of POST data

Note: RFC-2616 states that headers are case-insensitive so it is recommended to match headers in that way.

Here’s an example request received (the JSON has been formatted for readability):

Content-Type: application/json
X-RPM-Instance: Cube11
X-RPM-InstanceID: 2001
X-RPM-Subscriber: 1071
X-RPM-Signature: 930c0cacf3866d3fa71ed5c5dbac39fe5330b4e3908039e0d689dcf8a0ffb780
User-Agent: RPM-Webhook
Content-Length: 124
Expect: 100-continue
Connection: Keep-Alive

    "ObjectID": 67346,
    "ObjectType": 520,
    "ParentID": 2011,
    "ParentType": 510,
    "EventName": "form.edit",
    "RequestID": 416,
    "StatusID": 5415

Validating the request

To prevent forged requests or DDoS attacks, RPM sends the X-RPM-Signature header which is the result of encrypting the request body via an HMAC encryption using SHA256 (using the Secret as the crypto key).

The receiving system should repeat the same HMAC encryption process using the same shared secret key to confirm the request is valid. If the X-RPM-Signature header is missing or the checksum does not match then the system can consider the requesting IP address for blacklisting.



$headers = [];
// make headers lowercase
foreach (getallheaders() as $name => $value) {
	$name = strtolower($name);
	$headers[$name] = $value;
$body    = file_get_contents('php://input');
$secret  = 'valar morghulis'; // your own secret
$hashed  = hash_hmac('sha256', $body, $secret);

if ($hashed === $headers['x-rpm-signature']) {
	echo "This request is legit";
} else {
	die("You shall not pass");


var crypto = require('crypto');
var secret = 'Alles dreht sich!'; // Your own secret

function getSignature(data, secret) {
    var hmac = crypto.createHmac('sha256', secret);
    hmac.update(typeof data === 'object' ? JSON.stringify(data) : '' + data);
    return hmac.digest('hex');

function validateSignature(signRecieved, data, secret) {
    var signCalculated = getSignature(data, secret);
    if (signCalculated !== signRecieved) {
        throw new Error(`Wrong signature. Calculated: ${signCalculated}, recieved: ${signRecieved}`));

validateSignature(request.headers['x-rpm-signature'], request.body, secret);

Webhook payload content

Each webhook request will contain JSON encoded payload that contains details of the object that was involved, depending of the event type the payload changes:

form.start, form.edit, form.trash, form.restore

    "ObjectID": <int>, // FormID
    "ObjectType": 520, // Form
    "ParentID": <int>, // ProcessID
    "ParentType": 510, // Process
    "EventName": <string>, // The event
    "RequestID": <int>, // ID of the request, to match the webhook history
    "StatusID": <int> // ID of the status of the form

action.add, action.edit, action.trash

    "ObjectID": <int>, // Action ID
    "ObjectType": 525, // Action
    "ParentID": <int>, // Form ID
    "ParentType": 520, // Form
    "EventName": <string>, // Event name
    "RequestID": <int>, // ID of the request, to match the webhook history
    "StatusID": <int> // Whether the action is marked done (0 = no, 1 = yes) Currently only works on action.add and action.edit. The action.trash always reports 0

agency.add, agency.edit, agency.trash

    "ObjectID": <int>, // Agency ID
    "ObjectType": 200, // Agency
    "EventName": <string>, // Event name
    "RequestID": <int>, // ID of the request, to match the webhook history

rep.add, rep.edit, rep.trash

    "ObjectID": <int>, // Rep ID
    "ObjectType": 1, // Rep
    "EventName": <string>, // Event name
    "RequestID": <int>, // ID of the request, to match the webhook history

staff.add, staff.edit, staff.trash, staff.restore

    "ObjectID": <int>, // Staff ID
    "ObjectType": 3, // Staff
    "EventName": <string>, // Event name
    "RequestID": <int>, // ID of the request, to match the webhook history

commrun.calculate, commrun.open, commrun.closed, commrun.import

    "ObjectType": <int>, // Commission run ID
    "EventName": <string>, // Event name
    "RequestID": <int>, // ID of the request, to match the webhook history
    "CommRunYM": <string>, // Run year and month (e.g. 201811)
    "CommRunOpen": <bool>, // Is the run open or not (commrun.open will report false)
    "CommRunStaffOnly": <bool> // Staff only or not hidden (there's a bug where commrun.import always reports false)


  • CommRunOpen will always be true on commrun.open webhooks
  • CommRunOpen will always be false on commrun.closed webhooks

file.add, file.edit, file.trash, file.restore

    "ObjectID": <int>, // File ID
    "ObjectType": 450, // File
    "ParentID": <int>, // Form ID
    "ParentType": 520, // Form
    "EventName": <string>, // Event name
    "RequestID": <int>, // ID of the request, to match the webhook history
    "StatusID": 0 // always 0

supplier.add, supplier.edit, supplier.delete

    "ObjectID": <int>, // Supplier ID
    "ObjectType": 203, // Supplier
    "EventName": <string>, // Event name
    "RequestID": <int> // ID of the request, to match the webhook history

customer.add, customer.edit, customer.trash, customer.restore

    "ObjectID": <int>, // Customer ID
    "ObjectType": 5, // Customer
    "EventName": <string>, // Event name
    "RequestID": <int> // ID of the request, to match the webhook history

Webhooks and API requests

RPM’s web API allows applications to add, edit, archive, unarchive and trash forms, as well as adding and editing actions via API. By default, these request will not trigger webhooks.

To enable API users to trigger webhooks, a new WebhookEvaluate parameter has been added to those requests.

Be conscious that you could produce cyclic requests if not careful when making WebhookEvaluate true.

%d bloggers like this: