Skip to main content
Pulumi logo Pulumi logo
  1. Docs
  2. Discovery & Governance
  3. Context API
  4. Query the Context API

Query the Context API

    The Context API is a read-only Pulumi Cloud API for querying the infrastructure graph. For an introduction to the product, use cases, and access requirements, see the Context API overview.

    Most people use the Context API through an AI agent: ask a question in natural language, and the agent composes and runs a graph query. This guide is the human-readable technical reference for understanding and validating the selectors and responses behind that interaction or building a direct integration.

    The graph connects nodes like resources (IaC or Discovered) and stacks through relationships such as dependencies, parent-child links, provider ownership, and stack output consumption.

    A graph query uses a JSON selector that says where to start, which relationships to follow, and which parts of the result to return. The API returns the selected graph data as nodes, edges, and optional evidence paths. Selectors are plain JSON, not queries written in GraphQL, GQL, or the Cypher query language.

    The Context API is in public preview. The availability notice at the top of this page lists the Pulumi Cloud editions that can use it. Before running a query, make sure that:

    Pulumi Cloud role-based access control (RBAC) filters each response to the resources, stacks, and cloud accounts that the user or token making the request is permitted to read.

    Ask questions through an AI agent

    You can use Pulumi Neo, Claude Code, Cursor, Codex, or another agent that can run authenticated Pulumi CLI commands. Neo uses the Context API out of the box. To equip another agent, give it this command to fetch the current Markdown primer:

    pulumi api GetGraphSchema -F orgName=my-org
    

    Replace my-org with your organization name, then ask a natural-language question such as:

    Which stacks consume outputs from the payments/prod and payments/staging stacks?

    The primer gives the agent the current graph vocabulary, selector grammar, engine limits, worked examples, and guidance for handling pagination and response completeness.

    Walk through a representative query

    This teaching example uses one anchor, no traversal, and two projected fields to keep the selector and response focused. An agent may generate a more complex selector or make multiple requests to answer the same question. Read your agent’s transcript to inspect the exact queries it ran. To run the example directly, save this selector as query.json:

    {
      "anchor": {
        "nodeType": "resource",
        "match": {
          "type": "aws:s3/bucket:Bucket"
        }
      },
      "return": {
        "select": ["anchor"],
        "fields": ["name", "modified"]
      }
    }
    

    Read it in English as: Find visible S3 buckets, walk no relationships, and return each bucket’s name and modification time.

    Run the query against your Pulumi organization:

    pulumi api GraphQuery -F orgName=my-org --input query.json
    

    Replace my-org with your organization name. The first response page includes selected bucket nodes and their standard identity fields. When available, the requested name and modified values appear in each node’s fields object. Before treating the list as exhaustive, follow Check completeness before acting.

    You can also call the REST endpoint directly. This example requires a Pulumi access token in PULUMI_ACCESS_TOKEN:

    curl -X POST "https://api.pulumi.com/api/insights/my-org/graph/query" \
        -H "Authorization: token $PULUMI_ACCESS_TOKEN" \
        -H "Content-Type: application/json" \
        --data @query.json
    

    The example responses on this page are deliberately small. They show valid response shapes with fictional IDs and values, and their resultCount values match the entries shown. A real response can contain many more array entries. This editorial shortening is different from meta.resultMode: "truncated", which means an engine cap was reached or the search backend returned a partial answer, so the API cannot certify the underlying result as complete. The example schemaVersion is also illustrative.

    Representative response
    {
      "nodes": [
        {
          "id": "urn:pulumi:prod::website::aws:s3/bucket:Bucket::assets",
          "nodeType": "resource",
          "frontier": ["anchor"],
          "urn": "urn:pulumi:prod::website::aws:s3/bucket:Bucket::assets",
          "type": "aws:s3/bucket:Bucket",
          "stack": "prod",
          "project": "website",
          "fields": {
            "modified": "2026-08-26T19:04:12Z",
            "name": "assets"
          }
        }
      ],
      "edges": [],
      "aggregations": {
        "buckets": []
      },
      "pageInfo": {
        "resultCount": 1
      },
      "meta": {
        "resultMode": "exact",
        "schemaVersion": "2026-08-25",
        "visibility": "complete"
      }
    }
    

    The graph response envelope includes nodes, edges, aggregations, pageInfo, and meta. paths appears only when requested. For a graph result, pageInfo.resultCount counts nodes on that page.

    How queries work

    The core mental model is: find nodes, walk edges, choose the answer. A frontier is the named set of nodes produced at one stage of the query.

    StageSelector clauseWhat it does
    Find nodesanchorSelects the nodes where the query starts.
    Walk edgestraverseFollows typed relationships from one frontier of nodes to the next.
    Choose the answerreturnSelects which frontiers, projected fields, and evidence paths appear in the response.

    Three optional clauses support that flow. scope narrows anchor selection, aggregate groups and counts anchors instead of walking the graph, and page controls response pagination.

    The following snippets isolate one clause at a time. A selector containing only anchor is a complete request; the traverse and return objects are clause fragments to add to a request that already has an anchor. The complete query after them combines all three.

    Anchor: choose where to start

    An anchor always names a node type. With no predicate, it selects every visible node of that type.

    Before, every visible resource is an anchor:

    {
      "anchor": {
        "nodeType": "resource"
      }
    }
    

    After, only AWS provider resources below version 7.0.0 are anchors:

    {
      "anchor": {
        "nodeType": "resource",
        "match": {
          "type": "pulumi:providers:aws",
          "fields": {
            "provider_version": {
              "op": "lt",
              "value": "7.0.0"
            }
          }
        }
      }
    }
    

    All predicates in match must be true. The API compares provider_version as a semantic version, so 10.0.0 is newer than 7.0.0.

    Traverse: follow a relationship

    Without a traversal, the query stops at its anchors:

    {
      "traverse": []
    }
    

    Add a traversal step to follow provided_by inward from each provider to the resources it manages:

    {
      "traverse": [
        {
          "edgeTypes": ["provided_by"],
          "direction": "in",
          "depth": {
            "min": 1,
            "max": 1
          },
          "alias": "managed"
        }
      ]
    }
    

    depth is an inclusive range of edge hops, where one hop means following one edge. Here, min: 1 and max: 1 mean exactly one hop, and managed names the frontier containing the nodes reached at that depth. For edge types that support multiple hops, a wider range puts nodes reached at any allowed depth into the same frontier. Setting min to 0 also keeps the nodes where the step started. If you omit depth, the step defaults to exactly one hop.

    Directions are relative to an edge’s defined orientation. A provided_by edge points from a managed resource to its provider, so walking it in from a provider reaches managed resources.

    Return: choose what comes back

    This return clause includes both the starting providers and the managed traversal frontier:

    {
      "return": {
        "select": ["anchor", "managed"]
      }
    }
    

    Narrow the response to managed resources and only include the fields you need:

    {
      "return": {
        "select": ["managed"],
        "fields": ["name", "stack", "provider_version"]
      }
    }
    

    Every traversal alias names a frontier. return.select accepts anchor and those aliases. If you omit return.select, the API returns the anchor and the final traversal frontier.

    Put the clauses together

    This complete query finds resources managed by an outdated AWS provider in the payments project:

    {
      "scope": {
        "stacks": ["payments/*"]
      },
      "anchor": {
        "nodeType": "resource",
        "match": {
          "type": "pulumi:providers:aws",
          "fields": {
            "provider_version": {
              "op": "lt",
              "value": "7.0.0"
            }
          }
        }
      },
      "traverse": [
        {
          "edgeTypes": ["provided_by"],
          "direction": "in",
          "depth": {
            "min": 1,
            "max": 1
          },
          "alias": "managed"
        }
      ],
      "return": {
        "select": ["anchor", "managed"],
        "fields": ["name", "stack", "provider_version"]
      }
    }
    

    Read it in English as: Start at AWS providers below version 7.0.0 in payments stacks, follow provided_by inward, and return the providers and the resources they manage.

    Representative response
    {
      "nodes": [
        {
          "id": "urn:pulumi:prod::payments::aws:ec2/instance:Instance::payments-web-0",
          "nodeType": "resource",
          "frontier": ["managed"],
          "urn": "urn:pulumi:prod::payments::aws:ec2/instance:Instance::payments-web-0",
          "type": "aws:ec2/instance:Instance",
          "stack": "prod",
          "project": "payments",
          "fields": {
            "name": "payments-web-0",
            "stack": "prod"
          }
        },
        {
          "id": "urn:pulumi:prod::payments::pulumi:providers:aws::default_6_0_4",
          "nodeType": "resource",
          "frontier": ["anchor"],
          "urn": "urn:pulumi:prod::payments::pulumi:providers:aws::default_6_0_4",
          "type": "pulumi:providers:aws",
          "stack": "prod",
          "project": "payments",
          "fields": {
            "name": "default_6_0_4",
            "provider_version": "6.0.4",
            "stack": "prod"
          }
        }
      ],
      "edges": [
        {
          "id": "urn:pulumi:prod::payments::aws:ec2/instance:Instance::payments-web-0|provided_by|urn:pulumi:prod::payments::pulumi:providers:aws::default_6_0_4",
          "from": "urn:pulumi:prod::payments::aws:ec2/instance:Instance::payments-web-0",
          "to": "urn:pulumi:prod::payments::pulumi:providers:aws::default_6_0_4",
          "type": "provided_by"
        }
      ],
      "aggregations": {
        "buckets": []
      },
      "pageInfo": {
        "resultCount": 2
      },
      "meta": {
        "resultMode": "exact",
        "schemaVersion": "2026-08-25",
        "visibility": "complete"
      }
    }
    

    The frontier values distinguish the starting provider from the managed result. The edge keeps its defined managed-resource-to-provider orientation even though the query walked it inward. Requested field keys appear only when a value is stored, so the managed node in this example has no provider_version key.

    scope restricts only where the anchors come from. A traversal can leave the named scope.

    Read the response

    A graph query returns part of a graph rather than a table of rows.

    Response fieldMeaning
    nodesThe nodes selected for this page. Every node includes id, nodeType, and frontier; resource and stack identity fields appear when available.
    nodes[].frontieranchor and traversal aliases that selected the node. An empty array marks a node included only to support a returned path.
    nodes[].fieldsExtra values requested with return.fields.
    edgesDirected, typed relationships between returned nodes or nodes returned on an earlier page.
    pathsEvidence trails to the final traversal frontier when return.paths is true. A path is evidence of one route, not an enumeration of every route.
    aggregations.bucketsGroup keys and metrics returned by an aggregation query.
    pageInfoThe result count for this page and, when another page exists, an opaque continuation token.
    metaResult fidelity, the contract revision, and traversal visibility. These fields describe how the API evaluated the selector, not whether the indexed facts are current.

    Check completeness before acting

    Do not use a result to prove absence, produce an exhaustive cleanup-candidate list, report a total across all groups, or claim to have identified the full impact of a change until you have read every page, every page reports meta.resultMode: "exact", and, for a traversal, every page reports meta.visibility: "complete". These checks are necessary, but they certify the answer only relative to the selector, the caller’s RBAC permissions, and the current search index.

    Read these response signals together:

    SignalWhat it meansImplications and next steps
    meta.resultModeexact means no engine cap was reported and the search backend reported a complete answer. truncated means a cap was reached or the backend returned a partial answer, so matching data may exist that is unreachable by this query. The public response does not identify which cause applied.A truncated result cannot support an empty-result conclusion, an exhaustive list, a total across groups, an absence claim, or a full impact analysis. Returned nodes and edges, and the existence of returned aggregation buckets, remain positive evidence, but a bucket’s metric can be incomplete.
    meta.visibilityFor a traversal, complete means the API did not detect RBAC filtering during the walk. trimmed means RBAC excluded data from the traversal or the API could not verify whether filtering occurred. Aggregations omit it.A trimmed traversal cannot support an absence claim or a claim about the full impact of a change. If RBAC limited the walk, have a caller with broader read permissions run the query before making a broader claim.
    pageInfo.continuationTokenA present token means more of the result retained by the API remains. It is independent of resultMode. The key is absent on the final page of results.A conclusion that depends on the full result remains incomplete while a token is present. Pass the opaque token back as page.continuationToken without modifying it. When resultMode is exact, each returned bucket’s metric is complete for that key even if other bucket pages remain.
    meta.schemaVersionThe graph contract revision used to evaluate the selector. It is not a completeness signal by itself.If your client or saved selector assumes another revision, re-fetch the deployed schema and validate those assumptions before interpreting the result.

    If resultMode is truncated, retry the same selector once in case the search backend returned a transient partial response. If truncation persists, partition the anchors or narrow the scope or traversal. Remember that narrowing the selector also narrows the question it can answer.

    Even an exact, complete, fully drained response can fail to support the conclusion you intended:

    • Scope, predicates, edge types, and traversal depth define the question. For example, a three-hop query says nothing about a fourth-hop consumer and still reports exact.
    • Pulumi Cloud RBAC limits anchor selection, aggregation, and traversal to nodes the caller can read. meta.visibility reports when the API detects RBAC filtering during traversal; it does not report matching anchors excluded by RBAC. To make an organization-wide absence or total claim, run the query with read access to every stack and cloud account. Otherwise, limit the claim to nodes the caller can read.
    • The API answers from the search index. Indexing lag, relationships the graph does not model, and inferred_reference edges can make the indexed graph differ from the source of record. An inferred_reference represents a possible dependency rather than a declared relationship.
    • Resources pending deletion are outside the graph, so a graph count can be lower than a Resource Search count over an otherwise similar selection.
    • Pages are evaluated as they are requested. If the graph changes while you drain a query, the combined pages may be inconsistent.
    • fieldsUnavailable: true means projected fields could not be evaluated for that node. Do not base a field-dependent conclusion on that node’s missing keys.

    Process pages cumulatively. An edge or path on a later page can refer to a node returned earlier.

    Confirm high-stakes absence answers against current stack state before deleting or decommissioning infrastructure.

    Complete query examples

    Each heading below is a natural-language question you can give an agent. The selectors and responses are representative; an agent may use a different valid selector or several requests to answer the same question. Substitute your own project, stack, resource type, and provider version when adapting an example for direct use.

    Which stacks consume these stack outputs

    {
      "anchor": {
        "nodeType": "stack",
        "match": {
          "fields": {
            "name": {
              "op": "in",
              "values": ["payments/prod", "payments/staging"]
            }
          }
        }
      },
      "traverse": [
        {
          "edgeTypes": ["consumes_outputs_of"],
          "direction": "in",
          "depth": {
            "min": 1,
            "max": 3
          },
          "alias": "downstream"
        }
      ],
      "return": {
        "select": ["anchor", "downstream"],
        "paths": true
      }
    }
    

    Read it in English as: Start at the payments/prod and payments/staging stacks, follow stack output consumption inward for up to three hops, and return the starting stacks, their downstream consumers, and evidence paths.

    Representative response
    {
      "nodes": [
        {
          "id": "stack:my-org/invoicing/prod",
          "nodeType": "stack",
          "frontier": ["downstream"],
          "stack": "prod",
          "project": "invoicing"
        },
        {
          "id": "stack:my-org/payments/prod",
          "nodeType": "stack",
          "frontier": ["anchor"],
          "stack": "prod",
          "project": "payments"
        },
        {
          "id": "stack:my-org/payments/staging",
          "nodeType": "stack",
          "frontier": ["anchor"],
          "stack": "staging",
          "project": "payments"
        }
      ],
      "edges": [
        {
          "id": "stack:my-org/invoicing/prod|consumes_outputs_of|stack:my-org/payments/prod",
          "from": "stack:my-org/invoicing/prod",
          "to": "stack:my-org/payments/prod",
          "type": "consumes_outputs_of"
        }
      ],
      "paths": [
        {
          "nodes": [
            "stack:my-org/payments/prod",
            "stack:my-org/invoicing/prod"
          ],
          "edges": [
            "stack:my-org/invoicing/prod|consumes_outputs_of|stack:my-org/payments/prod"
          ]
        }
      ],
      "aggregations": {
        "buckets": []
      },
      "pageInfo": {
        "resultCount": 3
      },
      "meta": {
        "resultMode": "exact",
        "schemaVersion": "2026-08-25",
        "visibility": "complete"
      }
    }
    

    The path lists node IDs in traversal order and the edge IDs that connect them. The edge’s from and to fields still use the relationship’s defined orientation.

    This representative selector limits the traversal to three hops. Consumers farther away are not included, and that explicit bound does not set meta.resultMode to truncated.

    Which stacks could a provider upgrade affect

    {
      "scope": {
        "stacks": ["payments/*"]
      },
      "anchor": {
        "nodeType": "resource",
        "match": {
          "type": "pulumi:providers:aws",
          "fields": {
            "provider_version": {
              "op": "lt",
              "value": "7.0.0"
            }
          }
        }
      },
      "traverse": [
        {
          "edgeTypes": ["provided_by"],
          "direction": "in",
          "depth": {
            "min": 1,
            "max": 1
          },
          "alias": "managed"
        },
        {
          "edgeTypes": ["reference"],
          "direction": "in",
          "depth": {
            "min": 0,
            "max": 4
          },
          "alias": "dependents"
        },
        {
          "edgeTypes": ["in_stack"],
          "direction": "out",
          "depth": {
            "min": 1,
            "max": 1
          },
          "alias": "stacks"
        },
        {
          "edgeTypes": ["consumes_outputs_of"],
          "direction": "in",
          "depth": {
            "min": 0,
            "max": 3
          },
          "alias": "affectedStacks"
        }
      ],
      "return": {
        "select": ["affectedStacks"],
        "paths": true
      },
      "page": {
        "pageSize": 500
      }
    }
    

    Read it in English as: Start at outdated AWS providers in payments, find what they manage, follow declared dependents, move from resources to their stacks, follow stack output consumers, and return the affected stacks with evidence paths.

    Representative response
    {
      "nodes": [
        {
          "id": "stack:my-org/payments/prod",
          "nodeType": "stack",
          "frontier": ["affectedStacks"],
          "stack": "prod",
          "project": "payments"
        },
        {
          "id": "urn:pulumi:prod::payments::aws:ec2/instance:Instance::payments-web-0",
          "nodeType": "resource",
          "frontier": [],
          "urn": "urn:pulumi:prod::payments::aws:ec2/instance:Instance::payments-web-0",
          "type": "aws:ec2/instance:Instance",
          "stack": "prod",
          "project": "payments"
        },
        {
          "id": "urn:pulumi:prod::payments::pulumi:providers:aws::default_6_0_4",
          "nodeType": "resource",
          "frontier": [],
          "urn": "urn:pulumi:prod::payments::pulumi:providers:aws::default_6_0_4",
          "type": "pulumi:providers:aws",
          "stack": "prod",
          "project": "payments"
        }
      ],
      "edges": [
        {
          "id": "urn:pulumi:prod::payments::aws:ec2/instance:Instance::payments-web-0|in_stack|stack:my-org/payments/prod",
          "from": "urn:pulumi:prod::payments::aws:ec2/instance:Instance::payments-web-0",
          "to": "stack:my-org/payments/prod",
          "type": "in_stack"
        },
        {
          "id": "urn:pulumi:prod::payments::aws:ec2/instance:Instance::payments-web-0|provided_by|urn:pulumi:prod::payments::pulumi:providers:aws::default_6_0_4",
          "from": "urn:pulumi:prod::payments::aws:ec2/instance:Instance::payments-web-0",
          "to": "urn:pulumi:prod::payments::pulumi:providers:aws::default_6_0_4",
          "type": "provided_by"
        },
        {
          "id": "urn:pulumi:prod::payments::pulumi:providers:aws::default_6_0_4|in_stack|stack:my-org/payments/prod",
          "from": "urn:pulumi:prod::payments::pulumi:providers:aws::default_6_0_4",
          "to": "stack:my-org/payments/prod",
          "type": "in_stack"
        }
      ],
      "paths": [
        {
          "nodes": [
            "urn:pulumi:prod::payments::pulumi:providers:aws::default_6_0_4",
            "urn:pulumi:prod::payments::aws:ec2/instance:Instance::payments-web-0",
            "stack:my-org/payments/prod"
          ],
          "edges": [
            "urn:pulumi:prod::payments::aws:ec2/instance:Instance::payments-web-0|provided_by|urn:pulumi:prod::payments::pulumi:providers:aws::default_6_0_4",
            "urn:pulumi:prod::payments::aws:ec2/instance:Instance::payments-web-0|in_stack|stack:my-org/payments/prod"
          ]
        }
      ],
      "aggregations": {
        "buckets": []
      },
      "pageInfo": {
        "resultCount": 3
      },
      "meta": {
        "resultMode": "exact",
        "schemaVersion": "2026-08-25",
        "visibility": "complete"
      }
    }
    

    Only affectedStacks was selected for return. The provider and managed resource therefore have empty frontier arrays, but the API includes them to make the evidence path self-contained. pageInfo.resultCount includes these supporting nodes.

    page.pageSize requests up to 500 nodes per response page. Follow any pageInfo.continuationToken before treating the result as complete.

    This selector follows declared reference edges. Add inferred_reference to the same reference step when you want possible dependencies as leads, and verify them before acting. The selected depth bounds limit which nodes the query can reach. The engine path cap limits the returned evidence and marks the result as truncated if it’s reached.

    How many resources of each type are in a project

    {
      "scope": {
        "stacks": ["payments/*"]
      },
      "anchor": {
        "nodeType": "resource"
      },
      "aggregate": {
        "groupBy": ["type"],
        "metrics": [
          {
            "op": "count",
            "alias": "resources"
          }
        ]
      }
    }
    

    Read it in English as: Start at resources in every payments stack, group them by resource type, and count each group.

    Representative response
    {
      "nodes": [],
      "edges": [],
      "aggregations": {
        "buckets": [
          {
            "key": {
              "type": "aws:ec2/instance:instance"
            },
            "metrics": {
              "resources": 12
            }
          },
          {
            "key": {
              "type": "aws:rds/instance:instance"
            },
            "metrics": {
              "resources": 3
            }
          }
        ]
      },
      "pageInfo": {
        "resultCount": 2
      },
      "meta": {
        "resultMode": "exact",
        "schemaVersion": "2026-08-25"
      }
    }
    

    Aggregation returns buckets instead of nodes and edges. This response contains two buckets: the resources metric reports 12 EC2 instances and 3 RDS instances. pageInfo.resultCount is 2 because it counts buckets, and meta.visibility is absent because aggregation does not traverse the graph.

    Follow every pageInfo.continuationToken and require meta.resultMode: "exact" on every page before treating the bucket list as complete. A resource whose grouped field is missing, empty, or too long to be indexed as a grouping value is not included in any bucket. These omissions do not by themselves set meta.resultMode to truncated, so even a fully drained, exact bucket list does not establish the total number of matching resources.

    Run the query with access to all payments stacks when you need a project-wide view. Type bucket keys are lowercase even though resource type tokens use mixed case.

    Query reference

    This section summarizes the current selector contract. The Context API is in public preview, so use the deployed schema and OpenAPI definition described in Get the deployed schema instead of depending on this snapshot.

    Top-level clauses

    ClauseRequiredPurpose
    scopeNoNarrows anchor selection by stacks, Discovery cloud accounts, or discovered-resource inclusion.
    anchorYesSelects the zero-hop starting nodes.
    traverseNoApplies ordered edge-walking steps.
    aggregateNoGroups and measures resource anchors instead of returning a graph walk.
    returnNoChooses frontiers, projected fields, and evidence paths.
    pageNoSets a page size or continues from a previous response.

    Scope

    Use stacks with either a resource or stack anchor. This complete selector starts at IaC resources in every stack in the payments project:

    {
      "scope": {
        "stacks": ["payments/*"],
        "includeDiscovered": false
      },
      "anchor": {
        "nodeType": "resource"
      }
    }
    

    Each stacks entry is either an exact project/stack name, such as payments/prod, or project/*, which selects every stack in that project. The request already identifies the organization, so omit it from the entry.

    Use accounts with a resource anchor to select resources from Discovery cloud accounts:

    {
      "scope": {
        "accounts": ["production-aws"]
      },
      "anchor": {
        "nodeType": "resource"
      }
    }
    

    An account entry selects that account and its /-separated descendants. For example, production-aws also selects production-aws/us-west-2. To query stack nodes, use scope.stacks with a stack anchor.

    • accounts entries name cloud accounts visible to the caller. An account name that does not exist or is not visible to the caller is rejected rather than returning an empty result.
    • scope.accounts is not valid with a stack anchor.
    • Entries within each list are alternatives. When both lists are present on a resource query, an anchor must match a stack entry and an account entry.
    • includeDiscovered defaults to true. Set it to false to exclude resources found by Discovery. It has no effect on a stack anchor.
    • Scope restricts anchor selection only. Traversal can reach nodes outside it.

    Anchors and node types

    Resources and stacks use different names when you select them and when you read them from a response:

    • To select one resource, put "urn": { "op": "eq", "value": "urn:pulumi:prod::payments::aws:s3/bucket:Bucket::assets" } in match.fields. The returned node uses the same URN for both id and urn. When available, it also reports type, project, and stack.
    • To select one stack, put "name": { "op": "eq", "value": "payments/prod" } in match.fields. Stack selectors omit the organization. The returned node includes it in id, for example stack:my-org/payments/prod, and reports "project": "payments" and "stack": "prod" separately.

    An anchor accepts these fields:

    FieldRequiredMeaning
    nodeTypeYesThe node type to select: resource or stack.
    matchNoA structured match. All present predicates are combined with AND.
    queryNoA Resource Search query string. Valid only for a resource anchor and mutually exclusive with match.
    limitNoBounds resolved anchors. Under aggregation it limits returned buckets, not the resources counted in a returned bucket.

    Omitting both match and query selects every visible node of the given type. For resource anchors, match.type is an exact resource type token such as aws:s3/bucket:Bucket. For match.fields, use fields listed in the deployed schema under nodeTypes[].selectableFields.

    Field predicates

    OperatorJSON shapeMeaning
    eq{ "op": "eq", "value": "x" }Equal to one value.
    in{ "op": "in", "values": ["x", "y"] }Equal to any listed value.
    lt{ "op": "lt", "value": "x" }Less than the value.
    lte{ "op": "lte", "value": "x" }Less than or equal to the value.
    gt{ "op": "gt", "value": "x" }Greater than the value.
    gte{ "op": "gte", "value": "x" }Greater than or equal to the value.
    present{ "op": "present" }The field has a non-empty value or list.
    absent{ "op": "absent" }The field has no value or has an empty value or list.

    Except for provider_version, resource anchors evaluate ordered comparisons using each field’s index ordering, while resource traversal targets compare projected values lexicographically. Ordered provider_version comparisons use semantic-version ordering in both contexts. Predicate values are strings. An operator that requires value or values rejects a missing or empty operand. For a stack node’s name field, only eq and in are accepted.

    Traversal steps

    This step starts from the current frontier, follows incoming declared reference edges for one to three hops, keeps the EC2 instances it reaches, and names that result dependents:

    {
      "edgeTypes": ["reference"],
      "direction": "in",
      "depth": {
        "min": 1,
        "max": 3
      },
      "target": {
        "match": {
          "type": "aws:ec2/instance:Instance"
        }
      },
      "alias": "dependents"
    }
    
    FieldRequiredExample valueEffect
    edgeTypesYes["reference"]Follows declared dependency relationships. ["reference", "inferred_reference"] is the only current multi-edge combination.
    directionYes"in"For reference, walks from a dependency to resources that depend on it.
    depthNo{"min": 1, "max": 3}Includes nodes reached in one, two, or three hops. Omit it for exactly one hop. Set min to 0 to also keep the step’s source nodes.
    target.matchNo{"type": "aws:ec2/instance:Instance"}Keeps only reached EC2 instance nodes.
    target.absentNotrueIf added to target above, returns source nodes from which no matching EC2 instance is reachable within the depth range. Use it only on the final traversal step.
    aliasNo"dependents"Names this result so return.select can include it. Without an alias, the first step is step0, the second is step1, and so on.

    Before choosing an edge, direction, target field, or depth, fetch the deployed schema. Each edgeTypes[] entry describes the edge’s orientation and supported directions. The singleHop field identifies edges limited to one hop. For fields in target.match, use the target node type’s nodeTypes[].projectableFields.

    Aliases must be unique identifiers: a letter or underscore followed by letters, digits, or underscores. A traversal alias cannot be anchor or repeat an earlier alias.

    target.absent evaluates only the edge types named in that step. Include both reference and inferred_reference when possible dependents should count alongside declared dependents. Treat an absence result as complete only when meta.resultMode is exact and meta.visibility is complete.

    in_stack is a special traversal step. It resolves stack membership for every resource frontier accumulated so far, not only the immediately preceding frontier.

    Edge types and directions

    The following table is a snapshot. Directions describe what a walk reaches from the current frontier.

    Edge typeEdge orientationSupported directionsout reachesin reachesHopsBasis
    referencedependent resource to dependencyin, out, bothDependenciesDeclared dependentsMultipleDeclared
    inferred_referencepossible dependent to possible dependencyin, out, bothPossible dependenciesPossible dependentsMultipleInferred
    parentchild resource to parent resourcein, out, bothParentChildrenMultipleDeclared
    provided_bymanaged resource to provider resourcein, out, bothProviderManaged resourcesOneDeclared
    in_stackresource to stackoutStackNot supportedOneDeclared
    consumes_outputs_ofconsumer stack to producer stackin, outProducers it consumesConsumers of its outputsMultipleDeclared

    reference records a declared dependency. inferred_reference is produced heuristically by matching scanned property values to destination provider IDs; treat it as a lead, not a fact.

    A resource-level reference walk stops at a pulumi:pulumi:StackReference resource because that node has no direct edge to the producer stack it reads. The following selector answers a stack-level question: its StackReference anchor shows where the query starts, but the traversal returns every producer stack whose outputs invoicing/prod consumes:

    {
      "scope": {
        "stacks": ["invoicing/prod"]
      },
      "anchor": {
        "nodeType": "resource",
        "match": {
          "type": "pulumi:pulumi:StackReference"
        }
      },
      "traverse": [
        {
          "edgeTypes": ["in_stack"],
          "direction": "out",
          "depth": {
            "min": 1,
            "max": 1
          },
          "alias": "consumerStack"
        },
        {
          "edgeTypes": ["consumes_outputs_of"],
          "direction": "out",
          "depth": {
            "min": 1,
            "max": 1
          },
          "alias": "producerStacks"
        }
      ],
      "return": {
        "select": ["anchor", "consumerStack", "producerStacks"]
      }
    }
    

    The in_stack step moves from each StackReference resource to its containing consumer stack. Because consumes_outputs_of points from a consumer stack to a producer stack, the out step reaches the producer stacks. This second hop runs at stack level and can return multiple producer stacks. To find stacks that consume a producer’s outputs, use direction: "in" on the second step.

    Return, aggregation, and paging

    return supports:

    • select: Frontiers to include, using anchor and traversal aliases. It defaults to the anchor plus the final traversal frontier.
    • fields: Requests additional resource fields in nodes[].fields. Choose field names from nodeTypes[].projectableFields in the current schema. When projection succeeds, the response includes each requested field that has a stored value. fieldsUnavailable: true means the node has no indexed document, so the requested fields could not be evaluated.
    • paths: When true, includes up to one evidence path for each node in the final traversal frontier that has a recorded predecessor, regardless of which frontiers select includes. The engine returns at most limits.maxPaths paths for a query, currently 500. Reaching this result limit stops path generation and sets meta.resultMode to truncated; narrow the query when complete path evidence matters. Returned paths are not guaranteed to be shortest.

    Use aggregate when you need grouped counts instead of individual nodes. This selector counts resources in each payments stack:

    {
      "scope": {
        "stacks": ["payments/*"]
      },
      "anchor": {
        "nodeType": "resource"
      },
      "aggregate": {
        "groupBy": ["stack"],
        "metrics": [
          {
            "op": "count",
            "alias": "resources"
          }
        ]
      }
    }
    

    groupBy creates one bucket per stack, count counts the matching resources in each bucket, and alias names that count resources in the bucket’s metrics object. Aggregations return aggregations.buckets instead of resource nodes. See How many resources of each type are in a project for a complete response.

    aggregate supports resource anchors only and cannot be combined with traverse, return.select, return.fields, or return.paths. An anchor that compares provider_version by using lt, lte, gt, or gte is also incompatible. aggregate accepts:

    • groupBy: One or two distinct fields from nodeTypes[].groupByFields. Fields are applied in nesting order. Resources whose grouped field is missing, empty, or too long to be indexed as a grouping value appear in no bucket. These omissions do not by themselves set meta.resultMode to truncated.
    • metrics: One or more metrics for each bucket. The current metric operation is count; each metric also needs a unique identifier as its alias for the result key.

    page.pageSize defaults to 200. A response with pageInfo.continuationToken has another page. Copy the opaque value into page.continuationToken without modifying it.

    Current limits

    These values are a snapshot, not a compatibility promise. Check GetGraphSchema for current values.

    LimitCurrent capBehavior at the limit
    Traversal steps per query4A larger selector is rejected.
    depth.max per step6A larger selector is rejected.
    Entries in scope.stacks100A larger selector is rejected.
    Entries in scope.accounts100A larger selector is rejected.
    Fields in aggregate.groupBy2A larger selector is rejected.
    Resolved anchors, aggregation buckets, or in values1,000More matching anchors or aggregation buckets mark the result truncated; a traversal query that would clip anchors is rejected instead. An in predicate with more values is rejected.
    New nodes per traversal level8,000The result is marked truncated.
    Nodes held by one query20,000The result is marked truncated.
    Recorded edge crossings80,000The result is marked truncated.
    Evidence paths500The result is marked truncated.
    page.pageSize1,000A larger value is silently reduced to 1,000.

    Get the deployed schema

    Use GetGraphSchema to validate a selector against the deployed contract or equip an agent with the current graph vocabulary and selector guidance.

    For validation and integration tooling, fetch the JSON representation:

    pulumi api GetGraphSchema -F orgName=my-org --output=json
    

    The JSON response is authoritative for the deployed schema version, node types, fields available for selection, projection, and grouping, fixed field values, edge types and directions, metric operations, and engine limits. This response is not a complete JSON Schema for the request body.

    To equip an agent, fetch the Markdown primer:

    pulumi api GetGraphSchema -F orgName=my-org
    

    The primer describes the current graph vocabulary, selector grammar, engine limits, worked examples, and guidance for handling pagination and response completeness. Give the agent this command so it can refresh the primer as the API evolves.

    The Pulumi Cloud REST API reference defines the accepted request and response shapes. While the Context API remains in public preview, re-fetch the deployed schema instead of relying on a saved copy.

      The infrastructure as code platform for any cloud.