Orbit API

  • Tier: Premium, Ultimate
  • Offering: GitLab.com
  • Status: Beta

Use this API to run queries, retrieve schemas, and check cluster health for Orbit.

Search for graph entities

Searches for readable entities in a project or group and returns Orbit entity tokens. Results use GitLab search, and might be available before the graph indexes the entities. The endpoint selects Zoekt, Advanced Search, or Basic search independently for each scope. Every selected scope appears in scopes with a status, so you can tell an empty scope from one that was skipped, unavailable, or failed.

You need at least the Reporter role in a group under the target’s top-level group, and Orbit must be turned on for that top-level group. Without a Reporter membership in any Orbit-enabled top-level group, such as a project-only member, the endpoint returns 403 Forbidden. Otherwise, a target outside your Orbit-enabled top-level groups returns 404 Not Found. Where the instance license covers Orbit, administrators in Admin Mode and auditors only need Orbit turned on for the target’s top-level group. On GitLab.com, they still need the usual Orbit entitlement, like the other Orbit endpoints: a Reporter membership in an entitled top-level group. A search that GitLab search treats as abusive, such as a stop word or a term that is too short or too long, returns 400 Bad Request. The endpoint counts toward the Orbit query rate limit. It also counts once toward the search rate limit, plus once for each further scope it searches. Once you hit that limit, the remaining scopes return rate_limited without searching.

GET /api/v4/orbit/grep

Supported attributes:

Provide exactly one of full_path, namespace_id, or project_id. Scopes are searched in a fixed order: work items, merge requests, blobs, commits, notes, milestones, users, projects, and groups. The total limit applies only to the selected scopes.

If successful, returns 200 OK and the following response attributes for response_format=raw:

AttributeTypeDescription
requestobjectEffective request values: search, limit, and per_scope.
result_countintegerNumber of returned results.
resultsobject arrayResults from all scopes, in scope order.
results[].idinteger or stringEntity ID. The commit SHA for commits, and null for files.
results[].matchesobject arrayMatched lines for a file, when the search type returns them. Each entry has start_line, end_line, and text. Basic search keeps the first match for each file.
results[].referenceobjectStructured entity reference.
results[].scopestringScope that returned the result.
results[].textstringOne-line result text, truncated to 200 characters.
results[].tokenstringEntity token in the form Type[id]. null for files and commits.
results[].typestringOrbit entity type.
scopesobject arrayOne entry for every selected scope.
scopes[].backendstringSearch type used for the scope: basic, advanced, or zoekt. Present when status is ok.
scopes[].namestringSearch scope name.
scopes[].paginationobjectPresent when status is ok. has_more is true when the scope has more results than were returned.
scopes[].reasonobjectPresent when status is not ok. code is one of not_available_for_search_type, search_type_error, invalid_search, search_failed, timeout, rate_limited, or limit_reached.
scopes[].result_countintegerNumber of results from the scope. Present when status is ok.
scopes[].statusstringOne of ok, unavailable, skipped, or error. unavailable means the search type or target doesn’t support the scope, the search backend rejected the request (for example, a Zoekt project limit), or you don’t have access to it. skipped means the total limit was reached first. error means the search failed or hit the search rate limit, and other scopes keep their results.
targetobjectSearch target.
target.full_pathstringFull path of the target.
target.idintegerTarget ID.
target.tokenstringEntity token of the target.
target.typestringProject or Group.
truncatedbooleanWhether the total limit was reached before all scopes were exhausted, or a scope has more results than per_scope.
versionstringGrep response contract version.

Files use a reference with project_id, path, branch, and commit_sha. branch is the default branch for Advanced Search and Zoekt results. commit_sha is the indexed commit for Advanced Search results, and null otherwise. Commits use a reference with project_id and sha. Other entities use a reference with the global database id.

Example request:

shell
curl --request GET \
  --header "PRIVATE-TOKEN: <your_access_token>" \
  --url "https://gitlab.example.com/api/v4/orbit/grep?search=search%20term&project_id=1&scopes=merge_requests,blobs,notes&limit=2&per_scope=2"

