Skip to main content
The deployment webhook is how Periscope receives data about your deployments. You send a standardized JSON payload from your CI/CD pipeline (GitHub Actions, Azure DevOps, Jenkins, GitLab CI, etc.) after each deployment, and Periscope handles the rest.

Overview

  • Endpoint: POST https://app.periscope.sh/api/webhooks/deployments
  • Authentication: Bearer token (API key from Periscope settings)
  • Format: JSON
  • Idempotent: Duplicate deploymentId values update the existing record

Setup

1

Generate an API key

Go to Settings > Deployments in the Periscope dashboard and click Generate API Key. The key is scoped to your organization.
Copy the key immediately — you will not be able to view it again after closing the dialog.
2

Store the key as a secret

Add the API key as a secret in your CI/CD system:
  • GitHub Actions: Repository or organization secret
  • Azure DevOps: Pipeline variable (secret)
  • Jenkins: Credentials store
  • GitLab CI: CI/CD variable (masked)
3

Add the webhook call to your pipeline

Add an HTTP POST after your deployment step completes. See the CI/CD examples below for platform-specific snippets.

Payload schema

Required fields

FieldTypeDescription
deploymentIdstringUnique identifier from your CI/CD system (e.g., run ID). Used for idempotency.
commitShastringThe git commit SHA that was deployed. This is the key field — it links deployments to merged PRs for lead time calculation. Should be the full 40-character SHA.
environmentstringTarget environment: "production", "staging", "development", "preview", "test", or any custom value.
statusstringDeployment status: "pending", "in_progress", "success", "failure", "cancelled", or "rolled_back".
startedAtstringISO 8601 timestamp of when the deployment started.

Optional fields

FieldTypeDescription
organizationSlugstringYour org slug. Optional — the org is resolved from the API key. If provided, it is validated as a safety check.
completedAtstringISO 8601 timestamp. Required when status is "success", "failure", "cancelled", or "rolled_back".
servicestringName of the service being deployed. Periscope matches this to a repository by name. Used for per-service metrics and MTTR pairing.
branchstringThe branch that was deployed.
previousCommitShastringThe commit SHA of the previous deployment to this environment. Enables Periscope to link all PRs merged between the two commits.
triggeredBystringWho or what triggered the deployment (username, email, "automation", "schedule").
pipelineNamestringName of the CI/CD pipeline or workflow.
pipelineUrlstringURL to view this deployment in your CI/CD system. Displayed in the dashboard for quick navigation.
rawPayloadobjectThe original payload from your CI/CD system, stored for reference.
metadataobjectCustom key-value pairs (string, number, or boolean values). Use for version numbers, feature flags, release notes, etc.
The commitSha field is the most important optional detail beyond the required fields. Without an accurate commit SHA, Periscope cannot link deployments to PRs, and lead time data will be empty.

Example payloads

{
  "deploymentId": "azure-devops-run-12345",
  "commitSha": "a1b2c3d4e5f60ab2c3d4e5f60ab2c3d4e5f60ab2",
  "environment": "production",
  "status": "success",
  "startedAt": "2024-01-15T10:30:00Z",
  "completedAt": "2024-01-15T10:35:00Z"
}
{
  "deploymentId": "azure-devops-run-12345",
  "commitSha": "a1b2c3d4e5f60ab2c3d4e5f60ab2c3d4e5f60ab2",
  "environment": "production",
  "status": "success",
  "startedAt": "2024-01-15T10:30:00Z",
  "completedAt": "2024-01-15T10:35:00Z",
  "service": "api-gateway",
  "branch": "main",
  "previousCommitSha": "f9e8d7c6b5a4f3e2d1c0b9a8f7e6d5c4b3a2f1e0",
  "triggeredBy": "[email protected]",
  "pipelineName": "Production Deploy",
  "pipelineUrl": "https://dev.azure.com/org/project/_build/results?buildId=12345",
  "metadata": {
    "version": "2.1.0",
    "releaseNotes": "Bug fixes and performance improvements",
    "featureFlags": "new-checkout-enabled"
  }
}
{
  "deploymentId": "github-actions-run-67890",
  "commitSha": "b2c3d4e5f60ab2c3d4e5f60ab2c3d4e5f60ab2c3",
  "environment": "staging",
  "status": "in_progress",
  "startedAt": "2024-01-15T11:00:00Z",
  "service": "web-frontend",
  "branch": "release/v2.2",
  "triggeredBy": "automation",
  "pipelineName": "Staging Deploy"
}
{
  "deploymentId": "jenkins-build-99999",
  "commitSha": "c3d4e5f60ab2c3d4e5f60ab2c3d4e5f60ab2c3d4",
  "environment": "production",
  "status": "failure",
  "startedAt": "2024-01-15T12:00:00Z",
  "completedAt": "2024-01-15T12:05:00Z",
  "service": "payment-service",
  "triggeredBy": "bob",
  "pipelineUrl": "https://jenkins.company.com/job/deploy/99999",
  "metadata": {
    "errorCode": "HEALTH_CHECK_FAILED",
    "rollbackInitiated": true
  }
}

