Skip to main content

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.

💡 Webhook 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;
  • etc.

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 makes use of the JQ JSON processor to select, modify, concatenate, transform and perform other operations on existing fields and values from the webhook payload.

tip

By using the webhook mapping you can create/update a complete entity, or choose to update just a single property on an entity.

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:

{
"identifier": "pullRequestMapper",
"title": "Pull Request Mapper",
"description": "A webhook configuration for pull-request events from GitHub",
"icon": "Github",
"enabled": true,
"mappings": {
...
},
"security": {
...
}
}

Structure table

FieldDescriptionNotes
identifierUnique identifierThe identifier is used for API calls, programmatic access and distinguishing between different webhooks
titleNameRequired. Human-readable name for the webhook
descriptionDescription
iconIcon for the webhookSee the full icon list
enabledIs the webhook activeIf 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 makes use of the JQ JSON processor to extract information from the event payload into Port entity properties.

Here is an example mapping configuration:

{
"identifier": "pullRequestMapper",
"title": "Pull Request Mapper",
"enabled": true,
...
"mappings": [
{
"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"
}
}
}
],
"security": {
...
}
}

Available keys

When configuring the mapping, the following keys are available for use in the JQ expressions:

KeyDescription
.bodyThe entire payload body sent by the 3rd party service
.headersThe headers sent by the 3rd party service
.queryParamsThe query parameters sent by the 3rd party service
.itemA reference to items in the array specified in itemsToParse. Will be available in the JQ context if itemsToParse is used.
Known issues

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 an entity of based on the webhook payload:
{
...
"mappings": [
{
"blueprint": "pullRequest",
"filter": ".headers.\"X-GitHub-Event\" == \"pull_request\"",
...
}
]
...
}
  • 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"
}
}
}
]
...
}
note
  • 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 in itemsToParse. 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:

{
"identifier": "pullRequestMapper",
...
"mappings": [
...
],
"security": {
"secret": "WEBHOOK_SECRET",
"signatureHeaderName": "X-Hub-Signature-256",
"signatureAlgorithm": "sha256",
"signaturePrefix": "sha256=",
"requestIdentifierPath": ".headers.\"X-GitHub-Delivery\""
}
}
tip

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.
  ...
"security": {
"secret": "WEBHOOK_SECRET",
"signatureHeaderName": "X-Hub-Signature-256",
"signatureAlgorithm": "sha256",
"signaturePrefix": "sha256=",
"requestIdentifierPath": ".headers.\"X-GitHub-Delivery\""
}
...
}
info

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 the signatureHeaderName 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=";
  ...
"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

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 the webhookKey will change between webhooks configurations.

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 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.

Examples

Refer to the examples page for practical configurations and their corresponding blueprint definitions.