> ## Documentation Index
> Fetch the complete documentation index at: https://docs.hopx.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Async Webhooks

> Execute very long-running code with webhook callbacks in HopX sandboxes. Run code asynchronously and receive results via webhook callbacks when execution completes. Perfect for long-running tasks, batch processing, and async workflows. Learn how to configure webhooks and handle callback responses. Includes examples and webhook setup.

Async execution with webhooks is designed for very long-running tasks (>30 minutes) that would otherwise timeout. The execution happens asynchronously, and results are delivered to your webhook endpoint when complete.

## Overview

Async webhook execution is perfect for:

* ML model training (30+ minutes)
* Large dataset processing
* Video encoding/processing
* Long-running simulations
* Any task that exceeds normal timeout limits

<Note>
  Async webhook execution returns immediately with an `execution_id`. The agent executes code in the background and POSTs results to your callback URL when complete.
</Note>

## Basic Usage

Start async execution with a webhook callback:

<Tabs>
  <Tab title="Python">
    ```python theme={null}
    from hopx_ai import Sandbox

    sandbox = Sandbox.create(template="code-interpreter")

    # Start async execution
    response = sandbox.run_code_async(
        code='''
    import time
    print("Starting long task...")
    time.sleep(600)  # 10 minutes
    print("Task completed!")
    ''',
        callback_url='https://your-app.com/webhooks/HopX-complete',
        language='python',
        timeout=1800  # 30 minutes max
    )

    print(f"Execution ID: {response['execution_id']}")
    print(f"Status: {response['status']}")
    print(f"Callback URL: {response['callback_url']}")

    sandbox.kill()
    ```
  </Tab>

  <Tab title="JavaScript">
    ```javascript theme={null}
    import { Sandbox } from '@hopx-ai/sdk';

    const sandbox = await Sandbox.create({ template: 'code-interpreter' });

    // Start async execution
    const response = await sandbox.runCodeAsync(
      `
    import time
    print("Starting long task...")
    time.sleep(600)  # 10 minutes
    print("Task completed!")
    `,
      {
        callbackUrl: 'https://your-app.com/webhooks/HopX-complete',
        language: 'python',
        timeout: 1800  // 30 minutes max
      }
    );

    console.log(`Execution ID: ${response.executionId}`);
    console.log(`Status: ${response.status}`);
    console.log(`Callback URL: ${response.callbackUrl}`);

    await sandbox.kill();
    ```
  </Tab>
</Tabs>

## Webhook Callback

Your webhook endpoint will receive a POST request when execution completes:

<Tabs>
  <Tab title="Python (Flask)">
    ```python theme={null}
    from flask import Flask, request
    import hmac
    import hashlib

    app = Flask(__name__)

    @app.route('/webhooks/HopX-complete', methods=['POST'])
    def hopx_webhook():
        # Verify signature (if secret provided)
        signature = request.headers.get('X-HopX-Signature')
        timestamp = request.headers.get('X-HopX-Timestamp')
        
        # Verify signature if secret was provided
        # secret = 'your-webhook-secret'
        # expected_sig = hmac.new(
        #     secret.encode(),
        #     f"{timestamp}{request.data}".encode(),
        #     hashlib.sha256
        # ).hexdigest()
        # if signature != f"sha256={expected_sig}":
        #     return "Invalid signature", 401
        
        # Process webhook payload
        data = request.json
        execution_id = data['execution_id']
        status = data['status']
        stdout = data.get('stdout', '')
        stderr = data.get('stderr', '')
        exit_code = data.get('exit_code', 0)
        execution_time = data.get('execution_time', 0)
        
        print(f"Execution {execution_id} completed with status: {status}")
        print(f"Output: {stdout}")
        
        if status == 'completed':
            # Handle successful completion
            process_results(execution_id, stdout)
        else:
            # Handle failure
            handle_error(execution_id, stderr)
        
        return "OK", 200
    ```
  </Tab>

  <Tab title="JavaScript (Express)">
    ```javascript theme={null}
    const express = require('express');
    const crypto = require('crypto');

    const app = express();
    app.use(express.json());

    app.post('/webhooks/HopX-complete', (req, res) => {
      // Verify signature (if secret provided)
      const signature = req.headers['x-HopX-signature'];
      const timestamp = req.headers['x-HopX-timestamp'];
      
      // Verify signature if secret was provided
      // const secret = 'your-webhook-secret';
      // const expectedSig = crypto
      //   .createHmac('sha256', secret)
      //   .update(`${timestamp}${JSON.stringify(req.body)}`)
      //   .digest('hex');
      // if (signature !== `sha256=${expectedSig}`) {
      //   return res.status(401).send('Invalid signature');
      // }
      
      // Process webhook payload
      const { executionId, status, stdout, stderr, exitCode, executionTime } = req.body;
      
      console.log(`Execution ${executionId} completed with status: ${status}`);
      console.log(`Output: ${stdout}`);
      
      if (status === 'completed') {
        // Handle successful completion
        processResults(executionId, stdout);
      } else {
        // Handle failure
        handleError(executionId, stderr);
      }
      
      res.status(200).send('OK');
    });
    ```
  </Tab>