Example response:

json
{
  "version": "1.0.0",
  "request": {"search": "search term", "limit": 2, "per_scope": 2},
  "target": {"type": "Project", "id": 1, "token": "Project[1]", "full_path": "example-group/example-project"},
  "scopes": [
    {"name": "merge_requests", "status": "ok", "backend": "advanced", "result_count": 1, "pagination": {"has_more": false}},
    {"name": "blobs", "status": "ok", "backend": "advanced", "result_count": 1, "pagination": {"has_more": true}},
    {"name": "notes", "status": "skipped", "reason": {"code": "limit_reached"}}
  ],
  "results": [
    {
      "scope": "merge_requests",
      "type": "MergeRequest",
      "id": 42,
      "token": "MergeRequest[42]",
      "reference": {"id": 42},
      "text": "Add scoped search"
    },
    {
      "scope": "blobs",
      "type": "File",
      "id": null,
      "token": null,
      "reference": {"project_id": 1, "path": "README.md", "branch": "main", "commit_sha": "e79b2dc8c2cca1ed93c8c760dfa89b8e47ed1720"},
      "text": "README.md",
      "matches": [{"start_line": 3, "end_line": 5, "text": "A search term in context"}]
    }
  ],
  "result_count": 2,
  "truncated": true
}

For response_format=llm, the endpoint returns compact text/plain output:

orbit_grep version=1.0.0 target=Project[1] search="search term" scopes="merge_requests,blobs,notes" results=2 truncated=true
- MergeRequest[42] type=MergeRequest text="Add scoped search"
- File project_id="1" path="README.md" branch="main" commit_sha="e79b2dc8c2cca1ed93c8c760dfa89b8e47ed1720" type=File text="README.md"
! skipped scope=notes reason=limit_reached

Entity context

  • Status: Experiment

This endpoint is an experiment. The request and response shape can change without notice.

Resolves one token to a PostgreSQL summary of a merge request or work item. Optionally, it attaches the graph neighborhood of the entity.

Every request that reaches token resolution counts once against the orbit_query rate limit. This includes summary-only requests, not_found results, expansion errors, and 403 responses for users without an Orbit-enabled group. Requests rejected with 400 do not count.

With a fine-grained personal access token, the read_orbit permission is not enough. The token also needs read_merge_request or read_work_item on the project or group of the entity, and read_pipeline on the project for the pipeline status. Without them, the entity is returned as not_found. The pipeline status is null, and linked items you cannot read are left out of the summary. The token’s project scope applies to the entity summary only. The graph expansion is filtered by your own access, the same as for named queries.

POST /api/v4/orbit/context

Supported attributes:

AttributeTypeRequiredDescription
tokensstring arrayYesExactly one token. See token forms.
expandbooleanNoIf true, attaches the graph neighborhood of the entity. Default is false.
response_formatstringNoOne of raw or llm. Default is raw.

A tokens array that does not hold exactly one string, or a value for expand that is not the JSON boolean true or false, returns 400 Bad Request. The endpoint requires a JSON request body.

Other failures:

  • 403 Forbidden: Orbit is not licensed for you, or you have no Orbit-enabled group.
  • 404 Not Found: Orbit is not available for you.
  • 429 Too Many Requests: You exceeded the orbit_query rate limit.

Token forms

  • Type[id], for example MergeRequest[123]. id is the global database ID.
  • gid://gitlab/<Type>/<id>.
  • A merge request, issue, or work item URL on this instance, including group work item URLs under /groups/.
  • path!iid for a merge request, for example gitlab-org/gitlab!42.
  • path#iid for a work item, for example gitlab-org/gitlab#7. The path can also be a group.
  • Epic[id], gid://gitlab/Epic/<id>, an epic URL such as /groups/gitlab-org/-/epics/5, or group&iid, for example gitlab-org&5. id is the epic ID.