CI/CD examples

GitHub Actions

name: Deploy and notify Periscope

on:
  push:
    branches: [main]

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

      # Your deployment steps here...

      - name: Notify Periscope
        if: always()
        run: |
          STATUS="${{ job.status == 'success' && 'success' || 'failure' }}"
          curl -s -X POST https://app.periscope.sh/api/webhooks/deployments \
            -H "Authorization: Bearer ${{ secrets.PERISCOPE_API_KEY }}" \
            -H "Content-Type: application/json" \
            -d "{
              \"deploymentId\": \"gha-${{ github.run_id }}\",
              \"commitSha\": \"${{ github.sha }}\",
              \"environment\": \"production\",
              \"status\": \"$STATUS\",
              \"startedAt\": \"$(date -u -d '${{ github.event.head_commit.timestamp }}' '+%Y-%m-%dT%H:%M:%SZ' 2>/dev/null || date -u '+%Y-%m-%dT%H:%M:%SZ')\",
              \"completedAt\": \"$(date -u '+%Y-%m-%dT%H:%M:%SZ')\",
              \"service\": \"${{ github.event.repository.name }}\",
              \"branch\": \"${{ github.ref_name }}\",
              \"triggeredBy\": \"${{ github.actor }}\",
              \"pipelineName\": \"${{ github.workflow }}\",
              \"pipelineUrl\": \"${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}\"
            }"

Azure DevOps

# Add as the final stage in your pipeline
- stage: NotifyPeriscope
  condition: always()
  jobs:
    - job: Notify
      steps:
        - task: Bash@3
          displayName: "Notify Periscope"
          inputs:
            targetType: inline
            script: |
              if [ "$(Agent.JobStatus)" = "Succeeded" ]; then
                STATUS="success"
              else
                STATUS="failure"
              fi

              curl -s -X POST https://app.periscope.sh/api/webhooks/deployments \
                -H "Authorization: Bearer $(PERISCOPE_API_KEY)" \
                -H "Content-Type: application/json" \
                -d "{
                  \"deploymentId\": \"ado-$(Build.BuildId)\",
                  \"commitSha\": \"$(Build.SourceVersion)\",
                  \"environment\": \"production\",
                  \"status\": \"$STATUS\",
                  \"startedAt\": \"$(Build.QueuedTime)\",
                  \"completedAt\": \"$(date -u '+%Y-%m-%dT%H:%M:%SZ')\",
                  \"service\": \"$(Build.Repository.Name)\",
                  \"branch\": \"$(Build.SourceBranchName)\",
                  \"triggeredBy\": \"$(Build.RequestedFor)\",
                  \"pipelineName\": \"$(Build.DefinitionName)\",
                  \"pipelineUrl\": \"$(System.CollectionUri)$(System.TeamProject)/_build/results?buildId=$(Build.BuildId)\"
                }"

Generic curl

curl -X POST https://app.periscope.sh/api/webhooks/deployments \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "deploymentId": "deploy-12345",
    "commitSha": "a1b2c3d4e5f60ab2c3d4e5f60ab2c3d4e5f60ab2",
    "environment": "production",
    "status": "success",
    "startedAt": "2024-01-15T10:30:00Z",
    "completedAt": "2024-01-15T10:35:00Z",
    "service": "my-service"
  }'

Validation rules

The webhook validates your payload before processing:
  • deploymentId, commitSha, environment, status, and startedAt are all required strings
  • commitSha must be 5-40 hexadecimal characters
  • status must be one of: pending, in_progress, success, failure, cancelled, rolled_back
  • startedAt and completedAt must be valid ISO 8601 dates
  • completedAt is required when status is a terminal state (success, failure, cancelled, rolled_back)

Idempotency

The deploymentId field is used for idempotency. If you send a payload with a deploymentId that already exists for your organization, Periscope updates the existing deployment record rather than creating a duplicate. This is useful for:
  • Sending an initial in_progress event, then updating with success or failure when the deployment completes
  • Safely retrying failed webhook calls without worrying about duplicates

PR linking

When a successful production deployment is processed, Periscope automatically links it to merged PRs using the commitSha. If you also provide previousCommitSha, Periscope links all PRs merged between those two commits. This is what powers the lead time for changes metric. Providing the service field improves PR matching accuracy by scoping the link to the correct repository.

Trial activation

If your organization is on the Free plan and sends its first deployment event, Periscope automatically starts a 14-day trial of Business features. This gives you access to all DORA metrics, the MCP server, and AI-powered risk signals during the trial period.

Response format

Success (201 Created):
{
  "action": "created",
  "deploymentId": 42,
  "linkedPRs": 3
}
Update (200 OK):
{
  "action": "updated",
  "deploymentId": 42,
  "linkedPRs": 3
}
Validation error (400):
{
  "error": "Invalid payload: commitSha must be a valid hex string (5-40 characters)"
}
Authentication error (401):
{
  "error": "Invalid or missing API key"
}

API reference

See the full API reference for the deployment webhook endpoint.