Skip to main content
You can register webhooks for each workflow to receive events. Here are the different events you can subscribe to:
This event is sent once when the execution of the workflow starts. It returns a live view URL of the running browser agent embeddable as iframe.
{
  "event": "execution.start",
  "payload": {
    "session_id": "64355337-7561-45ec-82bc-b6266e11208c",
    "workflow_id": "745fdceb-53bf-4562-95dc-10495e303f0c",
    "live_view_url": "https://cyclone.cloudcruise.com/..."
  },
  "metadata": {
    "key1": "value1"
  },
  "timestamp": 1750068153,
  "expires_at": 1750068453
}
This event is sent every time the workflow is executing an action like clicking on a button or filling out an input field.
{
    "payload": {
        "session_id": "60673dc6-92a1-428a-ba51-8c3f324daea8", 
        "workflow_id": "00b82d36-8243-4a95-a0ca-e64096226731",
        "current_step": "Type user name",
        "next_step": "Click Next"
    },
    "metadata": {
        "key1": "value1"
    },
    "timestamp": 1724002463,
    "expires_at": 1724002763,
    "event": "execution.step"
}
This event is sent once when the execution of the workflow has finished successfully. It includes the result data that is returned from the workflow.
{
    "payload": {
        "session_id": "60673dc6-92a1-428a-ba51-8c3f324daea8", 
        "workflow_id": "00b82d36-8243-4a95-a0ca-e64096226731",
        "data": {
            "your": "defined",
            "output": "datamodel",
        },
        "errors": [],
        "status": "execution.success",
        "file_urls": [],
        "input_variables": {
            "USER_NAME": "adrian"
        },
        "encrypted_variables: {
            "USER_NAME"
        }
    },
    "metadata": {
        "key1": "value1"
    },
    "timestamp": 1724002463,
    "expires_at": 1724002763,
    "event": "execution.success"
}
This event is sent once the execution of the workflow fails and contains information about the error.
{
    "payload": {
        "session_id": "60673dc6-92a1-428a-ba51-8c3f324daea8", 
        "workflow_id": "00b82d36-8243-4a95-a0ca-e64096226731",
        "data": {
            "your": "defined",
            "output": "datamodel",
        },
        "errors": [{
            "message": "The login failed. The website showed an error that the credentials are invalid.",
            "error_id": "c4c7d4fe-b00c-49d8-ab67-40808acea59c",
            "error_code": "E001: LOGIN_FAILED",
            "llm_error_category": "AUTHENTICATION_ERROR",
            "full_url": "https://cloudcruise.com/login",
            "action_type": "CLICK",
            "action_display_name": "Click on login"
        }],
        "status": "execution.failed",
        "file_urls": [],
        "input_variables": {
            "$USER_NAME": "adrian"
        },
        "encrypted_variables": [
            "USER_NAME"
        ]
    },
    "timestamp": 1724002463,
    "expires_at": 1724002763,
    "event": "execution.failed"
}
The llm_error_category field contains an AI-generated categorization of the error type. This field may be null if AI analysis was not performed.
This event is sent when one or several workflow runs were cancelled. It respectively either contains session_id of the cancelled run or session_ids that contains all of the cancelled runs.Response for single run cancelled:
{
  "payload": {
    "message": "User interrupted workflow. Session was stopped.",
    "error_code": "INTERRUPTED-E0001",
    "session_id": "60673dc6-92a1-428a-ba51-8c3f324daea8", 
  },
  "timestamp": 1724002463,
  "expires_at": 1724002763,
  "event": "execution.stopped"
}
Response for multiple cancelled at once:
{
  "payload": {
    "message": "Execution was interrupted because another workflow was executed with the same credentials, but failed its login attempt. This is to prevent repeated incorrect login attempts.",
    "error_code": "INTERRUPTED-E0002",
    "session_causing_error": "60673dc6-92a1-428a-ba51-8c3f324daea8",
    "session_ids": ["60673dc6-92a1-428a-ba51-8c3f324daea8", "50673dc6-92a1-428a-ba51-8c3f324daea8"]
  },
  "metadata": {
        "key1": "value1"
  },
  "timestamp": 1724002463,
  "expires_at": 1724002763,
  "event": "execution.stopped"
}
This event is sent when the upload of the screenshot has finished.
{
    "payload": {
        "session_id": "60673dc6-92a1-428a-ba51-8c3f324daea8", 
        "error_screenshot": true,
        "created_at": "2024-08-30T17:35:00.432Z",
        "node_display_name": "Click on login",
        "screenshot_id": "6fdfad4a-3064-48cf-99f0-3d8d271f2c8e",
        "signed_screenshot_url": "https://cloudcruise.com/screenshots/...",
        "signed_screenshot_url_expires": "2024-08-25T17:35:00.432Z",
    },
    "metadata": {
        "key1": "value1"
    },
    "timestamp": 1724002463,
    "expires_at": 1724002763,
    "event": "screenshot.uploaded"
}
This event is sent when the upload of the workflow video has finished.
{
    "payload": {
        "session_id": "60673dc6-92a1-428a-ba51-8c3f324daea8", 
        "signed_screen_recording_url": "https://cloudcruise.com/...",
        "signed_screen_recording_url_expires": "2025-06-01T22:21:00.371579+00:00",
        "timestamp": "2025-05-25T22:21:00.371579+00:00"
    },
    "metadata": {
        "key1": "value1"
    },
    "timestamp": 1724002463,
    "expires_at": 1724002763,
    "event": "video.uploaded"
}
This event is sent when a file has been uploaded during workflow execution.
{
    "payload": {
        "signed_file_url": "https://example.com/storage/v1/object/sign/files/123/456/example.zip?token=dummy-token",
        "file_name": "example_file.zip",
        "timestamp": "2024-03-20T12:00:00.000Z",
        "signed_file_url_expires": "2024-03-27T12:00:00.000Z",
        "metadata": {
            "key1": "value1"
        },
        "session_id": "60673dc6-92a1-428a-ba51-8c3f324daea8",
    },
    "timestamp": 1710936000,
    "expires_at": 1710936300,
    "event": "file.uploaded"
}
This event is sent when user input is required to continue the workflow.
{
    "payload": {
        "current_step": "Enter 2FA Code",
        "missing_properties": ["two_factor_code"],
        "message": "Please enter the 6-digit code sent to your phone",
        "session_id": "session-123",
        "workflow_id": "workflow-456",
        "expected_json_schema_datamodel": {
            "type": "object",
            "properties": {
                "two_factor_code": {
                    "type": "string",
                    "pattern": "^\\d{6}$",
                    "description": "6-digit verification code"
                }
            },
            "required": ["two_factor_code"]
        }
    },
    "timestamp": 1710936000,
    "expires_at": 1710936300,
    "event": "interaction.waiting"
}
This event is sent when the user input was not provided in time or in a wrong format.
{
    "timestamp": 1724002463,
    "expires_at": 1724002763,
    "event": "interaction.failed"
}
This event is sent when the user input was provided and the workflow continues.
{
    "payload": {
        "current_step": "Enter 2FA Code",
        "message": "Please enter the 6-digit code sent to your phone",
        "session_id": "session-123",
        "workflow_id": "workflow-456",
        "provided_input": {
            "two_factor_code": 123456
        },
        "expected_json_schema_datamodel": {
            "type": "object",
            "properties": {
                "two_factor_code": {
                    "type": "string",
                    "pattern": "^\\d{6}$",
                    "description": "6-digit verification code"
                }
            },
            "required": ["two_factor_code"]
        }       
    },
    "timestamp": 1724002463,
    "expires_at": 1724002763,
    "event": "interaction.finished"
}
Simply navigate to our settings page to get started.