The supported types are MergeRequest and WorkItem. Issue is an alias for WorkItem, and the response reports WorkItem. An epic resolves to its work item in the same way: the response reports WorkItem, the ID of the work item, and work_item_type epic, and the expansion runs for the work item. Other types return unsupported_type.

Response

If successful, returns 200 OK and the following response attributes for response_format=raw:

AttributeTypeDescription
versionstringResponse contract version, 1.1.0.
entitiesobject arrayOne entry for the one token.
entities[].tokenstringToken as sent.
entities[].resolved_tokenstringToken in Type[id] form. Only present when found is true.
entities[].typestringMergeRequest or WorkItem. null when found is false.
entities[].idstringGlobal database ID, in the same form as the node IDs in the graph result. For iid forms, null when found is false. For Type[id] and GID tokens, the ID that was sent.
entities[].foundbooleanWhether the entity exists and you can read it.
entities[].summaryobjectSummary of a found entity. Contains namespace_path and project_path. project_path is null for group work items. Other fields can be added.
entities[].error.codestringinvalid_token, unsupported_type, or not_found. Present when found is false.
entities[].expansionobjectnull when expand is false or the token was not found. Otherwise, {"result": ...} or {"error": {"code": ...}}, plus query.
entities[].expansion.queryobjectThe named query that ran or would have run: name and parameters (node_ids and limit). Absent for expansion_unsupported_type.

Summaries include these fields:

  • For a merge request: title, state, author, branches, head pipeline status, reviewers, and linked issues.
  • For a work item: title, state, labels, assignees, milestone, and linked merge requests.

Lists are limited and contain only records you can read. summary.truncated is an array with the names of the lists that were cut, for example ["reviewers"]. It is empty when no list was cut. Entries in linked_issues and linked_merge_requests are ordered by ID and have type, id (a string), token, relation (CLOSES), iid, project_path, and title. Every failure after a lookup starts returns the same not_found error, whether the record is missing or you cannot read it.

Expansion

With expand, the request runs at most one graph query, using the expand_merge_request or expand_work_item named query. A request that runs one query is billed as one query. A failure of the expansion never fails the summary.

expansion.query has the name and parameters of the query. It is present on a result and on every error except expansion_unsupported_type, including when no query ran. After outside_graph_scope, a rerun is billed and returns nothing.

expansion.error.code is one of:

  • expansion_unsupported_type: The type has no expansion query.
  • outside_graph_scope: The entity is outside the part of the graph you can query. This is a group where you are not at least a Reporter, or a group where Orbit is not turned on. No query runs and nothing is billed.
  • billing_namespace_required: The error also has message, deep_link to your preferences, and candidates, the groups you can select. This matches the 422 response of POST /api/v4/orbit/query/:name.
  • quota_exhausted, timeout, or execution_error.

If pagination.truncated in the graph result is false, a missing center node means the entity has no readable neighbors. Otherwise, treat it as unknown.

Example request:

shell
curl --request POST \
  --header "PRIVATE-TOKEN: <your_access_token>" \
  --header "Content-Type: application/json" \
  --data '{"tokens": ["gitlab-org/gitlab#609451"], "expand": true}' \
  --url "https://gitlab.example.com/api/v4/orbit/context"

Example response:

