Skip to main content
Webhooks allow you to receive real-time notifications when events occur within Asteroid. Set up HTTP endpoints to automatically receive event data as it happens.
This documentation covers Webhook V2. If you’re using V1, see the Migration Guide below.

Overview

Real-time Notifications

Get instant updates for all execution lifecycle events

Secure Delivery

Webhooks are signed with cryptographic signatures for verification

Reliable Delivery

Automatic retries ensure your webhooks are delivered

Event-Specific Payloads

Structured payloads with execution context and event-specific data

Configuration

Setting Up Your Webhook

1

Create Webhook V2 Integration in Platform

In the platform, go to IntegrationsAdd Integration → select Webhook, then supply the webhook details:
  • URL: The endpoint that will receive execution events
  • Headers: Any custom headers required by your endpoint (e.g., authentication)
Webhooks use a standardized payload structure and do not require custom body templates.
2

Attach Webhook to Agents

After creating the integration, attach the webhook to individual agents. When those agents run and emit execution events, your webhook will be invoked automatically.
3

Verify Webhook Signature

All webhooks include a cryptographic signature for security verification (see Security section).

V2 Payload Structure

Every webhook request contains a standardized V2 payload structure:
{
  "type": "execution",
  "event_id": "8106b11d-564d-4192-a03a-5f81418c70d5",
  "timestamp": "2024-01-15T10:30:00Z",
  "info": {
    "event": "EXECUTION_COMPLETED",
    "execution_id": "5f813ea4-6713-4879-b1f5-c5e9f7e8040d",
    "execution_url": "https://platform.asteroid.ai/executions/5f813ea4-6713-4879-b1f5-c5e9f7e8040d",
    "agent_id": "4d407b97-cbde-4e0b-8d7f-a0e8d7ba6a06",
    "agent_name": "Data Extraction Agent",
    "payload": {
      "result": { /* execution result data */ },
      "reasoning": "Task completed successfully",
      "outcome": "Success"
    }
  }
}

Top-Level Fields

type
string
required
The category of the notification. Currently only "execution" is supported.
event_id
string
required
Unique identifier for this notification event. Use this for idempotency.Example: 8106b11d-564d-4192-a03a-5f81418c70d5
timestamp
string
required
ISO 8601 timestamp when the event occurred.Example: 2024-01-15T10:30:00Z
info
object
required
Type specific event information. See Execution Info below.

Execution Info Structure

When the type is Execution, the info object contains execution-specific details:
info.event
string
required
The specific execution event type. One of:
  • EXECUTION_STARTED
  • EXECUTION_COMPLETED
  • EXECUTION_FAILED
  • EXECUTION_CANCELLED
  • EXECUTION_PAUSED
  • EXECUTION_RESUMED
  • EXECUTION_AWAITING_CONFIRMATION
info.execution_id
string
required
Unique identifier for the execution.Example: 5f813ea4-6713-4879-b1f5-c5e9f7e8040d
info.execution_url
string
required
URL to view the execution in the Asteroid platform.Example: https://platform.asteroid.ai/executions/5f813ea4-6713-4879-b1f5-c5e9f7e8040d
info.agent_id
string
required
Identifier of the agent that is being run.
info.agent_name
string
required
Human-readable name of the agent that is being run.
info.payload
object
required
Event-specific payload data. The structure depends on the event field. See Execution Events below.

Execution Events

Each execution event type has a specific payload structure:

EXECUTION_STARTED

Sent when an execution begins. Payload: Empty object {}
{
  "type": "execution",
  "event_id": "evt_001",
  "timestamp": "2024-01-15T10:30:00Z",
  "info": {
    "event": "EXECUTION_STARTED",
    "execution_id": "4d407b97-cbde-4e0b-8d7f-a0e8d7ba6a06",
    "execution_url": "https://platform.asteroid.ai/executions/4d407b97-cbde-4e0b-8d7f-a0e8d7ba6a06",
    "agent_id": "4d407b97-cbde-4e0b-8d7f-a0e8d7ba6a06",
    "agent_name": "Data Extraction Agent",
    "payload": {}
  }
}

EXECUTION_COMPLETED

