Skip to main content
Execute very long-running code (>5 minutes) and receive results via webhook callback when complete. Perfect for tasks that take hours or need to run independently of your connection.

Endpoint

POST /execute/async

Request

Headers

Authorization: Bearer YOUR_JWT_TOKEN
Content-Type: application/json

Body Parameters

ParameterTypeRequiredDescription
codestringYesCode to execute
callback_urlstringYesURL to POST results to when complete
languagestringNoLanguage (default: python)
timeoutintegerNoMax execution time in seconds (default: 3600)
working_dirstringNoWorking directory
envobjectNoEnvironment variables
callback_headersobjectNoCustom headers for callback request
callback_signature_secretstringNoSecret to sign callback payload (HMAC-SHA256)

Example Request

curl -X POST https://sandbox_abc123xyz.hopx.dev/execute/async \
  -H "Authorization: Bearer YOUR_JWT_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "code": "import time\nfor i in range(3600):\n    if i % 60 == 0:\n        print(f\"Progress: {i//60} minutes\")\n    time.sleep(1)\nprint(\"Complete!\")",
    "callback_url": "https://your-server.com/webhook",
    "language": "python",
    "timeout": 7200,
    "callback_signature_secret": "your-secret-key"
  }'

Response

Success (200 OK)

{
  "execution_id": "exec_abc123",
  "status": "queued",
  "callback_url": "https://your-server.com/webhook"
}

Response Fields

FieldTypeDescription
execution_idstringExecution ID for tracking
statusstringInitial status (queued)
callback_urlstringWebhook URL that will receive results

Callback Behavior

When execution completes, the API POSTs results to your callback_url:

Callback Request

POST https://your-server.com/webhook
Content-Type: application/json
X-HopX-Signature: sha256=abc123...
X-HopX-Timestamp: 1706400000

Callback Payload

{
  "execution_id": "exec_abc123",
  "status": "completed",
  "success": true,
  "stdout": "Progress: 0 minutes\nProgress: 1 minutes\n...\nComplete!\n",
  "stderr": "",
  "exit_code": 0,
  "execution_time": 3600.5,
  "started_at": "2025-01-28T00:00:00Z",
  "completed_at": "2025-01-28T01:00:00Z"
}

Callback Headers

HeaderDescription
X-HopX-SignatureHMAC-SHA256 signature of payload (if secret provided)
X-HopX-TimestampUnix timestamp of callback
Custom headersFrom callback_headers in request

Status Codes

CodeDescription
200Execution queued
400Invalid request (missing callback_url, invalid URL)
401Unauthorized
500Failed to queue execution

Verify Webhook Signature

To verify the webhook signature:

Python

import hmac
import hashlib
import json

def verify_webhook(payload_bytes, signature, secret):
    """Verify HMAC-SHA256 signature"""
    expected_signature = hmac.new(
        secret.encode('utf-8'),
        payload_bytes,
        hashlib.sha256
    ).hexdigest()
    
    # Remove 'sha256=' prefix if present
    if signature.startswith('sha256='):
        signature = signature[7:]
    
    return hmac.compare_digest(signature, expected_signature)

# In your webhook handler
@app.route('/webhook', methods=['POST'])
def webhook():
    signature = request.headers.get('X-HopX-Signature')
    timestamp = request.headers.get('X-HopX-Timestamp')
    payload_bytes = request.get_data()
    
    if not verify_webhook(payload_bytes, signature, 'your-secret-key'):
        return 'Invalid signature', 401
    
    data = json.loads(payload_bytes)
    print(f"Execution {data['execution_id']}: {data['status']}")
    
    return 'OK', 200

JavaScript

const crypto = require('crypto');

function verifyWebhook(payloadString, signature, secret) {
  const expectedSignature = crypto
    .createHmac('sha256', secret)
    .update(payloadString)
    .digest('hex');
  
  // Remove 'sha256=' prefix if present
  const actualSignature = signature.startsWith('sha256=') 
    ? signature.slice(7) 
    : signature;
  
  return crypto.timingSafeEqual(
    Buffer.from(actualSignature),
    Buffer.from(expectedSignature)
  );
}

// In your webhook endpoint
app.post('/webhook', express.raw({type: 'application/json'}), (req, res) => {
  const signature = req.headers['x-HopX-signature'];
  const timestamp = req.headers['x-HopX-timestamp'];
  const payloadString = req.body.toString('utf8');
  
  if (!verifyWebhook(payloadString, signature, 'your-secret-key')) {
    return res.status(401).send('Invalid signature');
  }
  
  const data = JSON.parse(payloadString);
  console.log(`Execution ${data.execution_id}: ${data.status}`);
  
  res.send('OK');
});

Use Cases

Long-Running Model Training

curl -X POST https://sandbox_abc123xyz.hopx.dev/execute/async \
  -H "Authorization: Bearer YOUR_JWT_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "code": "import tensorflow as tf\nimport time\n\nprint(\"Starting model training...\")\nmodel = tf.keras.Sequential([...])\nmodel.fit(X_train, y_train, epochs=100, batch_size=32)\nmodel.save(\"/workspace/trained_model.h5\")\nprint(\"Training complete!\")",
    "callback_url": "https://your-server.com/training-complete",
    "language": "python",
    "timeout": 14400,
    "callback_signature_secret": "your-secret-key"
  }'

Batch Data Processing

curl -X POST https://sandbox_abc123xyz.hopx.dev/execute/async \
  -H "Authorization: Bearer YOUR_JWT_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "code": "import pandas as pd\nimport glob\n\nprint(\"Processing batch files...\")\nall_files = glob.glob(\"/workspace/data/*.csv\")\ndfs = [pd.read_csv(f) for f in all_files]\ncombined = pd.concat(dfs, ignore_index=True)\ncombined.to_csv(\"/workspace/combined_result.csv\")\nprint(f\"Processed {len(all_files)} files\")",
    "callback_url": "https://your-server.com/batch-complete",
    "language": "python",
    "timeout": 7200
  }'

Custom Callback Headers

curl -X POST https://sandbox_abc123xyz.hopx.dev/execute/async \
  -H "Authorization: Bearer YOUR_JWT_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "code": "print(\"Task complete\")",
    "callback_url": "https://your-server.com/webhook",
    "language": "python",
    "callback_headers": {
      "X-Custom-Header": "my-value",
      "X-Task-ID": "task-123"
    }
  }'

Next Steps