json
{
  "version": "1.1.0",
  "entities": [
    {
      "token": "gitlab-org/gitlab#609451",
      "resolved_token": "WorkItem[196910643]",
      "type": "WorkItem",
      "id": "196910643",
      "found": true,
      "summary": {
        "namespace_path": "gitlab-org",
        "project_path": "gitlab-org/gitlab",
        "title": "Describe graph entities",
        "state": "opened",
        "iid": 609451,
        "work_item_type": "issue",
        "web_url": "https://gitlab.example.com/gitlab-org/gitlab/-/work_items/609451",
        "labels": ["knowledge graph"],
        "assignees": [{"id": 7, "username": "alex_smith", "name": "Alex Smith"}],
        "milestone": null,
        "linked_merge_requests": [
          {
            "type": "MergeRequest",
            "id": "412345",
            "token": "MergeRequest[412345]",
            "relation": "CLOSES",
            "iid": 1201,
            "project_path": "gitlab-org/gitlab",
            "title": "Add entity context"
          }
        ],
        "truncated": []
      },
      "expansion": {
        "query": {
          "name": "expand_work_item",
          "parameters": {"node_ids": [196910643], "limit": 100}
        },
        "result": {
          "format_version": "5.0.4",
          "query_type": "neighbors",
          "nodes": [
            {"type": "WorkItem", "id": "196910643", "title": "Describe graph entities"},
            {"type": "MergeRequest", "id": "412345", "iid": 1201}
          ],
          "edges": [
            {"from": "MergeRequest", "from_id": "412345", "to": "WorkItem", "to_id": "196910643", "type": "CLOSES"}
          ],
          "pagination": {"has_more": false, "truncated": false}
        }
      }
    }
  ]
}

With response_format=llm, the response is text/plain. It contains the summary text, then the graph in TOON format under a graph: label, or a single error <code> line.

Endpoint reference

For the full reference of Orbit endpoints, including paths, parameters, and responses, see the interactive API reference. The following sections cover query DSL behavior and worked examples that the generated reference doesn’t show.

Query mode

The availability of GQL query mode is controlled by a feature flag named orbit_gql_queries. For more information, see the history.

Queries require JSON DSL objects by default. Enabling orbit_gql_queries switches queries to read-only GQL strings and discovery to GQL guidance. GQL follows openCypher 9 syntax. The flag targets individual users. REST and MCP callers cannot override the mode through parameters or headers. With the flag enabled, response_format: gql returns query results as a plain-text table.

Skills

Skill endpoints require authentication with the read_api scope. They don’t check the Orbit license, so unavailable skills return 404 Not Found instead of 403 Forbidden. The response is 404 Not Found with the message Skills are not available when:

  • The knowledge graph service is an older version without skill RPCs.
  • The knowledge graph service isn’t configured on the instance. On GitLab Self-Managed, this is controlled by orbit.enabled in gitlab.yml, or the legacy knowledge_graph.enabled setting.
  • The current user doesn’t have at least the Reporter role in a group whose top-level group has Orbit turned on. Administrators in Admin Mode and auditors are exempt from this role requirement.

In these cases, the Orbit CLI uses its embedded local skill.

List deployed skills

Lists the Orbit skills deployed with the connected knowledge graph service.

GET /orbit/skills

This request takes no attributes.

If successful, returns 200 OK and the following response attributes:

AttributeTypeDescription
server_versionstringVersion of the deployed knowledge graph service.
skillsarrayDeployed skills.
skills[].compatibilitystringEnvironment requirements from the skill manifest.
skills[].descriptionstringDescription from the skill manifest.
skills[].namestringSkill name.
skills[].versionstringSkill version from the manifest.

The response includes an ETag header derived from the sorted skill names and versions, and a Cache-Control header set to private, max-age=0, must-revalidate.

Example request:

shell
curl --request GET \
  --header "PRIVATE-TOKEN: <your_access_token>" \
  --url "https://gitlab.example.com/api/v4/orbit/skills"

Example response:

json
{
  "skills": [
    {
      "name": "orbit",
      "version": "0.29.0",
      "description": "Use the glab orbit CLI for knowledge graph queries.",
      "compatibility": "Requires Orbit CLI"
    }
  ],
  "server_version": "0.31.0"
}

Retrieve a deployed skill

Retrieves a complete deployed Orbit skill tree.

GET /orbit/skills/:name

Supported attributes:

AttributeTypeRequiredDescription
namestringYesSkill name.

If successful, returns 200 OK and the following response attributes:

AttributeTypeDescription
compatibilitystringEnvironment requirements from the skill manifest.
filesarrayFiles in the skill tree.
files[].contentstringUTF-8 file content.
files[].pathstringNormalized path relative to the skill root.
files[].sha256stringSHA-256 hash of the file content.
namestringSkill name.
server_versionstringVersion of the deployed knowledge graph service.
versionstringSkill version.

