# Workflow Triggers

Source: https://docs.ops0.com/docs/workflows/triggers

Configure when and how workflows run. Each workflow has a single trigger type set on the workflow itself.

## Trigger Types

![ops0 Workflow Settings panel with the Trigger Type dropdown expanded](https://docs.ops0.com/images/workflow-trigger-types.png)

Manual

User clicks Run button

Webhook

HTTP POST request

One-time Schedule

Runs once at a specific date/time

Recurring (Cron)

Cron expression

---

## Manual Trigger

Always enabled. Users can run workflows on-demand.

| Field | Description |
|-------|-------------|
| **Require Inputs** | Prompt for variables when running |
| **Input Schema** | Define input fields |

**Input Schema Example:**
```json
{
  "environment": {
    "type": "select",
    "required": true,
    "options": ["dev", "staging", "prod"]
  },
  "version": {
    "type": "string",
    "required": true
  }
}
```

---

## One-time Schedule

Run a workflow once at a specific date and time. Useful for a planned maintenance window or a one-off deploy.

| Field | Required | Description |
|-------|----------|-------------|
| **Date** | Yes | Calendar date to run |
| **Time** | Yes | Hour and minute to run |

---

## Recurring (Cron)

Run workflows repeatedly on a schedule using a cron expression.

| Field | Required | Description |
|-------|----------|-------------|
| **Cron Expression** | Yes | Standard cron syntax |

### Cron Format

```
┌───────────── minute (0-59)
│ ┌───────────── hour (0-23)
│ │ ┌───────────── day of month (1-31)
│ │ │ ┌───────────── month (1-12)
│ │ │ │ ┌───────────── day of week (0-6)
│ │ │ │ │
* * * * *
```

### Common Schedules

| Schedule | Cron | Description |
|----------|------|-------------|
| Every hour | `0 * * * *` | At minute 0 |
| Daily at 2am | `0 2 * * *` | 2:00 AM |
| Every Monday | `0 0 * * 1` | Monday midnight |
| Every 15 min | `*/15 * * * *` | Quarter hour |
| Weekdays 9am | `0 9 * * 1-5` | Mon-Fri |

---

## Webhook Trigger

Trigger workflows via HTTP POST requests from external systems like GitHub Actions, GitLab CI, monitoring tools, or custom applications. This is also how you trigger a workflow on a Git push — wire up a webhook call from your CI pipeline.

| Field | Description |
|-------|-------------|
| **URL** | Auto-created webhook endpoint |
| **Secret** | Authentication token for request validation |
| **Payload Mapping** | Map JSON fields to workflow variables |
| **IP Whitelist** | Optional IP restriction for security |

### Creating Webhook URL

### Navigate to Workflow Settings
Open your workflow and click **Triggers** tab.

### Enable Webhook Trigger
Toggle "Webhook" trigger on.

### Copy Webhook URL
ops0 provides a unique endpoint:
```
https://api.ops0.io/webhooks/wf_abc123def456/trigger
```

### Copy Secret Token
Secret automatically created for authentication:
```
whsec_abc123def456ghi789jkl012mno345
```

**URL Format:**
- `wf_abc123def456` - Workflow-specific identifier
- URL is unique per workflow
- URL remains same unless regenerated

### Regenerating Webhook Secret

Rotate secret token for security:

### Open Webhook Settings
Navigate to workflow triggers configuration.

### Click "Regenerate Secret"
Creates a new random secret token.

### Update External Systems
Replace old secret in all calling systems (GitHub, GitLab, etc.).

### Test New Secret
Verify webhook calls work with new secret.

**Secret Rotation**

Old secret stops working immediately after regeneration. Update all webhook callers before regenerating to avoid failed triggers.

**When to Regenerate:**
- Secret exposed or committed to version control
- Employee departure (secret access removal)
- Regular security rotation (quarterly recommended)
- Suspected unauthorized access

### Authentication

All webhook requests must include secret token in header:

**Required Header:**
```
X-Webhook-Secret: whsec_abc123def456ghi789jkl012mno345
```

**Example Request:**
```bash
curl -X POST \
  -H "X-Webhook-Secret: whsec_abc123def456ghi789jkl012mno345" \
  -H "Content-Type: application/json" \
  -d '{"version": "1.2.3", "environment": "prod"}' \
  https://api.ops0.io/webhooks/wf_abc123def456/trigger
```

**Response (Success):**
```json
{
  "status": "triggered",
  "workflow_id": "wf_abc123def456",
  "execution_id": "exec_xyz789",
  "message": "Workflow started successfully"
}
```

**Response (Failed Auth):**
```json
{
  "status": "error",
  "code": "INVALID_SECRET",
  "message": "Webhook secret is invalid"
}
```

### Payload Mapping

Map JSON fields from webhook payload to workflow variables:

**Incoming Payload:**
```json
{
  "version": "1.2.3",
  "environment": "production",
  "commit": {
    "sha": "a1b2c3d4",
    "author": "john@company.com",
    "message": "Fix critical bug"
  },
  "build": {
    "id": "12345",
    "status": "success"
  }
}
```

**Payload Mapping Configuration:**

| JSON Path | Workflow Variable Name |
|-----------|------------------------|
| `$.version` | `version` |
| `$.environment` | `env` |
| `$.commit.sha` | `commit_sha` |
| `$.commit.author` | `author` |
| `$.build.id` | `build_id` |

**Access in Workflow Steps:**
```yaml
steps:
  - name: deploy
    run: |
      echo "Deploying version ${trigger.payload.version}"
      echo "To environment: ${trigger.payload.env}"
      echo "Commit: ${trigger.payload.commit_sha}"
      echo "Build: ${trigger.payload.build_id}"
```

### IP Whitelisting

Restrict webhook calls to specific IP addresses:

### Enable IP Whitelist
In webhook settings, toggle "Restrict by IP".

### Add Allowed IPs
Enter CIDR blocks:
```
192.168.1.0/24
203.0.113.50/32
```

### Save Configuration
Requests from other IPs will be rejected with 403.

**Common IP Ranges:**

| Service | IP Ranges |
|---------|-----------|
| **GitHub Actions** | [GitHub Meta API](https://api.github.com/meta) - `actions` field |
| **GitLab CI** | [GitLab IP ranges](https://docs.gitlab.com/ee/user/gitlab_com/index.html#ip-range) |
| **Jenkins** | Your Jenkins server IP |
| **CircleCI** | [CircleCI IPs](https://circleci.com/docs/ip-ranges/) |

### Testing Webhooks

Test webhook integration before deploying:

### Use Test Button
In webhook settings, click **Test Webhook**.

### Provide Sample Payload
Enter JSON payload to test:
```json
{
  "version": "1.0.0-test",
  "environment": "staging"
}
```

### Review Execution
Workflow starts with test payload. Check execution logs.

### Verify Variable Mapping
Confirm variables populated correctly from payload.

**Command-Line Testing:**
```bash
# Test from terminal
curl -X POST \
  -H "X-Webhook-Secret: your_secret_here" \
  -H "Content-Type: application/json" \
  -d '{"version": "test", "environment": "dev"}' \
  https://api.ops0.io/webhooks/wf_abc123/trigger

# Expected response
{"status":"triggered","execution_id":"exec_test123"}
```

### Webhook Security Best Practices

Secret Storage

Store webhook secret in GitHub Secrets, GitLab CI/CD Variables, or environment variable manager. Never commit secrets to version control.

Use HTTPS Only

ops0 webhooks are HTTPS-only. Never use HTTP webhook endpoints (not supported).

Validate Payload

Use payload mapping to validate required fields. Workflow fails if mapped fields are missing from payload.

IP Whitelist

Enable IP whitelisting for production webhooks to prevent unauthorized trigger attempts.

### Webhook Logs

View webhook request history and debug failed requests:

**Webhook Log Columns:**

| Column | Description |
|--------|-------------|
| **Timestamp** | When request received |
| **Source IP** | Caller IP address |
| **Status** | Success, Auth Failed, or Error |
| **Payload Size** | Request body size |
| **Execution ID** | Triggered workflow execution (if successful) |

**Failed Request Details:**
```
Webhook Request Failed
─────────────────────────────────────
Time:         2024-01-15 14:30:22 UTC
Source IP:    203.0.113.45
Status:       401 Unauthorized
Reason:       Invalid webhook secret
Payload:      {"version": "1.2.3", ...}

Expected secret header:
  X-Webhook-Secret: whsec_...

Received header:
  X-Webhook-Secret: old_secret_123
```

### Integrating with GitHub Actions

**GitHub Workflow Example:**
```yaml
name: Trigger ops0 Deployment
on:
  push:
    branches: [main]

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - name: Trigger ops0 Workflow
        run: |
          curl -X POST \
            -H "X-Webhook-Secret: ${{ secrets.OPS0_WEBHOOK_SECRET }}" \
            -H "Content-Type: application/json" \
            -d '{
              "version": "${{ github.sha }}",
              "environment": "production",
              "commit": {
                "sha": "${{ github.sha }}",
                "author": "${{ github.actor }}",
                "message": "${{ github.event.head_commit.message }}"
              }
            }' \
            https://api.ops0.io/webhooks/wf_abc123/trigger
```

**Setup:**
1. Add webhook secret to GitHub repository secrets as `OPS0_WEBHOOK_SECRET`
2. Configure payload mapping in ops0 workflow
3. Push to `main` branch triggers deployment

### Troubleshooting Webhooks

Webhook Not Triggering

**Check:** Webhook trigger enabled, correct URL, valid secret, IP not blocked.

**Fix:** Review webhook logs for error details. Test with curl command.

401 Unauthorized

**Cause:** Invalid or missing `X-Webhook-Secret` header.

**Fix:** Copy current secret from workflow settings. Update calling system.

403 Forbidden

**Cause:** IP whitelist enabled and caller IP not allowed.

**Fix:** Add caller IP to whitelist or disable IP restriction.

Workflow Fails After Trigger

**Cause:** Payload mapping missing required fields or workflow step error.

**Fix:** Check execution logs. Verify payload structure matches mapping configuration.

---

## Example: GitOps Webhook Deployment

### Trigger Configuration

```
Trigger:       Webhook
Called from:   GitHub Actions on push to main
Path Filter:   terraform/**
```

### Workflow

```
┌─────────┐    ┌──────────┐    ┌─────────┐    ┌────────┐
│  Plan   │───▶│ Approval │───▶│  Apply  │───▶│ Notify │
└─────────┘    └──────────┘    └─────────┘    └────────┘
```

### Webhook Call from CI

```
Repository:  acme/infrastructure
Branch:      main
Commit:      a1b2c3d
Author:      jane@example.com
Message:     "Add new RDS instance"
Files:
  - terraform/rds.tf (modified)
  - terraform/variables.tf (modified)
```

### Execution

```
Workflow: infrastructure-deploy
Run ID:   wfr_xyz789
Status:   Running

Triggered By: Webhook
  Branch:  main
  Commit:  a1b2c3d
  Author:  jane@example.com

Steps:
  1. Plan         ▶ Running
  2. Approval     ○ Pending
  3. Apply        ○ Pending
  4. Notify       ○ Pending
```

---

## Example: Recurring Backup

### Trigger Configuration

```
Trigger:   Recurring (Cron)
Cron:      0 3 * * *
Variables:
  backup_type: full
  retention_days: 30
```

### Next Runs

```
Schedule: Daily at 3:00 AM
─────────────────────────────────────
Next 5 runs:
  1. Tomorrow    3:00 AM
  2. Wed Jan 8   3:00 AM
  3. Thu Jan 9   3:00 AM
  4. Fri Jan 10  3:00 AM
  5. Sat Jan 11  3:00 AM
```

---

## Example: Incident Response via K8s Incidents Step

Use the **K8s Incidents** step type inside a manually or cron-triggered workflow to gate downstream steps on active incidents matching your severity/cluster filters, then respond automatically.

### Workflow Steps

| Step | Type | Configuration |
|------|------|----------------|
| 1 | K8s Incidents | Clusters: `production-eks`, Severities: Critical, High |
| 2 | Script | Restart deployment via kubectl |
| 3 | HTTP Request | Health check |
| 4 | Approval Gate | Notify via Slack, wait for confirmation |

### Incident Event

```
Incident:  INC-2024-0147
Type:      OOMKilled
Severity:  Critical
Cluster:   production-eks
Namespace: api
Pod:       api-gateway-7d9f8c6b4d-2xkjp

Triggered Workflow: incident-response
Run ID: wfr_inc147
```
