Hello Miro Support Team and Community,
I am trying to set up a webhook subscription for a Miro board (boardId: [A specific Board ID]
) to receive real-time updates on board events. I am using Postman to send the subscription request to the Miro API, and a Cloudflare Worker as my callbackUrl
to handle the webhook challenge.
I have meticulously followed the official Miro API documentation for "Create board subscription" and "Getting started with webhooks."
Here's a summary of my setup and the issue:
1. Postman Request (to Miro API):
- URL:
https://api.miro.com/v2-experimental/webhooks/board_subscriptions
(Confirmed correct based on Miro's latest documentation) - Method:
POST
- Authorization: Bearer Token (Confirmed valid and provided in request headers)
- Body (raw, JSON): JSON
{
"boardId": ""Your Board ID]", // Placeholder for your specific board ID
"callbackUrl": ""Your Cloudflare Worker URL]", // Placeholder for your specific callback URL
"status": "enabled"
} - Result in Postman:
400 Bad Request
with the message: "An error occurred while exchanging the callback challenge. Verify that the callback URL is valid."
2. Cloudflare Worker (My callbackUrl
webhook listener):
- Purpose: To receive and respond to Miro's webhook challenge.
- Code: The Worker is deployed with robust code designed to handle both standard
POST
challenges (expectingchallenge
in body) and unexpectedGET
challenges (expectingchallenge
in query parameters, or providing a fallback dummy string if nochallenge
is found).
3. Observed Behavior (Miro's challenge to my Cloudflare Worker): Upon sending the Postman request, Miro attempts to validate my callbackUrl
.
- Miro Documentation States: The webhook challenge should be a
POST
request with thechallenge
string in the request body. - Actual Cloudflare Worker Logs Show (Consistently):
Incoming request received: GET Your Cloudflare Worker URL]
(Miro consistently sends a GET request, not a POST)Request parameters (GET): {}
(The GET request has no query parameters, so no 'challenge' is found here)Unexpected GET with no challenge param. Sending a dummy challenge string.
Miro webhook challenge detected. Responding with challenge value: miro_challenge_response
- The Worker sends a
200 OK
response withContent-Type: text/plain
and the bodymiro_challenge_response
.
My Core Questions:
- Why is Miro sending a
GET
request for the webhook challenge to mycallbackUrl
when the documentation explicitly states it should be aPOST
request? - If the challenge is sent via
GET
, where exactly is thechallenge
string located? My logs confirm it is not in the URL's query parameters and, by definition of a GET, not in the request body. Is it expected to be in headers, or is there another mechanism forGET
challenges? - Given that my Cloudflare Worker is now successfully responding to the observed
GET
request with a200 OK
and a placeholder challenge string, why am I still receiving a400 Bad Request
from Miro? Is there a strict requirement for the response to come from aPOST
even if the incoming challenge was aGET
? Or is the placeholder challenge string not sufficient, and a specific, unknown value is required?
I've exhausted all troubleshooting steps on my end, relying on the documentation and adapting to observed behavior, but this inconsistency is preventing me from creating webhook subscriptions. Any assistance or clarification from Miro Support or anyone in the community who has faced this specific challenge would be greatly appreciated.
Thank you.