> For the complete documentation index, see llms.txt.
Skip to main content

Check out Port for yourself ➜ 

Linear

Linear iconLinear icon
Loading version...

Port's Linear integration allows you to model Linear resources in your software catalog and ingest data into them.

Core support

Setup

Choose one of the following installation methods. Not sure which method is right for your use case? Check the available installation methods.

Configuration

Port integrations use a YAML mapping block to ingest data from the third-party api into Port.

The mapping makes use of the JQ JSON processor to select, modify, concatenate, transform and perform other operations on existing fields and values from the integration API.

Default mapping configuration

This is the default mapping configuration you get after installing the Linear integration.

Default mapping configuration (click to expand)

createMissingRelatedEntities: true
deleteDependentEntities: true
resources:
- kind: team
selector:
query: 'true'
port:
entity:
mappings:
identifier: .key
title: .name
blueprint: '"linearTeam"'
properties:
description: .description
workspaceName: .organization.name
url: '"https://linear.app/" + .organization.urlKey + "/team/" + .key'
relations:
members: "[.members.nodes[].id]"
parentTeam: .parent.key
subTeams: "[.children[].key]"
- kind: label
selector:
query: 'true'
port:
entity:
mappings:
identifier: .id
title: .name
blueprint: '"linearLabel"'
properties:
isGroup: .isGroup
relations:
parentLabel: .parent.id
childLabels: '[.children.edges[].node.id]'
- kind: issue
selector:
query: 'true'
port:
entity:
mappings:
identifier: .identifier
title: .title
blueprint: '"linearIssue"'
properties:
url: .url
status: .state.name
assignee: .assignee.email
creator: .creator.email
priority: .priorityLabel
created: .createdAt
updated: .updatedAt
relations:
team: .team.key
labels: .labelIds
parentIssue: .parent.identifier
- kind: document
selector:
query: 'true'
port:
entity:
mappings:
identifier: .id
title: .title
blueprint: '"linearDocument"'
properties:
url: .url
summary: .summary
content: .content
slugId: .slugId
parentType: 'if .project then "project" elif .issue then "issue" elif .team then "team" elif .initiative then "initiative" elif .cycle then "cycle" elif .release then "release" else null end'
project: .project.name
initiative: .initiative.name
cycle: .cycle.number
creator: .creator.email
owner: .owner.email
created: .createdAt
updated: .updatedAt
relations:
team: .team.key
issue: .issue.identifier
- kind: user
selector:
query: 'true'
port:
entity:
mappings:
identifier: .id
title: .displayName // .name
blueprint: '"linearUser"'
properties:
name: .name
email: .email
url: .url
title: .title
active: .active
admin: .admin
guest: .guest
created: .createdAt
updated: .updatedAt
relations:
teams: "[.teamMemberships.nodes[].team.key]"
- kind: project
selector:
query: 'true'
port:
entity:
mappings:
identifier: .id
title: .name
blueprint: '"linearProject"'
properties:
url: .url
description: .description
status: .status.name
health: .health
progress: .progress
priority: .priorityLabel
startDate: .startDate
targetDate: .targetDate
lead: .lead.email
created: .createdAt
updated: .updatedAt
relations:
teams: "[.teams.nodes[].key]"
leadUser: .lead.id
- kind: initiative
selector:
query: 'true'
port:
entity:
mappings:
identifier: .id
title: .name
blueprint: '"linearInitiative"'
properties:
url: .url
description: .description
status: .status
health: .health
priority: .priority
targetDate: if .targetDate then (.targetDate | if test("T") then . else . + "T00:00:00Z" end) else null end
startedAt: .startedAt
completedAt: .completedAt
slugId: .slugId
creator: .creator.email
owner: .owner.email
created: .createdAt
updated: .updatedAt
relations:
leadTeam: .leadTeam.key
ownerUser: .owner.id
parentInitiative: .parentInitiative.id
subInitiatives: "[.subInitiatives.nodes[].id]"
projects: "[.projects.nodes[].id]"
- kind: cycle
selector:
query: 'true'
port:
entity:
mappings:
identifier: .id
title: .name // "Cycle " + (.number | tostring)
blueprint: '"linearCycle"'
properties:
number: .number
description: .description
progress: .progress
startsAt: .startsAt
endsAt: .endsAt
completedAt: .completedAt
isActive: .isActive
isFuture: .isFuture
isPast: .isPast
created: .createdAt
updated: .updatedAt
relations:
team: .team.key

Monitoring and sync status

To learn more about how to monitor and check the sync status of your integration, see the relevant documentation.

Mapping & selectors per resource

To view and test the integration's mapping against examples of the third-party API responses, use the jq playground in your data sources page. Find the integration in the list of data sources and click on it to open the playground.

Set up live events

This integration supports live events, allowing real-time updates to your software catalog without waiting for the next scheduled sync.

Note that different events are supported depending on the resource type.

Live events base URL

For integrations installed using the Hosted by Port method, you can obtain the live events base URL from the Get an integration API. The response includes liveEventsUuid and liveEventsIngestHostname in the appSpec section.

Call the endpoint with your integration's identifier:

curl -X GET \
'https://api.port.io/v1/integration/<INTEGRATION_IDENTIFIER>' \
-H 'Authorization: Bearer <PORT_ACCESS_TOKEN>'
Selecting a Port API URL by account region

The port_region, port.baseUrl, portBaseUrl, port_base_url and OCEAN__PORT__BASE_URL parameters select which Port API instance to use:

In the response, look for the following fields under spec.appSpec:

{
"spec": {
"appSpec": {
"liveEventsUuid": "abcdefghijk",
"liveEventsIngestHostname": "ingest.ocean.getport.io"
}
}
}

Combine them to build the live events base URL:

https://<liveEventsIngestHostname>/live-events/<liveEventsUuid>

For example, with the values above, the live events base URL is:

https://ingest.ocean.getport.io/live-events/abcdefghijk

This is the same URL configured for the integration.