Webhooks
Webhooks overview
A webhook allows you to connect a platform you manage (either an API you create yourself, or a third party service) to a stream of future events.
Setting up a webhook on CircleCI enables you to receive information (referred to as events) from CircleCI, as they happen. This can help you avoid polling the API or manually checking the CircleCI web application for desired information.
The document details how to set up a webhook, as well as the shape of events that will be sent to your webhook destination.
Use cases for webhooks
Webhooks can be leveraged for various purposes. Some possible use cases for webhooks might include:
- Building a custom dashboard to visualize or analyze workflow/job events
- Sending data to incident management tools (such as PagerDuty)
- Using tools like Airtable to capture data and visualize it
- Alerting when a workflow is cancelled, then using the API to rerun the workflow
- Triggering internal notification systems to alert people when workflows/jobs complete
- Building your own automation plugins and tools
Communication protocol with webhooks
A webhook is sent whenever an event occurs on the CircleCI platform.
A webhook is sent using an HTTP POST to the URL that was registered when the webhook was created, with a body encoded using JSON.
CircleCI expects that the server that responds to a webhook will return a 2xx response code. If a non-2xx response is received, CircleCI will retry at a later time. If CircleCI does not receive a response to the webhook within a short period of time, CircleCI will assume that delivery has failed, and will retry at a later time. The timeout period is currently 5 seconds, but is subject to change during the preview period. The exact details of the retry policy are not currently documented, and are subject to change during the preview period.
Webhook requests may be duplicated. To deduplicate (prevent requests from being duplicated for a specific event), there is an id
property in the webhook payload that can be used to identify the event for this purpose.
If you have feedback about timeouts and retries, please get get in touch with our team.
Webhook headers
A number of HTTP headers are set on webhooks, as detailed in the table below.
Header Name | Value |
---|---|
Content-Type | application/json |
User-Agent | A string indicating that the sender was CircleCI (CircleCI-Webhook/1.0 ). The value is subject to change during the preview period. |
Circleci-Event-Type | The type of event, (workflow-completed , job-completed , etc) |
Circleci-Signature | When present, this signature can be used to verify that the sender of the webhook has access to the secret token. |
Setting up a webhook
Webhooks are set up on a per-project basis, either within the CircleCI app or via API.
To configure webhooks via API see our documentation for Webhooks Public API.
To configure webhooks within the CircleCI app:
- Visit a specific project you have set up on CircleCI
- Click on Project Settings
- In the sidebar of your Project Settings, click on Webhooks
- Click Add Webhook
- Fill out the webhook form (the table below describes the fields and their intent)
- Provided your receiving API or third party service is set up, click Test Ping Event to dispatch a test event. Note that the test ping event has an abbreviated payload for ease of testing. See full examples of workflow-completed and job-completed events below.
Field | Required? | Intent |
---|---|---|
Webhook name | Y | The name of your webhook |
URL | Y | The URL the webhook will make POST requests to |
Certificate Validation | Y | Ensure the receiving host has a valid SSL certificate before sending an event 1 |
Secret token | Y | Used by your API/platform to validate incoming data is from CircleCI |
Select an event | Y | You must select at least one event that will trigger a webhook |
1Only leave this unchecked for testing purposes.
There is a limit of 5 webhooks per project.
Webhook payload signature
You should validate incoming webhooks to verify that they are coming from CircleCI. To support this, when creating a webhook, you can optionally provide a secret token. Each outgoing HTTP request to your service will contain a circleci-signature
header. This header will consist of a comma-separated list of versioned signatures.
POST /uri HTTP/1.1
Host: your-webhook-host
circleci-signature: v1=4fcc06915b43d8a49aff193441e9e18654e6a27c2c428b02e8fcc41ccc2299f9,v2=...,v3=...
Currently, the latest (and only) signature version is v1. You should only check the latest signature type to prevent downgrade attacks.
The v1 signature is the HMAC-SHA256 digest of the request body, using the configured signing secret as the secret key.
Here are some example signatures for given request bodies:
Body | Secret Key | Signature |
---|---|---|
hello world | secret | 734cc62f32841568f45715aeb9f4d7891324e6d948e4c6c60c0621cdac48623a |
lalala | another-secret | daa220016c8f29a8b214fbfc3671aeec2145cfb1e6790184ffb38b6d0425fa00 |
an-important-request-payload | hunter123 | 9be2242094a9a8c00c64306f382a7f9d691de910b4a266f67bd314ef18ac49fa |
The following is an example of how you might validate signatures in Python:
import hmac
def verify_signature(secret, headers, body):
# get the v1 signature from the `circleci-signature` header
signature_from_header = {
k: v for k, v in [
pair.split('=') for pair in headers['circleci-signature'].split(',')
]
}['v1']
# Run HMAC-SHA256 on the request body using the configured signing secret
valid_signature = hmac.new(bytes(secret, 'utf-8'), bytes(body, 'utf-8'), 'sha256').hexdigest()
# use constant time string comparison to prevent timing attacks
return hmac.compare_digest(valid_signature, signature_from_header)
# the following will return `True`
verify_signature(
'secret',
{
'circleci-signature': 'v1=773ba44693c7553d6ee20f61ea5d2757a9a4f4a44d2841ae4e95b52e4cd62db4'
},
'foo',
)
# the following will return `False`
verify_signature(
'secret',
{
'circleci-signature': 'v1=not-a-valid-signature'
},
'foo',
)
Event specifications of webhooks
CircleCI currently offers webhooks for the following events:
Event type | Description | Potential statuses | Included sub-entities |
---|---|---|---|
workflow-completed | A workflow has reached a terminal state | “success”, “failed”, “error”, “canceled”, “unauthorized” | project, organization, workflow, pipeline |
job-completed | A job has reached a terminal state | “success”, “failed”, “canceled”, “unauthorized” | project, organization, workflow, pipeline, job |
Common top level keys of webhooks
Each webhook will have some common data as part of the event:
Field | Description | Type |
---|---|---|
id | ID used to uniquely identify each event from the system (the client can use this to dedupe events) | String |
happened_at | ISO 8601 timestamp representing when the event happened | String |
webhook | A map of metadata representing the webhook that was triggered | Map |
Note: The event payloads are open maps, meaning new fields may be added to maps in the webhook payload without considering it a breaking change.
Common sub-entities of webhooks
The next sections describe the payloads of different events offered with CircleCI webhooks. The schema of these webhook events will often share data with other webhooks - we refer to these as common maps of data as “sub-entities”. For example, when you receive an event payload for the job-completed
webhook, it will contain maps of data for your project, organization, job, workflow and pipeline.
Let us look at some of the common sub-entities that will appear across various webhooks:
Project
Data about the project associated with the webhook event.
Field | Always present? | Description |
---|---|---|
id | yes | Unique ID of the project |
slug | yes | String that can be used to refer to a specific project in many of CircleCI’s APIs (e.g. “gh/circleci/web-ui”) |
name | yes | Name of the project (e.g. “web-ui”) |
Organization
Data about the organization associated with the webhook event.
Field | Always present? | Description |
---|---|---|
id | yes | Unique ID of the organization |
name | yes | Name of the organization (e.g. “circleci”) |
Job
A job typically represents one phase in a CircleCI workload (e.g. “build”, “test”, or “deploy”) and contains a series of steps.
Data about the job associated with the webhook event.
Field | Always present? | Description |
---|---|---|
id | yes | Unique ID of the job |
number | yes | An auto-incrementing number for the job, sometimes used in CircleCI’s APIs to identify jobs within a project |
name | yes | Name of the job as defined in .circleci/config.yml |
status | yes | Current status of the job |
started_at | yes | When the job started running |
stopped_at | no | When the job reached a terminal state (if applicable) |
Workflow
Workflows contain many jobs, which can run in parallel and/or have dependencies between them. A single git-push can trigger zero or more workflows, depending on the CircleCI configuration (but typically one will be triggered).
Data about the workflow associated with the webhook event.
Field | Always present? | Description |
---|---|---|
id | Yes | Unique ID of the workflow |
name | Yes | Name of the workflow as defined in .circleci/config.yml |
status | No | Current status of the workflow. Not included in job-level webhooks |
created_at | Yes | When the workflow was created |
stopped_at | No | When the workflow reached a terminal state (if applicable) |
url | Yes | URL to the workflow in CircleCI’s UI |
Pipeline
Pipelines are the most high-level unit of work, and contain zero or more workflows. A single git-push always triggers up to one pipeline. Pipelines can also be triggered manually through the API.
Data about the pipeline associated with the webhook event.
Field | Always present? | Description |
---|---|---|
id | Yes | Globally unique ID of the pipeline |
number | Yes | Number of the pipeline, which is auto-incrementing / unique per project |
created_at | Yes | When the pipeline was created |
trigger | Yes | A map of metadata about what caused this pipeline to be created – see below |
trigger_parameters | No | A map of metadata about the pipeline – see below |
vcs | No | A map of metadata about the git commit associated with this pipeline – see below |
Trigger
Data about the trigger associated with the webhook event.
Field | Always present? | Description |
---|---|---|
type | yes | How this pipeline was triggered (e.g. “webhook”, “api”, “schedule”) |
Trigger parameters
Data associated to the pipeline. Present for pipelines associated with providers other than GitHub or Bitbucket. See VCS below for GitHub and Bitbucket.
Field | Always present? | Description |
---|---|---|
circleci | yes | A map containing trigger information – see below |
git | no | A map present when the pipeline is associated with a VCS provider |
gitlab | no | A map present when the pipeline is associated with a Gitlab trigger |
circleci
Field | Always present? | Description |
---|---|---|
event_time | yes | ISO 8601 timestamp representing when the pipeline was created |
event_type | yes | Provider event type that triggered the pipeline (e.g. “push”) |
trigger_type | yes | Trigger provider (e.g. “gitlab”) |
actor_id | no | CircleCI user id that the pipeline was attributed to |
VCS
The VCS map or its contents may not always be provided. Present for pipelines associated with GitHub and Bitbucket. See trigger parameters above for other providers.
Field | Always present? | Description |
---|---|---|
target_repository_url | no | URL to the repository building the commit |
origin_repository_url | no | URL to the repository where the commit was made (this will only be different in the case of a forked pull request) |
revision | no | Git commit being built |
commit.subject | no | Commit subject (first line of the commit message). Note that long commit subjects may be truncated. |
commit.body | no | Commit body (subsequent lines of the commit message). Note that long commit bodies may be truncated. |
commit.author.name | no | Name of the author of this commit |
commit.author.email | no | Email address of the author of this commit |
commit.authored_at | no | Timestamp of when the commit was authored |
commit.committer.name | no | Name of the committer of this commit |
commit.committer.email | no | Email address of the committer of this commit |
commit.committed_at | no | Timestamp of when the commit was committed |
branch | no | Branch being built |
tag | no | Tag being built (mutually exclusive with “branch”) |
Sample webhook payloads
workflow-completed for GitHub and Bitbucket
{
"id": "3888f21b-eaa7-38e3-8f3d-75a63bba8895",
"type": "workflow-completed",
"happened_at": "2021-09-01T22:49:34.317Z",
"webhook": {
"id": "cf8c4fdd-0587-4da1-b4ca-4846e9640af9",
"name": "Sample Webhook"
},
"project": {
"id": "84996744-a854-4f5e-aea3-04e2851dc1d2",
"name": "webhook-service",
"slug": "github/circleci/webhook-service"
},
"organization": {
"id": "f22b6566-597d-46d5-ba74-99ef5bb3d85c",
"name": "circleci"
},
"workflow": {
"id": "fda08377-fe7e-46b1-8992-3a7aaecac9c3",
"name": "build-test-deploy",
"created_at": "2021-09-01T22:49:03.616Z",
"stopped_at": "2021-09-01T22:49:34.170Z",
"url": "https://app.circleci.com/pipelines/github/circleci/webhook-service/130/workflows/fda08377-fe7e-46b1-8992-3a7aaecac9c3",
"status": "success"
},
"pipeline": {
"id": "1285fe1d-d3a6-44fc-8886-8979558254c4",
"number": 130,
"created_at": "2021-09-01T22:49:03.544Z",
"trigger": {
"type": "webhook"
},
"vcs": {
"provider_name": "github",
"origin_repository_url": "https://github.com/circleci/webhook-service",
"target_repository_url": "https://github.com/circleci/webhook-service",
"revision": "1dc6aa69429bff4806ad6afe58d3d8f57e25973e",
"commit": {
"subject": "Description of change",
"body": "More details about the change",
"author": {
"name": "Author Name",
"email": "author.email@example.com"
},
"authored_at": "2021-09-01T22:48:53Z",
"committer": {
"name": "Committer Name",
"email": "committer.email@example.com"
},
"committed_at": "2021-09-01T22:48:53Z"
},
"branch": "main"
}
}
}
job-completed for GitHub and Bitbucket
{
"id": "8bd71c28-4969-3677-8940-3e3a61c46660",
"type": "job-completed",
"happened_at": "2021-09-01T22:49:34.279Z",
"webhook": {
"id": "cf8c4fdd-0587-4da1-b4ca-4846e9640af9",
"name": "Sample Webhook"
},
"project": {
"id": "84996744-a854-4f5e-aea3-04e2851dc1d2",
"name": "webhook-service",
"slug": "github/circleci/webhook-service"
},
"organization": {
"id": "f22b6566-597d-46d5-ba74-99ef5bb3d85c",
"name": "circleci"
},
"pipeline": {
"id": "1285fe1d-d3a6-44fc-8886-8979558254c4",
"number": 130,
"created_at": "2021-09-01T22:49:03.544Z",
"trigger": {
"type": "webhook"
},
"vcs": {
"provider_name": "github",
"origin_repository_url": "https://github.com/circleci/webhook-service",
"target_repository_url": "https://github.com/circleci/webhook-service",
"revision": "1dc6aa69429bff4806ad6afe58d3d8f57e25973e",
"commit": {
"subject": "Description of change",
"body": "More details about the change",
"author": {
"name": "Author Name",
"email": "author.email@example.com"
},
"authored_at": "2021-09-01T22:48:53Z",
"committer": {
"name": "Committer Name",
"email": "committer.email@example.com"
},
"committed_at": "2021-09-01T22:48:53Z"
},
"branch": "main"
}
},
"workflow": {
"id": "fda08377-fe7e-46b1-8992-3a7aaecac9c3",
"name": "welcome",
"created_at": "2021-09-01T22:49:03.616Z",
"stopped_at": "2021-09-01T22:49:34.170Z",
"url": "https://app.circleci.com/pipelines/github/circleci/webhook-service/130/workflows/fda08377-fe7e-46b1-8992-3a7aaecac9c3"
},
"job": {
"id": "8b91f9a8-7975-4e60-916c-f0152ccbc937",
"name": "test",
"started_at": "2021-09-01T22:49:28.841Z",
"stopped_at": "2021-09-01T22:49:34.170Z",
"status": "success",
"number": 136
}
}
workflow-completed for Gitlab
{
"type": "workflow-completed",
"id": "cbabbb40-6084-4f91-8311-a326c0f4963a",
"happened_at": "2022-05-27T16:20:13.954328Z",
"webhook": {
"id": "e4da0d23-31cf-4047-8a7e-8ffb14cd0100",
"name": "test"
},
"workflow": {
"id": "c2006ece-778d-49fc-9e6e-b9965f72bee9",
"name": "build",
"created_at": "2022-05-27T16:20:07.631Z",
"stopped_at": "2022-05-27T16:20:13.812Z",
"url": "https://app.circleci.com/pipelines/circleci/DdaVtNusHqi24D4YT3X4eu/6EkDPZoN4ZdMKKZtBkRodt/1/workflows/c2006ece-778d-49fc-9e6e-b9965f72bee9",
"status": "failed"
},
"pipeline": {
"id": "37c74cb7-d64d-4032-8731-1cb95bfef921",
"number": 1,
"created_at": "2022-04-13T11:10:18.804Z",
"trigger": {
"type": "gitlab"
},
"trigger_parameters": {
"gitlab": {
"web_url": "https://gitlab.com/circleci/hello-world",
"commit_author_name": "Commit Author",
"user_id": "9534789",
"user_name": "User name",
"user_username": "username",
"branch": "main",
"commit_title": "Update README.md",
"commit_message": "Update README.md",
"total_commits_count": "1",
"repo_url": "git@gitlab.com:circleci/hello-world.git",
"user_avatar": "https://secure.gravatar.com/avatar",
"type": "push",
"project_id": "33852820",
"ref": "refs/heads/main",
"repo_name": "hello-world",
"commit_author_email": "committer.email@example.com",
"checkout_sha": "850a1519f25d14e968649cc420d1bd381715c05c",
"commit_timestamp": "2022-04-13T11:10:16+00:00",
"commit_sha": "850a1519f25d14e968649cc420d1bd381715c05c"
},
"git": {
"tag": "",
"checkout_sha": "850a1519f25d14e968649cc420d1bd381715c05c",
"ref": "refs/heads/main",
"branch": "main",
"checkout_url": "git@gitlab.com:circleci/hello-world.git"
},
"circleci": {
"event_time": "2022-04-13T11:10:18.349Z",
"actor_id": "6a19122c-40e0-4d56-a875-aac6ccc27700",
"event_type": "push",
"trigger_type": "gitlab"
}
}
},
"project": {
"id": "2a68fe5f-2fe5-4d4f-91e1-15f111116743",
"name": "hello-world",
"slug": "circleci/DdaVtNusHqi24D4YT3X4eu/6EkDPZoN4ZdMKKZtBkRodt"
},
"organization": {
"id": "66491562-90a9-4065-9249-4b0ce3b77452",
"name": "circleci"
}
}
job-completed for Gitlab
{
"type": "workflow-completed",
"id": "47a497be-4498-4da0-a4e8-2dabd889af0f",
"happened_at": "2022-05-27T16:20:13.954328Z",
"webhook": {
"id": "e4da0d23-31cf-4047-8a7e-8ffb14cd0100",
"name": "test"
},
"job": {
"id": "2fc6977d-7e45-4271-b355-0ea894d82017",
"name": "say-hello",
"started_at": "2022-07-11T12:16:37.435Z",
"stopped_at": "2022-07-11T12:16:59.982Z",
"status": "success",
"number": 1
}
"pipeline": {
"id": "37c74cb7-d64d-4032-8731-1cb95bfef921",
"number": 1,
"created_at": "2022-04-13T11:10:18.804Z",
"trigger": {
"type": "gitlab"
},
"trigger_parameters": {
"gitlab": {
"web_url": "https://gitlab.com/circleci/hello-world",
"commit_author_name": "Commit Author",
"user_id": "9534789",
"user_name": "User name",
"user_username": "username",
"branch": "main",
"commit_title": "Update README.md",
"commit_message": "Update README.md",
"total_commits_count": "1",
"repo_url": "git@gitlab.com:circleci/hello-world.git",
"user_avatar": "https://secure.gravatar.com/avatar",
"type": "push",
"project_id": "33852820",
"ref": "refs/heads/main",
"repo_name": "hello-world",
"commit_author_email": "committer.email@example.com",
"checkout_sha": "850a1519f25d14e968649cc420d1bd381715c05c",
"commit_timestamp": "2022-04-13T11:10:16+00:00",
"commit_sha": "850a1519f25d14e968649cc420d1bd381715c05c"
},
"git": {
"tag": "",
"checkout_sha": "850a1519f25d14e968649cc420d1bd381715c05c",
"ref": "refs/heads/main",
"branch": "main",
"checkout_url": "git@gitlab.com:circleci/hello-world.git"
},
"circleci": {
"event_time": "2022-04-13T11:10:18.349Z",
"actor_id": "6a19122c-40e0-4d56-a875-aac6ccc27700",
"event_type": "push",
"trigger_type": "gitlab"
}
}
},
"project": {
"id": "2a68fe5f-2fe5-4d4f-91e1-15f111116743",
"name": "hello-world",
"slug": "circleci/DdaVtNusHqi24D4YT3X4eu/6EkDPZoN4ZdMKKZtBkRodt"
},
"organization": {
"id": "66491562-90a9-4065-9249-4b0ce3b77452",
"name": "circleci"
}
}
Help make this document better
This guide, as well as the rest of our docs, are open source and available on GitHub. We welcome your contributions.
- Suggest an edit to this page (please read the contributing guide first).
- To report a problem in the documentation, or to submit feedback and comments, please open an issue on GitHub.
- CircleCI is always seeking ways to improve your experience with our platform. If you would like to share feedback, please join our research community.
Need support?
Our support engineers are available to help with service issues, billing, or account related questions, and can help troubleshoot build configurations. Contact our support engineers by opening a ticket.
You can also visit our support site to find support articles, community forums, and training resources.
CircleCI Documentation by CircleCI is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.