API Reference
All management endpoints require JWT authentication. The execution endpoint is public (authenticated via HMAC signature).
Management Endpoints
Base URL: https://api.aerostack.dev/api/smart-webhooks
List Webhooks
GET /api/smart-webhooks
Authorization: Bearer <JWT>Returns all Smart Webhooks owned by the authenticated user, ordered by most recently updated.
Response (200):
{
"webhooks": [
{
"id": "swh_9a2b3c4d5e6f7g8h9i",
"owner_id": "usr_abc123",
"workspace_id": "ws_def456",
"name": "github-triage",
"slug": "github-triage",
"description": "Auto-triage GitHub issues",
"source_type": "github",
"source_events": "[\"opened\"]",
"instructions": "When a new issue is created...",
"llm_provider": "openai",
"llm_model": "gpt-4o-mini",
"max_tool_calls": 10,
"timeout_ms": 30000,
"signing_secret": "whsec_abc123",
"status": "active",
"enabled": 1,
"created_at": "2026-03-15T10:00:00Z",
"updated_at": "2026-03-15T10:00:00Z"
}
]
}Create Webhook
POST /api/smart-webhooks
Authorization: Bearer <JWT>
Content-Type: application/jsonRequest Body:
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
name | string | Yes | — | 1-100 characters |
workspace_id | string | Yes | — | MCP workspace ID (1-100 chars) |
instructions | string | Yes | — | Plain English processing instructions (1-10000 chars) |
description | string | No | null | Short description (max 500 chars) |
slug | string | No | auto from name | URL slug (max 60 chars) |
source_type | string | No | "custom" | github, stripe, shopify, or custom |
source_events | string[] | No | null (accept all) | Event types to process (max 50 items, each max 100 chars). Use ["*"] for all. |
llm_provider | string | No | "openai" | openai, anthropic, gemini, groq, workers-ai, custom |
llm_model | string | No | "gpt-4o-mini" | Model identifier (max 100 chars) |
max_tool_calls | integer | No | 10 | 1-50. Max agentic loop iterations |
timeout_ms | integer | No | 30000 | 1000-120000 (hard cap at 60000 during execution) |
signing_secret | string | No | null | HMAC-SHA256 signing secret (max 256 chars) |
Response (201):
{
"webhook": {
"id": "swh_9a2b3c4d5e6f7g8h9i",
"name": "github-triage",
"slug": "github-triage",
"source_type": "github",
"status": "active",
"enabled": 1
},
"webhook_url": "https://api.aerostack.dev/api/webhooks/smart/github-triage"
}The webhook_url is the URL to configure in your external service. This is where the service should send webhook events.
Get Webhook
GET /api/smart-webhooks/:id
Authorization: Bearer <JWT>Returns a single webhook. Returns 404 if not found or not owned by the authenticated user.
Response (200):
{
"webhook": { "id": "swh_...", "name": "...", "slug": "..." }
}Update Webhook
PATCH /api/smart-webhooks/:id
Authorization: Bearer <JWT>
Content-Type: application/jsonAll fields from the create schema are optional. workspace_id cannot be changed after creation. Two additional fields are available for updates:
| Field | Type | Required | Description |
|---|---|---|---|
name | string | No | 1-100 characters |
description | string | No | Max 500 chars |
instructions | string | No | 1-10000 chars |
source_type | string | No | github, stripe, shopify, custom |
source_events | string[] | No | Event types to process |
llm_provider | string | No | Provider enum |
llm_model | string | No | Model identifier |
max_tool_calls | integer | No | 1-50 |
timeout_ms | integer | No | 1000-120000 |
signing_secret | string | No | HMAC signing secret |
status | string | No | "active" or "inactive" |
enabled | boolean / 0 / 1 | No | Master on/off toggle |
Response (200):
{
"webhook": { "id": "swh_...", "name": "...", "slug": "..." }
}Examples:
# Disable a webhook
curl -X PATCH https://api.aerostack.dev/api/smart-webhooks/swh_your_id \
-H "Authorization: Bearer YOUR_JWT_TOKEN" \
-H "Content-Type: application/json" \
-d '{"enabled": false}'# Change instructions and event filter
curl -X PATCH https://api.aerostack.dev/api/smart-webhooks/swh_your_id \
-H "Authorization: Bearer YOUR_JWT_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"instructions": "Updated instructions here...",
"source_events": ["opened", "closed", "reopened"]
}'Delete Webhook
DELETE /api/smart-webhooks/:id
Authorization: Bearer <JWT>Permanently deletes the webhook and all its run history.
Response (200):
{
"deleted": true
}List Runs
GET /api/smart-webhooks/:id/runs?limit=50
Authorization: Bearer <JWT>| Query Param | Type | Default | Max |
|---|---|---|---|
limit | integer | 50 | 100 |
Response (200):
{
"runs": [
{
"id": "whr_1a2b3c4d5e6f7g8h9i",
"webhook_id": "swh_9a2b3c4d5e6f7g8h9i",
"event_type": "opened",
"payload_summary": "{\"action\":\"opened\",\"issue\":{\"title\":\"...\"}}",
"actions_taken": "Classified issue as bug. Applied labels. Assigned to team.",
"tool_calls": "[{\"name\":\"github_add_labels\",\"success\":true}]",
"tokens_input": 1456,
"tokens_output": 234,
"cost_cents": 0.52,
"latency_ms": 4521,
"status": "success",
"error": null,
"created_at": "2026-03-15T10:30:12Z"
}
]
}Run fields:
| Field | Description |
|---|---|
event_type | Extracted from payload (type, event.type, event_type, or action) |
payload_summary | First 500 characters of the stringified payload |
actions_taken | AI response summarizing what it did (first 2000 chars) |
tool_calls | JSON array of tool call records with name, success, latency |
tokens_input | Total input tokens across all LLM calls |
tokens_output | Total output tokens |
cost_cents | Estimated platform cost |
latency_ms | Total execution time |
status | success or error |
Execution Endpoint
Process Webhook
POST /api/webhooks/smart/:slug
Content-Type: application/json
X-Hub-Signature-256: sha256=<hex_digest> (optional)This is the URL you configure in your external service (GitHub, Stripe, etc.).
Request Body: Any valid JSON payload from the webhook source. Non-JSON payloads are wrapped as { raw: "<body>" }.
Maximum request body size: 2 MB. Oversized payloads are silently accepted with { ok: true }.
Response (200) — Processed:
{
"ok": true,
"actions_taken": "Classified issue #42 as 'bug'. Applied labels. Assigned to team.",
"tool_calls": 3,
"cost_cents": 0.52
}Response (200) — Skipped (event filtered):
{
"ok": true,
"skipped": true
}Response (200) — Silent rejection (any error condition):
{
"ok": true
}All error conditions return { ok: true } with HTTP 200. This includes: webhook not found, webhook disabled, invalid HMAC signature, and rate limiting. Check the run history to verify actual processing.
Signature Headers
If a signing_secret is configured, the endpoint checks these headers (first match wins):
| Header | Standard |
|---|---|
X-Hub-Signature-256 | GitHub |
X-Signature-256 | Generic |
X-Webhook-Signature | Generic |
Expected format: sha256=<hex_digest> (HMAC-SHA256 of the raw request body).
Event Type Extraction
The event type is extracted from the payload in this order:
payload.typepayload.event.typepayload.event_typepayload.action
If none are found, the event type defaults to "unknown".
Rate Limits
| Scope | Limit | Window |
|---|---|---|
| Per webhook | 30 requests | 1 minute |
Error Codes Summary
Management Endpoints
| Code | Meaning |
|---|---|
| 400 | Validation failed or no fields to update |
| 404 | Not found or not owner |
Execution Endpoint
The execution endpoint always returns HTTP 200 with { ok: true } for security. No HTTP error codes are used.
Templates
Smart Webhook templates are available via the shared templates endpoint:
GET /api/agent-endpoints/templates/list?type=webhookPre-built webhook templates include:
| Template | Source | Description |
|---|---|---|
| Payment Failed Handler | Stripe | Notify customers on failed payments |
| PR Merged Notifier | GitHub | Announce merged PRs to team channels |
| New Order Processor | Shopify | Process and route new orders |
Templates provide pre-written instructions and suggested MCP servers to get started quickly.