Webhooks Basics

Intro to Webhooks

At a high-level, it works like this:

  1. Your application subscribes to callbacks.
    First, subscribe to event notifications (callbacks) by creating one or more webhooks. You can create a webhook manually via the API or have your application create a webhook dynamically. Each webhook must specify:

    • Which Smartsheet object to monitor for events

    • Which events in that object should trigger a callback

    • The HTTPS URL (that is, "callback URL") where Smartsheet should send callbacks when any of the specified events occur within the scope of the specified Smartsheet object. (NOTE: Smartsheet webhooks do not support callbacks to servers using self-signed certificates)

      Webhook examples in plain English:

  1. When any of the specified events occur within an object that is being monitored by a webhook, Smartsheet sends an HTTP POST (that is, "callback") to the callback URL that is associated with the webhook.

    • NOTE: The callback payload is a "skinny" payload -- it indicates which objects changed and the type of events that occurred, but does not contain any data from the objects themselves.
  2. Your application receives, acknowledges, and processes callbacks.
    Your application receives the HTTP POST, acknowledges the callback by responding with HTTP status 200, and processes the callback. Because the callbacks are "skinny", make one or more requests to the Smartsheet API to retrieve the actual data associated with the change.

Webhook Management

Use the Smartsheet API to create and manage webhooks. See Webhooks API Reference for details about the related API operations.

Webhook Ownership & Access

Webhooks can be created and managed via Direct API Access or by a third-party app.

A webhook that is created via Direct API Access:

  • is owned by the user who created it
  • can be managed only by the user who created it
  • is not affected if the access token that was used to create it is revoked, as long as the user remains active

A webhook that is created by a third-party app:

  • is owned by the user who authorized the third-party app (and is associated with the third-party app)
  • can be managed by the third-party app (as long as it maintains authorization) or by the user who owns it
  • is permanently disabled if the third-party app's permissions are revoked

Webhook Scope & Events

A webhook monitors the specified scope for the specified events.

A Webhook object's scope attribute indicates the scope of the subscription. It is specified when creating a webhook, and cannot subsequently be changed.

  • Currently, sheet is the only supported value for the scope attribute. However, you can set an array of columns as a subscope if you want to limit the webhook to specific columns.

A Webhook object's event attribute indicates the set of events that a webhook is subscribed to (that is, which events trigger a callback).

  • Events are represented as a dot-separated string in the format "object.event", where an asterisk (*) can be used to represent "all objects" or "all events".
  • Currently, *.* is the only supported value for the events attribute. This indicates a subscription to all objects in the scope, and all events for those objects.

NOTE: When a row is deleted on a sheet, even if you are using a subscope to monitor columns only and the cell in that column for that row is empty, you will receive a "row.deleted" event.

Example

Consider a Webhook object with the following attribute values:

  • scopeObjectId = 12345678901234
  • scope = "sheet"
  • events = "*.*"

This webhook monitors the specified sheet (id: 12345678901234) for all types of events. For example, if a row is added to the sheet, cell values are updated in the sheet, a column is deleted from the sheet, etc., a callback is sent to the webhook's callbackUrl.

Creating a Webhook

An API client can create a webhook by using the Create Webhook operation. A newly created webhook is disabled by default. After successfully creating a webhook, the API client must subsequently initiate and complete the verification process before the webhook is enabled. The following diagram illustrates the process of creating and enabling a webhook.

Workflow to create a webhook

Step-by-Step:

The process occurs synchronously:

  1. API client submits a Create Webhook request.

  2. Smartsheet creates the webhook, but does not initially enable the webhook (enabled: false and status: "NEW_NOT_VERIFIED").

  3. To initiate the verification process, the API client submits an Update Webhook request to specify enabled: true. (NOTE: The API client does not receive a response to this request until the verification process (steps 4 and 5) has completed.)

  4. When Smartsheet receives the request to enable the webhook, it sends a verification request to the subscriber (that is, to the callbackUrl that the API client specified in the Create Webhook request). The request specifies a unique random value in the Smartsheet-Hook-Challenge header and contains only challenge and webhookId in the request body. The challenge value is a repeat of the header value for API clients that don't interpret header values.

  5. The subscriber responds to the verification request by echoing back the same unique random value in the Smartsheet-Hook-Response header of the response. For API clients that don't interpret header values, you can also send a JSON body with a smartsheetHookResponse attribute and the same value that would have been in the header.

  6. Once the subscriber has successfully acknowledged the verification request, Smartsheet enables the webhook and responds to the API client's Update Webhook request to confirm that the webhook has been enabled (enabled: true and status: "ENABLED").

Webhook Verification

