Getting started with webhooks

Learn how to use OfficeRnD Webhooks infrastructure and APIs.

Webhooks allow developers to build integrations, which are notified when a certain event happens in OfficeRnD. When such an event is triggered, the webhook's configured URL will receive an HTTP POST request. Webhooks can be used to update a dataset in a local service deployment; to notify the change of a linked entity in another 3rd party service; or send messages to a set of email addresses; and more.

What are webhooks

Webhooks are a communication mechanism that delivers real-time notifications whenever a given event happens. The webhooks automatically 'push' a notification immediately when an event in the system occurs. In comparison, integration implementations can also poll (a 'pull' communication method) - periodically check for data additions or changes.

πŸ“˜

Important

We do not recommend polling as a viable approach to periodically pull large datasets. Normally changes in the data occur to a handful of entries and not to all. With that in mind, it is suboptimal to pull an entire dataset instead of iteratively applying the changes as they happen in OfficeRnD. Additionally, polling requests can hit the rate limit and be cut off.

The webhook has an associated URL (i.e., https://your.integration.url/webhooks). The notification payloads are delivered to the URL using HTTP POST requests. The webhook notification payload contains all relevant information about what happened in OfficeRnD, including the type of event and the data changes associated with that event. The webhook receiver uses the event details and decides what to do with it - such as deactivating the associated Salesforce entity.

Register Webhook

Developers can register webhooks that deliver notifications for one, a few, or for all events in their OfficeRnD account.

As a prerequisite to registering a webhook in OfficeRnD, you need to build your own custom endpoint on your server. Additionally, you must ensure the server URL is secure (i.e. it begins with https://), otherwise, you won't be allowed to register the webhook endpoint.

Following is an example code of a simple HTTP Node.js server that listens for POST requests at the /webhooks/callback endpoint.

const app = require('express')();
const bodyParser = require('body-parser');

app.use(bodyParser.raw({ type: 'application/json' }));
app.post('/webhooks/callback', (req, res) => {
  const { body, rawBody, headers } = req;

  const { eventType, data } = body;
  switch (eventType) {
    case 'company.paymentdetails.removed':
    // ...
    break;
    case 'member.paymentdetails.removed':
    // ...
    break;
    case 'member.removed':
    case 'company.removed':
    // ...
    break;
    default:
    // unhandled event
    console.log(`Event ${eventType} was not handled.`);
  }
}

app.listen(3000, () => console.log('Listening on 3000'));

You can find the complete node.js example in the webhooks-demo GitHub repo.

πŸ“˜

Note

The example above is purely for illustrative purposes. The technology of choice and server implementation details may differ greatly.

Register the webhook in Settings > Developer Tools > Webhooks. You'll be asked for the following:

  • Endpoint URL - the endpoint of your server where HTTP POST requests will be made
  • Description - an optional description allowing you to describe the purpose of the webhook
  • Events to send - the list of events for which requests will be made to the endpoint

Receiving Webhook notifications

For each event occurrence, the webhook is registered for, the webhook data is POSTed to your endpoint in JSON format.
The webhook notification request payload will be a JSON comprising the following fields:

PropertyTypeDescription
eventstringThe unique identifier of the event instance
eventTypestring (system event type)The event type that occurred in OfficeRnD
dataobjectThe event payload describing the event
data.objectobjectThe resource that has been added/changed/removed
data.previousAttributesobjectThe field is only present for updated events. The object describing all changed attributes and their previous values
data.<parent-resource-name>stringThe field is only present for subresource events to identify the parent resource - e.g. data.invoice, data.member, etc.
createdAtstring (ISO Date)The time of the event occurrence.

Below is an example of a webhook notification delivered when the payment card details of a company are deleted:

{
  "event": "5f8d6c9747120500116899e4",
  "eventType": "company.paymentdetails.removed",
  "data": {
    "object": {
      "_id": "5f86de9c1e74e400d416467c",
      "card": {
        "id": "card_<redacted>",
        "name": "E Corp.",
        "brand": "Visa",
        "funding": "credit",
        "exp_month": 11,
        "exp_year": 2022,
        "country": "US",
        "cvc_check": "pass",
        "last4": "1111"
      },
      "provider": "stripe",
      "providerId": "<redacted>",
      "sourceId": "card_<redacted>",
      "authorization": {
        "authProviderId": "seti_<redacted>",
        "status": "not_required"
      },
      "createdAt": "2020-10-14T11:18:52.130Z",
      "createdBy": "5e131db18edb280010c8d4ef"
    },
    "company": "5e1d8bd37f8759001051b0e8"
  },
  "createdAt": "2020-10-19T10:38:15.127Z"
}

Similarly, when a member is removed, a webhook notification for the member.removed event is delivered:

{
  "event": "5f8715ca1e74e400d4164ea6",
  "eventType": "member.removed",
  "data": {
    "object": {
      "_id": "5e8c9df14d46c7001064c3ef",
      "status": "active",
      "calculatedStatus": "drop-in",
      "team": "5e8c9eb94d46c7001064c427",
      "office": "5e1d8bd37f8759001051b0e2",
      "name": "Jonathan Brown",
      "description": "",
      "image": null,
      "organization": "5e1d8bd37f8759001051b0ca",
      "email": "[email protected]",
    }
  },
  "createdAt": "2020-10-14T15:14:18.042Z"
}

Responding to webhook notifications

To acknowledge that a notification has been received, your endpoint must respond with a 2xx HTTP status code to OfficeRnD. Sending a response body is not required.

Additionally to the response status code, your endpoint must also respond quickly - prior to running any complex logic upon receipt of a notification, ensure you've returned a 2xx HTTP status code.

If your endpoint doesn't respond accordingly with the correct status code or in a timely manner, the webhook notification attempt will be treated as a failure.

πŸ“˜

Important

Currently failed webhook notification attempts will not be automatically retried.

Test your webhooks

After configuring your server to be able to receive POST requests, and registering your webhook in OfficeRnD, it's time to test it. To that end, OfficeRnD's webhook details view provides you with a list of all webhook notification delivery attempts.

As your endpoint receives notification payloads asynchronously, its failures may not be immediately obvious. We generally recommend verifying that the webhook notification attempts are successful after making any changes to the server.

Secure your webhooks

Once your server is configured to receive notification payloads, it'll be open for any requests sent to it. For security reasons, you should verify all incoming requests and allow only those coming from OfficeRnD. All webhook notification payloads sent by OfficeRnD are signed and the signature is included in the officernd-signature header of the request. Using the webhook's secret and the payload body you should verify the computed signature matches that in the header.

Webhook secrets

Before you can verify signatures, you need to retrieve your endpoint’s secret from your Webhook's details view. Select a webhook that you want to obtain the secret for, then click the Click to reveal button.

πŸ“˜

Note

If you create a webhook by calling the public API, the response from your POST request will contain the secret.

In order to get it again after you've created the webhook you can use ?$select=secret, when calling the webhooks endpoint.

Webhook secrets are unique for each registered webhook endpoint. If you register several webhooks, you will need to verify the signatures for each with their respective webhook's secret.

Verifying payloads from OfficeRnD

OfficeRnD creates a hash signature with each payload. The signature is included in the headers of each webhook notification request as officernd-signature. For extra security, in addition to the signature, each header contains a timestamp (prefixed by t=) which is used in the hash computation.

The officernd-signature header has two fields, separated with ,: t and signature

An officernd-signature header will look similar to this:

officernd-signature: t=1602237976,signature=099618786b221c0bd88346a71e01c8deec60007c2db2ead5975284d4e957b8f5

Signatures are computed using an HMAC hex digest with SHA-256. The raw request body and timestamp are joined by a dot . and HMAC hashed. In order to verify the signature, you will have to repeat the process with the incoming data and compare your result to the signature in the header.

  1. Extract the timestamp and signatures

    • Split the header using the , as the separator.
    • Then split the resulting elements (t=.... and signature=...) using the = character as the separator. This will give you two pairs to work with - the timestamp and its value, and the signature and its hash.
    • Verify the payload timestamp and the current timestamp difference is within your tolerance
  2. Prepare the payload to sign. Concatenate the pieces in the following order:

    • The raw body payload (as a string)
    • The dot character .
    • The timestamp value
  3. Compute the expected signature

    • Compute an HMAC with the SHA-256 hash function
    • Use the webhook secret as the key
    • Use the concatenated string from step 2 as the message
  4. Compare the signatures

    • Compare the header signature from step 1 to the expected signature from step 3

You can find a complete example of the signature verification flow in the webhooks-demo GitHub repo

System event types

Below is a complete list of all types of events OfficeRnD can currently send webhook notifications for.

All event names follow a common pattern resource.(optional)subresource.operation such as invoice.created or invoice.charge.created. The supported operations are CUD - created, updated, deleted. This way it should be easy to understand what an event name corresponds to within OfficeRnD.

resourceoperationseventsdescription
companycreated
updated
removed
company.created
company.updated
company.removed
Occurs when a company is created, updated or removed.
company.paymentdetailscreated
removed
company.paymentdetails.created
company.paymentdetails.removed
Occurs when payment details (i.e. card details, bank details, etc.) are added or removed for a company.
membercreated
updated
removed
member.created
member.updated
member.removed
Occurs when a member is created, updated or removed.
member.paymentdetailscreated
removed
Occurs when payment details (i.e. card details, bank details, etc.) are added or removed for a member.
invoicecreated
updated
removed
invoice.created
invoice.updated
invoice.removed
Occurs when an Invoice, Credit Note or Overpayment is created, updated, or removed. Property data.object.documentType contains information about the type of document.
invoice.chargecreated
updated
removed
invoice.charge.created
invoice.charge.updated
invoice.charge.removed
Occurs when an Invoice receives a payment, when the payment status is updated, or when the payment is manually removed.
invoice.allocationcreated
removed
invoice.allocation.created
invoice.allocation.removed
Occurs when an Invoice is allocated a credit from a credit note, or when an allocation is manually removed.
feecreated
updated
removed
fee.created
fee.updated
fee.removed
Occurs when a fee is created, updated or removed.
membershipcreated
updated
removed
membership.created
membership.updated
membership.removed
Occurs when a membership is created, updated or removed.
bookingcreated
updated
removed
booking.created
booking.updated
booking.removed
Occurs when a booking is created, updated or removed.
contractcreated
updated
removed
contract.created
contract.updated
contract.removed
Occurs when a contract is created, updated or removed.
ticketcreated
updated
removed
ticket.created
ticket.updated
ticket.removed
Occurs when an issue is created, updated or removed.
integrationremovedintegration.removedOccurs when an integration is disconnected from the OfficeRnD organisation.
passcreated
updated
pass.created
pass.updated
Occurs when a day pass is either created or updated for a company/member.
occupancyslotcreated
removed
occupancyslot.created
occupancyslot.removed
Occurs when a booking is created in order to shows its occurrences.
The difference between the endpoint for bookings and this one is that the occupancy slots are explicitly for occurrences - useful for recurring bookings, that have more than one occurrence in order to track the slots, that are occupied by said booking.