Webhook
By using Port's generic webhook integration you can ingest data into the software catalog from any source or service that provides outgoing webhooks, even if Port doesn't provide a native integration for that source.
Common use casesโ
Our generic webhook makes it easy to fill the software catalog with live data directly from your 3rd-party services, for example:
- Map all of your Snyk vulnerabilities, Jira issues, SonarQube reports and other data sources.
- Make single property updates - update the current on-call of a service based on an event from Pager Duty or OpsGenie.
- Make event-based real-time updates to the software catalog.
- Create a single view for all of the data provided by the 3rd-party services you use.
How it worksโ
Port provides you with custom webhook endpoints, which you can use as the target for custom integrations provided by services you use (for example GitHub, Sentry, etc.)
Each webhook endpoint can receive a custom mapping, making it easy to turn the payload of events from your 3rd-party services into entities inside your software catalog.
The custom mapping uses the JQ JSON processor to select, modify, concatenate, transform and perform other operations on existing fields and values from the webhook payload.
By using the webhook mapping you can:
- Create/update a complete entity.
- Update a single property on an entity.
- Delete an entity from your catalog.
Webhook configurationโ
The webhook configuration is how you specify:
- The basic metadata of the custom webhook integration;
- The mapping configuration controlling which entities are created from the payload;
- The security configuration used to make sure that payloads that arrive to Port were really sent by a 3rd party you authorized.
Here is an example webhook configuration:
[
{
"blueprint": "pullRequest",
"filter": ".headers.\"X-GitHub-Event\" == \"pull_request\"",
"entity": {
"identifier": ".body.pull_request.id | tostring",
"title": ".body.pull_request.title",
"properties": {
"author": ".body.pull_request.user.login",
"url": ".body.pull_request.html_url"
}
}
}
]
Configuration structureโ
Metadata configurationโ
The metadata configuration of the webhook includes all properties related to the visibility and displaying of the webhook inside Port's UI, in addition to controlling whether the webhook is active or not.
Here is an example metadata configuration:
- API
- Terraform
{
"identifier": "pullRequestMapper",
"title": "Pull Request Mapper",
"description": "A webhook configuration for pull-request events from GitHub",
"icon": "Github",
"enabled": true,
"mappings": {
...
},
"security": {
...
}
}
resource "port_webhook" "myWebhook" {
identifier = "pullRequestMapper"
title = "Pull Request Mapper"
description = "A webhook configuration for pull-request events from GitHub"
icon = "Github"
enabled = true
mappings = {
...
}
security = {
...
}
}
Structure tableโ
Field | Description | Notes |
---|---|---|
identifier | Unique identifier | The identifier is used for API calls, programmatic access and distinguishing between different webhooks |
title | Name | Required. Human-readable name for the webhook |
description | Description | |
icon | Icon for the webhook | See the full icon list |
enabled | Is the webhook active | If the integration id disabled ("enabled": false ) then any incoming event will be dropped |
Mapping configurationโ
The mapping configuration of the webhook defines how the webhook event payload is mapped to one (or more) Port entities.
The mapping configuration uses the JQ JSON processor to extract information from the event payload and map it to Port entity properties.
Below is an example of a mapping configuration:
- API
- Terraform
{
"identifier": "pullRequestMapper",
"title": "Pull Request Mapper",
"enabled": true,
...
"mappings": [
{
"blueprint": "pullRequest",
"operation": "create",
"filter": ".headers.\"X-GitHub-Event\" == \"pull_request\"",
"entity": {
"identifier": ".body.pull_request.id | tostring",
"title": ".body.pull_request.title",
"properties": {
"author": ".body.pull_request.user.login",
"url": ".body.pull_request.html_url"
}
}
}
],
"security": {
...
}
}
resource "port_webhook" "myWebhook" {
identifier = "pullRequestMapper"
title = "Pull Request Mapper"
enabled = true
...
mappings = [
{
blueprint = "pullRequest"
operation = "create"
filter = ".headers.\"X-GitHub-Event\" == \"pull_request\""
entity = {
identifier = ".body.pull_request.id | tostring"
title = ".body.pull_request.title"
properties = {
author = ".body.pull_request.user.login"
url = ".body.pull_request.html_url"
}
}
}
]
security = {
...
}
}
Available keysโ
When configuring the mapping, the following keys are available for use in the JQ expressions:
Key | Description |
---|---|
.body | The entire payload body sent by the 3rd party service |
.headers | The headers sent by the 3rd party service |
.queryParams | The query parameters sent by the 3rd party service |
.item | A reference to items in the array specified in itemsToParse . Will be available in the JQ context if itemsToParse is used. |
As the webhook custom integration is receiving the requests from AWS API Gateway, there are some issues that might affect the value of the fields in one of the context keys. For example, the headers
key might not have the expected casing.
Please refer to the AWS API Gateway known issues for more information.
Structureโ
- The root key of the mapping configuration is the
mappings
key:
{
...
"mappings": [
{
# mapping
}
]
...
}
The mappings key stores an array of mappings, making it possible to create/update multiple entities in multiple blueprints from the same payload.
Now let's explore the structure of a single mapping object:
- The
blueprint
key is used to specify the identifier of the blueprint to create/update/delete an entity of based on the webhook payload:
{
...
"mappings": [
{
"blueprint": "pullRequest",
"filter": ".headers.\"X-GitHub-Event\" == \"pull_request\"",
...
}
]
...
}
- The
operation
key is used to specify the action to perform on an entity. Its available values are:-
create
- creates a new entity, or updates it if it already exists. -
delete
- deletes an existing entity. When using this operation, the only required key underentity
isidentifier
.
Delete dependent entitiesWhen deleting an entity using the
delete
operation, all dependent entities will also be deleted. To prevent this, you can set the value ofoperation
to be an object, and setdeleteDependents
tofalse
, like this:"operation": {"type":"delete", "deleteDependents": false},
-
{
...
"mappings": [
{
"blueprint": "pullRequest",
"operation": "create",
...
}
]
...
}
- The
filter
key lets you filter exactly which payloads sent to the webhook are processed:
{
...
"mappings": [
{
"blueprint": "pullRequest",
"filter": ".headers.\"X-GitHub-Event\" == \"pull_request\"" # JQ boolean query. If evaluated to false - skip the payload.
...
}
]
...
}
- The
itemsToParse
key makes it possible to create multiple entities from a single webhook event:
{
...
"mappings": [
{
"blueprint": "commits",
"itemsToParse": ".body.pull_request.commits",
// Checks if any of the modified files are in the frontend/src folder.
"filter": ".item.modified | any(test(\"/frontend/src\"))",
"entity": {
"identifier": ".item.id | tostring",
"title": ".item.message",
"properties": {
"author": ".item.author.email",
"url": ".item.url",
"repository": ".body.pusher.email"
}
}
}
]
...
}
- Any JQ expression can be used here, as long as it evaluates to an array of items.
item
will be added to the JQ context as a key containing a reference to items in the array specified initemsToParse
. Keys from the object in the array can be accessed using the.item.KEY_NAME
syntax, see the example JSON for more information.
- The
entity
key is used to map information from the webhook payload to Port entity properties using JQ:
{
...
"mappings": [
{
...
"filter": ".headers.\"X-GitHub-Event\" == \"pull_request\"",
"entity": {
"identifier": ".body.pull_request.id | tostring",
"title": ".body.pull_request.title",
"properties": {
"author": ".body.pull_request.user.login",
"url": ".body.pull_request.html_url"
}
}
}
]
...
}
Security configurationโ
When 3rd party services send payload to a specified webhook URL, they will usually also include a header containing a signed signature of the payload, or some agreed upon string for verification of the sender.
The signature might be generated by running a SHA-X (for example SHA-1 or SHA-256) hashing function on the payload, combined with a secret value specified by the user or provided by the 3rd party service at the time of webhook creation.
Since some 3rd party service do not offer sending the payload signature, and instead only offer sending an agreed upon string directly - the security option plain
is available for use. With this option, the signature is compared to the secret value without any modification. It allows users to directly compare the signature to the provided secret value. This can be useful in scenarios where a simpler security mechanism is desired.
The security configuration of the webhook is used to tell Port how to verify the hashed signature sent with the request from the 3rd party.
Here is an example security configuration:
- API
- Terraform
{
"identifier": "pullRequestMapper",
...
"mappings": [
...
],
"security": {
"secret": "WEBHOOK_SECRET",
"signatureHeaderName": "X-Hub-Signature-256",
"signatureAlgorithm": "sha256",
"signaturePrefix": "sha256=",
"requestIdentifierPath": ".headers.\"X-GitHub-Delivery\""
}
}
resource "port_webhook" "myWebhook" {
identifier = "pullRequestMapper"
...
mappings = [
...
]
security = {
secret = "WEBHOOK_SECRET"
signature_header_name = "X-Hub-Signature-256"
signature_algorithm = "sha256"
signature_prefix = "sha256="
request_identifier_path = ".headers.\"X-GitHub-Delivery\""
}
}
The security configuration is not mandatory, but it does provide an additional layer of security, making sure that Port only processes payloads that were actually sent from one of your 3rd party webhooks.
If you do not want to supply a security configuration with your webhook configuration, simply pass an empty object: "security": {}
with your webhook configuration.
Structureโ
- The root of the security configuration is the
security
key:
{
...
"security": {
"secret": "WEBHOOK_SECRET",
"signatureHeaderName": "X-Hub-Signature-256",
"signatureAlgorithm": "sha256",
"signaturePrefix": "sha256=",
"requestIdentifierPath": ".headers.\"X-GitHub-Delivery\""
}
...
}
- The
secret
key is used to specify the secret value used to validate the hashed signature of the received payload:- Depending on the service, the secret value might be autogenerated by the 3rd party or manually provided to the 3rd party by you.
...
"security": {
"secret": "WEBHOOK_SECRET",
"signatureHeaderName": "X-Hub-Signature-256",
"signatureAlgorithm": "sha256",
"signaturePrefix": "sha256=",
"requestIdentifierPath": ".headers.\"X-GitHub-Delivery\""
}
...
}
- The
signatureHeaderName
key is used to specify the name of the header that stores the hashed signature of the payload:- When a webhook endpoint receives a new payload, it will compare the value of this header with the hashed signature it will calculate from the received payload.
...
"security": {
"secret": "WEBHOOK_SECRET",
"signatureHeaderName": "X-Hub-Signature-256",
"signatureAlgorithm": "sha256",
"signaturePrefix": "sha256=",
"requestIdentifierPath": ".headers.\"X-GitHub-Delivery\""
}
...
}
- The
signatureAlgorithm
key is used to specify the hashing algorithm used to create the payloads' hashed signature:- Available values:
sha1
,sha256
,plain
; - When a webhook endpoint receives a new payload, it will use the specified algorithm to calculate the hashed signature of the received payload.
- Available values:
...
"security": {
"secret": "WEBHOOK_SECRET",
"signatureHeaderName": "X-Hub-Signature-256",
"signatureAlgorithm": "sha256",
"signaturePrefix": "sha256=",
"requestIdentifierPath": ".headers.\"X-GitHub-Delivery\""
}
...
}
When using the plain
algorithm, no hashing will be performed and the value of the secret saved in the Port webhook configuration will be compared to the value in the specified header without any modification.
- The
signaturePrefix
key is used to specify a static prefix string that appears before the hashedSignature in thesignatureHeaderName
key:- For example, in GitHub webhooks, the header containing the hashed signature always starts with
sha256=
, so the webhook should be configured with:"signaturePrefix": "sha256="
;
- For example, in GitHub webhooks, the header containing the hashed signature always starts with
...
"security": {
"secret": "WEBHOOK_SECRET",
"signatureHeaderName": "X-Hub-Signature-256",
"signatureAlgorithm": "sha256",
"signaturePrefix": "sha256=",
"requestIdentifierPath": ".headers.\"X-GitHub-Delivery\""
}
...
}
- The
requestIdentifierPath
key is used to specify a JQ pattern resulting in a unique identifier of the webhook payload:- This key is used to prevent Port from processing an event more than once;
- For example, in GitHub webhooks, the
X-GitHub-Delivery
header contains a GUID used to identify the delivery. So the webhook should be configured with:"requestIdentifierPath": ".headers.\"X-GitHub-Delivery\""
;
...
"security": {
"secret": "WEBHOOK_SECRET",
"signatureHeaderName": "X-Hub-Signature-256",
"signatureAlgorithm": "sha256",
"signaturePrefix": "sha256=",
"requestIdentifierPath": ".headers.\"X-GitHub-Delivery\""
}
...
}
Configuring webhook endpointsโ
- Using API
- Using Port UI
- Create webhook
- Update webhook
- Delete webhook
To create a new webhook, make an HTTP POST request to https://api.getport.io/v1/webhooks
with your webhook configuration in the request body.
The API response will include the complete configuration of the webhook, including the following important fields:
webhookKey
- this is the unique identifier used for the new generic webhook route you created;url
- this is the complete URL you need to pass to your 3rd party webhook configuration. Event payloads matching the webhook configuration you created should be sent to this URL;- The
url
will be of the format:https://ingest.getport.io/{webhookKey}
; - Note: The
https://ingest.getport.io
is constant, only thewebhookKey
will change between webhooks configurations.
- The
To update an existing webhook, make an HTTP PATCH request to https://api.getport.io/v1/webhooks/{WEBHOOK_IDENTIFIER}
with your updated webhook configuration in the request body.
When updating the webhook configuration, partial updates are supported, meaning you can pass only keys that need to be updated in the request body. Any key you do not specify will remain unchanged.
The API response will include the updated configuration of the webhook.
To delete an existing webhook, make an HTTP DELETE request to https://api.getport.io/v1/webhooks/{WEBHOOK_IDENTIFIER}
.
Port exposes two API instances, one for the EU region of Port, and one for the US region of Port.
- If you use the EU region of Port (https://app.getport.io), your API URL is
https://api.getport.io
. - If you use the US region of Port (https://app.us.getport.io), your API URL is
https://api.us.getport.io
.
- Create webhook
- Update webhook
- Delete webhook
Follow these steps to create a webhook using Port's UI:
- Go to the
Data sources
page of your portal. - Click on the
+ Data source
button in the top right corner. - Select the
Webhook
tab. - Click on
Custom integration
. - Give your webhook a title and a description, and select an icon to represent it, then click
Next
. - In the mapping tab, you can see your new Webhook URL.
- Scroll down to box number 3. Here you can use JQ to map the data received from your system to the blueprints in your data model.
- You can use the
Test mapping
button in box number 4 (in the bottom of the window) to test your mapping's validity. - When finished, click
Save
.
Follow these steps to update a webhook using Port's UI:
- Go the
Data sources
page of your portal. - Scroll down to the
Webhooks
section. - Click on the webhook that you want to modify.
- Make your changes, then click
Save
.
Follow these steps to delete a webhook using Port's UI:
- Go the
Data sources
page of your portal. - Scroll down to the
Webhooks
section. - Hover over the webhook that you want to delete, click on the
...
button in the top right corner, then clickDelete
.
Using the custom webhookโ
After creating and configuring your custom webhook, go to your 3rd party provider (i.e. GitHub, Sentry, Jira, etc.) and follow these steps to complete the webhook setup:
- Go to the new webhook setup menu in your 3rd party provider
- For example in GitHub: go to your desired organization/repository -> Settings -> Webhooks -> Add webhook.
- Paste the webhook URL you received from Port (
https://ingest.getport.io/{webhookKey}
) in the field specifying the webhook target URL;- For example in GitHub: paste the webhook URL in the
Payload URL
field.
- For example in GitHub: paste the webhook URL in the
- For content type, select
application/json
(if applicable); - In case the
secret
value is generated by your 3rd party, be sure to go back and update your security configuration with the secret value.
The maximum size of the webhook payload is 512KiB.
Reaching this limit will throw a 413 Request Entity Too Large
HTTP error.
Custom webhook HTTP responseโ
Custom webhook endpoints provided by Port perform their processing in an asynchronous manner.
Most 3rd party providers expect a webhook endpoint to provide a correct response after a short period of time.
In order to comply with the expectation from custom webhook endpoints, when you or your 3rd party provider makes a request to your custom webhook, you will see a 202 ACCEPTED
status code returned in the response. This is the expected behavior and it means that the payload sent to your custom webhook has been received and will undergo processing in the background, after which it will appear in your catalog as determined by your mapping configuration.
Examplesโ
Refer to the examples page for practical configurations and their corresponding blueprint definitions.