Skip to main content

Check out Port for yourself ➜ 

Configure mapping


The mapping of an integration's data source defines the ingested data and its destination. It allows you to specify:

  • Which data you wish to ingest from the integrated tool.
  • Which properties in the integration's blueprints will be filled with the ingested data.

How does mapping work?

Integration mapping is configured in the data sources page of your portal, under Exporters.
Each integration has its own mapping, which can be edited via a YAML editor or a visual form.

To understand how mapping works, let's take a look at an example. After you complete the onboarding and connect your Git provider to Port, you will see an exporter entry in your data sources page:



Clicking on this entry will open a modal. Click the Mapping tab to view and edit the mapping configuration.
From there, you can click {...} Edit YAML to open the YAML editor, or use the form editor to configure the mapping through a visual interface.

Note that Port provides default mapping, providing values to the properties defined in the relevant blueprint:



Port provides two ways to configure the mapping:

Mapping concepts

The following concepts are the building blocks of a mapping configuration. They apply to both editors - understanding them once lets you work fluently in either.

Resources and kinds

A resource represents one type of data to fetch and map. Each resource specifies a kind - the API object type to fetch (e.g., repository, pull-request, issue).

Resources are processed sequentially from top to bottom. This means:

  • The first resource is fetched and ingested before the second one starts.
  • If you have relations between entities (e.g., pull requests referencing repositories), list the target blueprint first so those entities exist before the relation is created.
  • For large datasets, the order can affect sync duration since each resource must complete before the next begins.

A mapping can contain multiple resources of the same kind with different filters, allowing you to map the same API object to different blueprints or with different property values.

Selector

Selectors belong to the Extraction phase. They modify the API request by adding query parameters or other parameters that change what the API returns. Unlike the query filter, selectors act on the request - not the response.

Available selectors vary per integration, based on the API capabilities of the integrated tool. Using selectors to narrow what the API returns can reduce sync time and help avoid hitting API rate limits.

Query

The query field belongs to the Transformation phase. It accepts a JQ expression evaluated against each object returned by the API:

  • If the expression evaluates to true, the object is ingested.
  • If it evaluates to false, the object is skipped.

Set query to "true" to ingest all objects, or use a JQ expression to filter specific objects. This field is called Data filter in the form editor. In YAML, it appears as query inside the selector block - but unlike other selector keys, it filters the API response rather than modifying the request.

Entity mappings

Entity mappings define how fields from the API response are translated into Port entity properties. For each resource, you specify a target blueprint, how to derive the entity's identifier and title, which API fields map to which blueprint properties, and optionally which relations to populate.

The exact structure of entity mappings differs between the two editors - see Transformation in the form editor section and Transformation in the YAML editor section.

JQ syntax for identifiers with hyphens

When using JQ to reference identifiers or property names that contain hyphens, you must wrap them in double quotes and use bracket notation.

For example, use .["my-property"] instead of .my-property, which JQ interprets as subtraction.

This applies to secrets, properties, and any other identifiers containing hyphens:

  • Secrets: .secrets["zendesk-api-token"]
  • Properties: .properties["my-custom-field"]
  • Relations: .relations["parent-service"]

Mapping relations

In Port, relations define how blueprints are connected to one another. The mapping configuration lets you populate these relations automatically during ingestion.


There are two ways to reference a related entity in a mapping:

  • Direct identifier reference - provide the related entity's identifier as a JQ expression. Use this when the API response contains the identifier of the related entity.
  • Search query - find the related entity by matching one of its properties. Use this when you don't have the identifier directly, but know a property value that uniquely identifies it.

Limitations for search query relations

  • A "single type" relation expects a single entity to be returned. A "many type" relation expects an array.
  • The maximum number of entities returned is 500.
  • Calculation properties are not supported.
  • Mirror properties are supported only for single type relations, one level deep.
  • Only the =, in, and contains operators are supported.
  • The search is scoped to the relation's target blueprint - it will not match entities from other blueprints, even if they have a matching property value.

Manually assigning relations in the UI

