Skip to main content

CI/CD Integration with Toggly Release Tracker

This guide explains how to integrate any CI/CD system (Azure DevOps, GitHub Actions, GitLab CI, Jenkins, CircleCI, etc.) with Toggly's Release Tracker feature via HTTP APIs.

Table of Contents​

  1. Concepts
  2. Authentication
  3. API Endpoints
  4. Integration Examples

Concepts​

Release​

A Release is a collection of feature flag changes that are managed together. Each release has:

  • Status: Draft, Pending, Live, RolledBack, or Failed
  • Feature Changes: The feature flags that will be modified when the release is activated
  • CI Links: Associations with CI/CD builds
  • Gates: Automated checks that must pass before activation
  • Environment: The target environment (e.g., "Production", "Staging")

Build Association​

Build Association links a CI/CD build to a release. This allows you to:

  • Track which builds are part of a release
  • View build information (branch, commit SHA, build number) in the release
  • Automatically find or create releases based on build criteria

Deployment Events​

Deployment Events notify Toggly about deployment lifecycle events:

  • deployment_started: Deployment has begun
  • deployment_completed: Deployment finished successfully
  • deployment_failed: Deployment failed

These events can trigger gate evaluations automatically.

Gates and Gate Status​

Gates are automated checks that validate whether a release is safe to activate. Gate types include:

  • Metrics: Check if metrics meet thresholds (e.g., error rate < 1%)
  • Anomaly: Detect anomalies in metrics
  • Manual: Require manual approval
  • External: Call external webhooks/APIs for validation

Gate status can be:

  • pending: Gate hasn't been evaluated yet or is waiting
  • passed: Gate check passed
  • failed: Gate check failed

Activate / Rollback​

  • Activate: Applies the feature flag changes defined in the release to the target environment. Captures the current state as fromState for potential rollback.
  • Rollback: Restores feature flags to their previous state (fromState) before the release was activated.

Authentication​

All API endpoints require authentication using a Bearer token (JWT).

Obtaining an API Token​

  1. Log in to Toggly
  2. Navigate to your Team settings
  3. Create an API credential with the apiAccess claim
  4. Copy the generated JWT token

Using the Token​

Include the token in the Authorization header:

Authorization: Bearer <your-jwt-token>

Permission Scope​

API tokens are scoped to:

  • Team: The token must have access to the team that owns the application
  • Application: Access is limited to applications within the authorized team
  • Environment: Access is limited to environments within the authorized application

Ensure your API token has the apiAccess: true claim and is associated with the correct team.

API Endpoints​

All endpoints are under the /api/releases base path and require the ReleaseTracker feature flag to be enabled.

1. Associate Build with Release​

Associates a CI/CD build with a release. If no matching draft release exists, creates a new one.

Endpoint: POST /api/releases/associate-build

Request Body:

{
"projectKey": "my-app",
"environment": "Production",
"ciProvider": "github",
"build": {
"runId": "123456",
"runUrl": "https://github.com/org/repo/actions/runs/123456",
"pipelineName": "deploy-production",
"branch": "main",
"commitSha": "abc123def456",
"buildNumber": "1.2.3"
},
"mode": "use-latest-draft-or-create",
"matchCriteria": {
"byBranch": true
},
"releaseTemplateKey": "default",
"createOptions": {
"namePattern": "Release ${branch} - ${buildNumber}"
}
}

Request Fields:

  • projectKey (required): Application ID or Name
  • environment (required): Environment name (e.g., "Production", "Staging")
  • ciProvider (required): CI provider identifier (e.g., "azure-devops", "github", "gitlab", "jenkins", "circleci")
  • build (required): Build information object
    • runId (required): Unique CI run/build ID
    • runUrl (optional): URL to view the build in CI system
    • pipelineName (required): Name of the pipeline/workflow
    • branch (optional): Git branch name
    • commitSha (optional): Git commit SHA
    • buildNumber (optional): Build number/version
  • mode (optional): How to find/create release. Default: "use-latest-draft-or-create"
  • matchCriteria (optional): Criteria for matching existing releases
    • byBranch: Match releases by branch name
    • byService: Match releases by service/project
  • releaseTemplateKey (optional): Template key to use when creating new release
  • createOptions (optional): Options for creating new releases
    • namePattern: Handlebars-style pattern (supports ${branch}, ${buildNumber}, ${commitSha})

Response (200 OK):

{
"releaseId": "abc123",
"releaseUrl": "https://toggly.io/releases/abc123"
}