When an API client attempts to enable a webhook, Smartsheet sends a verification request to the webhook's callbackUrl to verify that the subscriber expects and is able to successfully receive callbacks. The request specifies a unique random value in the Smartsheet-Hook-Challenge header and contains a JSON object in the request body with only challenge and webhookId attributes in the request body. The challenge attribute is a repeat of the header value for API clients that don't interpret header values.

  • To verify the webhook, the subscriber's response to the verification request must return a 200 HTTP status code and must echo back the same unique random value in the Smartsheet-Hook-Response header of the response or with a JSON smartsheetHookResponse attribute. Once Smartsheet receives this verification response, the webhook is enabled (enabled: true and status: "ENABLED").

  • If the subscriber does not successfully respond to the verification request, the webhook remains disabled (enabled: false and status: "DISABLED_VERIFICATION_FAILED").

When a webhook is initially created through the API, the response contains the webhook's Id. If you wish to guarantee that your server only receives callbacks for webhooks that you created, you can check the verification request's webhookId attribute against your list of created webhook Ids. If the Id is not in the list, return a response with a non-200 status code (for example, 403 Forbidden), and the webhook that initiated the verification request is disabled as described above.

NOTE: IMPORTANT: ONGOING VERIFICATION. Once a webhook has been enabled, Smartsheet sends a verification request to the webhook's callbackUrl once every 100 callbacks. The subscriber should respond to the verification request as described above -- failure to do so results in the webhook being disabled (enabled: false and status: "DISABLED_VERIFICATION_FAILED").

Webhook Status

A Webhook object's enabled attribute indicates whether a webhook is enabled or disabled, while the status attribute describes the reason that the webhook is enabled or disabled.

The following table lists all possible combinations of enabled and status attribute values.

Webhook.enabled Webhook.status Meaning Notes
false DISABLED_ADMINISTRATIVE Webhook has been disabled by Smartsheet support. A webhook in this state can only be re-enabled by Smartsheet (contact api@smartsheet.com). Attempting to re-enable a webhook in this state results in error code 1154.
false DISABLED_APP_REVOKED Webhook has been disabled because the third-party app associated with the webhook has had its access revoked. This is a terminal state (that is, webhook in this state cannot be re-enabled, and attempts to do so result in error code 1153). If the third-party app's access is subsequently restored, it must create new webhooks.
false DISABLED_BY_OWNER Webhook has been disabled by the owner. Owner can re-enable the webhook by using the Update Webhook operation to set enabled to true. Once the subscriber successfully acknowledges the verification request, the webhook is enabled.
false DISABLED_CALLBACK_FAILED Webhook has been disabled because callback was not successfully delivered to the callback URL. Owner can re-enable the webhook by using the Update Webhook operation to set enabled to true. Once the subscriber successfully acknowledges the verification request, the webhook is enabled.
false DISABLED_SCOPE_INACCESSIBLE Webhook has been disabled because its owner lost access to the corresponding data in Smartsheet (either because the object was deleted or sharing permissions were revoked). Webhook is automatically re-enabled if access to data is restored (for example, if deleted object is restored or sharing permissions are restored).
false DISABLED_VERIFICATION_FAILED Webhook verification has failed. Owner can re-enable the webhook by using the Update Webhook operation to set enabled to true. Once the subscriber successfully acknowledges the verification request, the webhook is enabled.
true ENABLED Webhook is active.
false NEW_NOT_VERIFIED Webhook has been created but is not enabled because it has not yet been verified. Owner can re-enable the webhook by using the Update Webhook operation to set enabled to true. Once the subscriber successfully acknowledges the verification request, the webhook is enabled.

NOTES:

  • When a Webhook object's enabled attribute is set to true, Smartsheet is monitoring for the specified events and sends a callback to the callbackUrl when the events occur.
  • When a Webhook object's enabled attribute is set to false, Smartsheet is not monitoring for the specified events and does not send a callback to the callbackUrl when the events occur. Depending on the value of the status attribute, it may be possible to (re-)enable the webhook. (See below for details).

Webhook Callbacks

A callback is the notification that Smartsheet sends to a webhook's callbackUrl. There are two types of callbacks:

  • Event callback: Alerts the subscriber when the specified events have occurred in Smartsheet. This is the most common type of callback.

  • Status Change callback: Alerts the subscriber that a webhook has been automatically disabled due to loss of access or automatically re-enabled due to restoration of access.

A Subscriber has the ability to verify the integrity of a callback payload (that is, ensure that the message was not tampered with en route) and authenticate the identity of its sender (that is, ensure that the callback did indeed originate from Smartsheet). For more information, see Authenticating Callbacks.

NOTES:

  • Smartsheet webhooks do not support callbacks to servers using self-signed certificates. The callback server must be using a signed certificate from a certificate authority.
  • The callbackURL cannot be a private IP address.
  • The callbackURL must use one of the following ports: 443 (default for HTTPS), 8000, 8008, 8080, or 8443.

Event Callbacks

Smartsheet sends an event callback to notify the subscriber when the specified events occur in Smartsheet.

WARNING: Any events that occur while a webhook is disabled are not communicated via callback if/when webhook is enabled.

Callback Acknowledgement

The subscriber must respond to an event callback with a 200 HTTP status code to acknowledge that the callback was received.

Retry Logic