Sent when an execution completes successfully. Payload:
result
object
required
Execution result data returned by the agent.
reasoning
string
required
Reasoning for the execution outcome.
outcome
string
required
Execution outcome description.
{
  "type": "execution",
  "event_id": "evt_002",
  "timestamp": "2024-01-15T10:35:00Z",
  "info": {
    "event": "EXECUTION_COMPLETED",
    "execution_id": "4d407b97-cbde-4e0b-8d7f-a0e8d7ba6a06",
    "execution_url": "https://platform.asteroid.ai/executions/4d407b97-cbde-4e0b-8d7f-a0e8d7ba6a06",
    "agent_id": "4d407b97-cbde-4e0b-8d7f-a0e8d7ba6a06",
    "agent_name": "Data Extraction Agent",
    "payload": {
      "result": {
        "data": "extracted data",
        "status": "success"
      },
      "reasoning": "Successfully extracted all required data",
      "outcome": "Task completed successfully"
    }
  }
}

EXECUTION_FAILED

Sent when an execution fails. Payload:
reason
string
required
Reason for the failure.
{
  "type": "execution",
  "event_id": "evt_003",
  "timestamp": "2024-01-15T10:35:00Z",
  "info": {
    "event": "EXECUTION_FAILED",
    "execution_id": "4d407b97-cbde-4e0b-8d7f-a0e8d7ba6a06",
    "execution_url": "https://platform.asteroid.ai/executions/4d407b97-cbde-4e0b-8d7f-a0e8d7ba6a06",
    "agent_id": "4d407b97-cbde-4e0b-8d7f-a0e8d7ba6a06",
    "agent_name": "Data Extraction Agent",
    "payload": {
      "reason": "Unable to access target website - connection timeout"
    }
  }
}

EXECUTION_CANCELLED

Sent when an execution is cancelled. Payload:
reason
string
required
Reason for cancellation.
cancelled_by
string
required
Who cancelled the execution (e.g., “user”, “system”, “admin”).
{
  "type": "execution",
  "event_id": "evt_004",
  "timestamp": "2024-01-15T10:32:00Z",
  "info": {
    "event": "EXECUTION_CANCELLED",
    "execution_id": "4d407b97-cbde-4e0b-8d7f-a0e8d7ba6a06",
    "execution_url": "https://platform.asteroid.ai/executions/4d407b97-cbde-4e0b-8d7f-a0e8d7ba6a06",
    "agent_id": "4d407b97-cbde-4e0b-8d7f-a0e8d7ba6a06",
    "agent_name": "Data Extraction Agent",
    "payload": {
      "reason": "User requested cancellation",
      "cancelled_by": "user"
    }
  }
}

EXECUTION_PAUSED

Sent when an execution is paused. Payload:
reason
string
required
Reason for the pause.
  • For user pauses: "paused by user"
  • For agent pauses: the query/message sent via send_user_message
paused_by
string
required
Who paused the execution (e.g., “user”, “agent”, “system”).
{
  "type": "execution",
  "event_id": "evt_005",
  "timestamp": "2024-01-15T10:31:00Z",
  "info": {
    "event": "EXECUTION_PAUSED",
    "execution_id": "4d407b97-cbde-4e0b-8d7f-a0e8d7ba6a06",
    "execution_url": "https://platform.asteroid.ai/executions/4d407b97-cbde-4e0b-8d7f-a0e8d7ba6a06",
    "agent_id": "4d407b97-cbde-4e0b-8d7f-a0e8d7ba6a06",
    "agent_name": "Data Extraction Agent",
    "payload": {
      "reason": "Need user input: Which account should I use?",
      "paused_by": "agent"
    }
  }
}

EXECUTION_RESUMED

Sent when an execution resumes after being paused. Payload:
reason
string
required
Reason for the resumption.
{
  "type": "execution",
  "event_id": "evt_006",
  "timestamp": "2024-01-15T10:33:00Z",
  "info": {
    "event": "EXECUTION_RESUMED",
    "execution_id": "4d407b97-cbde-4e0b-8d7f-a0e8d7ba6a06",
    "execution_url": "https://platform.asteroid.ai/executions/4d407b97-cbde-4e0b-8d7f-a0e8d7ba6a06",
    "agent_id": "4d407b97-cbde-4e0b-8d7f-a0e8d7ba6a06",
    "agent_name": "Data Extraction Agent",
    "payload": {
      "reason": "User provided input"
    }
  }
}