Response Fields:

  • releaseId: The release ID (use this for subsequent API calls)
  • releaseUrl: URL to view the release in Toggly UI

Error Responses:

  • 400 Bad Request: Validation errors (missing required fields, invalid environment, etc.)
  • 401 Unauthorized: Missing or invalid authentication token
  • 403 Forbidden: Token doesn't have access to the application/team
  • 404 Not Found: Application not found

2. Get Release Details​

Retrieves full details about a release, including feature changes, CI links, gate status, and deployment events.

Endpoint: GET /api/releases/{releaseId}

Path Parameters:

  • releaseId: The release ID (from associate-build response)

Response (200 OK):

{
"id": "abc123",
"applicationId": "app-456",
"environment": "Production",
"name": "Release main - 1.2.3",
"status": "Draft",
"releaseNotes": "Deploying new features",
"featureChanges": [
{
"flagKey": "new-feature",
"fromState": null,
"toState": [
{
"name": "AlwaysOn",
"parameters": null
}
]
}
],
"ciLinks": [
{
"provider": "github",
"pipelineName": "deploy-production",
"runId": "123456",
"runUrl": "https://github.com/org/repo/actions/runs/123456",
"branch": "main",
"commitSha": "abc123def456",
"buildNumber": "1.2.3"
}
],
"gateStatus": {
"releaseId": "abc123",
"overallStatus": "pending",
"gates": [
{
"gateId": "gate-1",
"name": "Error Rate Check",
"status": "pending",
"lastCheckedAt": null,
"details": null
}
]
},
"latestGateEvaluations": [],
"latestDeploymentEvents": [],
"createdBy": "user-789",
"createdAt": "2024-01-15T10:00:00Z",
"updatedAt": "2024-01-15T10:00:00Z",
"activatedAt": null,
"rolledBackAt": null
}

Response Fields:

  • id: Release ID
  • applicationId: Application ID
  • environment: Environment name
  • name: Release name
  • status: Release status (Draft, Pending, Live, RolledBack, Failed)
  • releaseNotes: Optional release notes
  • featureChanges: List of feature flag changes
  • ciLinks: List of associated CI builds
  • gateStatus: Current gate status summary
  • latestGateEvaluations: Recent gate evaluation results
  • latestDeploymentEvents: Recent deployment events
  • createdBy: User ID who created the release
  • createdAt: Creation timestamp
  • updatedAt: Last update timestamp
  • activatedAt: Activation timestamp (if activated)
  • rolledBackAt: Rollback timestamp (if rolled back)

Error Responses:

  • 401 Unauthorized: Missing or invalid authentication token
  • 403 Forbidden: Token doesn't have access to the release
  • 404 Not Found: Release not found

3. Notify Deployment Event​

Notifies Toggly about a deployment lifecycle event. This can trigger automatic gate evaluations.

Endpoint: POST /api/releases/{releaseId}/events

Path Parameters:

  • releaseId: The release ID

Request Body:

{
"eventType": "deployment_completed",
"provider": "github",
"environment": "Production",
"metadata": {
"deploymentId": "deploy-123",
"duration": 300,
"instanceCount": 3
}
}

Request Fields:

  • eventType (required): Event type - "deployment_started", "deployment_completed", or "deployment_failed"
  • provider (required): CI provider identifier
  • environment (required): Environment name
  • metadata (optional): Additional event metadata as key-value pairs

Response (200 OK):

Returns the updated gate status (same format as GET /api/releases/{releaseId}/gates/status):

{
"releaseId": "abc123",
"overallStatus": "passed",
"gates": [
{
"gateId": "gate-1",
"name": "Error Rate Check",
"status": "passed",
"lastCheckedAt": "2024-01-15T10:05:00Z",
"details": {
"errorRate": 0.005,
"threshold": 0.01
}
}
]
}

Error Responses:

  • 400 Bad Request: Invalid event type or validation errors
  • 401 Unauthorized: Missing or invalid authentication token
  • 403 Forbidden: Token doesn't have access to the release
  • 404 Not Found: Release not found

4. Get Gates Status​

Retrieves the current status of all gates for a release.

Endpoint: GET /api/releases/{releaseId}/gates/status

Path Parameters:

  • releaseId: The release ID

Response (200 OK):

{
"releaseId": "abc123",
"overallStatus": "pending",
"gates": [
{
"gateId": "gate-1",
"name": "Error Rate Check",
"status": "pending",
"lastCheckedAt": null,
"details": null
},
{
"gateId": "gate-2",
"name": "Manual Approval",
"status": "pending",
"lastCheckedAt": null,
"details": null
}
]
}