</Tabs>

## Signature Verification

Verify webhook signatures for security:

<Tabs>
  <Tab title="Python">
    ```python theme={null}
    from hopx_ai import Sandbox
    import hmac
    import hashlib

    sandbox = Sandbox.create(template="code-interpreter")

    # Start async execution with signature secret
    response = sandbox.run_code_async(
        code='import time; time.sleep(600); print("Done")',
        callback_url='https://your-app.com/webhooks/HopX-complete',
        callback_signature_secret='your-webhook-secret-123',
        language='python'
    )

    print(f"Execution ID: {response['execution_id']}")

    # In your webhook handler:
    # signature = request.headers.get('X-HopX-Signature')
    # timestamp = request.headers.get('X-HopX-Timestamp')
    # body = request.data
    # 
    # expected = hmac.new(
    #     'your-webhook-secret-123'.encode(),
    #     f"{timestamp}{body}".encode(),
    #     hashlib.sha256
    # ).hexdigest()
    # 
    # if signature == f"sha256={expected}":
    #     # Valid signature
    #     pass
    ```
  </Tab>

  <Tab title="JavaScript">
    ```javascript theme={null}
    import { Sandbox } from '@hopx-ai/sdk';
    import crypto from 'crypto';

    const sandbox = await Sandbox.create({ template: 'code-interpreter' });

    // Start async execution with signature secret
    const response = await sandbox.runCodeAsync(
      'import time; time.sleep(600); print("Done")',
      {
        callbackUrl: 'https://your-app.com/webhooks/HopX-complete',
        callbackSignatureSecret: 'your-webhook-secret-123',
        language: 'python'
      }
    );

    console.log(`Execution ID: ${response.executionId}`);

    // In your webhook handler:
    // const signature = req.headers['x-HopX-signature'];
    // const timestamp = req.headers['x-HopX-timestamp'];
    // const body = JSON.stringify(req.body);
    // 
    // const expected = crypto
    //   .createHmac('sha256', 'your-webhook-secret-123')
    //   .update(`${timestamp}${body}`)
    //   .digest('hex');
    // 
    // if (signature === `sha256=${expected}`) {
    //   // Valid signature
    // }
    ```
  </Tab>
</Tabs>

## Custom Headers

Include custom headers in webhook callbacks:

<Tabs>
  <Tab title="Python">
    ```python theme={null}
    from hopx_ai import Sandbox

    sandbox = Sandbox.create(template="code-interpreter")

    # Start async execution with custom headers
    response = sandbox.run_code_async(
        code='import time; time.sleep(600); print("Done")',
        callback_url='https://your-app.com/webhooks/HopX-complete',
        callback_headers={
            'Authorization': 'Bearer your-api-token',
            'X-Custom-Header': 'custom-value'
        },
        language='python'
    )

    print(f"Execution ID: {response['execution_id']}")

    # Your webhook will receive:
    # POST https://your-app.com/webhooks/HopX-complete
    # Authorization: Bearer your-api-token
    # X-Custom-Header: custom-value
    # X-HopX-Signature: sha256=...
    # X-HopX-Timestamp: 1698765432
    ```
  </Tab>

  <Tab title="JavaScript">
    ```javascript theme={null}
    import { Sandbox } from '@hopx-ai/sdk';

    const sandbox = await Sandbox.create({ template: 'code-interpreter' });

    // Start async execution with custom headers
    const response = await sandbox.runCodeAsync(
      'import time; time.sleep(600); print("Done")',
      {
        callbackUrl: 'https://your-app.com/webhooks/HopX-complete',
        callbackHeaders: {
          'Authorization': 'Bearer your-api-token',
          'X-Custom-Header': 'custom-value'
        },
        language: 'python'
      }
    );

    console.log(`Execution ID: ${response.executionId}`);

    // Your webhook will receive:
    // POST https://your-app.com/webhooks/HopX-complete
    // Authorization: Bearer your-api-token
    // X-Custom-Header: custom-value
    // X-HopX-Signature: sha256=...
    // X-HopX-Timestamp: 1698765432
    ```
  </Tab>
</Tabs>

## Complete Example

Here's a complete example with ML model training:

<Tabs>
  <Tab title="Python">
    ```python theme={null}
    from hopx_ai import Sandbox

    sandbox = Sandbox.create(template="code-interpreter")

    # Start ML training with async webhook
    response = sandbox.run_code_async(
        code='''
    import time
    import random

    print("Starting ML model training...")
    print("Epoch 1/10...")
    time.sleep(60)

    for epoch in range(2, 11):
        loss = random.uniform(0.1, 0.5)
        accuracy = random.uniform(0.8, 0.95)
        print(f"Epoch {epoch}/10 - Loss: {loss:.4f}, Accuracy: {accuracy:.4f}")
        time.sleep(60)

    print("Training completed!")
    print("Model saved to /workspace/model.pkl")
    ''',
        callback_url='https://your-app.com/webhooks/ml-training-complete',
        callback_headers={
            'Authorization': 'Bearer ml-api-token',
            'X-Training-ID': 'training-123'
        },
        callback_signature_secret='ml-webhook-secret',
        language='python',
        timeout=3600,  # 1 hour
        env={
            'WANDB_API_KEY': 'your-wandb-key',
            'HF_TOKEN': 'your-huggingface-token'
        }
    )

    print(f"✅ Training started")
    print(f"Execution ID: {response['execution_id']}")
    print(f"Status: {response['status']}")
    print(f"Results will be posted to: {response['callback_url']}")

    # Your webhook will receive results when training completes
    sandbox.kill()
    ```
  </Tab>

  <Tab title="JavaScript">
    ```javascript theme={null}
    import { Sandbox } from '@hopx-ai/sdk';

    const sandbox = await Sandbox.create({ template: 'code-interpreter' });

    // Start ML training with async webhook
    const response = await sandbox.runCodeAsync(
      `
    import time
    import random

    print("Starting ML model training...")
    print("Epoch 1/10...")
    time.sleep(60)

    for epoch in range(2, 11):
        loss = random.uniform(0.1, 0.5)
        accuracy = random.uniform(0.8, 0.95)
        print(f"Epoch {epoch}/10 - Loss: {loss:.4f}, Accuracy: {accuracy:.4f}")
        time.sleep(60)

    print("Training completed!")
    print("Model saved to /workspace/model.pkl")
    `,
      {
        callbackUrl: 'https://your-app.com/webhooks/ml-training-complete',
        callbackHeaders: {
          'Authorization': 'Bearer ml-api-token',
          'X-Training-ID': 'training-123'
        },
        callbackSignatureSecret: 'ml-webhook-secret',
        language: 'python',
        timeout: 3600,  // 1 hour
        env: {
          WANDB_API_KEY: 'your-wandb-key',
          HF_TOKEN: 'your-huggingface-token'
        }
      }
    );

    console.log('✅ Training started');
    console.log(`Execution ID: ${response.executionId}`);
    console.log(`Status: ${response.status}`);
    console.log(`Results will be posted to: ${response.callbackUrl}`);

    // Your webhook will receive results when training completes
    await sandbox.kill();
    ```
  </Tab>
</Tabs>

## Webhook Payload

The webhook payload structure:

```json theme={null}
{
  "execution_id": "abc123",
  "status": "completed",
  "stdout": "Task output...",
  "stderr": "",
  "exit_code": 0,
  "execution_time": 600.123
}
```

**Headers:**

* `X-HopX-Signature`: HMAC-SHA256 signature (if secret provided)
* `X-HopX-Timestamp`: Unix timestamp
* Custom headers from `callback_headers`

## Best Practices

<Steps>
  <Step title="1. Always Verify Signatures">
    Use `callback_signature_secret` and verify signatures in your webhook handler to ensure requests are authentic.
  </Step>

  <Step title="2. Handle Timeouts">
    Set appropriate timeouts based on expected execution time. Default is 30 minutes, max is typically 1 hour.
  </Step>

  <Step title="3. Use Custom Headers">
    Include authentication tokens or identifiers in `callback_headers` for secure webhook processing.
  </Step>

  <Step title="4. Handle Both Success and Failure">
    Check the `status` field in webhook payloads to handle both successful completions and failures.
  </Step>

  <Step title="5. Store Execution IDs">
    Store `execution_id` when starting async execution to correlate webhook callbacks with your records.
  </Step>
</Steps>

## Related

* **[Synchronous Execution](/core-concepts/code-execution/synchronous)** - Basic code execution
* **[Background Execution](/core-concepts/code-execution/background)** - Long-running tasks
* **[Streaming Execution](/core-concepts/code-execution/streaming)** - Real-time output streaming
* **SDK**: [sandbox.run\_code\_async()](/sdk/python/sandbox#run_code_async) - Python SDK method
* **API**: [POST /execute/async](/api/vm-agent/execute-async) - VM Agent API endpoint
* [CLI Code Execution](/cli/commands/run) - Execute code from CLI

## Next Steps

* Learn about [Background Execution](/core-concepts/code-execution/background) for shorter tasks
* Explore [Process Management](/core-concepts/code-execution/process-management) for monitoring
* Review [Synchronous Execution](/core-concepts/code-execution/synchronous) for immediate results