EXECUTION_AWAITING_CONFIRMATION

Sent when an execution requires user confirmation. Payload:
reason
string
required
Reason why confirmation is needed.
{
  "type": "execution",
  "event_id": "evt_007",
  "timestamp": "2024-01-15T10:34:00Z",
  "info": {
    "event": "EXECUTION_AWAITING_CONFIRMATION",
    "execution_id": "4d407b97-cbde-4e0b-8d7f-a0e8d7ba6a06",
    "execution_url": "https://platform.asteroid.ai/executions/4d407b97-cbde-4e0b-8d7f-a0e8d7ba6a06",
    "agent_id": "4d407b97-cbde-4e0b-8d7f-a0e8d7ba6a06",
    "agent_name": "Data Extraction Agent",
    "payload": {
      "reason": "About to submit payment form - please confirm"
    }
  }
}

Security & Signature Verification

All webhooks are signed using SHA256 hashing with RSA-PKCS1v15 signing and Base64 encoding for maximum security.

Signature Header

The webhook signature is included in the X-Asteroid-Signature header:
X-Asteroid-Signature: <signature>
Always verify the webhook signature to ensure the request is authentic and hasn’t been tampered with.

Public Key for Verification

Use this Base64-encoded public key to verify webhook signatures:
LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0KTUlJQklqQU5CZ2txaGtpRzl3MEJBUUVGQUFPQ0FROEFNSUlCQ2dLQ0FRRUF0SkZ6aXdXUjROUVJzTnpqZw==

JavaScript Implementation

Here’s a complete example of verifying webhook signatures in JavaScript:
const crypto = require('crypto');

function verifyWebhook(body, signature, publicKeyBase64) {
  // Step 1: Create SHA256 verifier
  const verifier = crypto.createVerify('SHA256');

  // Step 2: Update verifier with the request body
  verifier.update(body);

  // Step 3: Convert Base64 public key to buffer
  const publicKey = Buffer.from(publicKeyBase64, 'base64');

  // Step 4: Verify the signature
  const isValid = verifier.verify(publicKey, signature, 'base64');

  return isValid;
}

// Express.js example
app.post('/webhook', express.raw({ type: 'application/json' }), (req, res) => {
  const signature = req.headers['x-asteroid-signature'];
  const isValid = verifyWebhook(req.body, signature, PUBLIC_KEY);

  if (!isValid) {
    return res.status(401).json({ error: 'Invalid signature' });
  }

  // Parse and process the webhook
  const event = JSON.parse(req.body.toString());

  switch (event.info.event) {
    case 'EXECUTION_STARTED':
      console.log('Execution started:', event.info.execution_id);
      break;
    case 'EXECUTION_COMPLETED':
      console.log('Execution completed:', event.info.payload.result);
      break;
    case 'EXECUTION_FAILED':
      console.log('Execution failed:', event.info.payload.reason);
      break;
    // Handle other events...
  }

  res.status(200).json({ received: true });
});

Migrating from V1 to V2

If you’re currently using Webhook V1, follow this guide to migrate to V2.

What Changed

V2 introduces a more structured payload format with better support for execution lifecycle events: V1 Payload:
{
  "type": "<event_type>",
  "timestamp": "<timestamp>",
  "payload": "<event_payload>"
}
V2 Payload:
{
  "type": "execution",
  "event_id": "evt_abc123",
  "timestamp": "2024-01-15T10:30:00Z",
  "info": {
    "event": "EXECUTION_COMPLETED",
    "execution_id": "4d407b97-cbde-4e0b-8d7f-a0e8d7ba6a06",
    "execution_url": "https://platform.asteroid.ai/executions/4d407b97-cbde-4e0b-8d7f-a0e8d7ba6a06",
    "agent_id": "4d407b97-cbde-4e0b-8d7f-a0e8d7ba6a06",
    "agent_name": "Data Extraction Agent",
    "payload": { /* event-specific data */ }
  }
}

Migration Steps

1

Update Your Endpoint Code

Modify your webhook handler to parse the new V2 structure:
app.post('/webhook', (req, res) => {
  const { type, timestamp, payload } = req.body;

  console.log(`Event: ${type} at ${timestamp}`);
  console.log('Payload:', payload);

  res.status(200).json({ received: true });
});
2

Update Integration in Platform

In the Asteroid platform:
  1. Go to Integrations → find your webhook integration
  2. Edit the integration and change the version from V1 to V2
  3. Remove any custom body templates (V2 uses standardized payloads)
  4. Save the changes
Once you switch to V2, your endpoint will immediately start receiving V2-formatted payloads. Ensure your code is updated first!
3

Test Your Integration

Trigger a test execution to verify your endpoint correctly handles V2 payloads:
  1. Run an agent with the V2 webhook attached
  2. Verify you receive all execution events
  3. Check that you can parse and handle each event type
  4. Confirm signature verification still works
4

Update Idempotency Logic (Recommended)

V2 includes an event_id field for better idempotency handling:
const processedEvents = new Set();

app.post('/webhook', (req, res) => {
  const { event_id } = req.body;

  // Use event_id instead of timestamp-based deduplication
  if (processedEvents.has(event_id)) {
    return res.status(200).json({ message: 'Already processed' });
  }
  // Process the event...

  processedEvents.add(event_id);
});

Best Practices

Always return appropriate HTTP status codes:
  • 200 - Successfully processed
  • 400 - Bad request/invalid payload
  • 401 - Unauthorized/invalid signature
  • 500 - Server error
Use the event_id field to detect and handle duplicate webhook deliveries:
const processedEvents = new Set();

app.post('/webhook', (req, res) => {
  const { event_id } = req.body;

  if (processedEvents.has(event_id)) {
    return res.status(200).json({ message: 'Already processed' });
  }

  processedEvents.add(event_id);
  // Process the event...
});
Respond to webhooks within 30 seconds to avoid timeouts and retries:
app.post('/webhook', (req, res) => {
  // Acknowledge receipt immediately
  res.status(200).json({ received: true });

  // Process asynchronously
  setImmediate(() => {
    processWebhookEvent(req.body);
  });
});
Take advantage of the rich execution context in V2:
app.post('/webhook', (req, res) => {
  const { event_id, timestamp, info } = req.body;

  console.log({
    eventId: event_id,
    timestamp: timestamp,
    executionId: info.execution_id,
    executionUrl: info.execution_url,
    agentId: info.agent_id,
    agentName: info.agent_name,
    event: info.event
  });

  // This makes debugging much easier!
});

Testing Your Webhooks

Use webhook.site to test your webhook integration during development. It provides a temporary URL that captures and displays all incoming webhook requests.

Test Payload Example

Here’s a complete V2 test payload you can use:
{
  "type": "execution",
  "event_id": "evt_test_123",
  "timestamp": "2024-01-15T10:30:00Z",
  "info": {
    "event": "EXECUTION_COMPLETED",
    "execution_id": "4d407b97-cbde-4e0b-8d7f-a0e8d7ba6a06",
    "execution_url": "https://platform.asteroid.ai/executions/4d407b97-cbde-4e0b-8d7f-a0e8d7ba6a06",
    "agent_id": "4d407b97-cbde-4e0b-8d7f-a0e8d7ba6a06",
    "agent_name": "Data Extraction Agent",
    "payload": {
      "result": {
        "status": "success",
        "data": {
          "items_processed": 42,
          "completion_time": "5.2s"
        }
      },
      "reasoning": "Successfully processed all items in the queue",
      "outcome": "Task completed successfully"
    }
  }
}

Troubleshooting

  • Verify your endpoint URL is accessible from the internet
  • Check that your server is running and responding to POST requests
  • Ensure your firewall allows incoming connections
  • Confirm you’re using the correct public key
  • Verify you’re hashing the raw request body (not parsed JSON)
  • Check that you’re using Base64 decoding for both the key and signature
  • Implement idempotency using the event_id field
  • Return 200 status code for successfully processed events
  • Avoid returning error codes for already-processed events