Skip to main content

How GitHub Integration Works

Configure ABV webhooks

Set up webhooks in your ABV project to send events when prompts are created, updated, or deleted. ABV sends HTTP POST requests to your specified endpoint with full prompt data.Webhook configuration:
  • Navigate to Prompts > Webhooks in ABV
  • Create a webhook with your target endpoint
  • Choose events to trigger (created, updated, deleted)
  • Optionally filter by prompt labels or tags
ABV sends webhook payloads containing:
{
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "timestamp": "2024-07-10T10:30:00Z",
  "type": "prompt-version",
  "action": "created",
  "prompt": {
    "name": "movie-critic",
    "version": 3,
    "labels": ["production"],
    "prompt": "..."
  }
}

Set up GitHub Actions or webhook server

Choose one of two integration patterns based on your needs:Option 1: GitHub Actions (recommended for most users)
  • No infrastructure required
  • Uses GitHub’s repository_dispatch events
  • Perfect for triggering CI/CD workflows
  • Limited to GitHub-hosted workflows
Option 2: Webhook Server (for advanced use cases)
  • Requires hosting a webhook receiver
  • Syncs prompts to git repository as commits
  • Provides full git history for prompts
  • Can trigger additional automation beyond GitHub
Both options can be combined for maximum automation.

Automate testing and deployment

Once webhooks reach GitHub, configure workflows to:
  • Run automated tests on prompt changes
  • Validate prompt syntax and structure
  • Deploy prompts to staging/production environments
  • Notify teams via Slack or email
  • Create pull requests for review
Example workflow trigger:
on:
  repository_dispatch:
    types: [abv-prompt-update]
Access prompt data in workflows via github.event.client_payload.* to make deployment decisions.

Track versions in git history

If using the webhook server option, every prompt change creates a git commit:
commit abc123def456
Author: ABV Webhook <[email protected]v>
Date: 2024-07-10 10:30:00

    created: movie-critic v3

diff --git a/abv_prompt.json b/abv_prompt.json
+ "version": 3,
+ "prompt": "As a {{criticLevel}} movie critic..."
This provides a complete audit trail and enables git-based workflows (diff, blame, revert).

Integration Patterns

The simplest integration pattern. Trigger GitHub Actions workflows directly from ABV webhooks without hosting any infrastructure.How it works:
  1. ABV webhook sends POST to https://api.github.com/repos/{owner}/{repo}/dispatches
  2. GitHub dispatches a repository_dispatch event to your repo
  3. GitHub Actions workflow triggers on this event type
  4. Workflow runs tests, deployments, or any automation
Benefits:
  • Zero infrastructure required
  • GitHub-managed reliability
  • Native integration with Actions ecosystem
  • Secrets managed in GitHub
Limitations:
  • Doesn’t create git commits (no history tracking)
  • Limited to GitHub Actions workflows
  • Can’t sync prompts to repository files
