# Unified Pass Through

Pass Through lets you add or override fields in a Unified API request that aren't part of the standard model, so a request reaches a specific downstream provider with exactly the data that provider requires. There are three mechanisms: a `pass_through` query parameter for GET requests, `extend_object` for simple body extensions, and JSONPath `extend_paths` for precise nested modifications.

## Introduction

The Unified API normalizes each SaaS provider behind a single interface, which means some provider-specific fields fall outside the standard model by design. Pass Through closes that gap: it lets a request carry extra or overridden data so the downstream provider gets what it needs, without changing the standard model for every other connector.

### Purpose of the Guide

This guide covers all three Pass Through mechanisms (query parameter, `extend_object`, and JSONPath `extend_paths`), when to use each, and how they behave differently on JSON connectors versus XML/SOAP connectors like NetSuite, Sage Intacct, and Workday.

## Understanding the Pass Through Feature

### What is Pass Through?

Pass Through is the mechanism for adding or overriding fields in a Unified API request outside the standard model, so a specific downstream provider receives exactly the data it requires, even when that data has no equivalent field in the unified schema.

#### Definition and Purpose

- **Definition**: Pass Through enables you to inject custom data into your API requests, tailoring them to meet specific needs of various services.
- **Purpose**: The main purpose of Pass Through is to offer greater flexibility and control over the data transmitted through the Unified API, ensuring you can accommodate the unique requirements of different SaaS providers.

#### Use Cases and Examples

- **Custom Data Requirements**: When a service requires additional data that is not included in the standard API model, you can use Pass Through to add this data.
  - _Example_: A CRM service may require a specific custom field in the request that is not typically included in the standard integration.
- **Overriding Default Data**: If you need to override existing data in the request, Pass Through allows you to specify the new values.
  - _Example_: Updating the default currency from USD to EUR for a particular transaction.
- **Complex Data Structures**: For services that require nested or complex data structures, Pass Through can handle these requirements seamlessly.
  - _Example_: Sending a nested JSON object with detailed customer information to an analytics service.

### Key Features

#### Service-Specific Customization

- **Explanation and Benefits**: Pass Through allows you to customize the data sent to each service individually. This ensures that each service receives exactly the information it needs without affecting other services.
- **Example Scenarios**:
  - Sending additional user metadata to a marketing service while keeping the standard data model for other services.
  - Customizing the format of date fields for a specific financial service.

#### Simple Additions

- **How to Add Extra Data**: You can easily add extra data to your API requests using Pass Through. This can be a single value, a list, or more complex nested information.
- **Example Scenarios**:
  - Adding a promotional code to an e-commerce transaction request.
  - Including additional contact information in a customer profile update.

#### Advanced Customization

- **Navigating to Specific Points in Nested Objects/Arrays**: Pass Through supports advanced customization by allowing you to navigate to specific points within nested objects or arrays in your API request. This enables you to set, override, or nullify any value with precision.
- **Example Scenarios**:
  - **Tax Specification for Invoice Items**: Suppose there is a tax specification required for a specific type of invoice item that is only supported by Quickbooks. With Pass Through, you can navigate to the specific invoice item within a nested array and add the necessary tax information.
  - **Selective Data Modification**: For complex data structures, such as customer profiles with multiple nested attributes, you can target specific fields within the profile to update or nullify certain values based on the requirements of the service you are integrating with.
  - **Conditional Overrides**: Implement logic to conditionally override data within nested structures. For instance, if certain fields need to be updated only when specific conditions are met, Pass Through allows you to apply these changes accurately.

Together, these three capabilities cover the full range from a one-off field addition to precise, conditional edits inside deeply nested data.

## Implementation Details

Pass Through has three implementation methods: a query parameter for GET requests, `extend_object` for simple body extensions, and JSONPath `extend_paths` for precise nested modifications. Each is detailed below.

### Query Parameter Pass Through

Besides extending the request body, you can also use **`pass_through` as a query parameter**. This method forwards unmapped key/value pairs directly to the downstream service, which is particularly useful for filters, flags, or parameters not standardized in the Unified API.

- **Behavior**:
  - Parameters sent as `?pass_through[key]=value` will be forwarded as `?key=value` to the downstream API.
  - Multiple keys can be included simultaneously.

#### Examples:

1. **Basic Forwarding**:

   ```http
   GET /crm/leads?pass_through[search]=leads
   ```

   **Downstream**:

   ```http
   GET /crm/leads?search=leads
   ```

