Skip to content

Webhooks

Webhooks allow you to receive real-time HTTP notifications when events occur in AMP.

Overview

When an event occurs (e.g., content published, mission completed), AMP sends an HTTP POST request to your configured endpoint with event details.

sequenceDiagram
    participant AMP
    participant Your Server

    AMP->>Your Server: POST /webhook
    Note right of Your Server: Event payload
    Your Server-->>AMP: 200 OK

Configuring Webhooks

Create Webhook Endpoint

POST /webhooks

curl -X POST https://api.amp.dev/v1/webhooks \
  -H "Authorization: Bearer $AMP_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://your-server.com/webhooks/amp",
    "events": ["content.published", "mission.completed"],
    "secret": "whsec_your_secret_key"
  }'

Parameters

Parameter Type Required Description
url string Yes HTTPS endpoint URL
events array Yes Events to subscribe to
secret string No Signing secret (auto-generated if not provided)
enabled boolean No Whether webhook is active (default: true)

Response

{
  "id": "whk_2xK9mPqR4vN8sT3w",
  "url": "https://your-server.com/webhooks/amp",
  "events": ["content.published", "mission.completed"],
  "secret": "whsec_xxxxxxxxxxxxxxxxxxxx",
  "status": "active",
  "created_at": "2024-01-15T10:30:00Z"
}

Event Types

Content Events

Event Description
content.created New content generated
content.approved Content approved for publishing
content.rejected Content rejected during review
content.scheduled Content scheduled for publishing
content.published Content successfully published
content.failed Content publishing failed

Mission Events

Event Description
mission.created New mission created
mission.started Mission pipeline started
mission.paused Mission paused
mission.resumed Mission resumed
mission.completed Mission duration ended

Pipeline Events

Event Description
pipeline.stage_completed Pipeline stage finished
pipeline.job_completed Entire job finished
pipeline.job_failed Job failed after retries

Analytics Events

Event Description
analytics.kpi_achieved KPI target reached
analytics.anomaly_detected Unusual performance detected

Webhook Payload

All webhooks follow this structure:

{
  "id": "evt_3yL0mPqR5wN9tU4v",
  "type": "content.published",
  "created_at": "2024-01-15T10:30:00Z",
  "data": {
    "content_id": "cnt_xxx",
    "mission_id": "msn_yyy",
    "platform": "twitter",
    "platform_post_id": "1747293847293847",
    "platform_url": "https://twitter.com/yourbrand/status/1747293847293847",
    "published_at": "2024-01-15T10:30:15Z"
  }
}

Payload Fields

Field Type Description
id string Unique event ID
type string Event type
created_at datetime When event occurred
data object Event-specific data

Event Payloads

content.published

{
  "id": "evt_xxx",
  "type": "content.published",
  "created_at": "2024-01-15T10:30:00Z",
  "data": {
    "content_id": "cnt_xxx",
    "mission_id": "msn_yyy",
    "platform": "twitter",
    "type": "thread",
    "platform_post_id": "1747293847293847",
    "platform_url": "https://twitter.com/...",
    "published_at": "2024-01-15T10:30:15Z"
  }
}

mission.completed

{
  "id": "evt_xxx",
  "type": "mission.completed",
  "created_at": "2024-01-15T10:30:00Z",
  "data": {
    "mission_id": "msn_yyy",
    "name": "Q1 Developer Outreach",
    "duration_days": 90,
    "summary": {
      "content_published": 180,
      "total_impressions": 523400,
      "total_engagements": 18234,
      "kpis_achieved": 3,
      "kpis_total": 4
    }
  }
}

pipeline.job_failed

{
  "id": "evt_xxx",
  "type": "pipeline.job_failed",
  "created_at": "2024-01-15T10:30:00Z",
  "data": {
    "job_id": "job_xxx",
    "mission_id": "msn_yyy",
    "failed_stage": "content",
    "error": {
      "code": "provider_error",
      "message": "LLM provider rate limited"
    },
    "attempts": 3
  }
}

Signature Verification

All webhook requests include a signature header for verification:

X-AMP-Signature: sha256=xxxxxxxxxxxxxxxxxxxxxxxxxxxx
X-AMP-Timestamp: 1705319400

Verification Steps

  1. Extract the timestamp and signature from headers
  2. Concatenate timestamp and request body: timestamp.body
  3. Compute HMAC-SHA256 with your webhook secret
  4. Compare with the signature header

Example

const crypto = require('crypto');

function verifyWebhook(payload, signature, timestamp, secret) {
  const signedPayload = `${timestamp}.${payload}`;
  const expectedSignature = crypto
    .createHmac('sha256', secret)
    .update(signedPayload)
    .digest('hex');

  return crypto.timingSafeEqual(
    Buffer.from(signature),
    Buffer.from(`sha256=${expectedSignature}`)
  );
}

// Express middleware
app.post('/webhooks/amp', express.raw({type: 'application/json'}), (req, res) => {
  const signature = req.headers['x-amp-signature'];
  const timestamp = req.headers['x-amp-timestamp'];

  if (!verifyWebhook(req.body, signature, timestamp, WEBHOOK_SECRET)) {
    return res.status(401).send('Invalid signature');
  }

  const event = JSON.parse(req.body);
  // Handle event...

  res.status(200).send('OK');
});
import hmac
import hashlib

def verify_webhook(payload, signature, timestamp, secret):
    signed_payload = f"{timestamp}.{payload}"
    expected = hmac.new(
        secret.encode(),
        signed_payload.encode(),
        hashlib.sha256
    ).hexdigest()
    return hmac.compare_digest(f"sha256={expected}", signature)

# Flask route
@app.route('/webhooks/amp', methods=['POST'])
def handle_webhook():
    signature = request.headers.get('X-AMP-Signature')
    timestamp = request.headers.get('X-AMP-Timestamp')

    if not verify_webhook(request.data, signature, timestamp, WEBHOOK_SECRET):
        return 'Invalid signature', 401

    event = request.json
    # Handle event...

    return 'OK', 200
func verifyWebhook(payload, signature, timestamp, secret string) bool {
    signedPayload := fmt.Sprintf("%s.%s", timestamp, payload)
    mac := hmac.New(sha256.New, []byte(secret))
    mac.Write([]byte(signedPayload))
    expected := hex.EncodeToString(mac.Sum(nil))
    return hmac.Equal([]byte(signature), []byte("sha256="+expected))
}

Retry Policy

If your endpoint returns a non-2xx status or times out, AMP retries with exponential backoff:

Attempt Delay
1 Immediate
2 1 minute
3 5 minutes
4 30 minutes
5 2 hours

After 5 failed attempts, the webhook is marked as failing and notifications are sent.


Managing Webhooks

List Webhooks

curl https://api.amp.dev/v1/webhooks \
  -H "Authorization: Bearer $AMP_API_KEY"

Update Webhook

curl -X PUT https://api.amp.dev/v1/webhooks/whk_xxx \
  -H "Authorization: Bearer $AMP_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "events": ["content.published", "content.failed"],
    "enabled": true
  }'

Delete Webhook

curl -X DELETE https://api.amp.dev/v1/webhooks/whk_xxx \
  -H "Authorization: Bearer $AMP_API_KEY"

Test Webhook

Send a test event to verify your endpoint:

curl -X POST https://api.amp.dev/v1/webhooks/whk_xxx/test \
  -H "Authorization: Bearer $AMP_API_KEY"

Best Practices

  1. Always verify signatures — Prevents spoofed requests
  2. Respond quickly — Return 200 within 5 seconds, process asynchronously
  3. Handle duplicates — Use event id for idempotency
  4. Monitor failures — Set up alerts for webhook failures
  5. Use HTTPS — Required for production webhooks