Webhooks

This guide explains how to work with monday.com Webhooks using monday-client and how to implement URL verification on your server endpoint.

References: monday.com Webhooks API.

Overview

monday-client provides a Webhooks service with helpers to query, create, and delete webhooks for a board. After you create a webhook, monday.com will send a verification POST to your webhook URL with a JSON body containing a challenge token. Your endpoint must echo that token back to verify the URL.

Note

For standard webhooks, a personal API key is sufficient to create them. To receive webhooks that include a JWT Authorization header, or to prevent end-users from disabling integration webhooks, you must create the webhook using an integration app OAuth token.

URL Verification

When you create a webhook, monday.com verifies that you control the target URL by POSTing a JSON body containing a randomly generated token:

{ 'challenge': 'challenge_text_here' }

Your server must respond with HTTP 200 and the same JSON body:

{ 'challenge': 'challenge_text_here' }

Minimal endpoint examples

FastAPI:

from fastapi import FastAPI, Request
from fastapi.responses import JSONResponse

app = FastAPI()

@app.post('/webhooks/monday')
async def monday_webhook(request: Request):
   body = await request.json()
   if isinstance(body, dict) and 'challenge' in body:
      return JSONResponse(content={'challenge': body['challenge']}, status_code=200)
   # Handle normal webhook delivery here
   return JSONResponse(content={'ok': True}, status_code=200)

Flask:

from flask import Flask, request, jsonify

app = Flask(__name__)

@app.post('/webhooks/monday')
def monday_webhook():
   data = request.get_json(silent=True) or {}
   if 'challenge' in data:
      return jsonify({'challenge': data['challenge']}), 200
   # Handle normal webhook delivery here
   return jsonify({'ok': True}), 200

Notes:

  • Return quickly with 200; monday will retry once per minute for 30 minutes if not acknowledged.

  • If the request includes an Authorization header with a JWT (when created with an integration app token), verify it against your app’s signing secret before processing events.

  • Always use HTTPS and return JSON that exactly matches the received challenge object.

  • The webhook URL must be <= 255 characters, and must be reachable from monday.com’s servers.

  • See monday’s URL verification docs for full details: https://developer.monday.com/api-reference/reference/webhooks#url-verification

Creating and managing webhooks with monday-client

Create a webhook

import asyncio
from monday import MondayClient

async def main():
   monday_client = MondayClient(api_key='your_api_key')
   webhook = await monday_client.webhooks.create(
      board_id=1234567890,
      url='https://example.com/webhooks/monday',
      event='create_item',  # see supported events below
      # Optional config for supported events, e.g.:
      # config={'columnId': 'status'}
   )
   print(webhook)

asyncio.run(main())

Tip

If you need to call the webhooks API with a different token (for example, an integration app OAuth token to enable JWT-authenticated deliveries), use the monday.client.MondayClient.use_api_key() context manager (inside an async function):

import asyncio
from monday import MondayClient

async def main():
   monday_client = MondayClient(api_key='your_api_key')
   async with monday_client.use_api_key('integration_oauth_token'):
      await monday_client.webhooks.create(
         board_id=1234567890,
         url='https://example.com/webhooks/monday',
         event='create_item',
      )

asyncio.run(main())

Query webhooks

import asyncio
from monday import MondayClient

async def main():
   monday_client = MondayClient(api_key='your_api_key')
   hooks = await monday_client.webhooks.query(board_id=1234567890)
   print(hooks)

asyncio.run(main())

Delete a webhook

import asyncio
from monday import MondayClient

async def main():
   monday_client = MondayClient(api_key='your_api_key')
   deleted = await monday_client.webhooks.delete(webhook_id='123')
   print(deleted)

asyncio.run(main())

Supported events

The API supports these event types (see monday docs for details):

  • change_column_value

  • change_status_column_value

  • change_subitem_column_value

  • change_specific_column_value

  • change_name

  • create_item

  • item_archived

  • item_deleted

  • item_moved_to_any_group

  • item_moved_to_specific_group

  • item_restored

  • create_subitem

  • change_subitem_name

  • move_subitem

  • subitem_archived

  • subitem_deleted

  • create_column

  • create_update

  • edit_update

  • delete_update

  • create_subitem_update

Event configuration (config)

Some events accept a config JSON argument when creating the webhook. Examples:

  • change_specific_column_value: {'columnId': '<column_id>'}

  • change_status_column_value: {'columnValue': {'index': <index>}, 'columnId': '<column_id>'}

  • item_moved_to_specific_group: {'groupId': '<group_id>'}

Retry policy

monday retries failed webhook deliveries once per minute for 30 minutes.

Testing notes

When running integration/mutation tests in this repository, configure a target URL that responds to monday’s URL verification by echoing the challenge JSON back. Provide this via tests/integrations/config.yml as monday.webhook_target_url or the MONDAY_WEBHOOK_TARGET_URL environment variable.

Further reading