Best for: Teams that want automated deployments without managing infrastructure, and don’t need git history for prompts.
Host a webhook receiver that commits prompt changes to your git repository, providing full version control history.How it works:
  1. ABV webhook sends POST to your hosted endpoint (e.g., https://your-server.com/webhook/prompt)
  2. Webhook server receives payload and parses prompt data
  3. Server commits prompt content to repository file (e.g., abv_prompt.json)
  4. Git commit triggers normal CI/CD workflows (if configured)
Benefits:
  • Full git history for prompts (diff, blame, revert)
  • Prompts version-controlled alongside code
  • Can trigger multiple automation systems
  • Complete audit trail in repository
Limitations:
  • Requires hosting webhook server (Render, Fly.io, Heroku, etc.)
  • Additional infrastructure to maintain
  • Need GitHub PAT for API access
Best for: Teams that want prompts fully integrated into version control, or need comprehensive audit trails.
Use both patterns together for maximum automation and visibility.How it works:
  1. Set up webhook server to sync prompts to git (creates history)
  2. Git commits trigger GitHub Actions workflows (via standard on: push)
  3. Workflows run tests and deployments based on git changes
  4. All prompt changes visible in repository history
Benefits:
  • Git history for prompts
  • Automated CI/CD via GitHub Actions
  • Pull request reviews for prompt changes
  • Comprehensive automation and audit trail
Setup:
  • Deploy webhook server for git sync
  • Configure standard GitHub Actions on push events
  • Prompts flow: ABV β†’ Webhook Server β†’ Git Commit β†’ GitHub Actions β†’ Deployment
Best for: Enterprise teams requiring both automation and compliance/audit capabilities.
Secure your GitHub integration to prevent unauthorized access and ensure data integrity.Verify webhook signatures:
import hmac
import hashlib

def verify_signature(payload_body: bytes, signature: str, secret: str) -> bool:
    """Verify ABV webhook signature using signing secret."""
    expected = hmac.new(
        secret.encode(),
        payload_body,
        hashlib.sha256
    ).hexdigest()
    return hmac.compare_digest(f"sha256={expected}", signature)

# In your webhook handler:
signature = request.headers.get("x-abv-signature")
if not verify_signature(request.body, signature, SIGNING_SECRET):
    raise HTTPException(status_code=401, detail="Invalid signature")
Limit GitHub PAT scope:
  • Use fine-grained PATs restricted to specific repositories
  • For repository_dispatch: actions: read and write
  • For git commits: contents: read and write, metadata: read-only
  • Never use classic tokens with full repo scope unless absolutely necessary
Environment variables:
  • Store GitHub tokens in environment variables or secrets managers
  • Never commit tokens to repositories
  • Rotate tokens periodically
Handle retries safely:
  • ABV retries failed webhooks with exponential backoff
  • Design webhook handlers to be idempotent (duplicate events safe)
  • Return 2xx status codes on success to prevent retries

Implementation: GitHub Actions (No Infrastructure)

Create .github/workflows/abv-ci.yml in your repository:
name: ABV Prompt CI
on:
  repository_dispatch:
    types: [abv-prompt-update]
  workflow_dispatch:  # Allow manual triggers

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

      - name: Display prompt info
        run: |
          echo "Action: ${{ github.event.client_payload.action }}"
          echo "Prompt: ${{ github.event.client_payload.prompt.name }}"
          echo "Version: ${{ github.event.client_payload.prompt.version }}"
          echo "Labels: ${{ github.event.client_payload.prompt.labels }}"

      - name: Run tests
        run: |
          # Add your test commands
          npm test
          # or: python -m pytest

  deploy:
    needs: test
    runs-on: ubuntu-latest
    # Only deploy prompts labeled "production"
    if: contains(github.event.client_payload.prompt.labels, 'production')
    steps:
      - uses: actions/checkout@v4

      - name: Deploy to production
        run: |
          echo "Deploying ${{ github.event.client_payload.prompt.name }} v${{ github.event.client_payload.prompt.version }}"
          # Your deployment commands
          # ./deploy.sh

      - name: Notify team
        run: |
          echo "Deployed prompt: ${{ github.event.client_payload.prompt.name }} v${{ github.event.client_payload.prompt.version }}"
          # Send Slack notification, etc.
Accessing webhook data:
  • Use github.event.client_payload.* to access prompt data
  • github.event.client_payload.action: created, updated, or deleted
  • github.event.client_payload.prompt.*: Full prompt object (name, version, labels, content, etc.)
Conditional execution:
  • Use if: contains(github.event.client_payload.prompt.labels, 'production') to run steps only for production prompts
  • Use if: github.event.client_payload.action == 'created' to run on prompt creation only
Generate a GitHub Personal Access Token with appropriate permissions.Token creation steps:
  1. Go to GitHub Settings β†’ Developer settings β†’ Personal access tokens
  2. Click Generate new token
  3. Choose token type and set permissions:
Token TypeRequired Permissions
Personal Access Token (classic)repo scope (for private repos) or public_repo scope (for public repos)
Fine-grained PATRepository permissions: Actions: read and write, Metadata: read-only
GitHub AppRepository permissions: Actions: read and write
  1. Generate token and copy it immediately (won’t be shown again)
  2. Store token securely for ABV webhook configuration
Security tip: Fine-grained PATs are recommended as they can be scoped to specific repositories and have expiration dates.
Configure ABV to send webhooks to GitHub’s repository_dispatch API.Configuration steps:
  1. In ABV dashboard, navigate to Prompts β†’ Webhooks
  2. Click Create Webhook
  3. Set the following fields:
Endpoint URL:
https://api.github.com/repos/{owner}/{repo}/dispatches
Replace {owner} with your GitHub username or organization, and {repo} with repository name.Example:
https://api.github.com/repos/acme-corp/production-app/dispatches
Headers: Add two headers:
Accept: application/vnd.github+json
Authorization: Bearer {your_github_token}
Replace {your_github_token} with the PAT created in Step 2.Request body template (ABV automatically sends this):
{
  "event_type": "abv-prompt-update",
  "client_payload": {
    "action": "{{action}}",
    "prompt": {{prompt}}
  }
}
  1. (Optional) Filter events by labels or tags to reduce noise
  2. Click Save
Important: ABV encrypts and securely stores your GitHub token in the Authorization header.
Verify the integration works end-to-end.Testing steps:
  1. Update a prompt in ABV:
    • Open an existing prompt or create a new one
    • Make a change (edit content, update variables, etc.)
    • Assign the production label
    • Save the prompt (creates a new version)
  2. Check GitHub Actions tab:
    • Navigate to your repository on GitHub
    • Click Actions tab
    • Look for a new workflow run named β€œABV Prompt CI”
    • Workflow should show β€œrepository_dispatch” as trigger
  3. Verify workflow execution:
    • Click into the workflow run
    • Verify test job completes successfully
    • Verify deploy job runs (if prompt had production label)
    • Check job logs to see prompt data (name, version, labels)
  4. Troubleshooting:
    • If workflow doesn’t trigger: verify webhook URL and GitHub token
    • If workflow fails: check logs for errors
    • If deploy job skipped: verify prompt has production label
Expected result: Prompt change in ABV automatically triggers GitHub Actions workflow, runs tests, and deploys (if conditions met).

Implementation: Webhook Server (Git Sync)

The webhook server receives prompt change events from ABV and commits them to your GitHub repository.Flow diagram:GitHub Integration FlowWorkflow:
  1. User saves prompt in ABV
  2. ABV sends webhook POST to your server
  3. Server validates webhook signature
  4. Server fetches current file SHA from GitHub
  5. Server commits updated prompt JSON to repository
  6. Git commit triggers standard CI/CD workflows
Components:
  • ABV Webhook: Sends prompt change events
  • Webhook Server: FastAPI application (or any language/framework)
  • GitHub API: Receives commits via REST API
  • Repository: Stores prompts in version control
Set up a webhook in ABV to send events to your webhook server.Configuration steps:
  1. In ABV dashboard, navigate to Prompts β†’ Webhooks
  2. Click Create Webhook
  3. Configure the following:
Endpoint URL:
https://your-domain.com/webhook/prompt
(You’ll deploy the server in later steps)Events: (optional filters)
  • created: Trigger when new prompt versions are created
  • updated: Trigger when prompts are updated
  • deleted: Trigger when prompts are deleted
Default: all events selected.Signing Secret:
  • ABV generates a signing secret automatically
  • Copy this secret and save it securely (you’ll need it for webhook verification)
  1. Click Save
Sample webhook payload:
{
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "timestamp": "2024-07-10T10:30:00Z",
  "type": "prompt-version",
  "action": "created",
  "prompt": {
    "id": "prompt_abc123",
    "name": "movie-critic",
    "version": 3,
    "projectId": "xyz789",
    "labels": ["production", "latest"],
    "prompt": "As a {{criticLevel}} movie critic, rate {{movie}} out of 10.",
    "type": "text",
    "config": {},
    "commitMessage": "Improved critic persona",
    "tags": ["entertainment"],
    "createdAt": "2024-07-10T10:30:00Z",
    "updatedAt": "2024-07-10T10:30:00Z"
  }
}
Note: Your webhook endpoint must return 2xx status codes for successful processing. ABV retries failed webhooks with exponential backoff.
Set up GitHub repository and create a PAT for API access.Create .env file with configuration:
# GitHub credentials
GITHUB_TOKEN=ghp_your_github_pat_here
GITHUB_REPO_OWNER=your-username-or-org
GITHUB_REPO_NAME=your-repo-name

# Optional configurations
GITHUB_FILE_PATH=abv_prompt.json  # File to store prompts
GITHUB_BRANCH=main                # Target branch
REQUIRED_LABEL=production         # Only sync prompts with this label (empty = sync all)
GitHub PAT permissions: Create a token with minimal required permissions:
Permission TypeRequired Scopes
Fine-grained PAT (recommended)Repository permissions:
β€’ Contents: Read and write
β€’ Metadata: Read-only
Classic PATβ€’ public_repo (for public repositories)
β€’ repo (for private repositories)
PAT creation:
  1. GitHub Settings β†’ Developer settings β†’ Personal access tokens
  2. Generate new token (fine-grained recommended)
  3. Select repository access (specific repo or all repos)
  4. Set permissions as listed above
  5. Generate and copy token
  6. Add to .env file as GITHUB_TOKEN
Security: Fine-grained PATs expire and can be scoped to specific repositories, reducing security risk.
Create a webhook server that receives ABV events and commits to GitHub.Create main.py:
from typing import Any, Dict
from uuid import UUID
import json
import base64

import httpx
from pydantic import BaseModel, Field
from pydantic_settings import BaseSettings, SettingsConfigDict
from fastapi import FastAPI, HTTPException, Body

class GitHubSettings(BaseSettings):
    """GitHub repository configuration."""
    GITHUB_TOKEN: str
    GITHUB_REPO_OWNER: str
    GITHUB_REPO_NAME: str
    GITHUB_FILE_PATH: str = "abv_prompt.json"
    GITHUB_BRANCH: str = "main"
    REQUIRED_LABEL: str = ""  # Optional: only sync prompts with this label

    model_config = SettingsConfigDict(
        env_file=".env",
        env_file_encoding="utf-8",
        case_sensitive=True
    )

config = GitHubSettings()

class ABVEvent(BaseModel):
    """ABV webhook event structure."""
    id: UUID = Field(description="Event identifier")
    timestamp: str = Field(description="Event timestamp")
    type: str = Field(description="Event type")
    action: str = Field(description="Performed action")
    prompt: Dict[str, Any] = Field(description="Prompt content")

async def sync(event: ABVEvent) -> Dict[str, Any]:
    """Synchronize prompt data to GitHub repository."""
    # Check if prompt has required label (if specified)
    if config.REQUIRED_LABEL:
        prompt_labels = event.prompt.get("labels", [])
        if config.REQUIRED_LABEL not in prompt_labels:
            return {"skipped": f"Prompt does not have required label '{config.REQUIRED_LABEL}'"}

    api_endpoint = f"https://api.github.com/repos/{config.GITHUB_REPO_OWNER}/{config.GITHUB_REPO_NAME}/contents/{config.GITHUB_FILE_PATH}"

    request_headers = {
        "Authorization": f"Bearer {config.GITHUB_TOKEN}",
        "Accept": "application/vnd.github.v3+json"
    }

    content_json = json.dumps(event.prompt, indent=2)
    encoded_content = base64.b64encode(content_json.encode("utf-8")).decode("utf-8")

    name = event.prompt.get("name", "unnamed")
    version = event.prompt.get("version", "unknown")
    message = f"{event.action}: {name} v{version}"

    payload = {
        "message": message,
        "content": encoded_content,
        "branch": config.GITHUB_BRANCH
    }

    async with httpx.AsyncClient() as http_client:
        try:
            existing = await http_client.get(api_endpoint, headers=request_headers, params={"ref": config.GITHUB_BRANCH})
            if existing.status_code == 200:
                payload["sha"] = existing.json().get("sha")
        except Exception:
            pass

        try:
            response = await http_client.put(api_endpoint, headers=request_headers, json=payload)
            response.raise_for_status()
            return response.json()
        except Exception as e:
            raise HTTPException(status_code=500, detail=f"Repository sync failed: {str(e)}")

app = FastAPI(title="ABV GitHub Sync", version="1.0")

@app.post("/webhook/prompt", status_code=201)
async def receive_webhook(event: ABVEvent = Body(...)):
    """Process ABV webhook and sync to GitHub."""
    result = await sync(event)
    return {
        "status": "synced",
        "commit_info": result.get("commit", {}),
        "file_info": result.get("content", {})
    }

@app.get("/status")
async def health_status():
    """Service health check."""
    return {"healthy": True}
How it works:
  • Validates webhook payload structure using Pydantic
  • Checks if prompt has required label (if configured)
  • Fetches existing file SHA from GitHub (required for updates)
  • Commits prompt JSON to repository with descriptive message
  • Returns commit info to ABV (2xx status prevents retries)
Install dependencies:
pip install fastapi uvicorn pydantic-settings httpx
Run locally:
uvicorn main:app --reload --port 8000
Access health check at http://localhost:8000/status.Local testing: Use ngrok or similar to expose localhost for webhook testing:
ngrok http 8000
# Use ngrok URL in ABV webhook configuration
Deploy the webhook server to a public HTTPS endpoint.Deployment options:Option 1: Render.com (recommended for beginners)
  1. Create requirements.txt:
    fastapi
    uvicorn[standard]
    pydantic-settings
    httpx
    
  2. Create render.yaml:
    services:
      - type: web
        name: abv-github-sync
        env: python
        buildCommand: pip install -r requirements.txt
        startCommand: uvicorn main:app --host 0.0.0.0 --port $PORT
        envVars:
          - key: GITHUB_TOKEN
            sync: false
          - key: GITHUB_REPO_OWNER
            sync: false
          - key: GITHUB_REPO_NAME
            sync: false
    
  3. Push to GitHub and connect repository in Render dashboard
  4. Set environment variables in Render dashboard
  5. Deploy
Option 2: Fly.io
  1. Install Fly CLI: curl -L https://fly.io/install.sh | sh
  2. Run fly launch and follow prompts
  3. Set secrets: fly secrets set GITHUB_TOKEN=... GITHUB_REPO_OWNER=... GITHUB_REPO_NAME=...
  4. Deploy: fly deploy
Option 3: Heroku
  1. Create Procfile:
    web: uvicorn main:app --host 0.0.0.0 --port $PORT
    
  2. Deploy via Heroku CLI or GitHub integration
  3. Set config vars in Heroku dashboard
After deployment:
  1. Note your public HTTPS URL (e.g., https://abv-sync.onrender.com)
  2. Update ABV webhook endpoint to https://your-domain.com/webhook/prompt
  3. Test by updating a prompt in ABV
  4. Verify new commit appears in GitHub repository
Verify the webhook server is working correctly.Test health endpoint:
curl https://your-domain.com/status
# Should return: {"healthy": true}
Test webhook endpoint manually:
curl -X POST https://your-domain.com/webhook/prompt \
  -H "Content-Type: application/json" \
  -d '{
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "timestamp": "2024-07-10T10:30:00Z",
    "type": "prompt-version",
    "action": "created",
    "prompt": {
      "name": "test-prompt",
      "version": 1,
      "labels": ["production"],
      "prompt": "Test prompt content",
      "type": "text"
    }
  }'
Expected response:
{
  "status": "synced",
  "commit_info": {...},
  "file_info": {...}
}
Check GitHub repository:
  • Navigate to your repository
  • Look for new commit with message: created: test-prompt v1
  • Verify abv_prompt.json contains the prompt data
Common issues:
IssueSolution
401 UnauthorizedVerify GitHub token has correct permissions
404 Not FoundCheck GITHUB_REPO_OWNER and GITHUB_REPO_NAME are correct
409 ConflictFile SHA mismatch; ensure server fetches current SHA
No commit createdCheck server logs for errors; verify token has contents: write permission
ABV shows failed webhooksEnsure server returns 2xx status code; check server logs
View server logs:
  • Render: Dashboard β†’ Logs tab
  • Fly.io: fly logs
  • Heroku: heroku logs --tail

Advanced Patterns

Sync only prompts with specific labels (e.g., production) to avoid cluttering git history with experiments.In webhook server .env:
REQUIRED_LABEL=production
Behavior:
  • Prompts with production label: synced to GitHub
  • Prompts without production label: skipped (webhook returns success without committing)
In GitHub Actions:
# Only deploy production-labeled prompts
if: contains(github.event.client_payload.prompt.labels, 'production')
This pattern ensures only vetted, production-ready prompts trigger deployments or appear in git history.
Store different prompts in separate files for better organization.Modify webhook server to use prompt name as file path:
# Instead of fixed GITHUB_FILE_PATH
file_path = f"prompts/{event.prompt['name']}.json"

api_endpoint = f"https://api.github.com/repos/{config.GITHUB_REPO_OWNER}/{config.GITHUB_REPO_NAME}/contents/{file_path}"
Result:
prompts/
  movie-critic.json
  customer-support.json
  billing-assistant.json
Each prompt gets its own file, making git diffs clearer and enabling granular access control.
Trigger different workflows based on prompt metadata (labels, tags, name patterns).GitHub Actions with conditional workflows:
name: ABV Prompt Workflows
on:
  repository_dispatch:
    types: [abv-prompt-update]

jobs:
  deploy-production:
    if: contains(github.event.client_payload.prompt.labels, 'production')
    runs-on: ubuntu-latest
    steps:
      - name: Deploy to production
        run: ./deploy-prod.sh

  deploy-staging:
    if: contains(github.event.client_payload.prompt.labels, 'staging')
    runs-on: ubuntu-latest
    steps:
      - name: Deploy to staging
        run: ./deploy-staging.sh

  run-experiments:
    if: contains(github.event.client_payload.prompt.tags, 'experiment')
    runs-on: ubuntu-latest
    steps:
      - name: Run A/B test
        run: ./run-ab-test.sh
Use cases:
  • Deploy production-labeled prompts to production environment
  • Deploy staging-labeled prompts to staging environment
  • Run experiments for prompts tagged with experiment
Verify webhook signatures to ensure requests come from ABV and haven’t been tampered with.Add to webhook server:
import hmac
import hashlib
from fastapi import Header, HTTPException, Request

SIGNING_SECRET = "your-signing-secret-from-abv"

def verify_signature(payload_body: bytes, signature: str) -> bool:
    """Verify ABV webhook signature."""
    expected = hmac.new(
        SIGNING_SECRET.encode(),
        payload_body,
        hashlib.sha256
    ).hexdigest()
    return hmac.compare_digest(f"sha256={expected}", signature)

@app.post("/webhook/prompt", status_code=201)
async def receive_webhook(
    request: Request,
    event: ABVEvent = Body(...),
    x_abv_signature: str = Header(...)
):
    """Process ABV webhook with signature verification."""
    body = await request.body()

    if not verify_signature(body, x_abv_signature):
        raise HTTPException(status_code=401, detail="Invalid signature")

    result = await sync(event)
    return {"status": "synced", "commit_info": result.get("commit", {})}
Benefits:
  • Prevents unauthorized webhook calls
  • Ensures payload integrity (not modified in transit)
  • Production security best practice

Next Steps