The response includes these headers:

HeaderDescription
Cache-ControlSet to private, max-age=0, must-revalidate.
ETagSet to "<version>".

Send the current ETag value in the If-None-Match request header to revalidate a cached skill tree. A matching value returns 304 Not Modified with no response body.

Example request:

shell
curl --request GET \
  --header "PRIVATE-TOKEN: <your_access_token>" \
  --url "https://gitlab.example.com/api/v4/orbit/skills/orbit"

Example response:

json
{
  "name": "orbit",
  "version": "0.29.0",
  "compatibility": "Requires Orbit CLI",
  "server_version": "0.31.0",
  "files": [
    {
      "path": "SKILL.md",
      "sha256": "93f2655e8772217e4aa8c8fbe3ad0c1f36b2bdf4a63f9f6e2f6d89a3b01da9f8",
      "content": "# Orbit skill\n"
    }
  ]
}

If the skill doesn’t exist, returns 404 Not Found with the known skill names in the error message.

Check deployed skill metadata

Checks the identity and cache headers of a deployed skill without retrieving its files.

HEAD /orbit/skills/:name

Supported attributes:

AttributeTypeRequiredDescription
namestringYesSkill name.

If successful, returns 200 OK, the same ETag and Cache-Control headers as GET /orbit/skills/:name, and no response body.

Example request:

shell
curl --head \
  --header "PRIVATE-TOKEN: <your_access_token>" \
  --url "https://gitlab.example.com/api/v4/orbit/skills/orbit"

Retrieve a deployed skill file

Retrieves one file from a deployed skill as raw UTF-8 text. Use this route to fetch SKILL.md directly or follow relative links to files in the same skill tree.