2. **Multiple Parameters**:

   ```http
   GET /crm/leads?pass_through[status]=active&pass_through[limit]=50
   ```

   **Downstream**:

   ```http
   GET /crm/leads?status=active&limit=50
   ```

- **Common Use Cases**:
  - Forwarding custom filters (e.g., `search`, `status`, `archived`).
  - Adding experimental flags or preview options.
  - Handling provider-specific pagination parameters.

### Simple Pass Through

Simple pass through extends the request body with additional data specified in the `extend_object`, handling various types of data structures seamlessly.

- **Behavior**: Directly extends the request body with the specified data in `extend_object`.
- **Handling Undefined or Empty Data**: Manages cases where `pass_through` is undefined, an empty object, or an empty array gracefully.
- **Nested Objects and Arrays**: Supports complex structures, including nested objects and arrays.
- **Key Overrides**: Allows existing keys in the request data to be overridden with new values, including `null`, strings, objects, and arrays.

#### Examples:

1. **Basic Extension**:

   ```json
   {
     "foo": "bar",
     "pass_through": [
       {
         "service_id": "salesforce",
         "extend_object": {
           "baz": "qux"
         }
       }
     ]
   }
   ```

   **Result**:

   ```json
   {
     "foo": "bar",
     "baz": "qux"
   }
   ```

2. **Removing Data**:

   ```json
   {
     "foo": "bar",
     "baz": "qux",
     "pass_through": [
       {
         "service_id": "salesforce",
         "extend_object": {
           "baz": undefined
         }
       }
     ]
   }
   ```

   **Result**:

   ```json
   {
     "foo": "bar"
   }
   ```

3. **Adding Objects**:

   ```json
   {
     "foo": "bar",
     "pass_through": [
       {
         "service_id": "salesforce",
         "extend_object": {
           "baz": {
             "qux": "quux",
             "corge": "grault"
           }
         }
       }
     ]
   }
   ```

   **Result**:

   ```json
   {
     "foo": "bar",
     "baz": {
       "qux": "quux",
       "corge": "grault"
     }
   }
   ```

4. **Set a Property to null**:

   ```json
   {
     "foo": "bar",
     "baz": "qux",
     "pass_through": [
       {
         "service_id": "salesforce",
         "extend_object": {
           "baz": null
         }
       }
     ]
   }
   ```

   **Result**:

   ```json
   {
     "foo": "bar",
     "baz": null
   }
   ```

By utilizing the `extend_object` key within the `pass_through` array, you can seamlessly extend or override your API request data to meet the specific needs of different services. This method ensures flexibility and precision in handling various data structures.

### JSONPath Pass Through

The JSONPath pass through feature uses JSONPath expressions to target specific parts of the request body for modification. This allows for precise and advanced customization.

- **Behavior**: Uses JSONPath expressions to inject key-value pairs into the request body.
- **Key Injection**: Inserts key-value pairs even if the path does not initially exist.
- **Array Handling**: Applies value overrides within arrays, either selectively or for all items.
- **Conditional Overrides**: Supports JSONPath filters to conditionally overwrite items.
- **Error Handling**: Manages cases where JSONPath filters do not match any items or are poorly constructed.

#### Examples:

1. **Basic Key Injection**:

   ```json
   {
     "store": {
       "book": [
         {
           "category": "reference",
           "author": "Nigel Rees",
           "title": "Sayings of the Century",
           "price": 8.95
         },
         {
           "category": "fiction",
           "author": "Evelyn Waugh",
           "title": "Sword of Honour",
           "price": 12.99
         }
       ]
     },
     "pass_through": [
       {
         "service_id": "testService",
         "extend_paths": [
           {
             "path": "$.store.book[*].price",
             "value": 20
           }
         ]
       }
     ]
   }
   ```

   **Result**:

   ```json
   {
     "store": {
       "book": [
         {
           "category": "reference",
           "author": "Nigel Rees",
           "title": "Sayings of the Century",
           "price": 20
         },
         {
           "category": "fiction",
           "author": "Evelyn Waugh",
           "title": "Sword of Honour",
           "price": 20
         }
       ]
     }
   }
   ```

2. **Conditional Overrides Using Filters:**

   ```json
   {
     "items": [
       {
         "id": 1,
         "value": "originalValue",
         "type": "A"
       },
       {
         "id": 2,
         "value": "originalValue",
         "type": "B"
       }
     ],
     "pass_through": [
       {
         "service_id": "testService",
         "extend_paths": [
           {
             "path": "$.items[?(@.type=='A')].value",
             "value": "newValueForTypeA"
           }
         ]
       }
     ]
   }
   ```

   **Result**:

   ```json
   {
     "items": [
       {
         "id": 1,
         "value": "newValueForTypeA",
         "type": "A"
       },
       {
         "id": 2,
         "value": "originalValue",
         "type": "B"
       }
     ]
   }
   ```

3. **Injecting a New Property**:

   ```json
   {
     "items": [
       {
         "id": 1,
         "value": "originalValue"
       },
       {
         "id": 2,
         "value": "originalValue"
       }
     ],
     "pass_through": [
       {
         "service_id": "testService",
         "extend_paths": [
           {
             "path": "$.items[?(@.id==1)].details",
             "value": {
               "description": "Detailed information",
               "status": "active"
             }
           }
         ]
       }
     ]
   }
   ```

   Result:

   ```json
   {
     "items": [
       {
         "id": 1,
         "value": "originalValue",
         "details": {
           "description": "Detailed information",
           "status": "active"
         }
       },
       {
         "id": 2,
         "value": "originalValue"
       }
     ]
   }
   ```

By utilizing the `extend_paths` key within the `pass_through` array, you can navigate to specific points within your JSON request body and modify them as needed. This allows for precise and flexible customization of your API requests.

## XML and SOAP connectors

Everything above assumes the connector takes a JSON request body, where the root of the transformed request *is* the body. Some connectors send SOAP or XML on some or all of their write operations: NetSuite, Sage Intacct and Workday throughout, and Yuki, KashFlow, Clear Books, SAP S/4HANA Cloud, MRI Software, SD Worx and eBay on part of their surface. There the root of the request is the XML *document*, whose only legal children are the prolog and a single document element such as `soap:Envelope`.

That changes where a `pass_through` value may land:

| | JSON connector | XML / SOAP connector |
|---|---|---|
| `extend_object` at the root | Added to the request body | **Refused**: `UnsupportedPassThroughError` (400) |
| `extend_paths` resolving to the root | Added to the request body | **Refused**: `UnsupportedPassThroughError` (400) |
| `extend_paths` starting at the document element | n/a | **Applied** |

A root-level value on an XML connector would serialize as a sibling of the document element, giving the XML two roots, which the vendor rejects as malformed. Rather than send that, the request is refused with `UnsupportedPassThroughError`, listing the keys in `detail.unsupported_pass_through_keys` and the document element in `detail.xml_document_element`.

### Setting a value inside the document

Use `extend_paths` with a path that starts at the document element. This NetSuite example sets the `customForm` reference on a bill:

```json
{
  "supplier": { "id": "1159" },
  "pass_through": [
    {
      "service_id": "netsuite",
      "extend_paths": [
        {
          "path": "$['soap:Envelope']['soap:Body']['platformMsgs:add']['platformMsgs:record']['tranPurch:customForm']['_attributes']['internalId']",
          "value": "229"
        }
      ]
    }
  ]
}
```

Two things to know about that path:

- **It mirrors the SOAP message you are sending.** On a create the message node is `platformMsgs:add`; on an update it is `platformMsgs:update`. The connector's request shape determines the rest.
- **Reference fields are attributes, not text.** A NetSuite reference (`customForm`, `department`, `class`, `location`) is written as `['_attributes']['internalId']`, not as a value on the node itself.

The prolog and the request's content type are reserved: a `pass_through` that writes to `_declaration` or `_contentType` is refused for the same reason, since it would change the XML declaration or the serializer rather than the payload.

If the field you need is a common one, it is worth asking us to map it as a first-class unified field instead: that is portable across connectors, where a SOAP path is not.

## Conclusion

Pick the mechanism that matches the request: a `pass_through` query parameter to forward unmapped GET filters, `extend_object` to add or override simple body fields, or JSONPath `extend_paths` for precise, conditional edits inside nested objects and arrays. On XML/SOAP connectors like NetSuite, Sage Intacct, and Workday, only `extend_paths` starting at the document element is accepted; a root-level `extend_object` or `extend_paths` value is refused with `UnsupportedPassThroughError`.

If the field you need is common enough to be useful across integrations, ask Apideck to map it as a first-class unified field instead: that's portable across connectors, where a Pass Through path is not.
