Skip to content

Webhooks

Learn how to use webhooks by using the following tutorial.

Webhooks are a mechanism for the Nextmv platform to send real-time notifications to your application when certain events occur. They allow you to receive updates about changes in your data or the status of your operations without having to constantly poll the Nextmv Cloud API.

The following events can currently trigger webhooks:

  • run.status: triggered when the status of a run changes. E.g., from running to succeeded.

To use webhooks, you should follow these steps:

  1. Create a webhook. You must provide an endpoint URL where the Nextmv platform can send the webhook payloads. This URL should be publicly accessible and able to handle incoming HTTP POST requests.
  2. Retrieve the secret for the webhook. The secret is used to recompute the HMAC signature of the request to verify its authenticity. In the header of the request sent when the webhook is triggered, we will include the Nextmv-Signature that you can use to check the authenticity of the request. It has the following format:

    Nextmv-Signature: t=UNIX_TIME_IN_SECONDS,v1=HMAC_SHA256_ENCODED_SIGNATURE
    
  3. Implement a handler in your service to process the incoming webhook requests. This handler should:

    • Verify the HMAC signature of the request using the secret.
    • Reject requests whose t timestamp falls outside a short tolerance window. The signature by itself does not expire, so the timestamp is what prevents a captured request from being replayed.
    • Parse the JSON payload of the request.
    • Take appropriate action based on the event type and data received.

    In order to check the authenticity of the request, you need to recompute the HMAC signature using the secret, the payload (the request body), and the timestamp t from the header.

    Here is a simple Python example that shows how to verify the signature:

    def check_signature(
        payload: bytes,
        t: int,
        signature: str,
        secret: bytes,
        max_age_seconds: int = 300,
        max_skew_seconds: int = 60,
    ) -> bool:
        """
        Recompute the signature of the payload using the secret and the timestamp, and compare it
        with the signature provided in the header. Returns False if the timestamp falls outside the
        accepted window, so that a captured request cannot be replayed indefinitely.
        """
    
        # The signature stays valid for as long as the payload does, so the
        # timestamp is what bounds replay. Check it before anything else.
        now = time.time()
        if not (now - max_age_seconds < t < now + max_skew_seconds):
            return False
    
        mac = hmac.new(secret, digestmod=hashlib.sha256)
        mac.update(str(t).encode())
        mac.update(b".")
        mac.update(payload)
        recomputed_signature = mac.hexdigest()
    
        # Constant-time comparison. A plain `==` short-circuits on the first
        # differing character and leaks how many leading characters matched.
        return hmac.compare_digest(recomputed_signature, signature)
    

The request body of a triggered event look like this, according to the event type:

{
"event_type": "run.status",
"api_version": "2024-03-04",
"data": {
    "run_id": "test-run-id",
    "status": "succeeded",
    "status_v2": "succeeded",
    "created_at": "2024-03-21T13:19:34Z",
    "duration": 1000,
    "input_size": 1000,
    "output_size": 1000,
    "error": null,
    "application_id": "app_id",
    "application_instance_id": "app_instance_id",
    "application_version_id": "app_version_id"
}
}

Webhooks record conversations. A conversation is the request to and the response from the registered endpoint.