You can also assign relations manually without touching the mapping configuration:

  1. Go to the relevant catalog page (e.g., services).
  2. Hover over an entity, click ..., and select Edit.
  3. In the relation field, select the related entity from the dropdown, then click Update.

Relation values are overwritten on every sync

Every time an integration syncs an entity, the relation value in Port is replaced with whatever the mapping returns. Port does not merge or append relation values across syncs or across different integrations.

This has two important implications:

  1. If your JQ returns an empty array (e.g. because a filter excludes all items), Port will clear that relation - removing any previously set values.
  2. If two integrations both write to the same relation, the last sync to run wins. The other integration's values are overwritten.

The mapping expression must therefore return the complete desired set of relation identifiers every time it runs.

Workarounds:

  • Merge all sources in one mapping: Use a single integration that queries all sources and returns the merged set in the JQ expression.
  • Manage the relation manually: Exclude the relation from the integration's mapping entirely by omitting it from the relations block, and assign values manually in Port's UI.
  • Use an automation to append: Create a Port automation that reacts to entity changes and uses JQ array concatenation to append new values while preserving existing ones.

Map by property

In some cases you may not know the identifier of the entity you want to map to, but you do know one of its properties. Instead of a direct identifier, you can use a search query rule in the identifier, team, or relation fields to find and update the entity by that property. Search query mapping is only supported for these three fields.

This is useful when you want to map data directly to an existing entity without needing a separate blueprint. For example, a PagerDuty integration can map directly to a service entity using a pagerduty_service_id property, without a separate PagerDuty service blueprint.

Limitations

  • The search query must return exactly one entity - otherwise the entire request will fail.
    To avoid failures from identical property values across multiple blueprints, include an additional rule filtering by $blueprint.
  • If the search query returns no entities, a new entity will not be created.
  • The query is executed on the same blueprint from the request's URL.
  • Only the = and in operators are supported.
  • Calculation and mirror properties are not supported.
API usage

Searching by property can also be used when using Port's API to create an entity.

Items to parse

When the API returns an array that you want to expand into multiple separate Port entities, use items to parse. Port iterates over the array and creates one entity per item.

There are three related options:

  • itemsToParse - a JQ expression pointing to the array in the API response. Each element in the array becomes a separate entity.
  • itemsToParseName - a custom name for referencing each array element. Use this when your API response already has a top-level key that conflicts with the default name.
  • itemsToParseTopLevelTransform - controls whether the target array is removed from the payload during iteration. When true (default), the array is removed. When false, the original array remains accessible alongside individual items.

Limitations

  • itemsToParseName is not supported in non-Ocean integrations: Github app, Kubernetes, and Webhook integrations.
  • When itemsToParseName is enabled, you cannot use the "test mapping" option in Port's UI.

Advanced options

These options apply globally across all resources in a mapping configuration. In the YAML editor they are set at the root level, in the form editor they appear under Advanced Configuration.

UI labelYAML keyDescription
Auto-create related entitiescreateMissingRelatedEntitiesAutomatically creates missing related entities during ingestion. Useful when creating an entity whose related entity does not yet exist in Port.
Auto-delete related entitiesdeleteDependentEntitiesDeletes dependent Port entities when a related entity is removed. Required when two blueprints have a required relation and the source entity must be deleted alongside the target.
Deletion safety limitentityDeletionThresholdMaximum proportion of entities that can be deleted in a single sync (0 to 1). For example, 0.5 means deletion is skipped if more than 50% of entities would be deleted.

Additional options may be available depending on the integration. Refer to the specific integration's documentation for the full list.

Auto-create related entities in protected blueprints

The Auto-create related entities option cannot be used to create entities in protected blueprints such as _user and _team.

JQ runtime

Port's JQ runtime runs in UTC. This affects any time-based JQ functions you use in your mapping, such as now, todate, strftime, and strptime.

When writing JQ expressions that reference the current time or calculate relative durations, assume the server clock is UTC:

# Get the current time as a UTC ISO string
now | todate
# Output: "2024-04-15T10:30:00Z"

