Skip to main content

Overview

Crewship webhooks let you:
  • Incoming webhooks: Trigger runs via HTTP POST requests from external systems
  • Outgoing webhooks: Receive notifications when runs complete

Incoming Webhooks

Incoming webhooks provide unique URLs that trigger runs when called. Use them to integrate with:
  • CI/CD pipelines (GitHub Actions, GitLab CI, etc.)
  • Scheduling services (cron jobs, cloud schedulers)
  • No-code automation tools (Zapier, Make, n8n)
  • Custom integrations

Creating an Incoming Webhook

  1. Go to your deployment in the Console
  2. Click Webhooks
  3. Click Add Incoming Webhook
  4. Enter a name and select the environment (production/staging)
  5. Copy the generated webhook URL

Triggering a Run

Send a POST request to the webhook URL with your run input:
curl -X POST "https://api.crewship.dev/webhooks/runs/YOUR_WEBHOOK_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"topic": "AI agents", "year": "2025"}'
Response (202 Accepted):
{
  "run_id": "run_abc123",
  "version_id": "ver_xyz789",
  "version_number": 5,
  "status": "running"
}
The webhook waits for the run to start before returning, so a 202 response means the run is actually executing.

Example: GitHub Actions

Trigger a crew run on every push to main:
name: Run Crew
on:
  push:
    branches: [main]

jobs:
  run-crew:
    runs-on: ubuntu-latest
    steps:
      - name: Trigger Crewship Run
        run: |
          curl -X POST "${{ secrets.CREWSHIP_WEBHOOK_URL }}" \
            -H "Content-Type: application/json" \
            -d '{"commit": "${{ github.sha }}", "branch": "${{ github.ref_name }}"}'

Example: Scheduled Runs

Use a cron service or cloud scheduler to trigger runs on a schedule:
# Daily at 9am UTC via cron
0 9 * * * curl -X POST "https://api.crewship.dev/webhooks/runs/YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"date": "$(date +%Y-%m-%d)"}'

Outgoing Webhooks

Outgoing webhooks notify external systems when runs complete. Use them to:
  • Send Slack/Discord notifications
  • Update databases or dashboards
  • Trigger downstream workflows
  • Log results to external systems

Creating an Outgoing Webhook

  1. Go to your deployment in the Console
  2. Click Webhooks
  3. Click Add Outgoing Webhook
  4. Enter a name, target URL, and select events
  5. Save the signing secret securely

Events

EventDescription
run.succeededRun completed successfully
run.failedRun failed with an error

Webhook Payload

When a run completes, Crewship sends a POST request to your URL:
{
  "event": "run.succeeded",
  "timestamp": "2025-01-15T10:30:00.000Z",
  "data": {
    "run_id": "run_abc123",
    "deployment_id": "dep_xyz789",
    "status": "succeeded",
    "output": {
      "result": "Your crew output here..."
    },
    "started_at": "2025-01-15T10:29:15.000Z",
    "completed_at": "2025-01-15T10:30:00.000Z"
  }
}

Security Headers

Every outgoing webhook request includes these headers:
HeaderDescription
X-Crewship-EventEvent type (e.g., run.succeeded)
X-Crewship-DeliveryUnique delivery ID for debugging
X-Crewship-TimestampUnix timestamp when request was signed
X-Crewship-SignatureHMAC-SHA256 signature for verification

Verifying Signatures

Always verify webhook signatures to ensure requests are from Crewship:
import crypto from 'crypto'

function verifyWebhookSignature(
  secret: string,
  timestamp: string,
  body: string,
  signature: string
): boolean {
  const signedPayload = `${timestamp}.${body}`
  const expectedSignature = crypto
    .createHmac('sha256', secret)
    .update(signedPayload)
    .digest('hex')

  return signature === `sha256=${expectedSignature}`
}

// In your webhook handler
app.post('/webhook', (req, res) => {
  const timestamp = req.headers['x-crewship-timestamp']
  const signature = req.headers['x-crewship-signature']
  const body = JSON.stringify(req.body)

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

  // Process the webhook...
  const { event, data } = req.body
  console.log(`Run ${data.run_id} ${event}`)

  res.status(200).send('OK')
})

Python Example

import hmac
import hashlib

def verify_webhook_signature(secret: str, timestamp: str, body: str, signature: str) -> bool:
    signed_payload = f"{timestamp}.{body}"
    expected = hmac.new(
        secret.encode(),
        signed_payload.encode(),
        hashlib.sha256
    ).hexdigest()
    return signature == f"sha256={expected}"

# In your Flask handler
@app.route('/webhook', methods=['POST'])
def webhook():
    timestamp = request.headers.get('X-Crewship-Timestamp')
    signature = request.headers.get('X-Crewship-Signature')
    body = request.get_data(as_text=True)

    if not verify_webhook_signature(WEBHOOK_SECRET, timestamp, body, signature):
        return 'Invalid signature', 401

    data = request.json
    print(f"Run {data['data']['run_id']} {data['event']}")

    return 'OK', 200

Delivery Logs

View delivery attempts in the Console:
  1. Go to your deployment → Webhooks
  2. Find your outgoing webhook
  3. Click Deliveries
Each delivery shows:
  • Status (succeeded/failed)
  • HTTP response code
  • Error message (if failed)
  • Timestamp

Managing Webhooks

Regenerating Secrets

If your webhook secret is compromised:
  1. Go to your webhook in the Console
  2. Click Regenerate Secret
  3. Update your integration with the new secret
The old secret stops working immediately. Update your integration before regenerating.

Disabling Webhooks

Toggle the Enabled switch to temporarily disable a webhook without deleting it.

Environment Filtering

Webhooks are scoped to an environment (production or staging). Create separate webhooks if you need different behavior per environment.

Best Practices

Never process outgoing webhooks without verifying the signature. This prevents attackers from sending fake webhook payloads.
Return a 2xx response within 30 seconds. Crewship waits for your response before marking the delivery as complete.
Use the X-Crewship-Delivery header to deduplicate. In rare cases, the same event may be delivered twice.
Store webhook secrets in environment variables or a secrets manager. Never commit them to version control.

Testing Webhooks

Use webhook.site to test outgoing webhooks:
  1. Go to webhook.site and copy your unique URL
  2. Create an outgoing webhook with that URL
  3. Trigger a run
  4. View the payload on webhook.site