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
challengeobject.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_valuechange_status_column_valuechange_subitem_column_valuechange_specific_column_valuechange_namecreate_itemitem_archiveditem_deleteditem_moved_to_any_groupitem_moved_to_specific_groupitem_restoredcreate_subitemchange_subitem_namemove_subitemsubitem_archivedsubitem_deletedcreate_columncreate_updateedit_updatedelete_updatecreate_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
monday.com Webhooks reference: https://developer.monday.com/api-reference/reference/webhooks