Response Fields:

  • releaseId: Release ID
  • overallStatus: Overall gate status - "pending", "passed", or "failed"
  • gates: List of individual gate statuses
    • gateId: Gate identifier
    • name: Gate name
    • status: Gate status - "pending", "passed", or "failed"
    • lastCheckedAt: Timestamp of last evaluation
    • details: Additional evaluation details (JSON object)

Error Responses:

  • 401 Unauthorized: Missing or invalid authentication token
  • 403 Forbidden: Token doesn't have access to the release
  • 404 Not Found: Release not found

5. Activate Release​

Activates a release by applying all feature flag changes to the target environment. Captures the current state as fromState for potential rollback.

Endpoint: POST /api/releases/{releaseId}/activate

Path Parameters:

  • releaseId: The release ID

Request Body: None (empty body)

Response (200 OK):

Returns the updated release model (same format as GET /api/releases/{releaseId}):

{
"id": "abc123",
"applicationId": "app-456",
"environment": "Production",
"name": "Release main - 1.2.3",
"status": "Live",
"activatedAt": "2024-01-15T10:10:00Z",
...
}

Error Responses:

  • 400 Bad Request: Release is not in a valid state for activation (must be Draft or Pending)
  • 401 Unauthorized: Missing or invalid authentication token
  • 403 Forbidden: Token doesn't have access to the release
  • 404 Not Found: Release or application environment not found

Important Notes:

  • Activation captures the current feature flag state as fromState for each changed flag
  • Once activated, the release status changes to Live
  • Feature flags are immediately updated in the target environment

6. Rollback Release​

Rolls back a release by restoring feature flags to their previous state (fromState).

Endpoint: POST /api/releases/{releaseId}/rollback

Path Parameters:

  • releaseId: The release ID

Request Body: None (empty body)

Response (200 OK):

Returns the updated release model:

{
"id": "abc123",
"status": "RolledBack",
"rolledBackAt": "2024-01-15T10:15:00Z",
...
}

Error Responses:

  • 400 Bad Request: Release is not in a valid state for rollback (must be Live)
  • 401 Unauthorized: Missing or invalid authentication token
  • 403 Forbidden: Token doesn't have access to the release
  • 404 Not Found: Release or application environment not found

Important Notes:

  • Rollback only works for releases in Live status
  • If a feature flag was modified by another release after activation, a conflict is logged but rollback still proceeds
  • Once rolled back, the release status changes to RolledBack

Integration Examples​

Example 1: Generic curl-based Integration​

This example shows how to integrate using shell scripts and curl commands.

#!/bin/bash

# Configuration
TOGGLY_API_URL="https://api.toggly.io"
TOGGLY_API_TOKEN="your-jwt-token-here"
PROJECT_KEY="my-app"
ENVIRONMENT="Production"
CI_PROVIDER="github"
BUILD_RUN_ID="${GITHUB_RUN_ID}"
BUILD_RUN_URL="${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID}"
PIPELINE_NAME="deploy-production"
BRANCH="${GITHUB_REF#refs/heads/}"
COMMIT_SHA="${GITHUB_SHA}"
BUILD_NUMBER="${GITHUB_RUN_NUMBER}"

# Step 1: Associate build with release
echo "Associating build with release..."
ASSOCIATE_RESPONSE=$(curl -s -X POST "${TOGGLY_API_URL}/api/releases/associate-build" \
-H "Authorization: Bearer ${TOGGLY_API_TOKEN}" \
-H "Content-Type: application/json" \
-d "{
\"projectKey\": \"${PROJECT_KEY}\",
\"environment\": \"${ENVIRONMENT}\",
\"ciProvider\": \"${CI_PROVIDER}\",
\"build\": {
\"runId\": \"${BUILD_RUN_ID}\",
\"runUrl\": \"${BUILD_RUN_URL}\",
\"pipelineName\": \"${PIPELINE_NAME}\",
\"branch\": \"${BRANCH}\",
\"commitSha\": \"${COMMIT_SHA}\",
\"buildNumber\": \"${BUILD_NUMBER}\"
},
\"mode\": \"use-latest-draft-or-create\",
\"matchCriteria\": {
\"byBranch\": true
}
}")