Security

We sign webhook events with the secret you get when you register a webhook. We also add an expiry time. You can change the expiry time in the setting of the registered webhook. Here’s how we recommend to verify the message you receive from us:
  • Python
  • Javascript
import time
import hashlib
import hmac
import json

class VerificationError(Exception):
    """Custom exception to handle verification errors."""
    def __init__(self, message="Verification failed", status_code=400):
        super().__init__(message)
        self.status_code = status_code

def verify_hmac(received_data, received_signature, secret_key):
    # Ensure the received data is in bytes, necessary for HMAC
    if isinstance(received_data, str):
        received_data = received_data.encode()

    # Generate the HMAC new object with the secret key and SHA-256
    calculated_signature = hmac.new(secret_key.encode(), received_data, hashlib.sha256).hexdigest()

    # Safely compare the computed HMAC with the received HMAC
    return hmac.compare_digest(calculated_signature, received_signature)

# Extract signature from X-HMAC-Signature header, removing the 'sha256=' prefix
# receivedData = await request.body()
# receivedSignature = request.headers.get('X-HMAC-Signature').split('=')[1]
# secretKey = your_secret_key
def verify_message(received_data, received_signature, secret_key):
    if not received_data:
        raise VerificationError("Received request without body", 400)
    
    try:
        data_json = json.loads(received_data.decode('utf-8'))
    except json.JSONDecodeError as e:
        raise VerificationError("Failed to decode json: " + str(e), 400)
    
    # Check if the expiration is sent
    expires_at = data_json.get("expires_at")
    if not expires_at:
        raise VerificationError("No expiration date sent", 400)

    # Verify HMAC first
    if not verify_hmac(received_data, received_signature, secret_key):
        raise VerificationError("Invalid HMAC signature.", 401)

    # Check if the message is not expired
    if time.time() > expires_at:
        raise VerificationError("Webhook message expired.", 400)

    return data_json

Deliverability

CloudCruise tries resending webhooks 2 times. You can also trigger a manual resend over your CloudCruise dashboard in the run detail view. Alternatively, you can send a POST request to api.cloudcruise.com/webhooks/replay/{your_session_id}.