# Calculate days since last update (assuming .updated_at is an ISO string)
(now - (.updated_at | sub("\\.[0-9]+Z$"; "Z") | strptime("%Y-%m-%dT%H:%M:%SZ") | mktime)) / 86400 | floor
Local timezone offsets

If your source data contains timestamps with a non-UTC offset (e.g. 2024-04-15T12:30:00+02:00), you must normalize them to UTC before comparing against now or using Port's datetime property format. Port stores and displays all datetime values in UTC.

Common test errors

When running Test mapping, you may come across one of the following errors:

  • Missing kind example for {kind}
    Cause: No example data is available for this resource kind.
    Resolution: Add example data for the missing kind.

  • Selector query is false or evaluated to false
    Cause: The data filter expression returned false for the example data.
    Resolution: Update your JQ expression or use an example that matches your filter.

  • must NOT have additional properties, additional property: "[property]"
    Cause: The mapping YAML contains an unrecognized field, often due to a typo.
    Resolution: Check for typos in your field names and ensure they match the expected mapping schema.

  • Blueprint with identifier "[identifier]" was not found
    Cause: The blueprint identifier referenced in the mapping does not exist in your portal.
    Resolution: Verify the identifier matches an existing blueprint in your portal.


Form editor

Limited availability

The form editor is currently available for select integrations, with support for all integrations coming soon.

The form editor is the default mapping experience. It provides a visual, step-by-step interface for configuring the mapping concepts above.

To access the form editor, click on an integration in your data sources page and navigate to the Mapping tab. If AI is enabled for your organization, an Ask AI button is available at the top of the editor for mapping assistance.

YAML errors may block the form editor

If your mapping YAML contains an error, the form editor may be unavailable and the YAML view will be shown first. In that case, hover over the Edit form button to see a description of the issue, or use Test mapping in the YAML view to identify it.

Each integration resource appears as a collapsible strip that you can drag and drop to reorder. Click a strip to expand and configure it. To add a new resource, click + Resource at the bottom, then select a kind from the list of all supported kinds for that integration.

Each resource has three configuration phases:

Extraction

Configure selectors to shape the API request for this resource. Selectors add parameters to the API request, changing what the API returns. They do not filter the response after it arrives.

Click + Selector to see all selectors supported by the selected kind. Hover over a selector to read its description and purpose.

Transformation

Configure the entity mappings for this resource. The left panel contains the mapping configuration, and the right panel shows Raw data samples from the integration to help write and validate JQ expressions.

Target blueprint

Select the blueprint to map data into from the Target Blueprint dropdown. Click + New Blueprint to create a new blueprint directly from the mapping form - it will be created in Port and automatically selected.

Data filter

Filters which items from the API response are ingested. This corresponds to the query concept. Set to true to ingest all items, or use a JQ expression to filter specific items.

Property mapping

Maps API response fields to blueprint properties. Each row shows a JQ expression on the left and the target Port property on the right. Click the edit icon next to any property to open the JQ expression editor, which displays a raw data sample alongside your expression for real-time validation. If AI is enabled for your organization, the icon appears in the top-right corner of the JQ expression editor - click it to describe in natural language what you want to calculate, and AI will generate the JQ expression for you.

Click + Property to add more properties from the blueprint. To create a new property on the blueprint directly, click + New Property - it will be added to the blueprint and immediately available for mapping. For more details on properties, see setup blueprint.

Map by property

To find and update an entity by one of its properties instead of its identifier, click the edit icon next to the Identifier row. In the Edit JQ expression editor, toggle Search query in the top-right corner. This replaces the identifier with a search query rule.

For a description of this feature and its limitations, see Map by property in the mapping concepts section.

Relation mapping

Maps relations between entities. Click + Relation to add a relation from the blueprint. To create a new relation on the blueprint, click + New Relation. For more details, see relate blueprints.

Each relation supports the two approaches described in Mapping relations. When editing a relation's JQ expression, toggle Search query in the expression editor to switch from a direct identifier reference to a search-based relation.

Items to parse

To create multiple entities from an array field in the API response, click the ... menu at the bottom-right of the transformation panel and enable Items to parse. Three fields will appear above the property mapping section: Items To Parse, Items To Parse Name, and Items To Parse Top Level Transform.

