Last updated

Create an event-handling endpoint

Your event-handling endpoints (callback endpoints) are where you receive notifications for Smartsheet events you've subscribed to via webhooks. In these endpoints, you process incoming events and act on them to fit your business processes. Additionally, your endpoints acknowledge Smartsheet's ongoing verification challenges -- pings that make sure you're still listening to your subscriptions.

This tutorial shows how to create such a webhook event-handling endpoint.

Note: The tutorial demonstrates handling sheet events, but the same principles apply to handling events for other Smartsheet objects supported by webhooks.

Note: The example endpoint is written in Python and hosted on Flask, but you can write your endpoint in whatever language you like and host it on any web framework that supports hosting REST endpoints.

Prerequisites

  • Smartsheet API access token. You can generate an access token (key) in the Smartsheet UI.

  • A web framework that supports hosting REST endpoints.

  • Endpoint host server with a valid SSL certificate.

    Important: Smartsheet webhooks don't support self-signed certificates.

It's time to get started.

Step 1: Set up your endpoint

On your web framework, create an HTTPS endpoint that receives HTTPS POST requests.

Example Flask application:

# app.py
from flask import Flask, request, jsonify

app = Flask(__name__)

@app.route('/my-sheet-webhook', methods=['POST'])
def handle_callback():
    # We'll update this section in the next steps
    return jsonify({"message": "Callback received"}), 200

if __name__ == '__main__':
    # You must use a production server like Gunicorn or Nginx
    # to handle HTTPS. For development, you can use a tool like ngrok
    # to expose your localhost to the internet via an HTTPS URL.
    app.run(port=8000, debug=True)

The code above creates a web application with a single endpoint called /my-sheet-webhook.

For now, it's a simple endpoint that accepts HTTP POST requests, responds with a 200 status code, and returns a JSON-formatted message stating "Callback received".

Note: The example's endpoint-handling function is arbitrarily called handle_callback, but you can name your functions whatever you like.

In this example, the server hosts the endpoint at port 8000.

Important: Here are the valid ports for hosting Smartsheet webhook event-handling endpoints: 443 (default for HTTPS), 8000, 8008, 8080, or 8443.

You've laid the foundation for your endpoint. Next, add logic for acknowledging Smartsheet verification challenges.

Step 2: Handle verification challenges

When you activate a new webhook in Smartsheet, Smartsheet sends a verification challenge to its endpoint to make sure the endpoint is valid. Your endpoint must respond to this challenge correctly to qualify for receiving webhook events.

The challenge request contains a unique, randomly-generated challenge value -- the request contains the value in two locations:

  • Smartsheet-Hook-Challenge header

  • Request body challenge field

To pass the challenge, you must implement your endpoint to include these things in response to verification challenges:

  • HTTP status code of 200

  • The challenge value. You must include the value in either of these locations:

    • Response header named Smartsheet-Hook-Response
    • JSON-formatted response body attribute called smartsheetHookResponse

Update your endpoint code to satisfy the above-mentioned challenge requirements.

For example,

@app.route('/my-sheet-webhook', methods=['POST'])
def handle_callback():
    # Check for the Smartsheet verification challenge
    challenge_value = request.headers.get('Smartsheet-Hook-Challenge')
    if challenge_value:
        print(f"Received verification challenge: {challenge_value}")
        # Respond with the challenge value in the required header
        response = jsonify({"message": "Challenge accepted"})
        response.headers['Smartsheet-Hook-Response'] = challenge_value
        return response, 200

    # If it's not a challenge, proceed to handle the event callback
    return process_sheet_event(request.json)

def process_sheet_event(data):
    # This is a placeholder function for the next step
    print("Received event callback payload.")
    # Log the received data for now
    print(data)
    return jsonify({"message": "Event processed successfully"}), 200

The handle_callback function above first checks for the presence of the Smartsheet-Hook-Challenge header. If the header exists, we extract its value, print a message (optional), and return a 200 OK response that includes the Smartsheet-Hook-Response header set to the challenge value from the request.

The endpoint now handles Smartsheet verification challenges. Handling events is next!

Not: The webhook callback processing function above is arbitrarily named process_sheet_event -- you can name your functions whatever you like.

Step 3: Process callback events

Eventually, when you launch your webhook (covered in the next article), it sends callbacks to your endpoint.

Each callback is a JSON-formatted HTTP POST request that contains information about events that have occurred that match your webhook's event subscription. For example, a sheet webhook callback may include events such as these:

  • A row was added
  • A cell was updated
  • and more ...

The callback's JSON-formatted body has an events array that details the changes.

Note: See the Callback schema and specifically its events attribute, to learn more about callback events.

Update your endpoint to process the events to which you're subscribing.

The updated script below iterates over the sheet events and has a placeholder for event-handling logic.

from flask import Flask, request, jsonify
import json

app = Flask(__name__)

def process_webhook_callback(event_data):
    """
    Processes a Smartsheet sheet event.
    """
    print("Processing sheet event...")
    # Example: Print the sheet ID and the event type
    sheet_id = event_data.get('objectInfo', {}).get('id')
    event_type = event_data.get('eventType')
    
    if sheet_id and event_type:
        print(f"Sheet ID: {sheet_id}, Event Type: {event_type}")
        # Here you would add your custom business logic.
        # For example, updating a database, sending an email, or triggering a workflow.
    
    print("-" * 20)

@app.route('/my-sheet-webhook', methods=['POST'])
def handle_callback():
    # Handle the Smartsheet verification challenge
    challenge_value = request.headers.get('Smartsheet-Hook-Challenge')
    if challenge_value:
        print("Received verification challenge...")
        response = jsonify({"message": "Challenge accepted"})
        response.headers['Smartsheet-Hook-Response'] = challenge_value
        return response, 200

    # Handle the event callback
    try:
        payload = request.json
        print("Received event callback payload.")
        
        # A webhook can contain multiple events in the 'events' array
        events = payload.get('events', [])
        
        for event in events:
            # We are interested in events related to sheets
            if event.get('objectType') == 'sheet':
                process_sheet_event(event)
        
        return jsonify({"message": "Event processed successfully"}), 200
    except json.JSONDecodeError:
        print("Invalid JSON received.")
        return jsonify({"message": "Invalid JSON"}), 400
    except Exception as e:
        print(f"An error occurred: {e}")
        return jsonify({"message": "Internal server error"}), 500

if __name__ == '__main__':
    app.run(port=8000, debug=True)

The above application's handle_callback function handles verification challenges and callback events. It passes each callback event to a function called process_sheet_event.

This example's process_sheet_event function is a placeholder for processing events. You can add event-processing logic here to examine the events and act on them to fit your business needs.

You've created an endpoint that handles Smartsheet verification challenges and is ready for your custom event processing business logic!

Add your custom event-processing logic. Then launch your webhook!

What's next?

Launch a webhook

Webhook callbacks