Skip to main content

Check out Port for yourselfย 

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:

{
"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 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:

{
"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:

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/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 under entity is identifier.

    Delete dependent entities

    When deleting an entity using the delete operation, all dependent entities will also be deleted. To prevent this, you can set the value of operation to be an object, and set deleteDependents to false, 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"
}
}
}
]
...
}
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.
Selecting a Port API URL by account region

Port exposes two API instances, one for the EU region of Port, and one for the US region of Port.

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.
Payload size limitation

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.