For a description of each option, see Items to parse in the mapping concepts section.

Test

Click Test mapping to run the mapping against the raw data samples and preview the resulting Port entity. The test shows results only for the current resource.

If the test fails, see Common test errors for a description of each error and how to resolve it.

Advanced configuration

The Advanced Configuration section at the bottom of the modal exposes the advanced options for the integration. Common options include:

  • Auto-create related entities
  • Auto-delete related entities
  • Deletion safety limit

See the advanced options section for a description of each setting. Additional options may appear depending on the integration - refer to the integration's documentation for the full list of available options.

Once you have finished configuring the mapping, click Save and Resync to save your changes and trigger a full resync.


YAML editor

To access the YAML editor, click on an integration in your data sources page, navigate to the Mapping tab, and click {...} Edit YAML in the top-right corner. The YAML editor appears in the bottom corner of the modal, with the test result panel on the right. If AI is enabled for your organization, an Ask AI button is available in the right corner above the YAML editor for mapping assistance.

Extraction

This section covers the YAML keys used to control which objects are fetched from the integration's API. Some keys use JQ queries to filter the ingested data.

  • The resources key is the root of the YAML configuration:

    resources:
    - kind: repository
    ...

  • The kind key is a specifier for the object you wish to map from the tool's API (in this example, a Github repository).
    To see which kinds are available for mapping, refer to the integration's documentation. In this example, the available kinds are listed in the Github integration page.

    resources:
    - kind: repository
    selector:
    ...

  • The selector block can contain two types of keys:

    • Integration-specific keys (e.g., repoSearch, branchSearch) that add parameters to the API request, changing what the API returns. These correspond to the selector concept and vary by integration.
    • The query key, a JQ expression that filters the API response after it arrives. This corresponds to the query concept and is equivalent to the Data filter in the form editor.
    resources:
    - kind: repository
    selector:
    query: "true" # JQ expression - filters objects in the API response
    # Integration-specific selector parameters, for example:
    includedFiles:
    - README.md
    - CODEOWNERS
    port:

    Using a JQ expression, you can define your desired filter conditions. For example, to ingest only repositories with a name starting with "service", use the query key like this:

    query: .name | startswith("service")

For an explanation of these concepts, see Resources and kinds, Selector, and Query.

Transformation

This section covers the YAML keys used to map API response fields to Port entity properties.

The port.entity.mappings key contains the section used to map the object fields to Port entities.
Here you can specify the blueprint in Port to which the data should be mapped, and which API object will be ingested to each of its properties. To map properties, specify the property identifier as the key inside the properties object.

resources:
- kind: repository
selector:
query: "true"
port:
entity:
mappings: # Mappings between one GitHub API object to a Port entity. Each value is a JQ query.
identifier: ".name"
title: ".name"
blueprint: '"service"'
properties:
description: ".description"
url: ".html_url"
defaultBranch: ".default_branch"

To create multiple mappings of the same kind, add another item to the resources array:

resources:
- kind: repository
selector:
query: "true"
port:
entity:
mappings: # Mappings between one GitHub API object to a Port entity. Each value is a JQ query.
identifier: ".name"
title: ".name"
blueprint: '"service"'
properties:
description: ".description"
url: ".html_url"
defaultBranch: ".default_branch"
- kind: repository # In this instance repository is mapped again with a different filter
selector:
query: '.name == "MyRepositoryName"'
port:
entity:
mappings: ...

For an explanation of entity mappings, see Entity mappings.

Mapping relations

For instance, a Service blueprint can be related to a PagerDuty service blueprint.

After ingesting all of our Services and PagerDuty services, we want to connect each Service to its corresponding PagerDuty service.

Assigning relations automatically using the mapping configuration

You can define the relation in the mapping file of your data source (e.g. PagerDuty) so that relations are assigned automatically during ingestion.