# Extract release ID
RELEASE_ID=$(echo "${ASSOCIATE_RESPONSE}" | jq -r '.releaseId')
echo "Release ID: ${RELEASE_ID}"

# Step 2: Deploy application (your deployment logic here)
echo "Deploying application..."
# ... your deployment commands ...

# Step 3: Notify deployment started
echo "Notifying deployment started..."
curl -s -X POST "${TOGGLY_API_URL}/api/releases/${RELEASE_ID}/events" \
-H "Authorization: Bearer ${TOGGLY_API_TOKEN}" \
-H "Content-Type: application/json" \
-d "{
\"eventType\": \"deployment_started\",
\"provider\": \"${CI_PROVIDER}\",
\"environment\": \"${ENVIRONMENT}\"
}" > /dev/null

# Step 4: Wait for deployment to complete
# ... your deployment wait logic ...

# Step 5: Notify deployment completed
echo "Notifying deployment completed..."
curl -s -X POST "${TOGGLY_API_URL}/api/releases/${RELEASE_ID}/events" \
-H "Authorization: Bearer ${TOGGLY_API_TOKEN}" \
-H "Content-Type: application/json" \
-d "{
\"eventType\": \"deployment_completed\",
\"provider\": \"${CI_PROVIDER}\",
\"environment\": \"${ENVIRONMENT}\",
\"metadata\": {
\"duration\": 300,
\"instanceCount\": 3
}
}" > /dev/null

# Step 6: Poll gate status until passed or failed
echo "Waiting for gates to pass..."
MAX_ATTEMPTS=30
ATTEMPT=0
GATE_STATUS="pending"

while [ "${GATE_STATUS}" = "pending" ] && [ ${ATTEMPT} -lt ${MAX_ATTEMPTS} ]; do
sleep 10
ATTEMPT=$((ATTEMPT + 1))

GATE_RESPONSE=$(curl -s -X GET "${TOGGLY_API_URL}/api/releases/${RELEASE_ID}/gates/status" \
-H "Authorization: Bearer ${TOGGLY_API_TOKEN}")

GATE_STATUS=$(echo "${GATE_RESPONSE}" | jq -r '.overallStatus')
echo "Gate status (attempt ${ATTEMPT}): ${GATE_STATUS}"
done

# Step 7: Activate or rollback based on gate status
if [ "${GATE_STATUS}" = "passed" ]; then
echo "Gates passed! Activating release..."
curl -s -X POST "${TOGGLY_API_URL}/api/releases/${RELEASE_ID}/activate" \
-H "Authorization: Bearer ${TOGGLY_API_TOKEN}" \
-H "Content-Type: application/json" > /dev/null
echo "Release activated successfully!"
exit 0
elif [ "${GATE_STATUS}" = "failed" ]; then
echo "Gates failed! Rolling back release..."
curl -s -X POST "${TOGGLY_API_URL}/api/releases/${RELEASE_ID}/rollback" \
-H "Authorization: Bearer ${TOGGLY_API_TOKEN}" \
-H "Content-Type: application/json" > /dev/null
echo "Release rolled back!"
exit 1
else
echo "Gates still pending after ${MAX_ATTEMPTS} attempts. Manual intervention required."
exit 2
fi

Example 2: Pseudo-code CI Integration Flow​

This example shows a high-level integration flow that can be adapted to any CI/CD system.

# Pseudo-code for CI/CD integration
import requests
import time

class TogglyReleaseTracker:
def __init__(self, api_url, api_token):
self.api_url = api_url
self.headers = {
"Authorization": f"Bearer {api_token}",
"Content-Type": "application/json"
}

def associate_build(self, project_key, environment, ci_provider, build_info):
"""Associate CI build with a release (creates new if needed)"""
url = f"{self.api_url}/api/releases/associate-build"
payload = {
"projectKey": project_key,
"environment": environment,
"ciProvider": ci_provider,
"build": build_info,
"mode": "use-latest-draft-or-create",
"matchCriteria": {
"byBranch": True
}
}
response = requests.post(url, json=payload, headers=self.headers)
response.raise_for_status()
return response.json()["releaseId"]

def notify_deployment_event(self, release_id, event_type, provider, environment, metadata=None):
"""Notify Toggly about a deployment event"""
url = f"{self.api_url}/api/releases/{release_id}/events"
payload = {
"eventType": event_type,
"provider": provider,
"environment": environment,
"metadata": metadata or {}
}
response = requests.post(url, json=payload, headers=self.headers)
response.raise_for_status()
return response.json()

