The Teamwork Graph API provides powerful query capabilities to help you access and work with connected data. This guide introduces you to the query languages used to interact with Teamwork Graph: GraphQL for querying graph data and Cypher for traversing relationships in the Graph.
GraphQL is a query language for APIs that enables you to request exactly the data you need. Unlike traditional REST APIs where you might need to make multiple requests to different endpoints, GraphQL allows you to:
When you query Teamwork Graph using GraphQL, you can access objects and their relationships in a structured way. For example, you might query for work items and their related documents, comments, and contributors—all in a single request.
For all the relationships on the Teamwork Graph API, we’ll be providing examples of Cypher queries you can use. You don’t need to learn a whole new language to get started with the Teamwork Graph API, though if you are keen to understand more, this page describes how Cypher works in greater detail.
Cypher is a declarative query language designed specifically for working with graph databases. Created for Neo4j, Cypher provides an intuitive syntax for expressing graph patterns and traversing relationships.
First, let's start with the syntax basics:
| Type | Base case | With label (is of given type) | With multiple labels (is of one of the given types) | With properties |
|---|---|---|---|---|
| Node | ()Any node | (:A)Node with label A | (:A|B|C)Node with label A or B or C | (:A {id: '123', color: 'red'})Node with label A and properties |
| Node with Variable | (a)Any node assigned to variable 'a' | (a:A)Node with label A assigned to variable 'a' | (a:A|B|C)Node with label A or B or C assigned to variable 'a' | (a:A {id: 'xyz', color: 'red'})Node with label A and properties assigned to variable 'a' |
| Edge | -->Any edge (direction left to right) | -[:E]->Edge with label E | -[:E|F|G]->Edge with label E or F or G | -[:E {since: 2020, weight: 5}]->Edge with label E and properties |
| Edge with Variable | -[e]->Any edge assigned to variable 'e' | -[e:E]->Edge with label E assigned to variable 'e' | -[e:E|F|G]->Edge with label E or F or G assigned to variable 'e' | -[e:E {since: 2020, weight: 5}]->Edge with label E and properties assigned to variable 'e' |
MATCH to perform graph traversals.OPTIONAL MATCH is a variant of MATCH; when a pattern is not found in the graph, it returns null instead of ∅.Let's look at some sample Cypher queries in the context of the Teamwork Graph API.
Return an IdentityUser whose ari = 'ari:cloud:identity::user/xyz'
Inline filtering
1 2MATCH (user:IdentityUser {ari: 'ari:cloud:identity::user/xyz'}) RETURN user
Explicit filtering
1 2MATCH (user) WHERE user:IdentityUser AND user.ari = 'ari:cloud:identity::user/xyz' RETURN user
Most common usage
1 2MATCH (issue:JiraWorkItem {ari: 'ari:cloud:jira::issue/xyz'}) WHERE issue.status = 'done'
Return all issues ever reported by a given user.
user-reports-issue relation between a user and issues.1 2MATCH (user:IdentityUser {ari: 'ari:cloud:identity::user/xyz'})-[:atlassian_user_reported_jira_work_item]->(issue:JiraWorkItem) RETURN issue
Return all issues reported by a user in the last week.
lastUpdated time field on the edge (reported) is within last 7 days.1 2MATCH (user:IdentityUser)-[reported:atlassian_user_reported_jira_work_item]->(issue:JiraWorkItem) WHERE reported.lastUpdated > datetime() - duration('P7D') RETURN issue
Return all Jira projects in which a user created issues.
1 2MATCH (user:IdentityUser)-[reported:atlassian_user_reported_jira_work_item]->(issue:JiraWorkItem)<-[:jira_space_has_jira_work_item]-(project:JiraSpace) RETURN DISTINCT project
Note: this can also be written using two MATCH statements:
1 2MATCH (user:IdentityUser)-[reported:atlassian_user_reported_jira_work_item]->(issue:JiraWorkItem) MATCH (issue)<-[:jira_space_has_jira_work_item]-(project:JiraSpace) RETURN DISTINCT project
Return all projects a user reported issues in that are not 'done'.
1 2MATCH (user:IdentityUser)-[reported:atlassian_user_reported_jira_work_item]->(issue:JiraWorkItem)<-[:jira_space_has_jira_work_item]-(project:JiraSpace) WHERE issue.status != 'done' RETURN DISTINCT project
Typically we use optional matches in conjunction with matches if we want to "fetch" more information about a certain node.
For each user, find all issues assigned to that user and all Confluence pages they updated and return as a table user, list_of_issues, list_of_pages:
1 2MATCH (user:IdentityUser) OPTIONAL MATCH (user)<-[rel1:atlassian_user_assigned_jira_work_item]-(issue:JiraWorkItem) WHERE rel1.lastUpdated > datetime() - duration('P7D') OPTIONAL MATCH (user)-[rel2:atlassian_user_updated_confluence_page]->(page:ConfluencePage) WHERE rel2.lastUpdated > datetime() - duration('P7D') RETURN user, collect(issue) AS issues, collect(page) AS pages
Another popular use of optional matches is in conjunction with an is null predicate on a path;
this is used to perform negative checks.
Find users who got assigned an issue this week but did not create a PR this week:
1 2MATCH (user:IdentityUser)-[assigned_issues:atlassian_user_assigned_jira_work_item]->(issue:JiraWorkItem) WHERE assigned_issues.lastUpdated >= datetime() - duration('P7D') OPTIONAL MATCH (user)-[wrote_code:external_user_created_external_pull_request]->(pr:ExternalPullRequest) WHERE wrote_code.lastUpdated >= datetime() - duration('P7D') RETURN user
Return all Confluence pages and Jira issues that a user has created:
1 2MATCH (user:IdentityUser)-[:atlassian_user_created_confluence_page|atlassian_user_created_jira_work_item]->(target) RETURN user, target
Use the pipe | delimiter to separate relationship types. Filters and ordering are supported on edge fields given that the field is present on all relationship types with the same data type:
1 2MATCH (user:IdentityUser)-[r:atlassian_user_created_confluence_page|atlassian_user_created_jira_work_item]->(target) WHERE r.lastUpdated > datetime() - duration('P7D') RETURN user, target ORDER BY r.createdAt desc
where not predicate is used to check the non-existence of a pattern among the nodes and edges that
are typically pattern matches using other MATCH clauses.
Find pages linked to the issues assigned to me that I have not viewed yet:
1 2MATCH (user:IdentityUser {ari: $me}) OPTIONAL MATCH (user)-[:atlassian_user_assigned_jira_work_item]->(issue:JiraWorkItem) WHERE NOT (user)-[:atlassian_user_viewed_jira_work_item]->(issue) RETURN DISTINCT issue
We can also perform simple aggregations in the final return statement.
Get the distinct issues assigned to my teammates:
1 2MATCH (user:IdentityUser {ari: $me}) MATCH (user)-[:atlassian_user_is_in_atlassian_team]->(team:IdentityTeam)<-[atlassian_user_is_in_atlassian_team]-(mate:IdentityUser) MATCH (mate)-[:atlassian_user_assigned_jira_work_item]->(issue:JiraWorkItem) RETURN COLLECT(DISTINCT issue) as issues_without_pr_in_my_team
We have enhanced the performance of the Cypher query pipeline by implementing query plan caching. To truly benefit from this improvement, we encourage our customers to use parameterized Cypher queries instead of embedding parameters directly within the query string.
Example - preferred:
1 2{ "cypherQuery": "MATCH (:IdentityUser {ari: $id})<-[:atlassian_user_assigned_jira_work_item]-(issue:JiraWorkItem) RETURN collect(issue) AS issues", "params": { "id": "user_ari" } }
Example - less optimal:
1 2{ "cypherQuery": "MATCH (:IdentityUser {ari: 'user_ari'})<-[:atlassian_user_assigned_jira_work_item]-(issue:JiraWorkItem) RETURN collect(issue) AS issues" }
To query the Teamwork Graph API, use the GraphQL endpoint for your Atlassian site. Replace yourinstancename with your site's name:
1 2https://yourinstancename.atlassian.net/gateway/api/graphql/twg
For example, if your site is abc.atlassian.net, the endpoint would be:
1 2https://abc.atlassian.net/gateway/api/graphql/twg
1 2query companyName_userTeams($cypherQuery: String!, $params: CypherRequestParams) { cypherQuery(query: $cypherQuery, params: $params) { edges { node { columns { key value { __typename ... on CypherQueryResultListNode { nodes { id data { __typename ... on Team { # Adjust fragment spread depending on hydrated fields id displayName } } } } } } } } } }
Parameters:
$cypherQuery (String!): The Cypher query string.$params (CypherRequestParams): A JSON-like object containing parameters for the Cypher query. Use this to safely inject dynamic values into your query using $paramName syntax.1 2{ "cypherQuery": "MATCH (user:IdentityUser {ari: $id})-[:atlassian_user_is_in_atlassian_team]->(team:IdentityTeam) RETURN collect(distinct team) as teams", "params": { "id": "ari:cloud:identity::user/712020:5fb4febcfacfd60076a1c699" } }
Ready to start querying Teamwork Graph?

Call the Teamwork Graph API
Use the Teamwork Graph API to query unified data and relationships across Atlassian apps and connected tool.
Rate this page: