> ## Documentation Index
> Fetch the complete documentation index at: https://doc.lucidworks.com/llms.txt
> Use this file to discover all available pages before exploring further.

# REST Query

> Index pipeline stage configuration specifications

export const schema = {
  "type": "object",
  "title": "REST Query",
  "description": "Fetches data from an external REST endpoint and enriches the pipeline document with the returned values. Specify the endpoint URL, HTTP method, optional query parameters and headers, and XPath mapping rules to extract values from the response into document fields. Use this stage to look up related data from external APIs or microservices during indexing.",
  "required": ["params"],
  "properties": {
    "skip": {
      "type": "boolean",
      "title": "Skip This Stage",
      "description": "Controls whether this stage executes during pipeline processing at runtime. When set to `true`, the stage is completely bypassed and documents pass through unchanged to the next stage. Useful for A/B testing, gradual rollouts, or temporarily disabling a stage without removing it from the pipeline.",
      "default": false,
      "hints": ["advanced"]
    },
    "label": {
      "type": "string",
      "title": "Label",
      "description": "Human-readable identifier displayed in the Fusion Admin UI, monitoring dashboards, and log messages. Use descriptive labels like `Parse Product PDFs` to aid debugging and team collaboration. Labels appear in performance metrics and error reports, making it easier to identify which stage failed.",
      "hints": ["advanced"],
      "maxLength": 255
    },
    "condition": {
      "type": "string",
      "title": "Condition",
      "description": "JavaScript expression evaluated at runtime on each document to conditionally execute this stage. The expression must return `true` to execute or `false` to skip. Access document fields using `doc.getFieldValue('fieldName')` and request parameters via `request.getFirstParam('paramName')`. For example, `doc.getFieldValue('type') === 'premium'` executes this stage only for premium content.",
      "hints": ["code", "code/javascript", "advanced"]
    },
    "params": {
      "type": "object",
      "title": "Call Parameters",
      "required": ["uri", "method"],
      "properties": {
        "uri": {
          "type": "string",
          "title": "Endpoint URI",
          "description": "HTTP/HTTPS endpoint URL for endpoint uri service integration. Must be fully qualified with protocol (http:// or https://). Supports dynamic parameter substitution using template variables like `${param.name}` or `${doc.fieldName}` for request-specific or document-specific URLs."
        },
        "method": {
          "type": "string",
          "title": "Call method",
          "description": "Specifies the HTTP method used when calling the REST endpoint. Choose `get` for read-only data retrieval, `post` or `put` for payloads sent in the request body, or `delete` for removal operations. The method must match what the target endpoint accepts.",
          "enum": ["get", "put", "post", "delete"]
        },
        "queryParams": {
          "type": "object",
          "title": "Query parameters",
          "properties": {},
          "additionalProperties": {
            "type": "string"
          },
          "description": "Defines key-value pairs appended as query string parameters to the REST endpoint URL. For example, set `type=product` to filter results by type. Template variables referencing document fields are not supported here. Use the endpoint URI for dynamic substitution."
        },
        "headers": {
          "type": "object",
          "title": "Request protocol headers",
          "properties": {},
          "additionalProperties": {
            "type": "string"
          }
        },
        "entity": {
          "type": "string",
          "title": "Request entity (as string)",
          "hints": ["code", "lengthy"]
        }
      }
    },
    "debug": {
      "type": "boolean",
      "title": "Add Debugging Information",
      "description": "Controls whether debugging information is added to the document or pipeline context during stage execution. When `true`, extra properties such as the raw response body are added to the document for inspection. Disable in production to avoid adding noise to indexed documents.",
      "default": false
    },
    "hasNoSideEffects": {
      "type": "boolean",
      "title": "Run in simulation mode",
      "description": "Controls whether the stage runs in simulation mode without executing the actual REST call. When `true`, the stage is bypassed and no HTTP request is made, allowing the pipeline to be tested safely. Set to `false` in production to enable live REST calls.",
      "default": false
    },
    "mappingRules": {
      "type": "array",
      "title": "Mapping of Returned Values (as XPath Expressions) to Document Fields",
      "items": {
        "type": "object",
        "required": ["path", "target"],
        "properties": {
          "path": {
            "type": "string",
            "title": "XPath expression",
            "description": "Specifies the XPath expression used to select a value from the REST response body. Absolute expressions start from the root of the response document, while relative expressions are evaluated from the current context node. Use standard XPath syntax such as `//product/id` to navigate the response structure."
          },
          "target": {
            "type": "string",
            "title": "Target field"
          },
          "append": {
            "type": "boolean",
            "title": "Append to Existing Values in Target Field",
            "default": false
          },
          "xml": {
            "type": "boolean",
            "title": "Add as an XML Fragment",
            "default": false
          }
        }
      }
    }
  },
  "category": "Document Filtering and Enrichment",
  "categoryPriority": 8,
  "unsafe": true
};

export const SchemaParamFields = ({schema}) => {
  const sanitize = str => {
    if (typeof str !== "string") return str;
    return str.replace(/^"(.*)"$/s, "$1").replace(/\\/g, "").replace(/"/g, "'");
  };
  const renderMd = str => {
    const s = sanitize(str);
    const text = (/[.!?]\)*$/).test(s) ? s : `${s}.`;
    return text.split(/(\*\*[^*]+\*\*|_[^_]+_|`[^`]+`)/g).map((part, i) => {
      if (part.startsWith("**")) return <strong key={i}>{part.slice(2, -2)}</strong>;
      if (part.startsWith("_")) return <em key={i}>{part.slice(1, -1)}</em>;
      if (part.startsWith("`")) return <code key={i}>{part.slice(1, -1)}</code>;
      return part;
    });
  };
  const {description, properties = {}, required: requiredProps = []} = schema;
  const visibleProps = useMemo(() => Object.entries(properties).filter(([, prop]) => !prop.hints?.includes("hidden")), [properties]);
  const renderProp = ([name, prop]) => {
    const isRequired = requiredProps.includes(name);
    const hasDefault = prop.default !== undefined;
    const rawDefault = prop.default;
    const hints = prop.hints || [];
    const isComplexDefault = hasDefault && (typeof rawDefault === "object" || typeof rawDefault === "string" && (rawDefault.length > 20 || rawDefault.includes('"')));
    const postBadges = [];
    if (prop.title) {
      postBadges.push(<><span className="text-stone-400 dark:text-stone-500">API property: </span>{name}</>);
    }
    const constraints = [];
    if (prop.minimum !== undefined && prop.maximum !== undefined) {
      constraints.push(`Range: ${prop.minimum} – ${prop.maximum}`);
    } else if (prop.minimum !== undefined) {
      constraints.push(`Min: ${prop.minimum}`);
    } else if (prop.maximum !== undefined) {
      constraints.push(`Max: ${prop.maximum}`);
    }
    if (prop.minLength !== undefined && prop.maxLength !== undefined) {
      constraints.push(`Length: ${prop.minLength} – ${prop.maxLength}`);
    } else if (prop.minLength !== undefined) {
      constraints.push(`Min length: ${prop.minLength}`);
    } else if (prop.maxLength !== undefined) {
      constraints.push(`Max length: ${prop.maxLength}`);
    }
    const fieldProps = {
      key: name,
      body: prop.title || name,
      type: prop.type,
      ...postBadges.length > 0 && ({
        post: postBadges
      }),
      ...isRequired && ({
        required: true
      }),
      ...!isComplexDefault && hasDefault ? {
        default: sanitize(String(rawDefault))
      } : {}
    };
    const isObject = prop.type === "object" && prop.properties;
    const isArrayOfObjects = prop.type === "array" && prop.items?.type === "object" && prop.items.properties;
    return <ParamField {...fieldProps}>
        {prop.description && <p>{renderMd(prop.description)}</p>}

        {prop.enum && <p>
            Allowed values: 
            {prop.enum.map((v, i) => <>{i > 0 && ", "}<code key={i}>{String(v)}</code></>)}
          </p>}

        {constraints.length > 0 && <p className="text-stone-500 dark:text-stone-400 text-sm">
            {constraints.join(" · ")}
          </p>}

        {isComplexDefault && <div className="flex">
            <p>
              <strong>Default:</strong>
            </p>
            <pre className="!my-0">
              <code>
                {JSON.stringify(rawDefault, null, 2)}
              </code>
            </pre>
          </div>}

        {isArrayOfObjects && <Expandable title="item properties">
            <SchemaParamFields schema={{
      properties: prop.items.properties,
      required: prop.items.required
    }} />
          </Expandable>}

        {isObject && <Expandable title="properties">
            <SchemaParamFields schema={{
      properties: prop.properties,
      required: prop.required
    }} />
          </Expandable>}
      </ParamField>;
  };
  return <div>
      {description && <p>{renderMd(description)}</p>}

      {visibleProps.map(renderProp)}
    </div>;
};

export const LwTemplate = ({title = "Key questions to get you started", icon = "sparkles", cta = "Powered by Agent Studio", linkHref = "https://lucidworks.com/demo/?utm_source=docs&utm_medium=referral&utm_campaign=docs_cta_ai"}) => {
  const [isLoaded, setIsLoaded] = useState(false);
  useEffect(() => {
    const timer = setTimeout(() => {
      setIsLoaded(true);
    }, 500);
    return () => clearTimeout(timer);
  }, []);
  return <div className="lw-template-container">
      <Card title={title} icon={icon}>
        {isLoaded && <span dangerouslySetInnerHTML={{
    __html: `<lw-template id="a029c1a9-28be-427e-b0e1-5d918920246a"></lw-template
            >`
  }} />}
        <Link href={linkHref} className="agent-studio-link text-left text-gray-600 gap-2 dark:text-gray-400 text-sm font-medium flex flex-row items-center hover:text-primary dark:hover:text-primary-light group-hover:text-primary group-hover:dark:text-primary-light">Powered by Lucidworks Agent Studio</Link>
      </Card>
    </div>;
};

[localhost link]: http://localhost:3000/docs/lucidworks-search/09-developer-documentation/config-specs/index-pipeline-stages/rest-query

[mintlify link]: https://doc.lucidworks.com/docs/lucidworks-search/09-developer-documentation/config-specs/index-pipeline-stages/rest-query

[old doc.lw link]: https://doc.lucidworks.com/managed-fusion/5.9/a20hmu

The REST Query index stage performs remote procedure calls to external services, merges the response from that service with a document being processed by the pipeline, and makes calls to the external system for each document.

This stage can call external systems in any of the following ways:

* any HTTP or HTTPS request: `<http-protocol>://<path>`
* a Solr request: `solr://COLLECTION_NAME/…​`
* a Lucidworks Search service request: `extservice://SERVICE_NAME/<path>`

The call syntax allows the use of document field variables. For example, variable `${docField}` will be replaced with the value of the field named `docField` for the document being processed.

Several types of responses are also supported:

* POJOs
* JSON
* XML

<LwTemplate />

## Call Parameters

In the Lucidworks Search UI, select **include** to display the fields for the call parameters (`params` in the REST API). Parameters are specified as key-value pairs:

|                                           |                                                                                                                                                                                                                                                                                                                |
| ----------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Endpoint URI** / `uri`                  | A fully-qualified service URI. This can be an HTTP call, a Solr request, or a Lucidworks Search service call  <br />●any HTTP or HTTPS request: `<http-protocol>://<path>`  <br />●a Solr request: `solr://COLLECTION_NAME/…​`  <br />●a Lucidworks Search service request: `extservice://SERVICE_NAME/<path>` |
| **Call Method** / `method`                | The method to use for the RPC call. Supported methods are `GET`, `POST`, `PUT`, and `DELETE`.                                                                                                                                                                                                                  |
| **Query parameters** / `queryParams`      | Query parameters to be passed with the request.                                                                                                                                                                                                                                                                |
| **Request protocol headers** / `headers`  | Request protocol headers, such as `"Content-Type" : "application/json"`.                                                                                                                                                                                                                                       |
| **Request entity (as string)** / `entity` | The request body. The request body; [see the examples below](#examples).                                                                                                                                                                                                                                       |

The `params` can also take variable substitution expressions. Variables are expressed as `${fieldName}`, where `fieldName` is a name of the current document’s field (or `id` for the document’s id). Only the first value of a multi-valued field is used for substitution, and this value is treated as a string.

For example, a `queryParams` could be constructed as follows:

```json wrap  theme={"dark"}
"params" : {
  "uri" : "solr://collection1/select",
  "method" : "GET",
  "queryParams" : {
    "q" : "{!field f=a_txt v=$prodName}",
    "prodName" : "${prodName}"
  }
}
```

In this example, the variable `${prodName}` will be replaced with the string value of the 'prodName' field in the current document being processed. If the current document contains "iPhone 6" as the value of 'prodName', the resulting query in this example will be a fielded search `q=a_txt:iPhone 6`.

## Mapping Rules

The **mappingRules** property takes the following, specified as key-value pairs:

* path. an [XPath expression](http://www.w3schools.com/xml/xpath_syntax.asp). It is assumed that this expression always returns a node list, and each returned node is processed separately and its converted value is added to a multi-valued field.
* target. the name of the target field in the current document where the value(s) will be stored.
* append. if true, values extracted from the RPC response will be appended to the target multi-valued field. When this is false, the default, existing values in the target field will be discarded and replaced with the values from the RPC response.
* xml. if true, the extracted DOM nodes will be separately serialized to XML and the resulting XML-formatted text will be added to the target field. When this is false, the default, the extracted DOM nodes will be flattened and converted to a list of fields. Field names in this case will correspond to XML element names, dot-separated, and element attributes will be represented as fields with @attributeName suffix.

## Examples

**Create a REST Query stage to merge results from another Solr system:**

```json wrap  theme={"dark"}
{
  "type" : "indexing-rpc",
  "id" : "BasicSolrCall",
  "mappingRules" : [ {
    "path" : "//result/doc[1]/str[@name='foo_s']/text()",
    "target" : "foo_s",
    "append" : true,
    "xml" : false
  }, {
    "path" : "//result/doc/arr[@name='a_txt']/*",
    "target" : "doc_txt",
    "append" : true,
    "xml" : false
  } ],
  "debug" : true,
  "params" : {
    "uri" : "solr://collection1/select",
    "method" : "GET",
    "queryParams" : {
      "q" : "a_txt:${doc_value}"
    },
    "headers" : { }
  },
  "skip" : false,
  "label" : "indexing-rpc"
}
```

**Upload a stopword list:**

```json wrap  theme={"dark"}
{
 "type" : "indexing-rpc",
 "id" : "demo",
 "debug" : true,
 "params" : {
   "uri" : "http://EXAMPLE_COMPANY.lucidworks.cloud/api/stopwords/movies",
   "method" : "PUT",
   "headers" : { "Content-Type" : "application/json" },
   "entity" : "New stopword list"
 },
 "skip" : false,
 "label" : "Indexing RPC Demo",
 "type" : "indexing-rpc"
}
```

<Note>
  Replace `EXAMPLE_COMPANY` with the name provided by your Lucidworks representative.
</Note>

## Configuration

<Tip>
  When entering configuration values in the UI, use *unescaped* characters, such as `\t` for the tab character. When entering configuration values in the API, use *escaped* characters, such as `\\t` for the tab character.
</Tip>

<SchemaParamFields schema={schema} />