If the subscriber fails to respond with a 200 status, depending on the response, Smartsheet may retry delivery up to 14 times. (The first 7 retry attempts occur with a frequency that's determined using exponential backoff; thereafter, retry attempts occur once every three hours until all retries have been exhausted.)

Subscriber response Is retried?
HTTP 201 through 299 No
HTTP 309 through 399 No
HTTP 400 through 499 (except 410) Yes
HTTP 410 No
HTTP 500 through 599 Yes
Any other HTTP status No
Request timeout Yes

If the response is not retriable or retry attempts have been exhausted, the webhook is disabled (enabled: false and status: "DISABLED_CALLBACK_FAILED"). A webhook in this state can be re-enabled if the subscriber completes the verification process. (See Webhook Status for details.)

Status Change Callbacks

If a webhook's owner loses access to the object that a webhook is monitoring (either because the object was deleted or the webhook owner's access to the object was revoked in Smartsheet), Smartsheet automatically disables the webhook and sends a status change callback to alert the subscriber of this change in status. The newWebhookStatus attribute indicates that the webhook is now disabled (because the scope is inaccessible).

If the webhook owner's access to the object is subsequently restored, Smartsheet automatically re-enables the webhook and sends a status change callback to alert the subscriber of this change in status. The newWebhookStatus attribute indicates that the webhook is now enabled.

Authenticating Callbacks (optional)

A subscriber has the ability to verify the integrity of a callback payload (that is, ensure that the message was not tampered with en route) and authenticate the identity of its sender (that is, ensure that the callback did indeed originate from Smartsheet).

When you create a new webhook, it is assigned a randomly generated sharedSecret value. The shared secret is used by Smartsheet to sign callback payloads, and should not be shared with any third parties.

To authenticate a callback request:

  1. Calculate the HMAC of the webhook's sharedSecret and the request body. This must be done using the SHA-256 cryptographic hash algorithm.

  2. Format the calculated value as a string in base 16.

  3. Compare your result with the value of the Smartsheet-Hmac-SHA256 header of the request.

If the values match, you can be sure the request originated from Smartsheet and that the data has not been tampered with.

NOTE: For maximum security, you may also choose to reset your webhook's shared secret at periodic intervals.

Preventing Infinite Loops

It's possible that an application which subscribes to callbacks (using webhooks) might react to those callbacks by using the API to make additional changes in Smartsheet. But, consider the following scenario:

  1. App1 creates a webhook to monitor Sheet-A for changes, specifying an App-1 URL as the callbackUrl for the webhook.

  2. App-1 updates Sheet-A using the API.

  3. Smartsheet detects that Sheet-A has changed and sends a callback to App-1 (as the webhook specified).

  4. App-1 processes the callback, and in the course of doing so, updates Sheet-A again using the API.

  5. Return to Step 3.

To protect against infinite loops like the one outlined above, Smartsheet introduced support for a new header that an API client can include with any API request that adds, updates, or deletes data in Smartsheet. Here's how it works:

  • An API client includes the Smartsheet-Change-Agent header in any API request that adds, updates, or deletes data in Smartsheet.
    • The header value should be set to a string that the API client recognizes as identifying itself. * If any event callbacks fire as a result of an API request that contains the Smartsheet-Change-Agent header, the value of the Smartsheet-Change-Agent header is included in the callback in the value of the changeAgent attribute.
  • If a webhook subscriber receives a callback that contains the changeAgent attribute, it should evaluate the attribute value:
    • If the attribute value's comma-delimited list contains the subscriber API client's change agent value, this indicates that the change which triggered the callback was caused by the subscriber itself, and the subscriber can choose to ignore the callback, thus preventing an infinite loop.
    • Else, the change which triggered the callback was not caused by the subscriber itself, and if the subscriber is going to subsequently react to the callback by making a change in Smartsheet (via API request), the API client should append a comma and its own identifier to the original attribute value, and pass that value through using the Smartsheet-Change-Agent header of the API request. Doing so protects against cross-system infinite loops. NOTE: The Smartsheet-Change-Agent header value has a maximum length of 512 characters. If its length exceeds 512 characters, the excess is trimmed from the front of the string, so that the most recent agent information remains.

Webhook Errors

The following table specifies the recommended action for each Smartsheet-specific error code. Use this information to implement error handling logic according to the following guidelines:

  • If the error code indicates a permanent error condition, do not retry the request.
  • If the error code indicates a problem that can be fixed, do not retry the request until the problem has been fixed.
  • If the error code indicates a problem that could be overcome by retrying the request after a period of time, retry the request using exponential backoff.
HTTP status code Smartsheet errorCode Smartsheet message Recommended Action
400 1151 Scope '{0}', object id {1} was not found. Do not retry without fixing the problem.
400 1152 Only URLs with a protocol of 'https' are supported for attribute '{0}'. Do not retry without fixing the problem.
403 1153 This webhook cannot be enabled because access was revoked for the application that created it. Do not retry.
403 1154 Please contact api@smartsheet.com in order to enable this webhook. Do not retry.
➔ Next to Objects