Introduction

Using webhooks (also called a web callback or HTTP push API) is a streamlined, effortless way to build apps that respond to item updates on a Miro board in real time. You can listen and receive notifications when an item is updated on a Miro board, so your app automatically performs specific actions, and your integration stays in sync with Miro data. For example, you might want to update a third-party tool when the status of an app card item is updated on a Miro board.

Using a webhook is a performant alternative to having your app continuously poll Miro (via the REST APIs) to determine whether changes have occurred. With webhooks, getting real-time updates for events on Miro board items and implementing two-way sync for integrations is easy—all you need is a Miro app and a secure place for us to send your events.

📘

Note

  • Currently, all board items are supported barring tags and connectors.
  • Changes in item position do not trigger an event at the moment.
  • Subscriptions are created per user, per board.

Use Cases

Some common webhooks use cases include the following:

  • Receiving notifications about changes to items on a Miro board
  • Two-way sync for integration between the Miro board and third-party tools

Webhooks event workflow

  1. Subscribe to a webhook using the create webhook subscription API. Your endpoint must be an HTTPS URL with a valid SSL certificate that can correctly process event notifications. Payloads contain a JSON object with the data for the webhook event. For more information, see the create webhook subscription API.

  2. You will receive an HTTP POST request with a challenge in the body on the callback URL that you provide.

{
  "challenge": "c273e4a3-01cf-4505-9bc4-27a37dd83c1d"
}
  1. You must respond to this request from your backend, and ensure that:

    • Your response contains the same challenge.
    • Your response includes the content-type: application/json header.
  2. After the challenge verification is successful, you will receive the subscription data as a response to the webhook subscription creation request.

  3. Miro sends an HTTP POST request to the URL specified every time an event occurs. The HTTP POST request's parameters contain the JSON data relevant to the event that triggered the request. Miro verifies SSL certificates when delivering payloads to HTTPS webhook addresses. Make sure your server is correctly configured to support HTTPS with a valid SSL certificate.

  4. Before you process a webhook, you must verify that the webhook was sent from Miro. You can verify the webhook by calculating a digital signature.

    Each webhook request includes a base64-encoded X-Miro-Hmac-SHA256 header, which is generated using the app's client secret along with the data sent in the request.
    The initial challenge request doesn't contain this header. It's included in all subsequent ones.

    a. Compute the HMAC digest according to the following algorithm.
    b. Compare the computed value to the value in the X-Miro-Hmac-SHA256 header. If the HMAC digest and header value match, then the webhook was sent from Miro.

    # The following example uses Python and the Flask framework to verify a webhook request:
    from flask import Flask, request, abort
    import hmac
    import hashlib
    import base64
    app = Flask(__name__)
    # The Miro app's client secret, viewable from the App settings page. In a production environment, set the client secret as an environment variable to prevent exposing it in code.
    CLIENT_SECRET = 'my_client_secret'
    def verify_webhook(data, hmac_header):
        digest = hmac.new(CLIENT_SECRET.encode('utf-8'), data, digestmod=hashlib.sha256).digest()
        computed_hmac = base64.b64encode(digest)
    
        return hmac.compare_digest(computed_hmac, hmac_header.encode('utf-8'))
    @app.route('/webhook', methods=['POST'])
    def handle_webhook():
        data = request.get_data()
        verified = verify_webhook(data, request.headers.get('X-Miro-Hmac-SHA256'))
    
        if not verified:
            abort(401)
    
        # Process webhook payload
        # ...
    
        return ('', 200)
    
  5. Respond to the webhook. Your webhook acknowledges that it received data by sending a 200 OK response. Any response outside of the 200 range, including 3XX HTTP redirection codes, indicates that you didn't receive the webhook.

Webhooks event samples

{
 "eventType": "board_subscription",
 "event": {
  "boardId": "uXjVP-VcSu8=",
  "item": {
   "id": "3458764540089432657",
   "type": "sticky_note",
   "createdAt": "2022-11-30T19:49:11Z",
   "createdBy": {
    "id": "3074457360946109441",
    "type": "user"
   },
   "data": {
    "content": "<p>sample data</p>",
    "shape": "square"
   },
   "geometry": {
    "width": 107.46000000000001,
    "height": 123.12
   },
   "modifiedAt": "2022-12-02T01:24:35Z",
   "modifiedBy": {
    "id": "3074457360946109441",
    "type": "user"
   },
   "position": {
    "x": -346.980834960938,
    "y": -139.762786865234,
    "origin": "center",
    "relativeTo": "canvas_center"
   },
   "style": {
    "fillColor": "light_yellow",
    "textAlign": "CENTER",
    "textAlignVertical": "middle"
   }
  },
  "type": "update"
 },
 "eventTime": "2022-12-02T01:24:35Z",
 "appId": 3458764540087247854,
 "users": [
  3074457360946109441
 ]
}
{
   "eventType":"subscription_disabled",
   "event":{
    "id": "9fed041a-2bd2-4fb8-bef7-ec2316d6e546",
    "data": {
        "boardId": "uXjVOfjiK8A="
    },
    "callbackUrl": "https://ttymonkey.com/callback",
    "createdAt": "2022-10-20T18:52:03Z",
    "modifiedAt": "2022-10-20T18:52:03Z",
    "type": "board_subscription"
   },
   "eventTime":1666291951407,
   "appId":3458764513821447945,
   "users":[
      3458764513821447932
   ]
}

Resources