Here's an example:

  • Go to your data sources page and click on the PagerDuty exporter:

  • In the bottom left panel, edit the mapping YAML file, and add the following entry to it:

    - kind: services
    selector:
    query: "true"
    port:
    entity:
    mappings:
    identifier: .name
    blueprint: '"service"'
    properties: {}
    relations:
    pager_duty_service: .id

    In this mapping configuration, pager_duty_service is the name of the relation identifier, and .id is a JQ expression that extracts the identifier of the PagerDuty service from the ingested data.

    When the data is ingested:

    • If a service entity with the same identifier (.name) already exists in your catalog, Port will update it, adding the pager_duty_service relation and other associated data, such as the on-call property.
    • If no such service exists, Port will create a new service entity with that identifier and attach the related PagerDuty service and the on-call property.

    This configuration ensures your catalog stays up to date automatically. You can also customize the relation logic, for example, using search-based relations, depending on your naming conventions or data structure.

Mapping relations using search queries

In the example above we map a relation using a direct reference to the related entity's identifier.

Port also allows you to use a search query rule to map relations based on a property of the related entity.
This is useful in cases where you don't have the identifier of the related entity, but you do have one of its properties.

For example, consider the following scenario:
Say we have a service blueprint that has a relation (named service_owner) to a user blueprint. The user blueprint has a property named github_username.

Now, we want to map the service_owner relation based on the github_username property of the user entity.
To achieve this, we can use the following mapping configuration:

- kind: repository
selector:
query: 'true'
port:
entity:
mappings:
identifier: .name
title: .name
blueprint: '"service"'
relations:
service_owner:
combinator: '"and"'
rules:
- property: '"github_username"'
operator: '"="'
value: .owner.login

Instead of directly referencing the user entity's identifier, we use a search query rule to find the user entity whose github_username property matches the .owner.login value returned from GitHub's API.

When using a search query rule to map a relation, Port will query all entities of the relation's target blueprint (in this case - user) and return the one/s that match the rule.

Map by property

In some cases, we may not know the identifier of the entity we want to map to. If that entity has a property that we do know, we can use it to map the data.
This is especially useful when patching entities whose identifiers are not known in advance. Take the following example:

  • Say we installed Port's PagerDuty integration, and we want to connect each service (Git repository) to the relevant PagerDuty service.
  • We can create a property in our service blueprint named pagerduty_service_id, containing the identifier of the relevant PagerDuty service.
  • Then, in the PagerDuty integration mapping, we can use this property to map each PagerDuty service to the relevant service.
  • This way, we would not need to have a separate blueprint for PagerDuty services, since the integration maps directly to the service blueprint.

Mapping by property is done using a search query rule in the following format:

resources:
- kind: services
selector:
query: 'true'
port:
entity:
mappings:
identifier:
combinator: '"and"'
rules:
- operator: '"="'
property: '"pagerduty_service_id"'
value: .id
blueprint: '"service"'
properties:
oncall: .__oncall_user | sort_by(.escalation_level) | .[0].user.email
... # Any other properties you want to map

In the example above, we search for a service entity whose pagerduty_service_id property is equal to the id of the PagerDuty service, and map data from the PagerDuty service to it.

API usage

Searching by property can also be used when using Port's API to create an entity.

To avoid failures when multiple blueprints share the same property value, add a $blueprint rule to your search query to restrict matches to a specific blueprint:

combinator: "and"
rules:
- property: "$blueprint"
operator: "="
value: "service"
# your other rules...

Items to parse

itemsToParse

The following example iterates over a Jira issue's comments array and creates a separate comment entity for each comment, using .item to reference each element:

- kind: issue
selector:
query: .issueType == 'Bug'
port:
itemsToParse: .fields.comments
entity:
mappings:
identifier: .item.id
blueprint: '"comment"'
properties:
text: .item.text
author: .item.author.name
relations:
issue: .key

In this example, Port will iterate over each comment in the comments array and create a separate comment entity for each one. The .item reference allows you to access properties of the current comment being processed.

itemsToParseName

The following example uses itemsToParseName: 'comment' to replace the default .item reference, avoiding a naming conflict with a top-level item key in the API response:

