# 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](/api/smartsheet/guides/webhooks/webhook-verification) -- pings that make sure you're still listening to your subscriptions. This tutorial shows how to create such a webhook event-handling endpoint. > **Note:** This tutorial demonstrates handling **sheet webhook events**, but the same principles also apply to handling **plan-level webhook events**. > **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: ```python # 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**](/api/smartsheet/guides/webhooks/webhook-verification) 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, ```python from flask import Flask, request, jsonify import json app = Flask(__name__) @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(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 if __name__ == '__main__': app.run(port=8000, debug=True) ``` 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](/api/smartsheet/guides/webhooks/launch-a-webhook)), 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](/api/smartsheet/openapi/webhooks/callback) 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. ```python from flask import Flask, request, jsonify import json app = Flask(__name__) @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(f"Received verification challenge: {challenge_value}") 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 def process_sheet_event(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) 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](/api/smartsheet/guides/webhooks/launch-a-webhook)! ## What's next? [Launch a webhook](/api/smartsheet/guides/webhooks/launch-a-webhook) [Webhook callbacks](/api/smartsheet/guides/webhooks/webhook-callbacks)