def get_gate_status(self, release_id):
"""Get current gate status for a release"""
url = f"{self.api_url}/api/releases/{release_id}/gates/status"
response = requests.get(url, headers=self.headers)
response.raise_for_status()
return response.json()

def activate_release(self, release_id):
"""Activate a release (apply feature flag changes)"""
url = f"{self.api_url}/api/releases/{release_id}/activate"
response = requests.post(url, headers=self.headers)
response.raise_for_status()
return response.json()

def rollback_release(self, release_id):
"""Rollback a release (restore previous feature flag state)"""
url = f"{self.api_url}/api/releases/{release_id}/rollback"
response = requests.post(url, headers=self.headers)
response.raise_for_status()
return response.json()


# Main integration flow
def main():
# Initialize tracker
tracker = TogglyReleaseTracker(
api_url="https://api.toggly.io",
api_token=os.environ["TOGGLY_API_TOKEN"]
)

# Get CI/CD build information from environment variables
build_info = {
"runId": os.environ["CI_RUN_ID"],
"runUrl": os.environ["CI_RUN_URL"],
"pipelineName": os.environ["CI_PIPELINE_NAME"],
"branch": os.environ["CI_BRANCH"],
"commitSha": os.environ["CI_COMMIT_SHA"],
"buildNumber": os.environ["CI_BUILD_NUMBER"]
}

# Step 1: Associate build with release
print("Associating build with release...")
release_id = tracker.associate_build(
project_key="my-app",
environment="Production",
ci_provider="github",
build_info=build_info
)
print(f"Release ID: {release_id}")

# Step 2: Deploy application
print("Deploying application...")
deploy_application() # Your deployment logic

# Step 3: Notify deployment started
print("Notifying deployment started...")
tracker.notify_deployment_event(
release_id=release_id,
event_type="deployment_started",
provider="github",
environment="Production"
)

# Step 4: Wait for deployment to complete
wait_for_deployment_completion()

# Step 5: Notify deployment completed
print("Notifying deployment completed...")
gate_status_response = tracker.notify_deployment_event(
release_id=release_id,
event_type="deployment_completed",
provider="github",
environment="Production",
metadata={
"duration": 300,
"instanceCount": 3
}
)

# Step 6: Poll gate status until passed/failed
print("Waiting for gates to pass...")
max_attempts = 30
attempt = 0
overall_status = "pending"

while overall_status == "pending" and attempt < max_attempts:
time.sleep(10) # Wait 10 seconds between checks
attempt += 1

gate_status = tracker.get_gate_status(release_id)
overall_status = gate_status["overallStatus"]

print(f"Gate status (attempt {attempt}): {overall_status}")

# Print individual gate statuses
for gate in gate_status["gates"]:
print(f" - {gate['name']}: {gate['status']}")

# Step 7: Activate or rollback based on gate status
if overall_status == "passed":
print("Gates passed! Activating release...")
tracker.activate_release(release_id)
print("Release activated successfully!")
return 0
elif overall_status == "failed":
print("Gates failed! Rolling back release...")
tracker.rollback_release(release_id)
print("Release rolled back!")
return 1
else:
print(f"Gates still pending after {max_attempts} attempts. Manual intervention required.")
return 2


if __name__ == "__main__":
exit(main())

Additional Notes​

Release Templates​

When creating a new release via associate-build, you can specify a releaseTemplateKey to use predefined gate configurations and naming patterns. Templates are configured in the Toggly UI.

Gate Evaluation Timing​

  • Gates with trigger: OnDeploymentCompleted are automatically evaluated when you send a deployment_completed event
  • Manual gates require approval via the Toggly UI
  • You can poll gates/status to check gate evaluation progress

Error Handling​

All endpoints return standard HTTP status codes:

  • 200 OK: Success
  • 400 Bad Request: Validation errors (check response body for details)
  • 401 Unauthorized: Missing or invalid authentication token
  • 403 Forbidden: Token doesn't have access to the resource
  • 404 Not Found: Resource not found
  • 500 Internal Server Error: Server error

Always check the response status code and handle errors appropriately in your integration.

Rate Limiting​

Toggly may implement rate limiting on API endpoints. If you receive 429 Too Many Requests, implement exponential backoff and retry logic.

Feature Flag State Management​

  • When a release is activated, the current state of each feature flag is captured as fromState
  • The toState is then applied to the environment
  • On rollback, fromState is restored
  • If a feature flag is modified by another release after activation, rollback will still proceed but may log conflicts