- kind: issue
selector:
query: .issueType == 'Bug'
port:
itemsToParse: .fields.comments
itemsToParseName: 'comment'
entity:
mappings:
identifier: .comment.id
blueprint: '"comment"'
properties:
text: .comment.text
author: .comment.author.name
relations:
issue: .key

In this example, we use comment instead of item to reference each array element, avoiding conflicts with any top-level item property in the API response.

itemsToParseTopLevelTransform

By default, Port removes the target array specified in itemsToParse from the original payload to improve parsing performance. The itemsToParseTopLevelTransform flag controls this behavior:

  • When set to true (default): The target array is removed from the payload, and you can only access array items via .item (or your custom itemsToParseName).
  • When set to false: The target array remains in the payload, allowing you to access both the original array and individual items.

Example with itemsToParseTopLevelTransform: true (default):

- kind: issue
selector:
query: .issueType == 'Bug'
port:
itemsToParse: .fields.comments
itemsToParseTopLevelTransform: true
entity:
mappings:
identifier: .item.id
blueprint: '"comment"'
properties:
text: .item.text
issueKey: .key # Can access top-level properties
relations:
issue: .key

Example with itemsToParseTopLevelTransform: false:

- kind: issue
selector:
query: .issueType == 'Bug'
port:
itemsToParse: .fields.comments
itemsToParseTopLevelTransform: false
entity:
mappings:
identifier: .item.id
blueprint: '"comment"'
properties:
text: .item.text
totalComments: .fields.comments | length # Can access the original array
issueKey: .key
relations:
issue: .key

When itemsToParseTopLevelTransform is false, you can access the original array (e.g., .fields.comments) in addition to individual items via .item.

Limitations
  • The itemsToParseName key is not supported in non-Ocean integrations: Github app, Kubernetes and Webhook integrations.
  • When itemsToParseName is enabled, you cannot use the "test mapping" option in Port's UI.

The object returned from Jira for which we would apply this mapping might look like this (note the comments array):

Example Jira API response (click to expand)
{
"url": "https://example.com/issue/1",
"status": "Open",
"issueType": "Bug",
"comments": [
{
"id": "123",
"text": "This issue is not reproducing"
},
{
"id": "456",
"text": "Great issue!"
}
],
"assignee": "user1",
"reporter": "user2",
"creator": "user3",
"priority": "High",
"created": "2024-03-18T10:00:00Z",
"updated": "2024-03-18T12:30:00Z",
"key": "ISSUE-1"
}

Test

The mapping configuration window contains a JQ playground that allows you to test your JQ queries against example responses from the API of the integrated tool. This is useful for validating your queries and ensuring they return the expected results.

For integrations based on the Ocean framework, examples will be automatically generated for each resource kind in your mapping, based on real data ingested from the tool. You can disable this behavior by setting the sendRawDataExamples flag to false in the integration's configuration.

To test your mapping against the example data, click on the Test mapping button in the bottom-right panel.

Manually add test examples

For each resource kind in your mapping (in the bottom-left panel), you can add an example in the Test examples section.
Click on the Add kind button to add an example:

After adding your example, click on the Test mapping button in the bottom-right panel to test your mapping against the example data.

If the test fails, see Common test errors for a description of each error and how to resolve it.

Port's JQ playground

In addition to the aforementioned JQ playground, Port provides a general JQ playground where you can test any JSON snippet against JQ expressions with real-time filters and AI-powered assistance.

Advanced configuration

Additional options

To use the advanced options, add them to the root of the mapping configuration:

createMissingRelatedEntities: true
deleteDependentEntities: true
entityDeletionThreshold: 0.5
resources:
- kind: repository
...

Edit an integration's mapping

Once you have configured an integration's mapping to your liking, click the Resync button in the bottom-right to save your changes.

To edit an integration's mapping using Port's API, you can use the Patch integration route.

Resync via the API

To perform a simple resync of an integration via the API without changing its mapping, use the same Patch integration route with the integration identifier and an empty body.

Follow-up video

The following video follows up on the video at the top of this page.
It demonstrates how to add new properties to a blueprint and map data into them: