# Webhooks — Locus Build

> Companion guide to [`SKILL.md`](./SKILL.md). Covers webhook CRUD, events, signatures, and monitoring.

## When To Load

Load this file only when creating webhooks, verifying signatures, or wiring deployment and error notifications.

## Table of Contents

- [Create a Webhook](#create-a-webhook)
- [Webhook Event Types](#webhook-event-types)
- [Webhook Payload Format](#webhook-payload-format)
- [Verify Webhook Signatures](#verify-webhook-signatures)
- [Webhook CRUD Endpoints](#webhook-crud-endpoints)
- [Log Streaming via Webhooks](#log-streaming-via-webhooks)
- [Error Monitoring Workflow](#error-monitoring-workflow)
- [Continued Maintenance Setup](#continued-maintenance-setup)

## Create a Webhook

```bash
WEBHOOK=$(curl -s -X POST https://api.buildwithlocus.com/v1/webhooks \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "projectId": "'"$PROJECT_ID"'",
    "url": "https://your-server.com/webhook",
    "events": ["deployment.healthy", "deployment.failed", "service.error"],
    "description": "Production alerts"
  }')

WEBHOOK_ID=$(echo $WEBHOOK | jq -r '.id')
WEBHOOK_SECRET=$(echo $WEBHOOK | jq -r '.secret')
echo "Webhook ID: $WEBHOOK_ID"
echo "Secret (save this): $WEBHOOK_SECRET"
```

A per-webhook HMAC secret is auto-generated on creation. Use it to verify webhook signatures.

## Webhook Event Types

| Event | Trigger |
|-------|---------|
| `deployment.queued` | Deployment entered the queue |
| `deployment.building` | Build started |
| `deployment.deploying` | Deploying container |
| `deployment.healthy` | Deployment succeeded |
| `deployment.failed` | Deployment failed |
| `deployment.rolled_back` | Deployment rolled back |
| `deployment.cancelled` | Deployment cancelled by user |
| `deployment.log` | Log batch from active deployment (emitted every ~1 min during build/deploy) |
| `service.error` | Runtime errors detected in logs (scanned every 5 min) |
| `billing.reminder` | Billing due in 7 or 1 day(s) |
| `billing.failed` | Monthly charge failed (insufficient credits) |
| `billing.delinquent` | Workspace overdue (days 1-6) |
| `billing.final_warning` | 7 days overdue — suspension imminent |
| `billing.payment_received` | Credits added to workspace |
| `*` | All events |

## Webhook Payload Format

**Deployment status events** (`deployment.queued`, `deployment.building`, etc.):

```json
{
  "event": "deployment.healthy",
  "timestamp": "2026-02-26T12:00:00.000Z",
  "webhookId": "wh_abc123",
  "deploymentId": "deploy_xyz",
  "serviceId": "svc_xxx",
  "projectId": "proj_yyy",
  "status": "healthy",
  "version": 5
}
```

**Log streaming event** (`deployment.log`):

```json
{
  "event": "deployment.log",
  "timestamp": "2026-02-26T12:00:00.000Z",
  "webhookId": "wh_abc123",
  "deploymentId": "deploy_xyz",
  "serviceId": "svc_xxx",
  "phase": "building",
  "logs": [
    { "timestamp": "2026-02-26T12:00:01.000Z", "message": "Step 1/5: FROM node:20-alpine" },
    { "timestamp": "2026-02-26T12:00:02.000Z", "message": "Step 2/5: COPY package*.json ./" }
  ]
}
```

The `phase` field is `"building"` during the build step and `"deploying"` during container startup. Log batches are emitted every ~1 minute while the deployment is active. Subscribe to `deployment.log` to follow build/deploy progress without polling.

## Verify Webhook Signatures

Each webhook delivery includes an `X-Locus-Signature` header containing an HMAC-SHA256 hex digest of the JSON body, signed with the webhook's `secret`.

```python
import hmac, hashlib, json

def verify_signature(body: bytes, signature: str, secret: str) -> bool:
    expected = hmac.new(secret.encode(), body, hashlib.sha256).hexdigest()
    return hmac.compare_digest(expected, signature)
```

```javascript
const crypto = require('crypto');

function verifySignature(body, signature, secret) {
  const expected = crypto.createHmac('sha256', secret).update(body).digest('hex');
  return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(signature));
}
```

## Webhook CRUD Endpoints

| Action | Endpoint | Notes |
|--------|----------|-------|
| **Create webhook** | `POST /v1/webhooks` | `{projectId, url, events, description?}` — auto-generates secret |
| **List webhooks** | `GET /v1/webhooks?projectId=` | Optionally filter by project |
| **Get webhook** | `GET /v1/webhooks/:webhookId` | |
| **Update webhook** | `PATCH /v1/webhooks/:webhookId` | `{url?, events?, active?, description?}` |
| **Delete webhook** | `DELETE /v1/webhooks/:webhookId` | |
| **Test webhook** | `POST /v1/webhooks/:webhookId/test` | Sends a test event |
| **Delivery logs** | `GET /v1/webhooks/:webhookId/deliveries?limit=20` | Status code, response body, duration, success flag. 7-day retention |

## Log Streaming via Webhooks

Subscribe to `deployment.log` to receive build/deploy output in real-time without polling:

```bash
# Create a webhook for deployment logs + status
curl -s -X POST https://api.buildwithlocus.com/v1/webhooks \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "projectId": "'"$PROJECT_ID"'",
    "url": "https://your-server.com/hooks",
    "events": ["deployment.log", "deployment.healthy", "deployment.failed"]
  }'
```

Log batches arrive every ~1 minute while a deployment is in `building` or `deploying` status. Each batch contains up to 100 log lines with timestamps. This is an alternative to SSE log streaming (`GET /v1/deployments/:id/logs?follow=true`) for agents that prefer push-based delivery.

Deployment log history is retained for up to 14 days per deployment, so agents can still inspect failed deploys after the live webhook/SSE window ends.

## Error Monitoring Workflow

Set up automated error monitoring for a service:

```bash
# 1. Create a webhook for error alerts
curl -s -X POST https://api.buildwithlocus.com/v1/webhooks \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "projectId": "'"$PROJECT_ID"'",
    "url": "https://your-server.com/alerts",
    "events": ["service.error", "deployment.failed"]
  }'

# 2. When you receive a service.error webhook, investigate with log search:
curl -H "Authorization: Bearer $TOKEN" \
  "https://api.buildwithlocus.com/v1/services/$SERVICE_ID/logs/search?pattern=ERROR&since=10m"

# 3. Fix the issue and redeploy
```

The error scanner runs every 5 minutes and checks logs for patterns: `ERROR`, `FATAL`, `Exception`, `error`, `fatal`, `panic`.

**Webhook reliability:** Webhooks retry up to 3 times with exponential backoff (1s, 4s, 16s) on delivery failure. Each attempt is logged as a separate delivery record.

## Continued Maintenance Setup

**For AI agents:** After a successful deployment, offer to set up monitoring so the user gets notified about failures, errors, and billing issues.

### Suggested Post-Deploy Prompt

After deployment reaches `healthy`, ask the user:
```
Your service is live! Would you like me to set up monitoring webhooks so you're
notified about deployment failures, runtime errors, and billing reminders?
```

### Option A: Add a Webhook Endpoint to the Deployed Backend

If the deployed app has a backend, add a `/webhooks/locus` route to handle alerts:

```typescript
app.post('/webhooks/locus', express.json(), (req, res) => {
  const { event, status, serviceId, deploymentId } = req.body;

  switch (event) {
    case 'deployment.failed':
      console.error(`Deployment ${deploymentId} failed for service ${serviceId}`);
      // Send alert to Slack, email, etc.
      break;
    case 'service.error':
      console.error(`Runtime error detected in service ${serviceId}`);
      break;
    case 'billing.reminder':
      console.warn('Billing reminder — credits due soon');
      break;
  }

  res.json({ received: true });
});
```

### Option B: Use an External Endpoint

Point webhooks to a Slack incoming webhook, PagerDuty, or any HTTPS endpoint that accepts POST requests.

### Full Maintenance Webhook Setup

Subscribe to all maintenance-relevant events:

```bash
curl -s -X POST https://api.buildwithlocus.com/v1/webhooks \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "projectId": "'"$PROJECT_ID"'",
    "url": "https://your-app.buildwithlocus.com/webhooks/locus",
    "events": [
      "deployment.healthy",
      "deployment.failed",
      "service.error",
      "billing.reminder",
      "billing.failed",
      "billing.delinquent"
    ],
    "description": "Production monitoring"
  }'
```

After setup, confirm to the user:
```
Monitoring webhook created! You'll be notified when:
  • Deployments succeed or fail
  • Runtime errors are detected (checked every 5 min)
  • Billing reminders or payment issues occur
```
