GitHub Actions

Add human approval gates to your CI/CD pipelines. Block deployments until someone approves.

Setup

  1. Go to your repository Settings → Secrets and variables → Actions
  2. Add OTTR_API_KEY with your ottr API key

Deployment Workflow

This workflow creates an approval URL and waits for human approval before deploying:

.github/workflows/deploy.yml
name: Deploy with Approval

on:
  push:
    branches: [main]

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Build
        run: npm run build

      - name: Request Deployment Approval
        id: approval
        run: |
          RESPONSE=$(curl -s -X POST https://api.ottr.run/v1/approvals \
            -H "Authorization: Bearer ${{ secrets.OTTR_API_KEY }}" \
            -H "Content-Type: application/json" \
            -d '{
              "key_path": "deploy/${{ github.repository }}/${{ github.run_id }}",
              "value": "approved",
              "ttl_seconds": 1800,
              "info": "Deploy ${{ github.sha }} to production?",
              "actions": [
                {"key": "approve", "value": "approved", "label": "Deploy", "style": "primary"},
                {"key": "reject", "value": "rejected", "label": "Cancel", "style": "danger"}
              ]
            }')

          URL=$(echo $RESPONSE | jq -r '.url')
          echo "approval_url=$URL" >> $GITHUB_OUTPUT
          echo "::notice::Approval required: $URL"

      - name: Wait for Approval
        run: |
          echo "Waiting for approval at: ${{ steps.approval.outputs.approval_url }}"
          KEY_PATH="deploy/${{ github.repository }}/${{ github.run_id }}"

          while true; do
            RESULT=$(curl -s "https://api.ottr.run/v1/key/$KEY_PATH" \
              -H "Authorization: Bearer ${{ secrets.OTTR_API_KEY }}")

            if [ "$RESULT" = '"approved"' ]; then
              echo "Deployment approved!"
              exit 0
            elif [ "$RESULT" = '"rejected"' ]; then
              echo "Deployment rejected"
              exit 1
            fi

            sleep 10
          done
        timeout-minutes: 30

      - name: Deploy
        if: success()
        run: |
          echo "Deploying to production..."
          # Your deployment commands here

How it works

  1. Workflow runs on push to main
  2. Build step completes
  3. ottr creates an approval URL (visible in Actions log)
  4. Workflow polls for approval (up to 30 min timeout)
  5. Human clicks link and approves or rejects
  6. Deployment proceeds or workflow fails

Optional: Slack Notification

Add this step after creating the approval to notify your team in Slack:

slack notification step
      - name: Notify Slack
        run: |
          curl -X POST ${{ secrets.SLACK_WEBHOOK }} \
            -H "Content-Type: application/json" \
            -d '{
              "text": "Deployment approval needed",
              "blocks": [
                {
                  "type": "section",
                  "text": {
                    "type": "mrkdwn",
                    "text": "*Deployment Approval Required*\nRepository: ${{ github.repository }}\nCommit: ${{ github.sha }}"
                  }
                },
                {
                  "type": "actions",
                  "elements": [
                    {
                      "type": "button",
                      "text": {"type": "plain_text", "text": "Review & Approve"},
                      "url": "${{ steps.approval.outputs.approval_url }}"
                    }
                  ]
                }
              ]
            }'

Tips

  • Use timeout-minutes to control how long to wait
  • Include commit SHA in the info so reviewers know what they're approving
  • Use unique key_path per run to avoid conflicts
  • Configure ottr Slack integration for automatic notifications