diff --git a/packages/cli/cli/src/commands/upgrade/updateApiSpec.ts b/packages/cli/cli/src/commands/upgrade/updateApiSpec.ts
index e5788ebd846..2ae7af198dd 100644
--- a/packages/cli/cli/src/commands/upgrade/updateApiSpec.ts
+++ b/packages/cli/cli/src/commands/upgrade/updateApiSpec.ts
@@ -1,10 +1,10 @@
 import { generatorsYml, getFernDirectory } from "@fern-api/configuration";
+import { join, RelativeFilePath } from "@fern-api/fs-utils";
 import { Logger } from "@fern-api/logger";
 import { Project } from "@fern-api/project-loader";
 import * as fs from "fs";
 import { readFile, writeFile } from "fs/promises";
 import yaml from "js-yaml";
-import path from "path";
 import { Readable } from "stream";
 import { finished } from "stream/promises";
 import { ReadableStream } from "stream/web";
@@ -63,7 +63,11 @@ export async function updateApiSpec({
                 for (const api of apis) {
                     if (typeof api !== "string" && api.origin != null) {
                         cliContext.logger.info(`Origin found, fetching spec from ${api.origin}`);
-                        await fetchAndWriteFile(api.origin, path.join(fernDirectory, api.path), cliContext.logger);
+                        await fetchAndWriteFile(
+                            api.origin,
+                            join(workspace.absoluteFilepath, RelativeFilePath.of(api.path)),
+                            cliContext.logger
+                        );
                     }
                 }
             } else if (generatorConfig[generatorsYml.ASYNC_API_LOCATION_KEY] != null) {
@@ -74,7 +78,11 @@ export async function updateApiSpec({
                     const origin = generatorConfig[generatorsYml.API_ORIGIN_LOCATION_KEY];
                     const location = generatorConfig[generatorsYml.ASYNC_API_LOCATION_KEY];
                     if (origin != null && location != null) {
-                        await fetchAndWriteFile(origin, path.join(fernDirectory, location), cliContext.logger);
+                        await fetchAndWriteFile(
+                            origin,
+                            join(workspace.absoluteFilepath, RelativeFilePath.of(location)),
+                            cliContext.logger
+                        );
                     }
                 }
             } else if (generatorConfig[generatorsYml.OPENAPI_LOCATION_KEY] != null) {
@@ -89,7 +97,11 @@ export async function updateApiSpec({
                 if (apiOrigin != null && apiOutput != null) {
                     origin = apiOrigin;
                     cliContext.logger.info(`Origin found, fetching spec from ${apiOrigin}`);
-                    await fetchAndWriteFile(apiOrigin, path.join(fernDirectory, apiOutput), cliContext.logger);
+                    await fetchAndWriteFile(
+                        apiOrigin,
+                        join(workspace.absoluteFilepath, RelativeFilePath.of(apiOutput)),
+                        cliContext.logger
+                    );
                 }
             }
             return;
diff --git a/packages/cli/ete-tests/src/tests/update-api-unioned/__snapshots__/update-api.test.ts.snap b/packages/cli/ete-tests/src/tests/update-api-unioned/__snapshots__/update-api.test.ts.snap
new file mode 100644
index 00000000000..a8806ec4bf0
--- /dev/null
+++ b/packages/cli/ete-tests/src/tests/update-api-unioned/__snapshots__/update-api.test.ts.snap
@@ -0,0 +1,797 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`fern api update unioned fern api update unioned 1`] = `
+[
+  {
+    "contents": "default-group: local
+api:
+  path: ./openapi.json
+  origin: https://bump.sh/bump-examples/doc/train-travel-api.json
+",
+    "name": "generators.yml",
+    "type": "file",
+  },
+  {
+    "contents": "{
+  "info": {
+    "contact": {
+      "name": "Train Support",
+      "url": "https://example.com/support",
+      "email": "support@example.com"
+    },
+    "license": {
+      "name": "Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International",
+      "identifier": "CC-BY-NC-SA-4.0"
+    }
+  },
+  "servers": [
+    {
+      "url": "https://api.example.com",
+      "description": "Production"
+    }
+  ],
+  "security": [
+    {
+      "OAuth2": [
+        "read"
+      ]
+    }
+  ],
+  "x-topics": [
+    {
+      "title": "Getting started",
+      "content": {
+        "$ref": "./docs/getting-started.md"
+      }
+    }
+  ],
+  "tags": [
+    {
+      "name": "Stations",
+      "description": "Find and filter train stations across Europe, including their location\\nand local timezone.\\n"
+    },
+    {
+      "name": "Train Tracks",
+      "description": "Find and filter all the different rail roads available across Europe, including their location\\nand local timezone.\\n"
+    },
+    {
+      "name": "Trips",
+      "description": "Timetables and routes for train trips between stations, including pricing\\nand availability.\\n"
+    },
+    {
+      "name": "Bookings",
+      "description": "Create and manage bookings for train trips, including passenger details\\nand optional extras.\\n"
+    },
+    {
+      "name": "Payments",
+      "description": "Pay for bookings using a card or bank account, and view payment\\nstatus and history.\\n\\n> warn\\n> Bookings usually expire within 1 hour so you'll need to make your payment\\n> before the expiry date \\n"
+    }
+  ],
+  "webhooks": {
+    "newBooking": {
+      "post": {
+        "operationId": "new-booking",
+        "summary": "New Booking",
+        "description": "Subscribe to new bookings being created, to update integrations for your users.  Related data is available via the links provided in the request.\\n",
+        "tags": [
+          "Bookings"
+        ],
+        "requestBody": {
+          "content": {
+            "application/json": {
+              "schema": {
+                "allOf": [
+                  {
+                    "$ref": "#/components/schemas/Booking"
+                  },
+                  {
+                    "properties": {
+                      "links": {
+                        "allOf": [
+                          {
+                            "$ref": "#/components/schemas/Links-Self"
+                          },
+                          {
+                            "$ref": "#/components/schemas/Links-Pagination"
+                          }
+                        ]
+                      }
+                    }
+                  }
+                ]
+              },
+              "example": {
+                "id": "efdbb9d1-02c2-4bc3-afb7-6788d8782b1e",
+                "trip_id": "efdbb9d1-02c2-4bc3-afb7-6788d8782b1e",
+                "passenger_name": "John Doe",
+                "has_bicycle": true,
+                "has_dog": true,
+                "links": {
+                  "self": "https://api.example.com/bookings/1725ff48-ab45-4bb5-9d02-88745177dedb"
+                }
+              }
+            }
+          }
+        },
+        "responses": {
+          "200": {
+            "description": "Return a 200 status to indicate that the data was received successfully."
+          }
+        }
+      }
+    }
+  },
+  "components": {
+    "securitySchemes": {
+      "OAuth2": {
+        "type": "oauth2",
+        "description": "OAuth 2.0 authorization code following RFC8725 best practices.",
+        "flows": {
+          "authorizationCode": {
+            "authorizationUrl": "https://example.com/oauth/authorize",
+            "tokenUrl": "https://example.com/oauth/token",
+            "scopes": {
+              "read": "Read access",
+              "write": "Write access"
+            }
+          }
+        }
+      }
+    },
+    "schemas": {
+      "Station": {
+        "type": "object",
+        "xml": {
+          "name": "station"
+        },
+        "required": [
+          "id",
+          "name",
+          "address",
+          "country_code"
+        ],
+        "properties": {
+          "id": {
+            "type": "string",
+            "format": "uuid",
+            "description": "Unique identifier for the station.",
+            "examples": [
+              "efdbb9d1-02c2-4bc3-afb7-6788d8782b1e",
+              "b2e783e1-c824-4d63-b37a-d8d698862f1d"
+            ]
+          },
+          "name": {
+            "type": "string",
+            "description": "The name of the station",
+            "examples": [
+              "Berlin Hauptbahnhof",
+              "Paris Gare du Nord"
+            ]
+          },
+          "address": {
+            "type": "string",
+            "description": "The address of the station.",
+            "examples": [
+              "Invalidenstraße 10557 Berlin, Germany",
+              "18 Rue de Dunkerque 75010 Paris, France"
+            ]
+          },
+          "country_code": {
+            "type": "string",
+            "description": "The country code of the station.",
+            "format": "iso-country-code",
+            "examples": [
+              "DE",
+              "FR"
+            ]
+          },
+          "timezone": {
+            "type": "string",
+            "description": "The timezone of the station in the [IANA Time Zone Database format](https://www.iana.org/time-zones).",
+            "examples": [
+              "Europe/Berlin",
+              "Europe/Paris"
+            ]
+          }
+        }
+      },
+      "Links-Self": {
+        "type": "object",
+        "properties": {
+          "self": {
+            "type": "string",
+            "format": "uri"
+          }
+        }
+      },
+      "Links-Pagination": {
+        "type": "object",
+        "properties": {
+          "next": {
+            "type": "string",
+            "format": "uri"
+          },
+          "prev": {
+            "type": "string",
+            "format": "uri"
+          }
+        }
+      },
+      "Trip": {
+        "type": "object",
+        "xml": {
+          "name": "trip"
+        },
+        "properties": {
+          "id": {
+            "type": "string",
+            "format": "uuid",
+            "description": "Unique identifier for the trip",
+            "examples": [
+              "4f4e4e1-c824-4d63-b37a-d8d698862f1d"
+            ]
+          },
+          "origin": {
+            "type": "string",
+            "description": "The starting station of the trip",
+            "examples": [
+              "Berlin Hauptbahnhof",
+              "Paris Gare du Nord"
+            ]
+          },
+          "destination": {
+            "type": "string",
+            "description": "The destination station of the trip",
+            "examples": [
+              "Paris Gare du Nord",
+              "Berlin Hauptbahnhof"
+            ]
+          },
+          "departure_time": {
+            "type": "string",
+            "format": "date-time",
+            "description": "The date and time when the trip departs",
+            "examples": [
+              "2024-02-01T10:00:00Z"
+            ]
+          },
+          "arrival_time": {
+            "type": "string",
+            "format": "date-time",
+            "description": "The date and time when the trip arrives",
+            "examples": [
+              "2024-02-01T16:00:00Z"
+            ]
+          },
+          "operator": {
+            "type": "string",
+            "description": "The name of the operator of the trip",
+            "examples": [
+              "Deutsche Bahn",
+              "SNCF"
+            ]
+          },
+          "price": {
+            "type": "number",
+            "description": "The cost of the trip",
+            "examples": [
+              50
+            ]
+          },
+          "bicycles_allowed": {
+            "type": "boolean",
+            "description": "Indicates whether bicycles are allowed on the trip"
+          },
+          "dogs_allowed": {
+            "type": "boolean",
+            "description": "Indicates whether dogs are allowed on the trip"
+          }
+        }
+      },
+      "Booking": {
+        "type": "object",
+        "xml": {
+          "name": "booking"
+        },
+        "properties": {
+          "id": {
+            "type": "string",
+            "format": "uuid",
+            "description": "Unique identifier for the booking",
+            "readOnly": true,
+            "examples": [
+              "3f3e3e1-c824-4d63-b37a-d8d698862f1d"
+            ]
+          },
+          "trip_id": {
+            "type": "string",
+            "format": "uuid",
+            "description": "Identifier of the booked trip",
+            "examples": [
+              "4f4e4e1-c824-4d63-b37a-d8d698862f1d"
+            ]
+          },
+          "passenger_name": {
+            "type": "string",
+            "description": "Name of the passenger",
+            "examples": [
+              "John Doe"
+            ]
+          },
+          "has_bicycle": {
+            "type": "boolean",
+            "description": "Indicates whether the passenger has a bicycle."
+          },
+          "has_dog": {
+            "type": "boolean",
+            "description": "Indicates whether the passenger has a dog."
+          }
+        }
+      },
+      "Wrapper-Collection": {
+        "description": "This is a generic request/response wrapper which contains both data and links which serve as hypermedia controls (HATEOAS).",
+        "type": "object",
+        "properties": {
+          "data": {
+            "description": "The wrapper for a collection is an array of objects.",
+            "type": "array",
+            "items": {
+              "type": "object"
+            }
+          },
+          "links": {
+            "description": "A set of hypermedia links which serve as controls for the client.",
+            "type": "object",
+            "readOnly": true
+          }
+        },
+        "xml": {
+          "name": "data"
+        }
+      },
+      "BookingPayment": {
+        "type": "object",
+        "properties": {
+          "id": {
+            "description": "Unique identifier for the payment. This will be a unique identifier for the payment, and is used to reference the payment in other objects.",
+            "type": "string",
+            "format": "uuid",
+            "readOnly": true
+          },
+          "amount": {
+            "description": "Amount intended to be collected by this payment. A positive decimal figure describing the amount to be collected.",
+            "type": "number",
+            "exclusiveMinimum": 0,
+            "examples": [
+              49.99
+            ]
+          },
+          "currency": {
+            "description": "Three-letter [ISO currency code](https://www.iso.org/iso-4217-currency-codes.html), in lowercase.",
+            "type": "string",
+            "enum": [
+              "bam",
+              "bgn",
+              "chf",
+              "eur",
+              "gbp",
+              "nok",
+              "sek",
+              "try"
+            ]
+          },
+          "source": {
+            "unevaluatedProperties": false,
+            "description": "The payment source to take the payment from. This can be a card or a bank account. Some of these properties will be hidden on read to protect PII leaking.",
+            "anyOf": [
+              {
+                "title": "Card",
+                "description": "A card (debit or credit) to take payment from.",
+                "properties": {
+                  "object": {
+                    "type": "string",
+                    "const": "card"
+                  },
+                  "name": {
+                    "type": "string",
+                    "description": "Cardholder's full name as it appears on the card.",
+                    "examples": [
+                      "Francis Bourgeois"
+                    ]
+                  },
+                  "number": {
+                    "type": "string",
+                    "description": "The card number, as a string without any separators. On read all but the last four digits will be masked for security.",
+                    "examples": [
+                      "4242424242424242"
+                    ]
+                  },
+                  "cvc": {
+                    "type": "integer",
+                    "description": "Card security code, 3 or 4 digits usually found on the back of the card.",
+                    "minLength": 3,
+                    "maxLength": 4,
+                    "writeOnly": true,
+                    "example": 123
+                  },
+                  "exp_month": {
+                    "type": "integer",
+                    "format": "int64",
+                    "description": "Two-digit number representing the card's expiration month.",
+                    "examples": [
+                      12
+                    ]
+                  },
+                  "exp_year": {
+                    "type": "integer",
+                    "format": "int64",
+                    "description": "Four-digit number representing the card's expiration year.",
+                    "examples": [
+                      2025
+                    ]
+                  },
+                  "address_line1": {
+                    "type": "string",
+                    "writeOnly": true
+                  },
+                  "address_line2": {
+                    "type": "string",
+                    "writeOnly": true
+                  },
+                  "address_city": {
+                    "type": "string"
+                  },
+                  "address_country": {
+                    "type": "string"
+                  },
+                  "address_post_code": {
+                    "type": "string"
+                  }
+                },
+                "required": [
+                  "name",
+                  "number",
+                  "cvc",
+                  "exp_month",
+                  "exp_year",
+                  "address_country"
+                ]
+              },
+              {
+                "title": "Bank Account",
+                "description": "A bank account to take payment from. Must be able to make payments in the currency specified in the payment.",
+                "type": "object",
+                "properties": {
+                  "object": {
+                    "const": "bank_account",
+                    "type": "string"
+                  },
+                  "name": {
+                    "type": "string"
+                  },
+                  "number": {
+                    "type": "string",
+                    "description": "The account number for the bank account, in string form. Must be a current account."
+                  },
+                  "sort_code": {
+                    "type": "string",
+                    "description": "The sort code for the bank account, in string form. Must be a six-digit number."
+                  },
+                  "account_type": {
+                    "enum": [
+                      "individual",
+                      "company"
+                    ],
+                    "type": "string",
+                    "description": "The type of entity that holds the account. This can be either \`individual\` or \`company\`."
+                  },
+                  "bank_name": {
+                    "type": "string",
+                    "description": "The name of the bank associated with the routing number.",
+                    "examples": [
+                      "Starling Bank"
+                    ]
+                  },
+                  "country": {
+                    "type": "string",
+                    "description": "Two-letter country code (ISO 3166-1 alpha-2)."
+                  }
+                },
+                "required": [
+                  "name",
+                  "number",
+                  "account_type",
+                  "bank_name",
+                  "country"
+                ]
+              }
+            ]
+          },
+          "status": {
+            "description": "The status of the payment, one of \`pending\`, \`succeeded\`, or \`failed\`.",
+            "type": "string",
+            "enum": [
+              "pending",
+              "succeeded",
+              "failed"
+            ],
+            "readOnly": true
+          }
+        }
+      },
+      "Links-Booking": {
+        "type": "object",
+        "properties": {
+          "booking": {
+            "type": "string",
+            "format": "uri",
+            "examples": [
+              "https://api.example.com/bookings/1725ff48-ab45-4bb5-9d02-88745177dedb"
+            ]
+          }
+        }
+      }
+    },
+    "headers": {
+      "RateLimit": {
+        "description": "The RateLimit header communicates quota policies. It contains a \`limit\` to\\nconvey the expiring limit, \`remaining\` to convey the remaining quota units,\\nand \`reset\` to convey the time window reset time.\\n",
+        "schema": {
+          "type": "string",
+          "examples": [
+            "limit=10, remaining=0, reset=10"
+          ]
+        }
+      },
+      "Retry-After": {
+        "description": "The Retry-After header indicates how long the user agent should wait before making a follow-up request. \\nThe value is in seconds and can be an integer or a date in the future. \\nIf the value is an integer, it indicates the number of seconds to wait. \\nIf the value is a date, it indicates the time at which the user agent should make a follow-up request. \\n",
+        "schema": {
+          "type": "string"
+        },
+        "examples": {
+          "integer": {
+            "value": "120",
+            "summary": "Retry after 120 seconds"
+          },
+          "date": {
+            "value": "Fri, 31 Dec 2021 23:59:59 GMT",
+            "summary": "Retry after the specified date"
+          }
+        }
+      }
+    },
+    "responses": {
+      "BadRequest": {
+        "description": "Bad Request",
+        "headers": {
+          "RateLimit": {
+            "$ref": "#/components/headers/RateLimit"
+          }
+        },
+        "content": {
+          "application/problem+json": {
+            "schema": {
+              "$ref": "#/components/schemas/Problem"
+            },
+            "example": {
+              "type": "https://example.com/errors/bad-request",
+              "title": "Bad Request",
+              "status": 400,
+              "detail": "The request is invalid or missing required parameters."
+            }
+          },
+          "application/problem+xml": {
+            "schema": {
+              "$ref": "#/components/schemas/Problem"
+            },
+            "example": {
+              "type": "https://example.com/errors/bad-request",
+              "title": "Bad Request",
+              "status": 400,
+              "detail": "The request is invalid or missing required parameters."
+            }
+          }
+        }
+      },
+      "Conflict": {
+        "description": "Conflict",
+        "headers": {
+          "RateLimit": {
+            "$ref": "#/components/headers/RateLimit"
+          }
+        },
+        "content": {
+          "application/problem+json": {
+            "schema": {
+              "$ref": "#/components/schemas/Problem"
+            },
+            "example": {
+              "type": "https://example.com/errors/conflict",
+              "title": "Conflict",
+              "status": 409,
+              "detail": "There is a conflict with an existing resource."
+            }
+          },
+          "application/problem+xml": {
+            "schema": {
+              "$ref": "#/components/schemas/Problem"
+            },
+            "example": {
+              "type": "https://example.com/errors/conflict",
+              "title": "Conflict",
+              "status": 409,
+              "detail": "There is a conflict with an existing resource."
+            }
+          }
+        }
+      },
+      "Forbidden": {
+        "description": "Forbidden",
+        "headers": {
+          "RateLimit": {
+            "$ref": "#/components/headers/RateLimit"
+          }
+        },
+        "content": {
+          "application/problem+json": {
+            "schema": {
+              "$ref": "#/components/schemas/Problem"
+            },
+            "example": {
+              "type": "https://example.com/errors/forbidden",
+              "title": "Forbidden",
+              "status": 403,
+              "detail": "Access is forbidden with the provided credentials."
+            }
+          },
+          "application/problem+xml": {
+            "schema": {
+              "$ref": "#/components/schemas/Problem"
+            },
+            "example": {
+              "type": "https://example.com/errors/forbidden",
+              "title": "Forbidden",
+              "status": 403,
+              "detail": "Access is forbidden with the provided credentials."
+            }
+          }
+        }
+      },
+      "InternalServerError": {
+        "description": "Internal Server Error",
+        "headers": {
+          "RateLimit": {
+            "$ref": "#/components/headers/RateLimit"
+          }
+        },
+        "content": {
+          "application/problem+json": {
+            "schema": {
+              "$ref": "#/components/schemas/Problem"
+            },
+            "example": {
+              "type": "https://example.com/errors/internal-server-error",
+              "title": "Internal Server Error",
+              "status": 500,
+              "detail": "An unexpected error occurred."
+            }
+          },
+          "application/problem+xml": {
+            "schema": {
+              "$ref": "#/components/schemas/Problem"
+            },
+            "example": {
+              "type": "https://example.com/errors/internal-server-error",
+              "title": "Internal Server Error",
+              "status": 500,
+              "detail": "An unexpected error occurred."
+            }
+          }
+        }
+      },
+      "NotFound": {
+        "description": "Not Found",
+        "headers": {
+          "RateLimit": {
+            "$ref": "#/components/headers/RateLimit"
+          }
+        },
+        "content": {
+          "application/problem+json": {
+            "schema": {
+              "$ref": "#/components/schemas/Problem"
+            },
+            "example": {
+              "type": "https://example.com/errors/not-found",
+              "title": "Not Found",
+              "status": 404,
+              "detail": "The requested resource was not found."
+            }
+          },
+          "application/problem+xml": {
+            "schema": {
+              "$ref": "#/components/schemas/Problem"
+            },
+            "example": {
+              "type": "https://example.com/errors/not-found",
+              "title": "Not Found",
+              "status": 404,
+              "detail": "The requested resource was not found."
+            }
+          }
+        }
+      },
+      "TooManyRequests": {
+        "description": "Too Many Requests",
+        "headers": {
+          "RateLimit": {
+            "$ref": "#/components/headers/RateLimit"
+          },
+          "Retry-After": {
+            "$ref": "#/components/headers/Retry-After"
+          }
+        },
+        "content": {
+          "application/problem+json": {
+            "schema": {
+              "$ref": "#/components/schemas/Problem"
+            },
+            "example": {
+              "type": "https://example.com/errors/too-many-requests",
+              "title": "Too Many Requests",
+              "status": 429,
+              "detail": "You have exceeded the rate limit."
+            }
+          },
+          "application/problem+xml": {
+            "schema": {
+              "$ref": "#/components/schemas/Problem"
+            },
+            "example": {
+              "type": "https://example.com/errors/too-many-requests",
+              "title": "Too Many Requests",
+              "status": 429,
+              "detail": "You have exceeded the rate limit."
+            }
+          }
+        }
+      },
+      "Unauthorized": {
+        "description": "Unauthorized",
+        "headers": {
+          "RateLimit": {
+            "$ref": "#/components/headers/RateLimit"
+          }
+        },
+        "content": {
+          "application/problem+json": {
+            "schema": {
+              "$ref": "#/components/schemas/Problem"
+            },
+            "example": {
+              "type": "https://example.com/errors/unauthorized",
+              "title": "Unauthorized",
+              "status": 401,
+              "detail": "You do not have the necessary permissions."
+            }
+          },
+          "application/problem+xml": {
+            "schema": {
+              "$ref": "#/components/schemas/Problem"
+            },
+            "example": {
+              "type": "https://example.com/errors/unauthorized",
+              "title": "Unauthorized",
+              "status": 401,
+              "detail": "You do not have the necessary permissions."
+            }
+          }
+        }
+      }
+    }
+  }
+}",
+    "name": "openapi.json",
+    "type": "file",
+  },
+]
+`;
diff --git a/packages/cli/ete-tests/src/tests/update-api-unioned/fixtures/fern/fern.config.json b/packages/cli/ete-tests/src/tests/update-api-unioned/fixtures/fern/fern.config.json
new file mode 100644
index 00000000000..2e3e1df85fd
--- /dev/null
+++ b/packages/cli/ete-tests/src/tests/update-api-unioned/fixtures/fern/fern.config.json
@@ -0,0 +1,4 @@
+{
+    "version": "*",
+    "organization": "fern"
+}
\ No newline at end of file
diff --git a/packages/cli/ete-tests/src/tests/update-api-unioned/fixtures/fern/spec1/generators.yml b/packages/cli/ete-tests/src/tests/update-api-unioned/fixtures/fern/spec1/generators.yml
new file mode 100644
index 00000000000..cf0562dda16
--- /dev/null
+++ b/packages/cli/ete-tests/src/tests/update-api-unioned/fixtures/fern/spec1/generators.yml
@@ -0,0 +1,4 @@
+default-group: local
+api:
+  path: ./openapi.json
+  origin: https://bump.sh/bump-examples/doc/train-travel-api.json
diff --git a/packages/cli/ete-tests/src/tests/update-api-unioned/fixtures/fern/spec1/openapi.json b/packages/cli/ete-tests/src/tests/update-api-unioned/fixtures/fern/spec1/openapi.json
new file mode 100644
index 00000000000..3a835787863
--- /dev/null
+++ b/packages/cli/ete-tests/src/tests/update-api-unioned/fixtures/fern/spec1/openapi.json
@@ -0,0 +1,778 @@
+{
+  "info": {
+    "contact": {
+      "name": "Train Support",
+      "url": "https://example.com/support",
+      "email": "support@example.com"
+    },
+    "license": {
+      "name": "Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International",
+      "identifier": "CC-BY-NC-SA-4.0"
+    }
+  },
+  "servers": [
+    {
+      "url": "https://api.example.com",
+      "description": "Production"
+    }
+  ],
+  "security": [
+    {
+      "OAuth2": [
+        "read"
+      ]
+    }
+  ],
+  "x-topics": [
+    {
+      "title": "Getting started",
+      "content": {
+        "$ref": "./docs/getting-started.md"
+      }
+    }
+  ],
+  "tags": [
+    {
+      "name": "Stations",
+      "description": "Find and filter train stations across Europe, including their location\nand local timezone.\n"
+    },
+    {
+      "name": "Train Tracks",
+      "description": "Find and filter all the different rail roads available across Europe, including their location\nand local timezone.\n"
+    },
+    {
+      "name": "Trips",
+      "description": "Timetables and routes for train trips between stations, including pricing\nand availability.\n"
+    },
+    {
+      "name": "Bookings",
+      "description": "Create and manage bookings for train trips, including passenger details\nand optional extras.\n"
+    },
+    {
+      "name": "Payments",
+      "description": "Pay for bookings using a card or bank account, and view payment\nstatus and history.\n\n> warn\n> Bookings usually expire within 1 hour so you'll need to make your payment\n> before the expiry date \n"
+    }
+  ],
+  "webhooks": {
+    "newBooking": {
+      "post": {
+        "operationId": "new-booking",
+        "summary": "New Booking",
+        "description": "Subscribe to new bookings being created, to update integrations for your users.  Related data is available via the links provided in the request.\n",
+        "tags": [
+          "Bookings"
+        ],
+        "requestBody": {
+          "content": {
+            "application/json": {
+              "schema": {
+                "allOf": [
+                  {
+                    "$ref": "#/components/schemas/Booking"
+                  },
+                  {
+                    "properties": {
+                      "links": {
+                        "allOf": [
+                          {
+                            "$ref": "#/components/schemas/Links-Self"
+                          },
+                          {
+                            "$ref": "#/components/schemas/Links-Pagination"
+                          }
+                        ]
+                      }
+                    }
+                  }
+                ]
+              },
+              "example": {
+                "id": "efdbb9d1-02c2-4bc3-afb7-6788d8782b1e",
+                "trip_id": "efdbb9d1-02c2-4bc3-afb7-6788d8782b1e",
+                "passenger_name": "John Doe",
+                "has_bicycle": true,
+                "has_dog": true,
+                "links": {
+                  "self": "https://api.example.com/bookings/1725ff48-ab45-4bb5-9d02-88745177dedb"
+                }
+              }
+            }
+          }
+        },
+        "responses": {
+          "200": {
+            "description": "Return a 200 status to indicate that the data was received successfully."
+          }
+        }
+      }
+    }
+  },
+  "components": {
+    "securitySchemes": {
+      "OAuth2": {
+        "type": "oauth2",
+        "description": "OAuth 2.0 authorization code following RFC8725 best practices.",
+        "flows": {
+          "authorizationCode": {
+            "authorizationUrl": "https://example.com/oauth/authorize",
+            "tokenUrl": "https://example.com/oauth/token",
+            "scopes": {
+              "read": "Read access",
+              "write": "Write access"
+            }
+          }
+        }
+      }
+    },
+    "schemas": {
+      "Station": {
+        "type": "object",
+        "xml": {
+          "name": "station"
+        },
+        "required": [
+          "id",
+          "name",
+          "address",
+          "country_code"
+        ],
+        "properties": {
+          "id": {
+            "type": "string",
+            "format": "uuid",
+            "description": "Unique identifier for the station.",
+            "examples": [
+              "efdbb9d1-02c2-4bc3-afb7-6788d8782b1e",
+              "b2e783e1-c824-4d63-b37a-d8d698862f1d"
+            ]
+          },
+          "name": {
+            "type": "string",
+            "description": "The name of the station",
+            "examples": [
+              "Berlin Hauptbahnhof",
+              "Paris Gare du Nord"
+            ]
+          },
+          "address": {
+            "type": "string",
+            "description": "The address of the station.",
+            "examples": [
+              "Invalidenstraße 10557 Berlin, Germany",
+              "18 Rue de Dunkerque 75010 Paris, France"
+            ]
+          },
+          "country_code": {
+            "type": "string",
+            "description": "The country code of the station.",
+            "format": "iso-country-code",
+            "examples": [
+              "DE",
+              "FR"
+            ]
+          },
+          "timezone": {
+            "type": "string",
+            "description": "The timezone of the station in the [IANA Time Zone Database format](https://www.iana.org/time-zones).",
+            "examples": [
+              "Europe/Berlin",
+              "Europe/Paris"
+            ]
+          }
+        }
+      },
+      "Links-Self": {
+        "type": "object",
+        "properties": {
+          "self": {
+            "type": "string",
+            "format": "uri"
+          }
+        }
+      },
+      "Links-Pagination": {
+        "type": "object",
+        "properties": {
+          "next": {
+            "type": "string",
+            "format": "uri"
+          },
+          "prev": {
+            "type": "string",
+            "format": "uri"
+          }
+        }
+      },
+      "Trip": {
+        "type": "object",
+        "xml": {
+          "name": "trip"
+        },
+        "properties": {
+          "id": {
+            "type": "string",
+            "format": "uuid",
+            "description": "Unique identifier for the trip",
+            "examples": [
+              "4f4e4e1-c824-4d63-b37a-d8d698862f1d"
+            ]
+          },
+          "origin": {
+            "type": "string",
+            "description": "The starting station of the trip",
+            "examples": [
+              "Berlin Hauptbahnhof",
+              "Paris Gare du Nord"
+            ]
+          },
+          "destination": {
+            "type": "string",
+            "description": "The destination station of the trip",
+            "examples": [
+              "Paris Gare du Nord",
+              "Berlin Hauptbahnhof"
+            ]
+          },
+          "departure_time": {
+            "type": "string",
+            "format": "date-time",
+            "description": "The date and time when the trip departs",
+            "examples": [
+              "2024-02-01T10:00:00Z"
+            ]
+          },
+          "arrival_time": {
+            "type": "string",
+            "format": "date-time",
+            "description": "The date and time when the trip arrives",
+            "examples": [
+              "2024-02-01T16:00:00Z"
+            ]
+          },
+          "operator": {
+            "type": "string",
+            "description": "The name of the operator of the trip",
+            "examples": [
+              "Deutsche Bahn",
+              "SNCF"
+            ]
+          },
+          "price": {
+            "type": "number",
+            "description": "The cost of the trip",
+            "examples": [
+              50
+            ]
+          },
+          "bicycles_allowed": {
+            "type": "boolean",
+            "description": "Indicates whether bicycles are allowed on the trip"
+          },
+          "dogs_allowed": {
+            "type": "boolean",
+            "description": "Indicates whether dogs are allowed on the trip"
+          }
+        }
+      },
+      "Booking": {
+        "type": "object",
+        "xml": {
+          "name": "booking"
+        },
+        "properties": {
+          "id": {
+            "type": "string",
+            "format": "uuid",
+            "description": "Unique identifier for the booking",
+            "readOnly": true,
+            "examples": [
+              "3f3e3e1-c824-4d63-b37a-d8d698862f1d"
+            ]
+          },
+          "trip_id": {
+            "type": "string",
+            "format": "uuid",
+            "description": "Identifier of the booked trip",
+            "examples": [
+              "4f4e4e1-c824-4d63-b37a-d8d698862f1d"
+            ]
+          },
+          "passenger_name": {
+            "type": "string",
+            "description": "Name of the passenger",
+            "examples": [
+              "John Doe"
+            ]
+          },
+          "has_bicycle": {
+            "type": "boolean",
+            "description": "Indicates whether the passenger has a bicycle."
+          },
+          "has_dog": {
+            "type": "boolean",
+            "description": "Indicates whether the passenger has a dog."
+          }
+        }
+      },
+      "Wrapper-Collection": {
+        "description": "This is a generic request/response wrapper which contains both data and links which serve as hypermedia controls (HATEOAS).",
+        "type": "object",
+        "properties": {
+          "data": {
+            "description": "The wrapper for a collection is an array of objects.",
+            "type": "array",
+            "items": {
+              "type": "object"
+            }
+          },
+          "links": {
+            "description": "A set of hypermedia links which serve as controls for the client.",
+            "type": "object",
+            "readOnly": true
+          }
+        },
+        "xml": {
+          "name": "data"
+        }
+      },
+      "BookingPayment": {
+        "type": "object",
+        "properties": {
+          "id": {
+            "description": "Unique identifier for the payment. This will be a unique identifier for the payment, and is used to reference the payment in other objects.",
+            "type": "string",
+            "format": "uuid",
+            "readOnly": true
+          },
+          "amount": {
+            "description": "Amount intended to be collected by this payment. A positive decimal figure describing the amount to be collected.",
+            "type": "number",
+            "exclusiveMinimum": 0,
+            "examples": [
+              49.99
+            ]
+          },
+          "currency": {
+            "description": "Three-letter [ISO currency code](https://www.iso.org/iso-4217-currency-codes.html), in lowercase.",
+            "type": "string",
+            "enum": [
+              "bam",
+              "bgn",
+              "chf",
+              "eur",
+              "gbp",
+              "nok",
+              "sek",
+              "try"
+            ]
+          },
+          "source": {
+            "unevaluatedProperties": false,
+            "description": "The payment source to take the payment from. This can be a card or a bank account. Some of these properties will be hidden on read to protect PII leaking.",
+            "anyOf": [
+              {
+                "title": "Card",
+                "description": "A card (debit or credit) to take payment from.",
+                "properties": {
+                  "object": {
+                    "type": "string",
+                    "const": "card"
+                  },
+                  "name": {
+                    "type": "string",
+                    "description": "Cardholder's full name as it appears on the card.",
+                    "examples": [
+                      "Francis Bourgeois"
+                    ]
+                  },
+                  "number": {
+                    "type": "string",
+                    "description": "The card number, as a string without any separators. On read all but the last four digits will be masked for security.",
+                    "examples": [
+                      "4242424242424242"
+                    ]
+                  },
+                  "cvc": {
+                    "type": "integer",
+                    "description": "Card security code, 3 or 4 digits usually found on the back of the card.",
+                    "minLength": 3,
+                    "maxLength": 4,
+                    "writeOnly": true,
+                    "example": 123
+                  },
+                  "exp_month": {
+                    "type": "integer",
+                    "format": "int64",
+                    "description": "Two-digit number representing the card's expiration month.",
+                    "examples": [
+                      12
+                    ]
+                  },
+                  "exp_year": {
+                    "type": "integer",
+                    "format": "int64",
+                    "description": "Four-digit number representing the card's expiration year.",
+                    "examples": [
+                      2025
+                    ]
+                  },
+                  "address_line1": {
+                    "type": "string",
+                    "writeOnly": true
+                  },
+                  "address_line2": {
+                    "type": "string",
+                    "writeOnly": true
+                  },
+                  "address_city": {
+                    "type": "string"
+                  },
+                  "address_country": {
+                    "type": "string"
+                  },
+                  "address_post_code": {
+                    "type": "string"
+                  }
+                },
+                "required": [
+                  "name",
+                  "number",
+                  "cvc",
+                  "exp_month",
+                  "exp_year",
+                  "address_country"
+                ]
+              },
+              {
+                "title": "Bank Account",
+                "description": "A bank account to take payment from. Must be able to make payments in the currency specified in the payment.",
+                "type": "object",
+                "properties": {
+                  "object": {
+                    "const": "bank_account",
+                    "type": "string"
+                  },
+                  "name": {
+                    "type": "string"
+                  },
+                  "number": {
+                    "type": "string",
+                    "description": "The account number for the bank account, in string form. Must be a current account."
+                  },
+                  "sort_code": {
+                    "type": "string",
+                    "description": "The sort code for the bank account, in string form. Must be a six-digit number."
+                  },
+                  "account_type": {
+                    "enum": [
+                      "individual",
+                      "company"
+                    ],
+                    "type": "string",
+                    "description": "The type of entity that holds the account. This can be either `individual` or `company`."
+                  },
+                  "bank_name": {
+                    "type": "string",
+                    "description": "The name of the bank associated with the routing number.",
+                    "examples": [
+                      "Starling Bank"
+                    ]
+                  },
+                  "country": {
+                    "type": "string",
+                    "description": "Two-letter country code (ISO 3166-1 alpha-2)."
+                  }
+                },
+                "required": [
+                  "name",
+                  "number",
+                  "account_type",
+                  "bank_name",
+                  "country"
+                ]
+              }
+            ]
+          },
+          "status": {
+            "description": "The status of the payment, one of `pending`, `succeeded`, or `failed`.",
+            "type": "string",
+            "enum": [
+              "pending",
+              "succeeded",
+              "failed"
+            ],
+            "readOnly": true
+          }
+        }
+      },
+      "Links-Booking": {
+        "type": "object",
+        "properties": {
+          "booking": {
+            "type": "string",
+            "format": "uri",
+            "examples": [
+              "https://api.example.com/bookings/1725ff48-ab45-4bb5-9d02-88745177dedb"
+            ]
+          }
+        }
+      }
+    },
+    "headers": {
+      "RateLimit": {
+        "description": "The RateLimit header communicates quota policies. It contains a `limit` to\nconvey the expiring limit, `remaining` to convey the remaining quota units,\nand `reset` to convey the time window reset time.\n",
+        "schema": {
+          "type": "string",
+          "examples": [
+            "limit=10, remaining=0, reset=10"
+          ]
+        }
+      },
+      "Retry-After": {
+        "description": "The Retry-After header indicates how long the user agent should wait before making a follow-up request. \nThe value is in seconds and can be an integer or a date in the future. \nIf the value is an integer, it indicates the number of seconds to wait. \nIf the value is a date, it indicates the time at which the user agent should make a follow-up request. \n",
+        "schema": {
+          "type": "string"
+        },
+        "examples": {
+          "integer": {
+            "value": "120",
+            "summary": "Retry after 120 seconds"
+          },
+          "date": {
+            "value": "Fri, 31 Dec 2021 23:59:59 GMT",
+            "summary": "Retry after the specified date"
+          }
+        }
+      }
+    },
+    "responses": {
+      "BadRequest": {
+        "description": "Bad Request",
+        "headers": {
+          "RateLimit": {
+            "$ref": "#/components/headers/RateLimit"
+          }
+        },
+        "content": {
+          "application/problem+json": {
+            "schema": {
+              "$ref": "#/components/schemas/Problem"
+            },
+            "example": {
+              "type": "https://example.com/errors/bad-request",
+              "title": "Bad Request",
+              "status": 400,
+              "detail": "The request is invalid or missing required parameters."
+            }
+          },
+          "application/problem+xml": {
+            "schema": {
+              "$ref": "#/components/schemas/Problem"
+            },
+            "example": {
+              "type": "https://example.com/errors/bad-request",
+              "title": "Bad Request",
+              "status": 400,
+              "detail": "The request is invalid or missing required parameters."
+            }
+          }
+        }
+      },
+      "Conflict": {
+        "description": "Conflict",
+        "headers": {
+          "RateLimit": {
+            "$ref": "#/components/headers/RateLimit"
+          }
+        },
+        "content": {
+          "application/problem+json": {
+            "schema": {
+              "$ref": "#/components/schemas/Problem"
+            },
+            "example": {
+              "type": "https://example.com/errors/conflict",
+              "title": "Conflict",
+              "status": 409,
+              "detail": "There is a conflict with an existing resource."
+            }
+          },
+          "application/problem+xml": {
+            "schema": {
+              "$ref": "#/components/schemas/Problem"
+            },
+            "example": {
+              "type": "https://example.com/errors/conflict",
+              "title": "Conflict",
+              "status": 409,
+              "detail": "There is a conflict with an existing resource."
+            }
+          }
+        }
+      },
+      "Forbidden": {
+        "description": "Forbidden",
+        "headers": {
+          "RateLimit": {
+            "$ref": "#/components/headers/RateLimit"
+          }
+        },
+        "content": {
+          "application/problem+json": {
+            "schema": {
+              "$ref": "#/components/schemas/Problem"
+            },
+            "example": {
+              "type": "https://example.com/errors/forbidden",
+              "title": "Forbidden",
+              "status": 403,
+              "detail": "Access is forbidden with the provided credentials."
+            }
+          },
+          "application/problem+xml": {
+            "schema": {
+              "$ref": "#/components/schemas/Problem"
+            },
+            "example": {
+              "type": "https://example.com/errors/forbidden",
+              "title": "Forbidden",
+              "status": 403,
+              "detail": "Access is forbidden with the provided credentials."
+            }
+          }
+        }
+      },
+      "InternalServerError": {
+        "description": "Internal Server Error",
+        "headers": {
+          "RateLimit": {
+            "$ref": "#/components/headers/RateLimit"
+          }
+        },
+        "content": {
+          "application/problem+json": {
+            "schema": {
+              "$ref": "#/components/schemas/Problem"
+            },
+            "example": {
+              "type": "https://example.com/errors/internal-server-error",
+              "title": "Internal Server Error",
+              "status": 500,
+              "detail": "An unexpected error occurred."
+            }
+          },
+          "application/problem+xml": {
+            "schema": {
+              "$ref": "#/components/schemas/Problem"
+            },
+            "example": {
+              "type": "https://example.com/errors/internal-server-error",
+              "title": "Internal Server Error",
+              "status": 500,
+              "detail": "An unexpected error occurred."
+            }
+          }
+        }
+      },
+      "NotFound": {
+        "description": "Not Found",
+        "headers": {
+          "RateLimit": {
+            "$ref": "#/components/headers/RateLimit"
+          }
+        },
+        "content": {
+          "application/problem+json": {
+            "schema": {
+              "$ref": "#/components/schemas/Problem"
+            },
+            "example": {
+              "type": "https://example.com/errors/not-found",
+              "title": "Not Found",
+              "status": 404,
+              "detail": "The requested resource was not found."
+            }
+          },
+          "application/problem+xml": {
+            "schema": {
+              "$ref": "#/components/schemas/Problem"
+            },
+            "example": {
+              "type": "https://example.com/errors/not-found",
+              "title": "Not Found",
+              "status": 404,
+              "detail": "The requested resource was not found."
+            }
+          }
+        }
+      },
+      "TooManyRequests": {
+        "description": "Too Many Requests",
+        "headers": {
+          "RateLimit": {
+            "$ref": "#/components/headers/RateLimit"
+          },
+          "Retry-After": {
+            "$ref": "#/components/headers/Retry-After"
+          }
+        },
+        "content": {
+          "application/problem+json": {
+            "schema": {
+              "$ref": "#/components/schemas/Problem"
+            },
+            "example": {
+              "type": "https://example.com/errors/too-many-requests",
+              "title": "Too Many Requests",
+              "status": 429,
+              "detail": "You have exceeded the rate limit."
+            }
+          },
+          "application/problem+xml": {
+            "schema": {
+              "$ref": "#/components/schemas/Problem"
+            },
+            "example": {
+              "type": "https://example.com/errors/too-many-requests",
+              "title": "Too Many Requests",
+              "status": 429,
+              "detail": "You have exceeded the rate limit."
+            }
+          }
+        }
+      },
+      "Unauthorized": {
+        "description": "Unauthorized",
+        "headers": {
+          "RateLimit": {
+            "$ref": "#/components/headers/RateLimit"
+          }
+        },
+        "content": {
+          "application/problem+json": {
+            "schema": {
+              "$ref": "#/components/schemas/Problem"
+            },
+            "example": {
+              "type": "https://example.com/errors/unauthorized",
+              "title": "Unauthorized",
+              "status": 401,
+              "detail": "You do not have the necessary permissions."
+            }
+          },
+          "application/problem+xml": {
+            "schema": {
+              "$ref": "#/components/schemas/Problem"
+            },
+            "example": {
+              "type": "https://example.com/errors/unauthorized",
+              "title": "Unauthorized",
+              "status": 401,
+              "detail": "You do not have the necessary permissions."
+            }
+          }
+        }
+      }
+    }
+  }
+}
\ No newline at end of file
diff --git a/packages/cli/ete-tests/src/tests/update-api-unioned/fixtures/fern/spec2/generators.yml b/packages/cli/ete-tests/src/tests/update-api-unioned/fixtures/fern/spec2/generators.yml
new file mode 100644
index 00000000000..cf0562dda16
--- /dev/null
+++ b/packages/cli/ete-tests/src/tests/update-api-unioned/fixtures/fern/spec2/generators.yml
@@ -0,0 +1,4 @@
+default-group: local
+api:
+  path: ./openapi.json
+  origin: https://bump.sh/bump-examples/doc/train-travel-api.json
diff --git a/packages/cli/ete-tests/src/tests/update-api-unioned/fixtures/fern/spec2/openapi.json b/packages/cli/ete-tests/src/tests/update-api-unioned/fixtures/fern/spec2/openapi.json
new file mode 100644
index 00000000000..a7705520202
--- /dev/null
+++ b/packages/cli/ete-tests/src/tests/update-api-unioned/fixtures/fern/spec2/openapi.json
@@ -0,0 +1,1435 @@
+{
+  "info": {
+    "contact": {
+      "name": "Train Support",
+      "url": "https://example.com/support",
+      "email": "support@example.com"
+    },
+    "license": {
+      "name": "Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International",
+      "identifier": "CC-BY-NC-SA-4.0"
+    }
+  },
+  "servers": [
+    {
+      "url": "https://api.example.com",
+      "description": "Production"
+    }
+  ],
+  "security": [
+    {
+      "OAuth2": [
+        "read"
+      ]
+    }
+  ],
+  "x-topics": [
+    {
+      "title": "Getting started",
+      "content": {
+        "$ref": "./docs/getting-started.md"
+      }
+    }
+  ],
+  "tags": [
+    {
+      "name": "Stations",
+      "description": "Find and filter train stations across Europe, including their location\nand local timezone.\n"
+    },
+    {
+      "name": "Train Tracks",
+      "description": "Find and filter all the different rail roads available across Europe, including their location\nand local timezone.\n"
+    },
+    {
+      "name": "Trips",
+      "description": "Timetables and routes for train trips between stations, including pricing\nand availability.\n"
+    },
+    {
+      "name": "Bookings",
+      "description": "Create and manage bookings for train trips, including passenger details\nand optional extras.\n"
+    },
+    {
+      "name": "Payments",
+      "description": "Pay for bookings using a card or bank account, and view payment\nstatus and history.\n\n> warn\n> Bookings usually expire within 1 hour so you'll need to make your payment\n> before the expiry date \n"
+    }
+  ],
+  "paths": {
+    "/trips": {
+      "get": {
+        "summary": "Get available train trips",
+        "description": "Returns a list of available train trips between the specified origin and destination stations on the given date, and allows for filtering by bicycle and dog allowances.",
+        "operationId": "get-trips",
+        "tags": [
+          "Trips"
+        ],
+        "parameters": [
+          {
+            "name": "origin",
+            "in": "query",
+            "description": "The ID of the origin station",
+            "required": true,
+            "schema": {
+              "type": "string",
+              "format": "uuid"
+            },
+            "example": "efdbb9d1-02c2-4bc3-afb7-6788d8782b1e"
+          },
+          {
+            "name": "destination",
+            "in": "query",
+            "description": "The ID of the destination station",
+            "required": true,
+            "schema": {
+              "type": "string",
+              "format": "uuid"
+            },
+            "example": "b2e783e1-c824-4d63-b37a-d8d698862f1d"
+          },
+          {
+            "name": "date",
+            "in": "query",
+            "description": "The date and time of the trip in ISO 8601 format in origin station's timezone.",
+            "required": true,
+            "schema": {
+              "type": "string",
+              "format": "date-time"
+            },
+            "example": "2024-02-01T09:00:00Z"
+          },
+          {
+            "name": "bicycles",
+            "in": "query",
+            "description": "Only return trips where bicycles are known to be allowed",
+            "required": false,
+            "schema": {
+              "type": "boolean",
+              "default": false
+            }
+          },
+          {
+            "name": "dogs",
+            "in": "query",
+            "description": "Only return trips where dogs are known to be allowed",
+            "required": false,
+            "schema": {
+              "type": "boolean",
+              "default": false
+            }
+          }
+        ],
+        "responses": {
+          "200": {
+            "description": "A list of available train trips",
+            "headers": {
+              "RateLimit": {
+                "$ref": "#/components/headers/RateLimit"
+              }
+            },
+            "content": {
+              "application/json": {
+                "schema": {
+                  "allOf": [
+                    {
+                      "$ref": "#/components/schemas/Wrapper-Collection"
+                    },
+                    {
+                      "properties": {
+                        "data": {
+                          "type": "array",
+                          "items": {
+                            "$ref": "#/components/schemas/Trip"
+                          }
+                        }
+                      }
+                    },
+                    {
+                      "properties": {
+                        "links": {
+                          "allOf": [
+                            {
+                              "$ref": "#/components/schemas/Links-Self"
+                            },
+                            {
+                              "$ref": "#/components/schemas/Links-Pagination"
+                            }
+                          ]
+                        }
+                      }
+                    }
+                  ]
+                },
+                "example": {
+                  "data": [
+                    {
+                      "id": "ea399ba1-6d95-433f-92d1-83f67b775594",
+                      "origin": "efdbb9d1-02c2-4bc3-afb7-6788d8782b1e",
+                      "destination": "b2e783e1-c824-4d63-b37a-d8d698862f1d",
+                      "departure_time": "2024-02-01T10:00:00Z",
+                      "arrival_time": "2024-02-01T16:00:00Z",
+                      "price": 50,
+                      "operator": "Deutsche Bahn",
+                      "bicycles_allowed": true,
+                      "dogs_allowed": true
+                    },
+                    {
+                      "id": "4d67459c-af07-40bb-bb12-178dbb88e09f",
+                      "origin": "b2e783e1-c824-4d63-b37a-d8d698862f1d",
+                      "destination": "efdbb9d1-02c2-4bc3-afb7-6788d8782b1e",
+                      "departure_time": "2024-02-01T12:00:00Z",
+                      "arrival_time": "2024-02-01T18:00:00Z",
+                      "price": 50,
+                      "operator": "SNCF",
+                      "bicycles_allowed": true,
+                      "dogs_allowed": true
+                    }
+                  ],
+                  "links": {
+                    "self": "https://api.example.com/trips?origin=efdbb9d1-02c2-4bc3-afb7-6788d8782b1e&destination=b2e783e1-c824-4d63-b37a-d8d698862f1d&date=2024-02-01",
+                    "next": "https://api.example.com/trips?origin=efdbb9d1-02c2-4bc3-afb7-6788d8782b1e&destination=b2e783e1-c824-4d63-b37a-d8d698862f1d&date=2024-02-01&page=2"
+                  }
+                }
+              },
+              "application/xml": {
+                "schema": {
+                  "allOf": [
+                    {
+                      "$ref": "#/components/schemas/Wrapper-Collection"
+                    },
+                    {
+                      "properties": {
+                        "data": {
+                          "type": "array",
+                          "xml": {
+                            "name": "trips",
+                            "wrapped": true
+                          },
+                          "items": {
+                            "$ref": "#/components/schemas/Trip"
+                          }
+                        }
+                      }
+                    },
+                    {
+                      "properties": {
+                        "links": {
+                          "allOf": [
+                            {
+                              "$ref": "#/components/schemas/Links-Self"
+                            },
+                            {
+                              "$ref": "#/components/schemas/Links-Pagination"
+                            }
+                          ]
+                        }
+                      }
+                    }
+                  ]
+                }
+              }
+            }
+          },
+          "400": {
+            "$ref": "#/components/responses/BadRequest"
+          },
+          "401": {
+            "$ref": "#/components/responses/Unauthorized"
+          },
+          "403": {
+            "$ref": "#/components/responses/Forbidden"
+          },
+          "429": {
+            "$ref": "#/components/responses/TooManyRequests"
+          },
+          "500": {
+            "$ref": "#/components/responses/InternalServerError"
+          }
+        }
+      }
+    },
+    "/bookings": {
+      "get": {
+        "operationId": "get-bookings",
+        "summary": "List existing bookings",
+        "description": "Returns a list of all trip bookings by the authenticated user.",
+        "tags": [
+          "Bookings"
+        ],
+        "responses": {
+          "200": {
+            "description": "A list of bookings",
+            "headers": {
+              "RateLimit": {
+                "$ref": "#/components/headers/RateLimit"
+              }
+            },
+            "content": {
+              "application/json": {
+                "schema": {
+                  "allOf": [
+                    {
+                      "$ref": "#/components/schemas/Wrapper-Collection"
+                    },
+                    {
+                      "properties": {
+                        "data": {
+                          "type": "array",
+                          "items": {
+                            "$ref": "#/components/schemas/Booking"
+                          }
+                        }
+                      }
+                    },
+                    {
+                      "properties": {
+                        "links": {
+                          "allOf": [
+                            {
+                              "$ref": "#/components/schemas/Links-Self"
+                            },
+                            {
+                              "$ref": "#/components/schemas/Links-Pagination"
+                            }
+                          ]
+                        }
+                      }
+                    }
+                  ]
+                },
+                "example": {
+                  "data": [
+                    {
+                      "id": "efdbb9d1-02c2-4bc3-afb7-6788d8782b1e",
+                      "trip_id": "efdbb9d1-02c2-4bc3-afb7-6788d8782b1e",
+                      "passenger_name": "John Doe",
+                      "has_bicycle": true,
+                      "has_dog": true
+                    },
+                    {
+                      "id": "b2e783e1-c824-4d63-b37a-d8d698862f1d",
+                      "trip_id": "b2e783e1-c824-4d63-b37a-d8d698862f1d",
+                      "passenger_name": "Jane Smith",
+                      "has_bicycle": false,
+                      "has_dog": false
+                    }
+                  ],
+                  "links": {
+                    "self": "https://api.example.com/bookings",
+                    "next": "https://api.example.com/bookings?page=2"
+                  }
+                }
+              },
+              "application/xml": {
+                "schema": {
+                  "allOf": [
+                    {
+                      "$ref": "#/components/schemas/Wrapper-Collection"
+                    },
+                    {
+                      "properties": {
+                        "data": {
+                          "type": "array",
+                          "xml": {
+                            "name": "bookings",
+                            "wrapped": true
+                          },
+                          "items": {
+                            "$ref": "#/components/schemas/Booking"
+                          }
+                        }
+                      }
+                    },
+                    {
+                      "properties": {
+                        "links": {
+                          "allOf": [
+                            {
+                              "$ref": "#/components/schemas/Links-Self"
+                            },
+                            {
+                              "$ref": "#/components/schemas/Links-Pagination"
+                            }
+                          ]
+                        }
+                      }
+                    }
+                  ]
+                }
+              }
+            }
+          },
+          "400": {
+            "$ref": "#/components/responses/BadRequest"
+          },
+          "401": {
+            "$ref": "#/components/responses/Unauthorized"
+          },
+          "403": {
+            "$ref": "#/components/responses/Forbidden"
+          },
+          "429": {
+            "$ref": "#/components/responses/TooManyRequests"
+          },
+          "500": {
+            "$ref": "#/components/responses/InternalServerError"
+          }
+        }
+      },
+      "post": {
+        "operationId": "create-booking",
+        "summary": "Create a booking",
+        "description": "A booking is a temporary hold on a trip. It is not confirmed until the payment is processed.",
+        "tags": [
+          "Bookings"
+        ],
+        "security": [
+          {
+            "OAuth2": [
+              "write"
+            ]
+          }
+        ],
+        "requestBody": {
+          "required": true,
+          "content": {
+            "application/json": {
+              "schema": {
+                "$ref": "#/components/schemas/Booking"
+              }
+            },
+            "application/xml": {
+              "schema": {
+                "$ref": "#/components/schemas/Booking"
+              }
+            }
+          }
+        },
+        "responses": {
+          "201": {
+            "description": "Booking successful",
+            "content": {
+              "application/json": {
+                "schema": {
+                  "allOf": [
+                    {
+                      "$ref": "#/components/schemas/Booking"
+                    },
+                    {
+                      "properties": {
+                        "links": {
+                          "$ref": "#/components/schemas/Links-Self"
+                        }
+                      }
+                    }
+                  ]
+                },
+                "example": {
+                  "id": "efdbb9d1-02c2-4bc3-afb7-6788d8782b1e",
+                  "trip_id": "efdbb9d1-02c2-4bc3-afb7-6788d8782b1e",
+                  "passenger_name": "John Doe",
+                  "has_bicycle": true,
+                  "has_dog": true,
+                  "links": {
+                    "self": "https://api.example.com/bookings/efdbb9d1-02c2-4bc3-afb7-6788d8782b1e"
+                  }
+                }
+              },
+              "application/xml": {
+                "schema": {
+                  "allOf": [
+                    {
+                      "$ref": "#/components/schemas/Booking"
+                    },
+                    {
+                      "properties": {
+                        "links": {
+                          "$ref": "#/components/schemas/Links-Self"
+                        }
+                      }
+                    }
+                  ]
+                }
+              }
+            }
+          },
+          "400": {
+            "$ref": "#/components/responses/BadRequest"
+          },
+          "401": {
+            "$ref": "#/components/responses/Unauthorized"
+          },
+          "404": {
+            "$ref": "#/components/responses/NotFound"
+          },
+          "409": {
+            "$ref": "#/components/responses/Conflict"
+          },
+          "429": {
+            "$ref": "#/components/responses/TooManyRequests"
+          },
+          "500": {
+            "$ref": "#/components/responses/InternalServerError"
+          }
+        }
+      }
+    },
+    "/bookings/{bookingId}": {
+      "parameters": [
+        {
+          "name": "bookingId",
+          "in": "path",
+          "required": true,
+          "description": "The ID of the booking to retrieve.",
+          "schema": {
+            "type": "string",
+            "format": "uuid"
+          },
+          "example": "1725ff48-ab45-4bb5-9d02-88745177dedb"
+        }
+      ],
+      "get": {
+        "summary": "Get a booking",
+        "description": "Returns the details of a specific booking.",
+        "operationId": "get-booking",
+        "tags": [
+          "Bookings"
+        ],
+        "responses": {
+          "200": {
+            "description": "The booking details",
+            "headers": {
+              "RateLimit": {
+                "$ref": "#/components/headers/RateLimit"
+              }
+            },
+            "content": {
+              "application/json": {
+                "schema": {
+                  "allOf": [
+                    {
+                      "$ref": "#/components/schemas/Booking"
+                    },
+                    {
+                      "properties": {
+                        "links": {
+                          "$ref": "#/components/schemas/Links-Self"
+                        }
+                      }
+                    }
+                  ]
+                },
+                "example": {
+                  "id": "efdbb9d1-02c2-4bc3-afb7-6788d8782b1e",
+                  "trip_id": "efdbb9d1-02c2-4bc3-afb7-6788d8782b1e",
+                  "passenger_name": "John Doe",
+                  "has_bicycle": true,
+                  "has_dog": true,
+                  "links": {
+                    "self": "https://api.example.com/bookings/1725ff48-ab45-4bb5-9d02-88745177dedb"
+                  }
+                }
+              },
+              "application/xml": {
+                "schema": {
+                  "allOf": [
+                    {
+                      "$ref": "#/components/schemas/Booking"
+                    },
+                    {
+                      "properties": {
+                        "links": {
+                          "$ref": "#/components/schemas/Links-Self"
+                        }
+                      }
+                    }
+                  ]
+                }
+              }
+            }
+          },
+          "400": {
+            "$ref": "#/components/responses/BadRequest"
+          },
+          "401": {
+            "$ref": "#/components/responses/Unauthorized"
+          },
+          "403": {
+            "$ref": "#/components/responses/Forbidden"
+          },
+          "404": {
+            "$ref": "#/components/responses/NotFound"
+          },
+          "429": {
+            "$ref": "#/components/responses/TooManyRequests"
+          },
+          "500": {
+            "$ref": "#/components/responses/InternalServerError"
+          }
+        }
+      },
+      "delete": {
+        "summary": "Delete a booking",
+        "description": "Deletes a booking, cancelling the hold on the trip.",
+        "operationId": "delete-booking",
+        "security": [
+          {
+            "OAuth2": [
+              "write"
+            ]
+          }
+        ],
+        "tags": [
+          "Bookings"
+        ],
+        "responses": {
+          "204": {
+            "description": "Booking deleted"
+          },
+          "400": {
+            "$ref": "#/components/responses/BadRequest"
+          },
+          "401": {
+            "$ref": "#/components/responses/Unauthorized"
+          },
+          "403": {
+            "$ref": "#/components/responses/Forbidden"
+          },
+          "404": {
+            "$ref": "#/components/responses/NotFound"
+          },
+          "429": {
+            "$ref": "#/components/responses/TooManyRequests"
+          },
+          "500": {
+            "$ref": "#/components/responses/InternalServerError"
+          }
+        }
+      }
+    },
+    "/bookings/{bookingId}/payment": {
+      "parameters": [
+        {
+          "name": "bookingId",
+          "in": "path",
+          "required": true,
+          "description": "The ID of the booking to pay for.",
+          "schema": {
+            "type": "string",
+            "format": "uuid"
+          },
+          "example": "1725ff48-ab45-4bb5-9d02-88745177dedb"
+        }
+      ],
+      "post": {
+        "summary": "Pay for a Booking",
+        "description": "A payment is an attempt to pay for the booking, which will confirm the booking for the user and enable them to get their tickets.",
+        "operationId": "create-booking-payment",
+        "tags": [
+          "Payments"
+        ],
+        "requestBody": {
+          "content": {
+            "application/json": {
+              "schema": {
+                "$ref": "#/components/schemas/BookingPayment"
+              },
+              "examples": {
+                "Card": {
+                  "summary": "Card Payment",
+                  "value": {
+                    "amount": 49.99,
+                    "currency": "gbp",
+                    "source": {
+                      "object": "card",
+                      "name": "J. Doe",
+                      "number": "4242424242424242",
+                      "cvc": 123,
+                      "exp_month": 12,
+                      "exp_year": 2025,
+                      "address_line1": "123 Fake Street",
+                      "address_line2": "4th Floor",
+                      "address_city": "London",
+                      "address_country": "gb",
+                      "address_post_code": "N12 9XX"
+                    }
+                  }
+                },
+                "Bank": {
+                  "summary": "Bank Account Payment",
+                  "value": {
+                    "amount": 100.5,
+                    "currency": "gbp",
+                    "source": {
+                      "object": "bank_account",
+                      "name": "J. Doe",
+                      "number": "00012345",
+                      "sort_code": "000123",
+                      "account_type": "individual",
+                      "bank_name": "Starling Bank",
+                      "country": "gb"
+                    }
+                  }
+                }
+              }
+            }
+          }
+        },
+        "responses": {
+          "200": {
+            "description": "Payment successful",
+            "headers": {
+              "RateLimit": {
+                "$ref": "#/components/headers/RateLimit"
+              }
+            },
+            "content": {
+              "application/json": {
+                "schema": {
+                  "allOf": [
+                    {
+                      "$ref": "#/components/schemas/BookingPayment"
+                    },
+                    {
+                      "properties": {
+                        "links": {
+                          "$ref": "#/components/schemas/Links-Booking"
+                        }
+                      }
+                    }
+                  ]
+                },
+                "examples": {
+                  "Card": {
+                    "summary": "Card Payment",
+                    "value": {
+                      "id": "2e3b4f5a-6b7c-8d9e-0f1a-2b3c4d5e6f7a",
+                      "amount": 49.99,
+                      "currency": "gbp",
+                      "source": {
+                        "object": "card",
+                        "name": "J. Doe",
+                        "number": "************4242",
+                        "cvc": 123,
+                        "exp_month": 12,
+                        "exp_year": 2025,
+                        "address_country": "gb",
+                        "address_post_code": "N12 9XX"
+                      },
+                      "status": "succeeded",
+                      "links": {
+                        "booking": "https://api.example.com/bookings/1725ff48-ab45-4bb5-9d02-88745177dedb/payment"
+                      }
+                    }
+                  },
+                  "Bank": {
+                    "summary": "Bank Account Payment",
+                    "value": {
+                      "id": "2e3b4f5a-6b7c-8d9e-0f1a-2b3c4d5e6f7a",
+                      "amount": 100.5,
+                      "currency": "gbp",
+                      "source": {
+                        "object": "bank_account",
+                        "name": "J. Doe",
+                        "account_type": "individual",
+                        "number": "*********2345",
+                        "sort_code": "000123",
+                        "bank_name": "Starling Bank",
+                        "country": "gb"
+                      },
+                      "status": "succeeded",
+                      "links": {
+                        "booking": "https://api.example.com/bookings/1725ff48-ab45-4bb5-9d02-88745177dedb"
+                      }
+                    }
+                  }
+                }
+              }
+            }
+          },
+          "400": {
+            "$ref": "#/components/responses/BadRequest"
+          },
+          "401": {
+            "$ref": "#/components/responses/Unauthorized"
+          },
+          "403": {
+            "$ref": "#/components/responses/Forbidden"
+          },
+          "429": {
+            "$ref": "#/components/responses/TooManyRequests"
+          },
+          "500": {
+            "$ref": "#/components/responses/InternalServerError"
+          }
+        }
+      }
+    }
+  },
+  "components": {
+    "securitySchemes": {
+      "OAuth2": {
+        "type": "oauth2",
+        "description": "OAuth 2.0 authorization code following RFC8725 best practices.",
+        "flows": {
+          "authorizationCode": {
+            "authorizationUrl": "https://example.com/oauth/authorize",
+            "tokenUrl": "https://example.com/oauth/token",
+            "scopes": {
+              "read": "Read access",
+              "write": "Write access"
+            }
+          }
+        }
+      }
+    },
+    "schemas": {
+      "Station": {
+        "type": "object",
+        "xml": {
+          "name": "station"
+        },
+        "required": [
+          "id",
+          "name",
+          "address",
+          "country_code"
+        ],
+        "properties": {
+          "id": {
+            "type": "string",
+            "format": "uuid",
+            "description": "Unique identifier for the station.",
+            "examples": [
+              "efdbb9d1-02c2-4bc3-afb7-6788d8782b1e",
+              "b2e783e1-c824-4d63-b37a-d8d698862f1d"
+            ]
+          },
+          "name": {
+            "type": "string",
+            "description": "The name of the station",
+            "examples": [
+              "Berlin Hauptbahnhof",
+              "Paris Gare du Nord"
+            ]
+          },
+          "address": {
+            "type": "string",
+            "description": "The address of the station.",
+            "examples": [
+              "Invalidenstraße 10557 Berlin, Germany",
+              "18 Rue de Dunkerque 75010 Paris, France"
+            ]
+          },
+          "country_code": {
+            "type": "string",
+            "description": "The country code of the station.",
+            "format": "iso-country-code",
+            "examples": [
+              "DE",
+              "FR"
+            ]
+          },
+          "timezone": {
+            "type": "string",
+            "description": "The timezone of the station in the [IANA Time Zone Database format](https://www.iana.org/time-zones).",
+            "examples": [
+              "Europe/Berlin",
+              "Europe/Paris"
+            ]
+          }
+        }
+      },
+      "Links-Self": {
+        "type": "object",
+        "properties": {
+          "self": {
+            "type": "string",
+            "format": "uri"
+          }
+        }
+      },
+      "Links-Pagination": {
+        "type": "object",
+        "properties": {
+          "next": {
+            "type": "string",
+            "format": "uri"
+          },
+          "prev": {
+            "type": "string",
+            "format": "uri"
+          }
+        }
+      },
+      "Trip": {
+        "type": "object",
+        "xml": {
+          "name": "trip"
+        },
+        "properties": {
+          "id": {
+            "type": "string",
+            "format": "uuid",
+            "description": "Unique identifier for the trip",
+            "examples": [
+              "4f4e4e1-c824-4d63-b37a-d8d698862f1d"
+            ]
+          },
+          "origin": {
+            "type": "string",
+            "description": "The starting station of the trip",
+            "examples": [
+              "Berlin Hauptbahnhof",
+              "Paris Gare du Nord"
+            ]
+          },
+          "destination": {
+            "type": "string",
+            "description": "The destination station of the trip",
+            "examples": [
+              "Paris Gare du Nord",
+              "Berlin Hauptbahnhof"
+            ]
+          },
+          "departure_time": {
+            "type": "string",
+            "format": "date-time",
+            "description": "The date and time when the trip departs",
+            "examples": [
+              "2024-02-01T10:00:00Z"
+            ]
+          },
+          "arrival_time": {
+            "type": "string",
+            "format": "date-time",
+            "description": "The date and time when the trip arrives",
+            "examples": [
+              "2024-02-01T16:00:00Z"
+            ]
+          },
+          "operator": {
+            "type": "string",
+            "description": "The name of the operator of the trip",
+            "examples": [
+              "Deutsche Bahn",
+              "SNCF"
+            ]
+          },
+          "price": {
+            "type": "number",
+            "description": "The cost of the trip",
+            "examples": [
+              50
+            ]
+          },
+          "bicycles_allowed": {
+            "type": "boolean",
+            "description": "Indicates whether bicycles are allowed on the trip"
+          },
+          "dogs_allowed": {
+            "type": "boolean",
+            "description": "Indicates whether dogs are allowed on the trip"
+          }
+        }
+      },
+      "Booking": {
+        "type": "object",
+        "xml": {
+          "name": "booking"
+        },
+        "properties": {
+          "id": {
+            "type": "string",
+            "format": "uuid",
+            "description": "Unique identifier for the booking",
+            "readOnly": true,
+            "examples": [
+              "3f3e3e1-c824-4d63-b37a-d8d698862f1d"
+            ]
+          },
+          "trip_id": {
+            "type": "string",
+            "format": "uuid",
+            "description": "Identifier of the booked trip",
+            "examples": [
+              "4f4e4e1-c824-4d63-b37a-d8d698862f1d"
+            ]
+          },
+          "passenger_name": {
+            "type": "string",
+            "description": "Name of the passenger",
+            "examples": [
+              "John Doe"
+            ]
+          },
+          "has_bicycle": {
+            "type": "boolean",
+            "description": "Indicates whether the passenger has a bicycle."
+          },
+          "has_dog": {
+            "type": "boolean",
+            "description": "Indicates whether the passenger has a dog."
+          }
+        }
+      },
+      "Wrapper-Collection": {
+        "description": "This is a generic request/response wrapper which contains both data and links which serve as hypermedia controls (HATEOAS).",
+        "type": "object",
+        "properties": {
+          "data": {
+            "description": "The wrapper for a collection is an array of objects.",
+            "type": "array",
+            "items": {
+              "type": "object"
+            }
+          },
+          "links": {
+            "description": "A set of hypermedia links which serve as controls for the client.",
+            "type": "object",
+            "readOnly": true
+          }
+        },
+        "xml": {
+          "name": "data"
+        }
+      },
+      "BookingPayment": {
+        "type": "object",
+        "properties": {
+          "id": {
+            "description": "Unique identifier for the payment. This will be a unique identifier for the payment, and is used to reference the payment in other objects.",
+            "type": "string",
+            "format": "uuid",
+            "readOnly": true
+          },
+          "amount": {
+            "description": "Amount intended to be collected by this payment. A positive decimal figure describing the amount to be collected.",
+            "type": "number",
+            "exclusiveMinimum": 0,
+            "examples": [
+              49.99
+            ]
+          },
+          "currency": {
+            "description": "Three-letter [ISO currency code](https://www.iso.org/iso-4217-currency-codes.html), in lowercase.",
+            "type": "string",
+            "enum": [
+              "bam",
+              "bgn",
+              "chf",
+              "eur",
+              "gbp",
+              "nok",
+              "sek",
+              "try"
+            ]
+          },
+          "source": {
+            "unevaluatedProperties": false,
+            "description": "The payment source to take the payment from. This can be a card or a bank account. Some of these properties will be hidden on read to protect PII leaking.",
+            "anyOf": [
+              {
+                "title": "Card",
+                "description": "A card (debit or credit) to take payment from.",
+                "properties": {
+                  "object": {
+                    "type": "string",
+                    "const": "card"
+                  },
+                  "name": {
+                    "type": "string",
+                    "description": "Cardholder's full name as it appears on the card.",
+                    "examples": [
+                      "Francis Bourgeois"
+                    ]
+                  },
+                  "number": {
+                    "type": "string",
+                    "description": "The card number, as a string without any separators. On read all but the last four digits will be masked for security.",
+                    "examples": [
+                      "4242424242424242"
+                    ]
+                  },
+                  "cvc": {
+                    "type": "integer",
+                    "description": "Card security code, 3 or 4 digits usually found on the back of the card.",
+                    "minLength": 3,
+                    "maxLength": 4,
+                    "writeOnly": true,
+                    "example": 123
+                  },
+                  "exp_month": {
+                    "type": "integer",
+                    "format": "int64",
+                    "description": "Two-digit number representing the card's expiration month.",
+                    "examples": [
+                      12
+                    ]
+                  },
+                  "exp_year": {
+                    "type": "integer",
+                    "format": "int64",
+                    "description": "Four-digit number representing the card's expiration year.",
+                    "examples": [
+                      2025
+                    ]
+                  },
+                  "address_line1": {
+                    "type": "string",
+                    "writeOnly": true
+                  },
+                  "address_line2": {
+                    "type": "string",
+                    "writeOnly": true
+                  },
+                  "address_city": {
+                    "type": "string"
+                  },
+                  "address_country": {
+                    "type": "string"
+                  },
+                  "address_post_code": {
+                    "type": "string"
+                  }
+                },
+                "required": [
+                  "name",
+                  "number",
+                  "cvc",
+                  "exp_month",
+                  "exp_year",
+                  "address_country"
+                ]
+              },
+              {
+                "title": "Bank Account",
+                "description": "A bank account to take payment from. Must be able to make payments in the currency specified in the payment.",
+                "type": "object",
+                "properties": {
+                  "object": {
+                    "const": "bank_account",
+                    "type": "string"
+                  },
+                  "name": {
+                    "type": "string"
+                  },
+                  "number": {
+                    "type": "string",
+                    "description": "The account number for the bank account, in string form. Must be a current account."
+                  },
+                  "sort_code": {
+                    "type": "string",
+                    "description": "The sort code for the bank account, in string form. Must be a six-digit number."
+                  },
+                  "account_type": {
+                    "enum": [
+                      "individual",
+                      "company"
+                    ],
+                    "type": "string",
+                    "description": "The type of entity that holds the account. This can be either `individual` or `company`."
+                  },
+                  "bank_name": {
+                    "type": "string",
+                    "description": "The name of the bank associated with the routing number.",
+                    "examples": [
+                      "Starling Bank"
+                    ]
+                  },
+                  "country": {
+                    "type": "string",
+                    "description": "Two-letter country code (ISO 3166-1 alpha-2)."
+                  }
+                },
+                "required": [
+                  "name",
+                  "number",
+                  "account_type",
+                  "bank_name",
+                  "country"
+                ]
+              }
+            ]
+          },
+          "status": {
+            "description": "The status of the payment, one of `pending`, `succeeded`, or `failed`.",
+            "type": "string",
+            "enum": [
+              "pending",
+              "succeeded",
+              "failed"
+            ],
+            "readOnly": true
+          }
+        }
+      },
+      "Links-Booking": {
+        "type": "object",
+        "properties": {
+          "booking": {
+            "type": "string",
+            "format": "uri",
+            "examples": [
+              "https://api.example.com/bookings/1725ff48-ab45-4bb5-9d02-88745177dedb"
+            ]
+          }
+        }
+      }
+    },
+    "headers": {
+      "RateLimit": {
+        "description": "The RateLimit header communicates quota policies. It contains a `limit` to\nconvey the expiring limit, `remaining` to convey the remaining quota units,\nand `reset` to convey the time window reset time.\n",
+        "schema": {
+          "type": "string",
+          "examples": [
+            "limit=10, remaining=0, reset=10"
+          ]
+        }
+      },
+      "Retry-After": {
+        "description": "The Retry-After header indicates how long the user agent should wait before making a follow-up request. \nThe value is in seconds and can be an integer or a date in the future. \nIf the value is an integer, it indicates the number of seconds to wait. \nIf the value is a date, it indicates the time at which the user agent should make a follow-up request. \n",
+        "schema": {
+          "type": "string"
+        },
+        "examples": {
+          "integer": {
+            "value": "120",
+            "summary": "Retry after 120 seconds"
+          },
+          "date": {
+            "value": "Fri, 31 Dec 2021 23:59:59 GMT",
+            "summary": "Retry after the specified date"
+          }
+        }
+      }
+    },
+    "responses": {
+      "BadRequest": {
+        "description": "Bad Request",
+        "headers": {
+          "RateLimit": {
+            "$ref": "#/components/headers/RateLimit"
+          }
+        },
+        "content": {
+          "application/problem+json": {
+            "schema": {
+              "$ref": "#/components/schemas/Problem"
+            },
+            "example": {
+              "type": "https://example.com/errors/bad-request",
+              "title": "Bad Request",
+              "status": 400,
+              "detail": "The request is invalid or missing required parameters."
+            }
+          },
+          "application/problem+xml": {
+            "schema": {
+              "$ref": "#/components/schemas/Problem"
+            },
+            "example": {
+              "type": "https://example.com/errors/bad-request",
+              "title": "Bad Request",
+              "status": 400,
+              "detail": "The request is invalid or missing required parameters."
+            }
+          }
+        }
+      },
+      "Conflict": {
+        "description": "Conflict",
+        "headers": {
+          "RateLimit": {
+            "$ref": "#/components/headers/RateLimit"
+          }
+        },
+        "content": {
+          "application/problem+json": {
+            "schema": {
+              "$ref": "#/components/schemas/Problem"
+            },
+            "example": {
+              "type": "https://example.com/errors/conflict",
+              "title": "Conflict",
+              "status": 409,
+              "detail": "There is a conflict with an existing resource."
+            }
+          },
+          "application/problem+xml": {
+            "schema": {
+              "$ref": "#/components/schemas/Problem"
+            },
+            "example": {
+              "type": "https://example.com/errors/conflict",
+              "title": "Conflict",
+              "status": 409,
+              "detail": "There is a conflict with an existing resource."
+            }
+          }
+        }
+      },
+      "Forbidden": {
+        "description": "Forbidden",
+        "headers": {
+          "RateLimit": {
+            "$ref": "#/components/headers/RateLimit"
+          }
+        },
+        "content": {
+          "application/problem+json": {
+            "schema": {
+              "$ref": "#/components/schemas/Problem"
+            },
+            "example": {
+              "type": "https://example.com/errors/forbidden",
+              "title": "Forbidden",
+              "status": 403,
+              "detail": "Access is forbidden with the provided credentials."
+            }
+          },
+          "application/problem+xml": {
+            "schema": {
+              "$ref": "#/components/schemas/Problem"
+            },
+            "example": {
+              "type": "https://example.com/errors/forbidden",
+              "title": "Forbidden",
+              "status": 403,
+              "detail": "Access is forbidden with the provided credentials."
+            }
+          }
+        }
+      },
+      "InternalServerError": {
+        "description": "Internal Server Error",
+        "headers": {
+          "RateLimit": {
+            "$ref": "#/components/headers/RateLimit"
+          }
+        },
+        "content": {
+          "application/problem+json": {
+            "schema": {
+              "$ref": "#/components/schemas/Problem"
+            },
+            "example": {
+              "type": "https://example.com/errors/internal-server-error",
+              "title": "Internal Server Error",
+              "status": 500,
+              "detail": "An unexpected error occurred."
+            }
+          },
+          "application/problem+xml": {
+            "schema": {
+              "$ref": "#/components/schemas/Problem"
+            },
+            "example": {
+              "type": "https://example.com/errors/internal-server-error",
+              "title": "Internal Server Error",
+              "status": 500,
+              "detail": "An unexpected error occurred."
+            }
+          }
+        }
+      },
+      "NotFound": {
+        "description": "Not Found",
+        "headers": {
+          "RateLimit": {
+            "$ref": "#/components/headers/RateLimit"
+          }
+        },
+        "content": {
+          "application/problem+json": {
+            "schema": {
+              "$ref": "#/components/schemas/Problem"
+            },
+            "example": {
+              "type": "https://example.com/errors/not-found",
+              "title": "Not Found",
+              "status": 404,
+              "detail": "The requested resource was not found."
+            }
+          },
+          "application/problem+xml": {
+            "schema": {
+              "$ref": "#/components/schemas/Problem"
+            },
+            "example": {
+              "type": "https://example.com/errors/not-found",
+              "title": "Not Found",
+              "status": 404,
+              "detail": "The requested resource was not found."
+            }
+          }
+        }
+      },
+      "TooManyRequests": {
+        "description": "Too Many Requests",
+        "headers": {
+          "RateLimit": {
+            "$ref": "#/components/headers/RateLimit"
+          },
+          "Retry-After": {
+            "$ref": "#/components/headers/Retry-After"
+          }
+        },
+        "content": {
+          "application/problem+json": {
+            "schema": {
+              "$ref": "#/components/schemas/Problem"
+            },
+            "example": {
+              "type": "https://example.com/errors/too-many-requests",
+              "title": "Too Many Requests",
+              "status": 429,
+              "detail": "You have exceeded the rate limit."
+            }
+          },
+          "application/problem+xml": {
+            "schema": {
+              "$ref": "#/components/schemas/Problem"
+            },
+            "example": {
+              "type": "https://example.com/errors/too-many-requests",
+              "title": "Too Many Requests",
+              "status": 429,
+              "detail": "You have exceeded the rate limit."
+            }
+          }
+        }
+      },
+      "Unauthorized": {
+        "description": "Unauthorized",
+        "headers": {
+          "RateLimit": {
+            "$ref": "#/components/headers/RateLimit"
+          }
+        },
+        "content": {
+          "application/problem+json": {
+            "schema": {
+              "$ref": "#/components/schemas/Problem"
+            },
+            "example": {
+              "type": "https://example.com/errors/unauthorized",
+              "title": "Unauthorized",
+              "status": 401,
+              "detail": "You do not have the necessary permissions."
+            }
+          },
+          "application/problem+xml": {
+            "schema": {
+              "$ref": "#/components/schemas/Problem"
+            },
+            "example": {
+              "type": "https://example.com/errors/unauthorized",
+              "title": "Unauthorized",
+              "status": 401,
+              "detail": "You do not have the necessary permissions."
+            }
+          }
+        }
+      }
+    }
+  }
+}
\ No newline at end of file
diff --git a/packages/cli/ete-tests/src/tests/update-api-unioned/fixtures/fern/unioned/api.yml b/packages/cli/ete-tests/src/tests/update-api-unioned/fixtures/fern/unioned/api.yml
new file mode 100644
index 00000000000..a939faab6cf
--- /dev/null
+++ b/packages/cli/ete-tests/src/tests/update-api-unioned/fixtures/fern/unioned/api.yml
@@ -0,0 +1 @@
+name: api
\ No newline at end of file
diff --git a/packages/cli/ete-tests/src/tests/update-api-unioned/fixtures/fern/unioned/definition/spec1/__package__.yml b/packages/cli/ete-tests/src/tests/update-api-unioned/fixtures/fern/unioned/definition/spec1/__package__.yml
new file mode 100644
index 00000000000..7e022f0d628
--- /dev/null
+++ b/packages/cli/ete-tests/src/tests/update-api-unioned/fixtures/fern/unioned/definition/spec1/__package__.yml
@@ -0,0 +1 @@
+export: spec1
\ No newline at end of file
diff --git a/packages/cli/ete-tests/src/tests/update-api-unioned/fixtures/fern/unioned/definition/spec2/__package__.yml b/packages/cli/ete-tests/src/tests/update-api-unioned/fixtures/fern/unioned/definition/spec2/__package__.yml
new file mode 100644
index 00000000000..aad3c796b2f
--- /dev/null
+++ b/packages/cli/ete-tests/src/tests/update-api-unioned/fixtures/fern/unioned/definition/spec2/__package__.yml
@@ -0,0 +1 @@
+export: spec2
\ No newline at end of file
diff --git a/packages/cli/ete-tests/src/tests/update-api-unioned/fixtures/fern/unioned/dependencies.yml b/packages/cli/ete-tests/src/tests/update-api-unioned/fixtures/fern/unioned/dependencies.yml
new file mode 100644
index 00000000000..45e0bfea97e
--- /dev/null
+++ b/packages/cli/ete-tests/src/tests/update-api-unioned/fixtures/fern/unioned/dependencies.yml
@@ -0,0 +1,3 @@
+dependencies:
+  spec1: ../spec1
+  spec2: ../spec2
diff --git a/packages/cli/ete-tests/src/tests/update-api-unioned/fixtures/fern/unioned/generators.yml b/packages/cli/ete-tests/src/tests/update-api-unioned/fixtures/fern/unioned/generators.yml
new file mode 100644
index 00000000000..c8ae78dfffa
--- /dev/null
+++ b/packages/cli/ete-tests/src/tests/update-api-unioned/fixtures/fern/unioned/generators.yml
@@ -0,0 +1,9 @@
+default-group: local
+groups:
+  local:
+    generators:
+      - name: fernapi/fern-typescript-node-sdk
+        version: 0.9.5
+        output:
+          location: local-file-system
+          path: ../sdks/typescript
diff --git a/packages/cli/ete-tests/src/tests/update-api-unioned/update-api.test.ts b/packages/cli/ete-tests/src/tests/update-api-unioned/update-api.test.ts
new file mode 100644
index 00000000000..235d4f3497f
--- /dev/null
+++ b/packages/cli/ete-tests/src/tests/update-api-unioned/update-api.test.ts
@@ -0,0 +1,25 @@
+import { AbsoluteFilePath, getDirectoryContents } from "@fern-api/fs-utils";
+import { cp } from "fs/promises";
+import path from "path";
+import tmp from "tmp-promise";
+import { runFernCli } from "../../utils/runFernCli";
+
+const FIXTURES_DIR = path.join(__dirname, "fixtures");
+
+describe("fern api update unioned", () => {
+    it("fern api update unioned", async () => {
+        // Create tmpdir and copy contents
+        const tmpDir = await tmp.dir();
+        const directory = AbsoluteFilePath.of(tmpDir.path);
+
+        await cp(FIXTURES_DIR, directory, { recursive: true });
+
+        const outputPath = AbsoluteFilePath.of(path.join(directory, "fern", "spec1"));
+
+        await runFernCli(["api", "update"], {
+            cwd: directory
+        });
+
+        expect(await getDirectoryContents(outputPath)).toMatchSnapshot();
+    }, 60_000);
+});