GET /orbit/skills/:name/*path

Supported attributes:

If successful, returns 200 OK with the file content, not a JSON object. Markdown files (.md) have the content type text/markdown; charset=utf-8. Other files have the content type text/plain; charset=utf-8; no skill file is served as HTML. Only exact paths listed in the skill manifest are available. Unknown paths return 404 Not Found. Malformed paths can return 400 Bad Request. API error responses are JSON, rather than raw file content; requests blocked by path traversal middleware can return a plain-text error.

The response includes a strong ETag from the file’s SHA-256 and a Cache-Control header set to private, max-age=0, must-revalidate. Send the ETag in If-None-Match to receive 304 Not Modified when the file is unchanged. HEAD returns the same headers without a body.

Example request:

shell
curl --request GET \
  --header "PRIVATE-TOKEN: <your_access_token>" \
  --url "https://gitlab.example.com/api/v4/orbit/skills/orbit/SKILL.md"

Example response:

markdown
# Orbit skill

Named queries

Prefer POST /api/v4/orbit/query/:name over POST /api/v4/orbit/query for programmatic consumers. A named query’s structure lives on the server, so it cannot drift from the DSL grammar or ontology. Named requests keep JSON envelopes and run templates in the active mode.

Query templates

The named query templates listed at GET /api/v4/orbit/query/templates include raw_query rendered for the authenticated user:

  • Templates contain JSON objects in JSON mode and GQL strings in GQL mode.
  • Identity values, like the ID of the authenticated user, are resolved server-side from the request credentials. The same request returns different raw_query values for different users. Do not cache or share templates across users or modes.
  • Invoke parameterized queries by name with their arguments, as the catalog excludes them.

Prefer executing named queries directly over using templates. Use templates only where displaying the query text is the goal, such as populating a query editor or explorer with the text of a preset.

Query examples

The following examples show the Orbit query DSL for each query type. All examples use POST /api/v4/orbit/query. For the complete query language, see Orbit query language.

In GQL mode, pass a GQL string as the query value to POST /api/v4/orbit/query:

json
{"query": "MATCH (u:User {id: 1}) RETURN u LIMIT 1"}

The examples use the default raw response format. In each response, the result object lists the matched nodes and the edges between them. Node IDs are strings. Aggregation queries also return columns, group_columns, and rows.

Retrieve a user by username:

shell
curl --request POST \
  --header "PRIVATE-TOKEN: <your_access_token>" \
  --header "Content-Type: application/json" \
  --data '{
    "query": {
      "query_type": "traversal",
      "nodes": [
        {"id": "u", "entity": "User", "filters": {"username": "john_smith"}}
      ]
    }
  }' \
  --url "https://gitlab.example.com/api/v4/orbit/query"

Example response:

json
{
  "result": {
    "format_version": "5.0.3",
    "query_type": "traversal",
    "nodes": [
      {"type": "User", "id": "1", "username": "john_smith", "name": "John Smith", "state": "active"}
    ],
    "edges": [],
    "pagination": {"has_more": false, "truncated": false}
  },
  "query_type": "traversal",
  "row_count": 1
}

Find merged merge requests in a project:

shell
curl --request POST \
  --header "PRIVATE-TOKEN: <your_access_token>" \
  --header "Content-Type: application/json" \
  --data '{
    "query": {
      "query_type": "traversal",
      "nodes": [
        {"id": "p", "entity": "Project", "node_ids": [8]},
        {"id": "mr", "entity": "MergeRequest", "filters": {"state": "merged"}}
      ],
      "relationships": [{"type": "IN_PROJECT", "from": "mr", "to": "p"}]
    }
  }' \
  --url "https://gitlab.example.com/api/v4/orbit/query"

Example response:

json
{
  "result": {
    "format_version": "5.0.3",
    "query_type": "traversal",
    "nodes": [
      {"type": "Project", "id": "8", "name": "Diaspora Client", "full_path": "diaspora/diaspora-client", "visibility_level": "public"},
      {"type": "MergeRequest", "id": "43", "iid": 1, "title": "Resolve connection timeout on large payloads", "state": "merged", "source_branch": "fix-timeout", "target_branch": "main"},
      {"type": "MergeRequest", "id": "44", "iid": 2, "title": "Replace deprecated API calls in federation module", "state": "merged", "source_branch": "update-federation-api", "target_branch": "main"}
    ],
    "edges": [
      {"from": "MergeRequest", "from_id": "43", "to": "Project", "to_id": "8", "type": "IN_PROJECT"},
      {"from": "MergeRequest", "from_id": "44", "to": "Project", "to_id": "8", "type": "IN_PROJECT"}
    ],
    "pagination": {"has_more": false, "truncated": false}
  },
  "query_type": "traversal",
  "row_count": 2
}

Count merge requests per project. Traversal and aggregation queries must bound at least one node with node_ids or filters:

shell
curl --request POST \
  --header "PRIVATE-TOKEN: <your_access_token>" \
  --header "Content-Type: application/json" \
  --data '{
    "query": {
      "query_type": "aggregation",
      "nodes": [
        {"id": "p", "entity": "Project", "node_ids": [5, 8]},
        {"id": "mr", "entity": "MergeRequest"}
      ],
      "relationships": [{"type": "IN_PROJECT", "from": "mr", "to": "p"}],
      "aggregations": [{"count": "mr", "as": "mr_count"}],
      "group_by": ["p"]
    }
  }' \
  --url "https://gitlab.example.com/api/v4/orbit/query"

Example response:

json
{
  "result": {
    "format_version": "5.0.3",
    "query_type": "aggregation",
    "nodes": [],
    "edges": [],
    "columns": [{"name": "mr_count", "function": "count", "target": "mr"}],
    "group_columns": [{"name": "p", "kind": "node", "node": "p", "entity": "Project"}],
    "rows": [
      {"p": {"type": "Project", "id": "8", "properties": {"name": "Diaspora Client", "full_path": "diaspora/diaspora-client", "visibility_level": "public"}}, "mr_count": 8},
      {"p": {"type": "Project", "id": "5", "properties": {"name": "Puppet", "full_path": "brightbox/puppet", "visibility_level": "public"}}, "mr_count": 6}
    ],
    "pagination": {"has_more": false, "truncated": false}
  },
  "query_type": "aggregation",
  "row_count": 2
}

Find outgoing neighbors of a user. The direction attribute accepts outgoing, incoming, or both, and defaults to outgoing:

shell
curl --request POST \
  --header "PRIVATE-TOKEN: <your_access_token>" \
  --header "Content-Type: application/json" \
  --data '{
    "query": {
      "query_type": "neighbors",
      "nodes": [{"id": "u", "entity": "User", "node_ids": [1]}],
      "neighbors": {"direction": "outgoing"}
    }
  }' \
  --url "https://gitlab.example.com/api/v4/orbit/query"

Example response:

json
{
  "result": {
    "format_version": "5.0.3",
    "query_type": "neighbors",
    "nodes": [
      {"type": "User", "id": "1", "username": "john_smith", "name": "John Smith", "state": "active"},
      {"type": "Group", "id": "29", "name": "diaspora", "full_path": "diaspora", "visibility_level": "public"},
      {"type": "MergeRequest", "id": "43", "iid": "1", "title": "Resolve connection timeout on large payloads", "state": "merged", "source_branch": "fix-timeout", "target_branch": "main"},
      {"type": "MergeRequest", "id": "44", "iid": "2", "title": "Replace deprecated API calls in federation module", "state": "merged", "source_branch": "update-federation-api", "target_branch": "main"}
    ],
    "edges": [
      {"from": "User", "from_id": "1", "to": "Group", "to_id": "29", "type": "MEMBER_OF"},
      {"from": "User", "from_id": "1", "to": "MergeRequest", "to_id": "43", "type": "AUTHORED"},
      {"from": "User", "from_id": "1", "to": "MergeRequest", "to_id": "44", "type": "AUTHORED"}
    ],
    "pagination": {"has_more": false, "truncated": false}
  },
  "query_type": "neighbors",
  "row_count": 3
}

Find the shortest path from a user to a project. Path finding queries require rel_types, and follow each relationship type only in its defined direction, from the from node toward the to node:

shell
curl --request POST \
  --header "PRIVATE-TOKEN: <your_access_token>" \
  --header "Content-Type: application/json" \
  --data '{
    "query": {
      "query_type": "path_finding",
      "nodes": [
        {"id": "u", "entity": "User", "node_ids": [1]},
        {"id": "p", "entity": "Project", "node_ids": [8]}
      ],
      "path": {
        "type": "shortest",
        "from": "u",
        "to": "p",
        "max_depth": 3,
        "rel_types": ["MEMBER_OF", "CONTAINS"]
      }
    }
  }' \
  --url "https://gitlab.example.com/api/v4/orbit/query"

Example response:

json
{
  "result": {
    "format_version": "5.0.3",
    "query_type": "path_finding",
    "nodes": [
      {"type": "User", "id": "1", "username": "john_smith", "name": "John Smith", "state": "active"},
      {"type": "Group", "id": "29", "name": "diaspora", "full_path": "diaspora", "visibility_level": "public"},
      {"type": "Project", "id": "8", "name": "Diaspora Client", "full_path": "diaspora/diaspora-client", "visibility_level": "public"}
    ],
    "edges": [
      {"from": "User", "from_id": "1", "to": "Group", "to_id": "29", "type": "MEMBER_OF", "path_id": 0, "step": 0},
      {"from": "Group", "from_id": "29", "to": "Project", "to_id": "8", "type": "CONTAINS", "path_id": 0, "step": 1}
    ],
    "pagination": {"has_more": false, "truncated": false}
  },
  "query_type": "path_finding",
  "row_count": 1
}

Access and cluster health

GET /api/v4/orbit/status always returns 200 OK, regardless of access or service health. When the user has no access, system is null:

json
{
  "user": {
    "available": false
  },
  "system": null
}