diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 3c4a087..99ec9fb 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -1,12 +1,14 @@
 name: CI
 on:
   push:
-    branches-ignore:
-      - 'generated'
-      - 'codegen/**'
-      - 'integrated/**'
-      - 'stl-preview-head/**'
-      - 'stl-preview-base/**'
+    branches:
+      - '**'
+      - '!integrated/**'
+      - '!stl-preview-head/**'
+      - '!stl-preview-base/**'
+      - '!generated'
+      - '!codegen/**'
+      - 'codegen/stl/**'
   pull_request:
     branches-ignore:
       - 'stl-preview-head/**'
@@ -17,7 +19,7 @@ jobs:
     timeout-minutes: 10
     name: lint
     runs-on: ${{ github.repository == 'stainless-sdks/parallel-sdk-python' && 'depot-ubuntu-24.04' || 'ubuntu-latest' }}
-    if: github.event_name == 'push' || github.event.pull_request.head.repo.fork
+    if: (github.event_name == 'push' || github.event.pull_request.head.repo.fork) && (github.event_name != 'push' || github.event.head_commit.message != 'codegen metadata')
     steps:
       - uses: actions/checkout@v6
 
@@ -36,7 +38,7 @@ jobs:
         run: ./scripts/lint
 
   build:
-    if: github.event_name == 'push' || github.event.pull_request.head.repo.fork
+    if: (github.event_name == 'push' || github.event.pull_request.head.repo.fork) && (github.event_name != 'push' || github.event.head_commit.message != 'codegen metadata')
     timeout-minutes: 10
     name: build
     permissions:
diff --git a/.gitignore b/.gitignore
index 95ceb18..3824f4c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,5 @@
 .prism.log
+.stdy.log
 _dev
 
 __pycache__
diff --git a/.release-please-manifest.json b/.release-please-manifest.json
index 980ea05..2aca35a 100644
--- a/.release-please-manifest.json
+++ b/.release-please-manifest.json
@@ -1,3 +1,3 @@
 {
-  ".": "0.4.2"
+  ".": "0.5.0"
 }
\ No newline at end of file
diff --git a/CHANGELOG.md b/CHANGELOG.md
index a52aa9d..8834f1e 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,38 @@
 # Changelog
 
+## 0.5.0 (2026-04-01)
+
+Full Changelog: [v0.4.2...v0.5.0](https://github.com/parallel-web/parallel-sdk-python/compare/v0.4.2...v0.5.0)
+
+### Features
+
+* **internal:** implement indices array format for query and form serialization ([3df5972](https://github.com/parallel-web/parallel-sdk-python/commit/3df5972e34c9aa1709eabc4eb5b8cbbc0adccae2))
+
+
+### Bug Fixes
+
+* **deps:** bump minimum typing-extensions version ([964a46d](https://github.com/parallel-web/parallel-sdk-python/commit/964a46ddfc9ead64e4105e42192a780bc91716b0))
+* **pydantic:** do not pass `by_alias` unless set ([f0793c1](https://github.com/parallel-web/parallel-sdk-python/commit/f0793c171465dd57d0fbf82a3bb2281d046f500e))
+* sanitize endpoint path params ([5931597](https://github.com/parallel-web/parallel-sdk-python/commit/59315972d27246485be5cb52671aecaa3aa46253))
+
+
+### Chores
+
+* **ci:** skip lint on metadata-only changes ([403448c](https://github.com/parallel-web/parallel-sdk-python/commit/403448c7760e70fe0f4b3998a20f048910e91cd6))
+* **internal:** tweak CI branches ([014c802](https://github.com/parallel-web/parallel-sdk-python/commit/014c80287318df0db6207df1579be00c4717f24d))
+* **internal:** update gitignore ([1f4f6b0](https://github.com/parallel-web/parallel-sdk-python/commit/1f4f6b0e5d2a46e1a5457879e937ea5aa551073c))
+* **tests:** bump steady to v0.19.4 ([ebee2e7](https://github.com/parallel-web/parallel-sdk-python/commit/ebee2e761e2a8587cc6aa4c2decfd6310092b039))
+* **tests:** bump steady to v0.19.5 ([2774099](https://github.com/parallel-web/parallel-sdk-python/commit/2774099f753bc0826e9c6b6e9fbb40d4e72e3405))
+* **tests:** bump steady to v0.19.6 ([8e3ee3d](https://github.com/parallel-web/parallel-sdk-python/commit/8e3ee3d04dc149b2bcedb0e4acd92474fafd8d05))
+* **tests:** bump steady to v0.19.7 ([4bcf12e](https://github.com/parallel-web/parallel-sdk-python/commit/4bcf12e670b7997e23ade3d991711fe1ef741e35))
+* **tests:** bump steady to v0.20.1 ([d82ce60](https://github.com/parallel-web/parallel-sdk-python/commit/d82ce601c5687553b0e96990e418289fd8a14e00))
+* **tests:** bump steady to v0.20.2 ([746ca39](https://github.com/parallel-web/parallel-sdk-python/commit/746ca39c749f899dc9137a2f9be5de9aa39210c6))
+
+
+### Refactors
+
+* **tests:** switch from prism to steady ([032745e](https://github.com/parallel-web/parallel-sdk-python/commit/032745ea1a03b3d2516b789a28a3c8b8034660d8))
+
 ## 0.4.2 (2026-03-09)
 
 Full Changelog: [v0.4.1...v0.4.2](https://github.com/parallel-web/parallel-sdk-python/compare/v0.4.1...v0.4.2)
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 3276e79..1ecb266 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -85,7 +85,7 @@ $ pip install ./path-to-wheel-file.whl
 
 ## Running tests
 
-Most tests require you to [set up a mock server](https://github.com/stoplightio/prism) against the OpenAPI spec to run the tests.
+Most tests require you to [set up a mock server](https://github.com/dgellow/steady) against the OpenAPI spec to run the tests.
 
 ```sh
 $ ./scripts/mock
diff --git a/pyproject.toml b/pyproject.toml
index 12646c4..b55996c 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,6 @@
 [project]
 name = "parallel-web"
-version = "0.4.2"
+version = "0.5.0"
 description = "The official Python library for the Parallel API"
 dynamic = ["readme"]
 license = "MIT"
@@ -11,7 +11,7 @@ authors = [
 dependencies = [
   "httpx>=0.23.0, <1",
   "pydantic>=1.9.0, <3",
-  "typing-extensions>=4.10, <5",
+  "typing-extensions>=4.14, <5",
   "anyio>=3.5.0, <5",
   "distro>=1.7.0, <2",
   "sniffio",
diff --git a/scripts/mock b/scripts/mock
index bcf3b39..5cd7c15 100755
--- a/scripts/mock
+++ b/scripts/mock
@@ -19,34 +19,34 @@ fi
 
 echo "==> Starting mock server with URL ${URL}"
 
-# Run prism mock on the given spec
+# Run steady mock on the given spec
 if [ "$1" == "--daemon" ]; then
   # Pre-install the package so the download doesn't eat into the startup timeout
-  npm exec --package=@stainless-api/prism-cli@5.15.0 -- prism --version
+  npm exec --package=@stdy/cli@0.20.2 -- steady --version
 
-  npm exec --package=@stainless-api/prism-cli@5.15.0 -- prism mock "$URL" &> .prism.log &
+  npm exec --package=@stdy/cli@0.20.2 -- steady --host 127.0.0.1 -p 4010 --validator-query-array-format=comma --validator-form-array-format=comma --validator-query-object-format=brackets --validator-form-object-format=brackets "$URL" &> .stdy.log &
 
-  # Wait for server to come online (max 30s)
+  # Wait for server to come online via health endpoint (max 30s)
   echo -n "Waiting for server"
   attempts=0
-  while ! grep -q "✖  fatal\|Prism is listening" ".prism.log" ; do
+  while ! curl --silent --fail "http://127.0.0.1:4010/_x-steady/health" >/dev/null 2>&1; do
+    if ! kill -0 $! 2>/dev/null; then
+      echo
+      cat .stdy.log
+      exit 1
+    fi
     attempts=$((attempts + 1))
     if [ "$attempts" -ge 300 ]; then
       echo
-      echo "Timed out waiting for Prism server to start"
-      cat .prism.log
+      echo "Timed out waiting for Steady server to start"
+      cat .stdy.log
       exit 1
     fi
     echo -n "."
     sleep 0.1
   done
 
-  if grep -q "✖  fatal" ".prism.log"; then
-    cat .prism.log
-    exit 1
-  fi
-
   echo
 else
-  npm exec --package=@stainless-api/prism-cli@5.15.0 -- prism mock "$URL"
+  npm exec --package=@stdy/cli@0.20.2 -- steady --host 127.0.0.1 -p 4010 --validator-query-array-format=comma --validator-form-array-format=comma --validator-query-object-format=brackets --validator-form-object-format=brackets "$URL"
 fi
diff --git a/scripts/test b/scripts/test
index dbeda2d..b8143aa 100755
--- a/scripts/test
+++ b/scripts/test
@@ -9,8 +9,8 @@ GREEN='\033[0;32m'
 YELLOW='\033[0;33m'
 NC='\033[0m' # No Color
 
-function prism_is_running() {
-  curl --silent "http://localhost:4010" >/dev/null 2>&1
+function steady_is_running() {
+  curl --silent "http://127.0.0.1:4010/_x-steady/health" >/dev/null 2>&1
 }
 
 kill_server_on_port() {
@@ -25,7 +25,7 @@ function is_overriding_api_base_url() {
   [ -n "$TEST_API_BASE_URL" ]
 }
 
-if ! is_overriding_api_base_url && ! prism_is_running ; then
+if ! is_overriding_api_base_url && ! steady_is_running ; then
   # When we exit this script, make sure to kill the background mock server process
   trap 'kill_server_on_port 4010' EXIT
 
@@ -36,19 +36,19 @@ fi
 if is_overriding_api_base_url ; then
   echo -e "${GREEN}✔ Running tests against ${TEST_API_BASE_URL}${NC}"
   echo
-elif ! prism_is_running ; then
-  echo -e "${RED}ERROR:${NC} The test suite will not run without a mock Prism server"
+elif ! steady_is_running ; then
+  echo -e "${RED}ERROR:${NC} The test suite will not run without a mock Steady server"
   echo -e "running against your OpenAPI spec."
   echo
   echo -e "To run the server, pass in the path or url of your OpenAPI"
-  echo -e "spec to the prism command:"
+  echo -e "spec to the steady command:"
   echo
-  echo -e "  \$ ${YELLOW}npm exec --package=@stainless-api/prism-cli@5.15.0 -- prism mock path/to/your.openapi.yml${NC}"
+  echo -e "  \$ ${YELLOW}npm exec --package=@stdy/cli@0.20.2 -- steady path/to/your.openapi.yml --host 127.0.0.1 -p 4010 --validator-query-array-format=comma --validator-form-array-format=comma --validator-query-object-format=brackets --validator-form-object-format=brackets${NC}"
   echo
 
   exit 1
 else
-  echo -e "${GREEN}✔ Mock prism server is running with your OpenAPI spec${NC}"
+  echo -e "${GREEN}✔ Mock steady server is running with your OpenAPI spec${NC}"
   echo
 fi
 
diff --git a/src/parallel/_compat.py b/src/parallel/_compat.py
index 020ffeb..340c91a 100644
--- a/src/parallel/_compat.py
+++ b/src/parallel/_compat.py
@@ -2,7 +2,7 @@
 
 from typing import TYPE_CHECKING, Any, Union, Generic, TypeVar, Callable, cast, overload
 from datetime import date, datetime
-from typing_extensions import Self, Literal
+from typing_extensions import Self, Literal, TypedDict
 
 import pydantic
 from pydantic.fields import FieldInfo
@@ -131,6 +131,10 @@ def model_json(model: pydantic.BaseModel, *, indent: int | None = None) -> str:
     return model.model_dump_json(indent=indent)
 
 
+class _ModelDumpKwargs(TypedDict, total=False):
+    by_alias: bool
+
+
 def model_dump(
     model: pydantic.BaseModel,
     *,
@@ -142,6 +146,9 @@ def model_dump(
     by_alias: bool | None = None,
 ) -> dict[str, Any]:
     if (not PYDANTIC_V1) or hasattr(model, "model_dump"):
+        kwargs: _ModelDumpKwargs = {}
+        if by_alias is not None:
+            kwargs["by_alias"] = by_alias
         return model.model_dump(
             mode=mode,
             exclude=exclude,
@@ -149,7 +156,7 @@ def model_dump(
             exclude_defaults=exclude_defaults,
             # warnings are not supported in Pydantic v1
             warnings=True if PYDANTIC_V1 else warnings,
-            by_alias=by_alias,
+            **kwargs,
         )
     return cast(
         "dict[str, Any]",
diff --git a/src/parallel/_qs.py b/src/parallel/_qs.py
index ada6fd3..de8c99b 100644
--- a/src/parallel/_qs.py
+++ b/src/parallel/_qs.py
@@ -101,7 +101,10 @@ def _stringify_item(
                     items.extend(self._stringify_item(key, item, opts))
                 return items
             elif array_format == "indices":
-                raise NotImplementedError("The array indices format is not supported yet")
+                items = []
+                for i, item in enumerate(value):
+                    items.extend(self._stringify_item(f"{key}[{i}]", item, opts))
+                return items
             elif array_format == "brackets":
                 items = []
                 key = key + "[]"
diff --git a/src/parallel/_utils/__init__.py b/src/parallel/_utils/__init__.py
index b70d7b2..f1aef8a 100644
--- a/src/parallel/_utils/__init__.py
+++ b/src/parallel/_utils/__init__.py
@@ -1,3 +1,4 @@
+from ._path import path_template as path_template
 from ._sync import asyncify as asyncify
 from ._proxy import LazyProxy as LazyProxy
 from ._utils import (
diff --git a/src/parallel/_utils/_path.py b/src/parallel/_utils/_path.py
new file mode 100644
index 0000000..4d6e1e4
--- /dev/null
+++ b/src/parallel/_utils/_path.py
@@ -0,0 +1,127 @@
+from __future__ import annotations
+
+import re
+from typing import (
+    Any,
+    Mapping,
+    Callable,
+)
+from urllib.parse import quote
+
+# Matches '.' or '..' where each dot is either literal or percent-encoded (%2e / %2E).
+_DOT_SEGMENT_RE = re.compile(r"^(?:\.|%2[eE]){1,2}$")
+
+_PLACEHOLDER_RE = re.compile(r"\{(\w+)\}")
+
+
+def _quote_path_segment_part(value: str) -> str:
+    """Percent-encode `value` for use in a URI path segment.
+
+    Considers characters not in `pchar` set from RFC 3986 §3.3 to be unsafe.
+    https://datatracker.ietf.org/doc/html/rfc3986#section-3.3
+    """
+    # quote() already treats unreserved characters (letters, digits, and -._~)
+    # as safe, so we only need to add sub-delims, ':', and '@'.
+    # Notably, unlike the default `safe` for quote(), / is unsafe and must be quoted.
+    return quote(value, safe="!$&'()*+,;=:@")
+
+
+def _quote_query_part(value: str) -> str:
+    """Percent-encode `value` for use in a URI query string.
+
+    Considers &, = and characters not in `query` set from RFC 3986 §3.4 to be unsafe.
+    https://datatracker.ietf.org/doc/html/rfc3986#section-3.4
+    """
+    return quote(value, safe="!$'()*+,;:@/?")
+
+
+def _quote_fragment_part(value: str) -> str:
+    """Percent-encode `value` for use in a URI fragment.
+
+    Considers characters not in `fragment` set from RFC 3986 §3.5 to be unsafe.
+    https://datatracker.ietf.org/doc/html/rfc3986#section-3.5
+    """
+    return quote(value, safe="!$&'()*+,;=:@/?")
+
+
+def _interpolate(
+    template: str,
+    values: Mapping[str, Any],
+    quoter: Callable[[str], str],
+) -> str:
+    """Replace {name} placeholders in `template`, quoting each value with `quoter`.
+
+    Placeholder names are looked up in `values`.
+
+    Raises:
+        KeyError: If a placeholder is not found in `values`.
+    """
+    # re.split with a capturing group returns alternating
+    # [text, name, text, name, ..., text] elements.
+    parts = _PLACEHOLDER_RE.split(template)
+
+    for i in range(1, len(parts), 2):
+        name = parts[i]
+        if name not in values:
+            raise KeyError(f"a value for placeholder {{{name}}} was not provided")
+        val = values[name]
+        if val is None:
+            parts[i] = "null"
+        elif isinstance(val, bool):
+            parts[i] = "true" if val else "false"
+        else:
+            parts[i] = quoter(str(values[name]))
+
+    return "".join(parts)
+
+
+def path_template(template: str, /, **kwargs: Any) -> str:
+    """Interpolate {name} placeholders in `template` from keyword arguments.
+
+    Args:
+        template: The template string containing {name} placeholders.
+        **kwargs: Keyword arguments to interpolate into the template.
+
+    Returns:
+        The template with placeholders interpolated and percent-encoded.
+
+        Safe characters for percent-encoding are dependent on the URI component.
+        Placeholders in path and fragment portions are percent-encoded where the `segment`
+        and `fragment` sets from RFC 3986 respectively are considered safe.
+        Placeholders in the query portion are percent-encoded where the `query` set from
+        RFC 3986 §3.3 is considered safe except for = and & characters.
+
+    Raises:
+        KeyError: If a placeholder is not found in `kwargs`.
+        ValueError: If resulting path contains /./ or /../ segments (including percent-encoded dot-segments).
+    """
+    # Split the template into path, query, and fragment portions.
+    fragment_template: str | None = None
+    query_template: str | None = None
+
+    rest = template
+    if "#" in rest:
+        rest, fragment_template = rest.split("#", 1)
+    if "?" in rest:
+        rest, query_template = rest.split("?", 1)
+    path_template = rest
+
+    # Interpolate each portion with the appropriate quoting rules.
+    path_result = _interpolate(path_template, kwargs, _quote_path_segment_part)
+
+    # Reject dot-segments (. and ..) in the final assembled path.  The check
+    # runs after interpolation so that adjacent placeholders or a mix of static
+    # text and placeholders that together form a dot-segment are caught.
+    # Also reject percent-encoded dot-segments to protect against incorrectly
+    # implemented normalization in servers/proxies.
+    for segment in path_result.split("/"):
+        if _DOT_SEGMENT_RE.match(segment):
+            raise ValueError(f"Constructed path {path_result!r} contains dot-segment {segment!r} which is not allowed")
+
+    result = path_result
+    if query_template is not None:
+        result += "?" + _interpolate(query_template, kwargs, _quote_query_part)
+    if fragment_template is not None:
+        result += "#" + _interpolate(fragment_template, kwargs, _quote_fragment_part)
+
+    return result
diff --git a/src/parallel/_version.py b/src/parallel/_version.py
index 1b80dfe..e3da38f 100644
--- a/src/parallel/_version.py
+++ b/src/parallel/_version.py
@@ -1,4 +1,4 @@
 # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
 
 __title__ = "parallel"
-__version__ = "0.4.2"  # x-release-please-version
+__version__ = "0.5.0"  # x-release-please-version
diff --git a/src/parallel/resources/beta/findall.py b/src/parallel/resources/beta/findall.py
index ef5ab71..acc3d98 100644
--- a/src/parallel/resources/beta/findall.py
+++ b/src/parallel/resources/beta/findall.py
@@ -9,7 +9,7 @@
 import httpx
 
 from ..._types import Body, Omit, Query, Headers, NotGiven, omit, not_given
-from ..._utils import is_given, maybe_transform, strip_not_given, async_maybe_transform
+from ..._utils import is_given, path_template, maybe_transform, strip_not_given, async_maybe_transform
 from ..._compat import cached_property
 from ..._resource import SyncAPIResource, AsyncAPIResource
 from ..._response import (
@@ -209,7 +209,7 @@ def retrieve(
         }
         extra_headers = {"parallel-beta": "findall-2025-09-15", **(extra_headers or {})}
         return self._get(
-            f"/v1beta/findall/runs/{findall_id}",
+            path_template("/v1beta/findall/runs/{findall_id}", findall_id=findall_id),
             options=make_request_options(
                 extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
             ),
@@ -256,7 +256,7 @@ def cancel(
         }
         extra_headers = {"parallel-beta": "findall-2025-09-15", **(extra_headers or {})}
         return self._post(
-            f"/v1beta/findall/runs/{findall_id}/cancel",
+            path_template("/v1beta/findall/runs/{findall_id}/cancel", findall_id=findall_id),
             options=make_request_options(
                 extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
             ),
@@ -312,7 +312,7 @@ def enrich(
         }
         extra_headers = {"parallel-beta": "findall-2025-09-15", **(extra_headers or {})}
         return self._post(
-            f"/v1beta/findall/runs/{findall_id}/enrich",
+            path_template("/v1beta/findall/runs/{findall_id}/enrich", findall_id=findall_id),
             body=maybe_transform(
                 {
                     "output_schema": output_schema,
@@ -375,7 +375,7 @@ def events(
         }
         extra_headers = {"parallel-beta": "findall-2025-09-15", **(extra_headers or {})}
         return self._get(
-            f"/v1beta/findall/runs/{findall_id}/events",
+            path_template("/v1beta/findall/runs/{findall_id}/events", findall_id=findall_id),
             options=make_request_options(
                 extra_headers=extra_headers,
                 extra_query=extra_query,
@@ -439,7 +439,7 @@ def extend(
         }
         extra_headers = {"parallel-beta": "findall-2025-09-15", **(extra_headers or {})}
         return self._post(
-            f"/v1beta/findall/runs/{findall_id}/extend",
+            path_template("/v1beta/findall/runs/{findall_id}/extend", findall_id=findall_id),
             body=maybe_transform(
                 {"additional_match_limit": additional_match_limit}, findall_extend_params.FindAllExtendParams
             ),
@@ -542,7 +542,7 @@ def result(
         }
         extra_headers = {"parallel-beta": "findall-2025-09-15", **(extra_headers or {})}
         return self._get(
-            f"/v1beta/findall/runs/{findall_id}/result",
+            path_template("/v1beta/findall/runs/{findall_id}/result", findall_id=findall_id),
             options=make_request_options(
                 extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
             ),
@@ -589,7 +589,7 @@ def schema(
         }
         extra_headers = {"parallel-beta": "findall-2025-09-15", **(extra_headers or {})}
         return self._get(
-            f"/v1beta/findall/runs/{findall_id}/schema",
+            path_template("/v1beta/findall/runs/{findall_id}/schema", findall_id=findall_id),
             options=make_request_options(
                 extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
             ),
@@ -754,7 +754,7 @@ async def retrieve(
         }
         extra_headers = {"parallel-beta": "findall-2025-09-15", **(extra_headers or {})}
         return await self._get(
-            f"/v1beta/findall/runs/{findall_id}",
+            path_template("/v1beta/findall/runs/{findall_id}", findall_id=findall_id),
             options=make_request_options(
                 extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
             ),
@@ -801,7 +801,7 @@ async def cancel(
         }
         extra_headers = {"parallel-beta": "findall-2025-09-15", **(extra_headers or {})}
         return await self._post(
-            f"/v1beta/findall/runs/{findall_id}/cancel",
+            path_template("/v1beta/findall/runs/{findall_id}/cancel", findall_id=findall_id),
             options=make_request_options(
                 extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
             ),
@@ -857,7 +857,7 @@ async def enrich(
         }
         extra_headers = {"parallel-beta": "findall-2025-09-15", **(extra_headers or {})}
         return await self._post(
-            f"/v1beta/findall/runs/{findall_id}/enrich",
+            path_template("/v1beta/findall/runs/{findall_id}/enrich", findall_id=findall_id),
             body=await async_maybe_transform(
                 {
                     "output_schema": output_schema,
@@ -920,7 +920,7 @@ async def events(
         }
         extra_headers = {"parallel-beta": "findall-2025-09-15", **(extra_headers or {})}
         return await self._get(
-            f"/v1beta/findall/runs/{findall_id}/events",
+            path_template("/v1beta/findall/runs/{findall_id}/events", findall_id=findall_id),
             options=make_request_options(
                 extra_headers=extra_headers,
                 extra_query=extra_query,
@@ -984,7 +984,7 @@ async def extend(
         }
         extra_headers = {"parallel-beta": "findall-2025-09-15", **(extra_headers or {})}
         return await self._post(
-            f"/v1beta/findall/runs/{findall_id}/extend",
+            path_template("/v1beta/findall/runs/{findall_id}/extend", findall_id=findall_id),
             body=await async_maybe_transform(
                 {"additional_match_limit": additional_match_limit}, findall_extend_params.FindAllExtendParams
             ),
@@ -1087,7 +1087,7 @@ async def result(
         }
         extra_headers = {"parallel-beta": "findall-2025-09-15", **(extra_headers or {})}
         return await self._get(
-            f"/v1beta/findall/runs/{findall_id}/result",
+            path_template("/v1beta/findall/runs/{findall_id}/result", findall_id=findall_id),
             options=make_request_options(
                 extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
             ),
@@ -1134,7 +1134,7 @@ async def schema(
         }
         extra_headers = {"parallel-beta": "findall-2025-09-15", **(extra_headers or {})}
         return await self._get(
-            f"/v1beta/findall/runs/{findall_id}/schema",
+            path_template("/v1beta/findall/runs/{findall_id}/schema", findall_id=findall_id),
             options=make_request_options(
                 extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
             ),
diff --git a/src/parallel/resources/beta/task_group.py b/src/parallel/resources/beta/task_group.py
index b15eab7..54f8cb1 100644
--- a/src/parallel/resources/beta/task_group.py
+++ b/src/parallel/resources/beta/task_group.py
@@ -9,7 +9,7 @@
 import httpx
 
 from ..._types import Body, Omit, Query, Headers, NotGiven, omit, not_given
-from ..._utils import is_given, maybe_transform, strip_not_given, async_maybe_transform
+from ..._utils import is_given, path_template, maybe_transform, strip_not_given, async_maybe_transform
 from ..._compat import cached_property
 from ..._resource import SyncAPIResource, AsyncAPIResource
 from ..._response import (
@@ -129,7 +129,7 @@ def retrieve(
             raise ValueError(f"Expected a non-empty value for `task_group_id` but received {task_group_id!r}")
         extra_headers = {"parallel-beta": "search-extract-2025-10-10", **(extra_headers or {})}
         return self._get(
-            f"/v1beta/tasks/groups/{task_group_id}",
+            path_template("/v1beta/tasks/groups/{task_group_id}", task_group_id=task_group_id),
             options=make_request_options(
                 extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
             ),
@@ -188,7 +188,7 @@ def add_runs(
         }
         extra_headers = {"parallel-beta": "search-extract-2025-10-10", **(extra_headers or {})}
         return self._post(
-            f"/v1beta/tasks/groups/{task_group_id}/runs",
+            path_template("/v1beta/tasks/groups/{task_group_id}/runs", task_group_id=task_group_id),
             body=maybe_transform(
                 {
                     "inputs": inputs,
@@ -235,7 +235,7 @@ def events(
         extra_headers = {"Accept": "text/event-stream", **(extra_headers or {})}
         extra_headers = {"parallel-beta": "search-extract-2025-10-10", **(extra_headers or {})}
         return self._get(
-            f"/v1beta/tasks/groups/{task_group_id}/events",
+            path_template("/v1beta/tasks/groups/{task_group_id}/events", task_group_id=task_group_id),
             options=make_request_options(
                 extra_headers=extra_headers,
                 extra_query=extra_query,
@@ -300,7 +300,7 @@ def get_runs(
         extra_headers = {"Accept": "text/event-stream", **(extra_headers or {})}
         extra_headers = {"parallel-beta": "search-extract-2025-10-10", **(extra_headers or {})}
         return self._get(
-            f"/v1beta/tasks/groups/{task_group_id}/runs",
+            path_template("/v1beta/tasks/groups/{task_group_id}/runs", task_group_id=task_group_id),
             options=make_request_options(
                 extra_headers=extra_headers,
                 extra_query=extra_query,
@@ -416,7 +416,7 @@ async def retrieve(
             raise ValueError(f"Expected a non-empty value for `task_group_id` but received {task_group_id!r}")
         extra_headers = {"parallel-beta": "search-extract-2025-10-10", **(extra_headers or {})}
         return await self._get(
-            f"/v1beta/tasks/groups/{task_group_id}",
+            path_template("/v1beta/tasks/groups/{task_group_id}", task_group_id=task_group_id),
             options=make_request_options(
                 extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
             ),
@@ -475,7 +475,7 @@ async def add_runs(
         }
         extra_headers = {"parallel-beta": "search-extract-2025-10-10", **(extra_headers or {})}
         return await self._post(
-            f"/v1beta/tasks/groups/{task_group_id}/runs",
+            path_template("/v1beta/tasks/groups/{task_group_id}/runs", task_group_id=task_group_id),
             body=await async_maybe_transform(
                 {
                     "inputs": inputs,
@@ -522,7 +522,7 @@ async def events(
         extra_headers = {"Accept": "text/event-stream", **(extra_headers or {})}
         extra_headers = {"parallel-beta": "search-extract-2025-10-10", **(extra_headers or {})}
         return await self._get(
-            f"/v1beta/tasks/groups/{task_group_id}/events",
+            path_template("/v1beta/tasks/groups/{task_group_id}/events", task_group_id=task_group_id),
             options=make_request_options(
                 extra_headers=extra_headers,
                 extra_query=extra_query,
@@ -587,7 +587,7 @@ async def get_runs(
         extra_headers = {"Accept": "text/event-stream", **(extra_headers or {})}
         extra_headers = {"parallel-beta": "search-extract-2025-10-10", **(extra_headers or {})}
         return await self._get(
-            f"/v1beta/tasks/groups/{task_group_id}/runs",
+            path_template("/v1beta/tasks/groups/{task_group_id}/runs", task_group_id=task_group_id),
             options=make_request_options(
                 extra_headers=extra_headers,
                 extra_query=extra_query,
diff --git a/src/parallel/resources/beta/task_run.py b/src/parallel/resources/beta/task_run.py
index 3fb567c..b19286a 100644
--- a/src/parallel/resources/beta/task_run.py
+++ b/src/parallel/resources/beta/task_run.py
@@ -8,7 +8,7 @@
 import httpx
 
 from ..._types import Body, Omit, Query, Headers, NotGiven, omit, not_given
-from ..._utils import is_given, maybe_transform, strip_not_given, async_maybe_transform
+from ..._utils import is_given, path_template, maybe_transform, strip_not_given, async_maybe_transform
 from ..._compat import cached_property
 from ..._resource import SyncAPIResource, AsyncAPIResource
 from ..._response import (
@@ -198,7 +198,7 @@ def events(
         extra_headers = {"Accept": "text/event-stream", **(extra_headers or {})}
         extra_headers = {"parallel-beta": "search-extract-2025-10-10", **(extra_headers or {})}
         return self._get(
-            f"/v1beta/tasks/runs/{run_id}/events",
+            path_template("/v1beta/tasks/runs/{run_id}/events", run_id=run_id),
             options=make_request_options(
                 extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
             ),
@@ -248,7 +248,7 @@ def result(
         }
         extra_headers = {"parallel-beta": "search-extract-2025-10-10", **(extra_headers or {})}
         return self._get(
-            f"/v1/tasks/runs/{run_id}/result?beta=true",
+            path_template("/v1/tasks/runs/{run_id}/result?beta=true", run_id=run_id),
             options=make_request_options(
                 extra_headers=extra_headers,
                 extra_query=extra_query,
@@ -426,7 +426,7 @@ async def events(
         extra_headers = {"Accept": "text/event-stream", **(extra_headers or {})}
         extra_headers = {"parallel-beta": "search-extract-2025-10-10", **(extra_headers or {})}
         return await self._get(
-            f"/v1beta/tasks/runs/{run_id}/events",
+            path_template("/v1beta/tasks/runs/{run_id}/events", run_id=run_id),
             options=make_request_options(
                 extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
             ),
@@ -476,7 +476,7 @@ async def result(
         }
         extra_headers = {"parallel-beta": "search-extract-2025-10-10", **(extra_headers or {})}
         return await self._get(
-            f"/v1/tasks/runs/{run_id}/result?beta=true",
+            path_template("/v1/tasks/runs/{run_id}/result?beta=true", run_id=run_id),
             options=make_request_options(
                 extra_headers=extra_headers,
                 extra_query=extra_query,
diff --git a/src/parallel/resources/task_run.py b/src/parallel/resources/task_run.py
index eb0df13..af330cd 100644
--- a/src/parallel/resources/task_run.py
+++ b/src/parallel/resources/task_run.py
@@ -11,7 +11,7 @@
 
 from ..types import task_run_create_params, task_run_result_params
 from .._types import Body, Omit, Query, Headers, NotGiven, omit, not_given
-from .._utils import maybe_transform, async_maybe_transform
+from .._utils import path_template, maybe_transform, async_maybe_transform
 from .._compat import cached_property
 from .._resource import SyncAPIResource, AsyncAPIResource
 from .._response import (
@@ -161,7 +161,7 @@ def retrieve(
         if not run_id:
             raise ValueError(f"Expected a non-empty value for `run_id` but received {run_id!r}")
         return self._get(
-            f"/v1/tasks/runs/{run_id}",
+            path_template("/v1/tasks/runs/{run_id}", run_id=run_id),
             options=make_request_options(
                 extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
             ),
@@ -195,7 +195,7 @@ def result(
         if not run_id:
             raise ValueError(f"Expected a non-empty value for `run_id` but received {run_id!r}")
         return self._get(
-            f"/v1/tasks/runs/{run_id}/result",
+            path_template("/v1/tasks/runs/{run_id}/result", run_id=run_id),
             options=make_request_options(
                 extra_headers=extra_headers,
                 extra_query=extra_query,
@@ -460,7 +460,7 @@ async def retrieve(
         if not run_id:
             raise ValueError(f"Expected a non-empty value for `run_id` but received {run_id!r}")
         return await self._get(
-            f"/v1/tasks/runs/{run_id}",
+            path_template("/v1/tasks/runs/{run_id}", run_id=run_id),
             options=make_request_options(
                 extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
             ),
@@ -494,7 +494,7 @@ async def result(
         if not run_id:
             raise ValueError(f"Expected a non-empty value for `run_id` but received {run_id!r}")
         return await self._get(
-            f"/v1/tasks/runs/{run_id}/result",
+            path_template("/v1/tasks/runs/{run_id}/result", run_id=run_id),
             options=make_request_options(
                 extra_headers=extra_headers,
                 extra_query=extra_query,
diff --git a/tests/api_resources/beta/test_findall.py b/tests/api_resources/beta/test_findall.py
index 6ee829d..18996a9 100644
--- a/tests/api_resources/beta/test_findall.py
+++ b/tests/api_resources/beta/test_findall.py
@@ -296,7 +296,6 @@ def test_path_params_enrich(self, client: Parallel) -> None:
                 },
             )
 
-    @pytest.mark.skip(reason="Mock server doesn't support text/event-stream responses")
     @parametrize
     def test_method_events(self, client: Parallel) -> None:
         findall_stream = client.beta.findall.events(
@@ -304,7 +303,6 @@ def test_method_events(self, client: Parallel) -> None:
         )
         findall_stream.response.close()
 
-    @pytest.mark.skip(reason="Mock server doesn't support text/event-stream responses")
     @parametrize
     def test_method_events_with_all_params(self, client: Parallel) -> None:
         findall_stream = client.beta.findall.events(
@@ -315,7 +313,6 @@ def test_method_events_with_all_params(self, client: Parallel) -> None:
         )
         findall_stream.response.close()
 
-    @pytest.mark.skip(reason="Mock server doesn't support text/event-stream responses")
     @parametrize
     def test_raw_response_events(self, client: Parallel) -> None:
         response = client.beta.findall.with_raw_response.events(
@@ -326,7 +323,6 @@ def test_raw_response_events(self, client: Parallel) -> None:
         stream = response.parse()
         stream.close()
 
-    @pytest.mark.skip(reason="Mock server doesn't support text/event-stream responses")
     @parametrize
     def test_streaming_response_events(self, client: Parallel) -> None:
         with client.beta.findall.with_streaming_response.events(
@@ -340,7 +336,6 @@ def test_streaming_response_events(self, client: Parallel) -> None:
 
         assert cast(Any, response.is_closed) is True
 
-    @pytest.mark.skip(reason="Mock server doesn't support text/event-stream responses")
     @parametrize
     def test_path_params_events(self, client: Parallel) -> None:
         with pytest.raises(ValueError, match=r"Expected a non-empty value for `findall_id` but received ''"):
@@ -811,7 +806,6 @@ async def test_path_params_enrich(self, async_client: AsyncParallel) -> None:
                 },
             )
 
-    @pytest.mark.skip(reason="Mock server doesn't support text/event-stream responses")
     @parametrize
     async def test_method_events(self, async_client: AsyncParallel) -> None:
         findall_stream = await async_client.beta.findall.events(
@@ -819,7 +813,6 @@ async def test_method_events(self, async_client: AsyncParallel) -> None:
         )
         await findall_stream.response.aclose()
 
-    @pytest.mark.skip(reason="Mock server doesn't support text/event-stream responses")
     @parametrize
     async def test_method_events_with_all_params(self, async_client: AsyncParallel) -> None:
         findall_stream = await async_client.beta.findall.events(
@@ -830,7 +823,6 @@ async def test_method_events_with_all_params(self, async_client: AsyncParallel)
         )
         await findall_stream.response.aclose()
 
-    @pytest.mark.skip(reason="Mock server doesn't support text/event-stream responses")
     @parametrize
     async def test_raw_response_events(self, async_client: AsyncParallel) -> None:
         response = await async_client.beta.findall.with_raw_response.events(
@@ -841,7 +833,6 @@ async def test_raw_response_events(self, async_client: AsyncParallel) -> None:
         stream = await response.parse()
         await stream.close()
 
-    @pytest.mark.skip(reason="Mock server doesn't support text/event-stream responses")
     @parametrize
     async def test_streaming_response_events(self, async_client: AsyncParallel) -> None:
         async with async_client.beta.findall.with_streaming_response.events(
@@ -855,7 +846,6 @@ async def test_streaming_response_events(self, async_client: AsyncParallel) -> N
 
         assert cast(Any, response.is_closed) is True
 
-    @pytest.mark.skip(reason="Mock server doesn't support text/event-stream responses")
     @parametrize
     async def test_path_params_events(self, async_client: AsyncParallel) -> None:
         with pytest.raises(ValueError, match=r"Expected a non-empty value for `findall_id` but received ''"):
diff --git a/tests/api_resources/beta/test_task_group.py b/tests/api_resources/beta/test_task_group.py
index cc200ce..f186596 100644
--- a/tests/api_resources/beta/test_task_group.py
+++ b/tests/api_resources/beta/test_task_group.py
@@ -212,7 +212,6 @@ def test_path_params_add_runs(self, client: Parallel) -> None:
                 ],
             )
 
-    @pytest.mark.skip(reason="Mock server doesn't support text/event-stream responses")
     @parametrize
     def test_method_events(self, client: Parallel) -> None:
         task_group_stream = client.beta.task_group.events(
@@ -220,7 +219,6 @@ def test_method_events(self, client: Parallel) -> None:
         )
         task_group_stream.response.close()
 
-    @pytest.mark.skip(reason="Mock server doesn't support text/event-stream responses")
     @parametrize
     def test_method_events_with_all_params(self, client: Parallel) -> None:
         task_group_stream = client.beta.task_group.events(
@@ -230,7 +228,6 @@ def test_method_events_with_all_params(self, client: Parallel) -> None:
         )
         task_group_stream.response.close()
 
-    @pytest.mark.skip(reason="Mock server doesn't support text/event-stream responses")
     @parametrize
     def test_raw_response_events(self, client: Parallel) -> None:
         response = client.beta.task_group.with_raw_response.events(
@@ -241,7 +238,6 @@ def test_raw_response_events(self, client: Parallel) -> None:
         stream = response.parse()
         stream.close()
 
-    @pytest.mark.skip(reason="Mock server doesn't support text/event-stream responses")
     @parametrize
     def test_streaming_response_events(self, client: Parallel) -> None:
         with client.beta.task_group.with_streaming_response.events(
@@ -255,7 +251,6 @@ def test_streaming_response_events(self, client: Parallel) -> None:
 
         assert cast(Any, response.is_closed) is True
 
-    @pytest.mark.skip(reason="Mock server doesn't support text/event-stream responses")
     @parametrize
     def test_path_params_events(self, client: Parallel) -> None:
         with pytest.raises(ValueError, match=r"Expected a non-empty value for `task_group_id` but received ''"):
@@ -263,7 +258,6 @@ def test_path_params_events(self, client: Parallel) -> None:
                 task_group_id="",
             )
 
-    @pytest.mark.skip(reason="Mock server doesn't support text/event-stream responses")
     @parametrize
     def test_method_get_runs(self, client: Parallel) -> None:
         task_group_stream = client.beta.task_group.get_runs(
@@ -271,7 +265,6 @@ def test_method_get_runs(self, client: Parallel) -> None:
         )
         task_group_stream.response.close()
 
-    @pytest.mark.skip(reason="Mock server doesn't support text/event-stream responses")
     @parametrize
     def test_method_get_runs_with_all_params(self, client: Parallel) -> None:
         task_group_stream = client.beta.task_group.get_runs(
@@ -283,7 +276,6 @@ def test_method_get_runs_with_all_params(self, client: Parallel) -> None:
         )
         task_group_stream.response.close()
 
-    @pytest.mark.skip(reason="Mock server doesn't support text/event-stream responses")
     @parametrize
     def test_raw_response_get_runs(self, client: Parallel) -> None:
         response = client.beta.task_group.with_raw_response.get_runs(
@@ -294,7 +286,6 @@ def test_raw_response_get_runs(self, client: Parallel) -> None:
         stream = response.parse()
         stream.close()
 
-    @pytest.mark.skip(reason="Mock server doesn't support text/event-stream responses")
     @parametrize
     def test_streaming_response_get_runs(self, client: Parallel) -> None:
         with client.beta.task_group.with_streaming_response.get_runs(
@@ -308,7 +299,6 @@ def test_streaming_response_get_runs(self, client: Parallel) -> None:
 
         assert cast(Any, response.is_closed) is True
 
-    @pytest.mark.skip(reason="Mock server doesn't support text/event-stream responses")
     @parametrize
     def test_path_params_get_runs(self, client: Parallel) -> None:
         with pytest.raises(ValueError, match=r"Expected a non-empty value for `task_group_id` but received ''"):
@@ -513,7 +503,6 @@ async def test_path_params_add_runs(self, async_client: AsyncParallel) -> None:
                 ],
             )
 
-    @pytest.mark.skip(reason="Mock server doesn't support text/event-stream responses")
     @parametrize
     async def test_method_events(self, async_client: AsyncParallel) -> None:
         task_group_stream = await async_client.beta.task_group.events(
@@ -521,7 +510,6 @@ async def test_method_events(self, async_client: AsyncParallel) -> None:
         )
         await task_group_stream.response.aclose()
 
-    @pytest.mark.skip(reason="Mock server doesn't support text/event-stream responses")
     @parametrize
     async def test_method_events_with_all_params(self, async_client: AsyncParallel) -> None:
         task_group_stream = await async_client.beta.task_group.events(
@@ -531,7 +519,6 @@ async def test_method_events_with_all_params(self, async_client: AsyncParallel)
         )
         await task_group_stream.response.aclose()
 
-    @pytest.mark.skip(reason="Mock server doesn't support text/event-stream responses")
     @parametrize
     async def test_raw_response_events(self, async_client: AsyncParallel) -> None:
         response = await async_client.beta.task_group.with_raw_response.events(
@@ -542,7 +529,6 @@ async def test_raw_response_events(self, async_client: AsyncParallel) -> None:
         stream = await response.parse()
         await stream.close()
 
-    @pytest.mark.skip(reason="Mock server doesn't support text/event-stream responses")
     @parametrize
     async def test_streaming_response_events(self, async_client: AsyncParallel) -> None:
         async with async_client.beta.task_group.with_streaming_response.events(
@@ -556,7 +542,6 @@ async def test_streaming_response_events(self, async_client: AsyncParallel) -> N
 
         assert cast(Any, response.is_closed) is True
 
-    @pytest.mark.skip(reason="Mock server doesn't support text/event-stream responses")
     @parametrize
     async def test_path_params_events(self, async_client: AsyncParallel) -> None:
         with pytest.raises(ValueError, match=r"Expected a non-empty value for `task_group_id` but received ''"):
@@ -564,7 +549,6 @@ async def test_path_params_events(self, async_client: AsyncParallel) -> None:
                 task_group_id="",
             )
 
-    @pytest.mark.skip(reason="Mock server doesn't support text/event-stream responses")
     @parametrize
     async def test_method_get_runs(self, async_client: AsyncParallel) -> None:
         task_group_stream = await async_client.beta.task_group.get_runs(
@@ -572,7 +556,6 @@ async def test_method_get_runs(self, async_client: AsyncParallel) -> None:
         )
         await task_group_stream.response.aclose()
 
-    @pytest.mark.skip(reason="Mock server doesn't support text/event-stream responses")
     @parametrize
     async def test_method_get_runs_with_all_params(self, async_client: AsyncParallel) -> None:
         task_group_stream = await async_client.beta.task_group.get_runs(
@@ -584,7 +567,6 @@ async def test_method_get_runs_with_all_params(self, async_client: AsyncParallel
         )
         await task_group_stream.response.aclose()
 
-    @pytest.mark.skip(reason="Mock server doesn't support text/event-stream responses")
     @parametrize
     async def test_raw_response_get_runs(self, async_client: AsyncParallel) -> None:
         response = await async_client.beta.task_group.with_raw_response.get_runs(
@@ -595,7 +577,6 @@ async def test_raw_response_get_runs(self, async_client: AsyncParallel) -> None:
         stream = await response.parse()
         await stream.close()
 
-    @pytest.mark.skip(reason="Mock server doesn't support text/event-stream responses")
     @parametrize
     async def test_streaming_response_get_runs(self, async_client: AsyncParallel) -> None:
         async with async_client.beta.task_group.with_streaming_response.get_runs(
@@ -609,7 +590,6 @@ async def test_streaming_response_get_runs(self, async_client: AsyncParallel) ->
 
         assert cast(Any, response.is_closed) is True
 
-    @pytest.mark.skip(reason="Mock server doesn't support text/event-stream responses")
     @parametrize
     async def test_path_params_get_runs(self, async_client: AsyncParallel) -> None:
         with pytest.raises(ValueError, match=r"Expected a non-empty value for `task_group_id` but received ''"):
diff --git a/tests/api_resources/beta/test_task_run.py b/tests/api_resources/beta/test_task_run.py
index 794846d..c28344a 100644
--- a/tests/api_resources/beta/test_task_run.py
+++ b/tests/api_resources/beta/test_task_run.py
@@ -95,7 +95,6 @@ def test_streaming_response_create(self, client: Parallel) -> None:
 
         assert cast(Any, response.is_closed) is True
 
-    @pytest.mark.skip(reason="Mock server doesn't support text/event-stream responses")
     @parametrize
     def test_method_events(self, client: Parallel) -> None:
         task_run_stream = client.beta.task_run.events(
@@ -103,7 +102,6 @@ def test_method_events(self, client: Parallel) -> None:
         )
         task_run_stream.response.close()
 
-    @pytest.mark.skip(reason="Mock server doesn't support text/event-stream responses")
     @parametrize
     def test_raw_response_events(self, client: Parallel) -> None:
         response = client.beta.task_run.with_raw_response.events(
@@ -114,7 +112,6 @@ def test_raw_response_events(self, client: Parallel) -> None:
         stream = response.parse()
         stream.close()
 
-    @pytest.mark.skip(reason="Mock server doesn't support text/event-stream responses")
     @parametrize
     def test_streaming_response_events(self, client: Parallel) -> None:
         with client.beta.task_run.with_streaming_response.events(
@@ -128,7 +125,6 @@ def test_streaming_response_events(self, client: Parallel) -> None:
 
         assert cast(Any, response.is_closed) is True
 
-    @pytest.mark.skip(reason="Mock server doesn't support text/event-stream responses")
     @parametrize
     def test_path_params_events(self, client: Parallel) -> None:
         with pytest.raises(ValueError, match=r"Expected a non-empty value for `run_id` but received ''"):
@@ -265,7 +261,6 @@ async def test_streaming_response_create(self, async_client: AsyncParallel) -> N
 
         assert cast(Any, response.is_closed) is True
 
-    @pytest.mark.skip(reason="Mock server doesn't support text/event-stream responses")
     @parametrize
     async def test_method_events(self, async_client: AsyncParallel) -> None:
         task_run_stream = await async_client.beta.task_run.events(
@@ -273,7 +268,6 @@ async def test_method_events(self, async_client: AsyncParallel) -> None:
         )
         await task_run_stream.response.aclose()
 
-    @pytest.mark.skip(reason="Mock server doesn't support text/event-stream responses")
     @parametrize
     async def test_raw_response_events(self, async_client: AsyncParallel) -> None:
         response = await async_client.beta.task_run.with_raw_response.events(
@@ -284,7 +278,6 @@ async def test_raw_response_events(self, async_client: AsyncParallel) -> None:
         stream = await response.parse()
         await stream.close()
 
-    @pytest.mark.skip(reason="Mock server doesn't support text/event-stream responses")
     @parametrize
     async def test_streaming_response_events(self, async_client: AsyncParallel) -> None:
         async with async_client.beta.task_run.with_streaming_response.events(
@@ -298,7 +291,6 @@ async def test_streaming_response_events(self, async_client: AsyncParallel) -> N
 
         assert cast(Any, response.is_closed) is True
 
-    @pytest.mark.skip(reason="Mock server doesn't support text/event-stream responses")
     @parametrize
     async def test_path_params_events(self, async_client: AsyncParallel) -> None:
         with pytest.raises(ValueError, match=r"Expected a non-empty value for `run_id` but received ''"):
diff --git a/tests/test_utils/test_path.py b/tests/test_utils/test_path.py
new file mode 100644
index 0000000..c0364ff
--- /dev/null
+++ b/tests/test_utils/test_path.py
@@ -0,0 +1,89 @@
+from __future__ import annotations
+
+from typing import Any
+
+import pytest
+
+from parallel._utils._path import path_template
+
+
+@pytest.mark.parametrize(
+    "template, kwargs, expected",
+    [
+        ("/v1/{id}", dict(id="abc"), "/v1/abc"),
+        ("/v1/{a}/{b}", dict(a="x", b="y"), "/v1/x/y"),
+        ("/v1/{a}{b}/path/{c}?val={d}#{e}", dict(a="x", b="y", c="z", d="u", e="v"), "/v1/xy/path/z?val=u#v"),
+        ("/{w}/{w}", dict(w="echo"), "/echo/echo"),
+        ("/v1/static", {}, "/v1/static"),
+        ("", {}, ""),
+        ("/v1/?q={n}&count=10", dict(n=42), "/v1/?q=42&count=10"),
+        ("/v1/{v}", dict(v=None), "/v1/null"),
+        ("/v1/{v}", dict(v=True), "/v1/true"),
+        ("/v1/{v}", dict(v=False), "/v1/false"),
+        ("/v1/{v}", dict(v=".hidden"), "/v1/.hidden"),  # dot prefix ok
+        ("/v1/{v}", dict(v="file.txt"), "/v1/file.txt"),  # dot in middle ok
+        ("/v1/{v}", dict(v="..."), "/v1/..."),  # triple dot ok
+        ("/v1/{a}{b}", dict(a=".", b="txt"), "/v1/.txt"),  # dot var combining with adjacent to be ok
+        ("/items?q={v}#{f}", dict(v=".", f=".."), "/items?q=.#.."),  # dots in query/fragment are fine
+        (
+            "/v1/{a}?query={b}",
+            dict(a="../../other/endpoint", b="a&bad=true"),
+            "/v1/..%2F..%2Fother%2Fendpoint?query=a%26bad%3Dtrue",
+        ),
+        ("/v1/{val}", dict(val="a/b/c"), "/v1/a%2Fb%2Fc"),
+        ("/v1/{val}", dict(val="a/b/c?query=value"), "/v1/a%2Fb%2Fc%3Fquery=value"),
+        ("/v1/{val}", dict(val="a/b/c?query=value&bad=true"), "/v1/a%2Fb%2Fc%3Fquery=value&bad=true"),
+        ("/v1/{val}", dict(val="%20"), "/v1/%2520"),  # escapes escape sequences in input
+        # Query: slash and ? are safe, # is not
+        ("/items?q={v}", dict(v="a/b"), "/items?q=a/b"),
+        ("/items?q={v}", dict(v="a?b"), "/items?q=a?b"),
+        ("/items?q={v}", dict(v="a#b"), "/items?q=a%23b"),
+        ("/items?q={v}", dict(v="a b"), "/items?q=a%20b"),
+        # Fragment: slash and ? are safe
+        ("/docs#{v}", dict(v="a/b"), "/docs#a/b"),
+        ("/docs#{v}", dict(v="a?b"), "/docs#a?b"),
+        # Path: slash, ? and # are all encoded
+        ("/v1/{v}", dict(v="a/b"), "/v1/a%2Fb"),
+        ("/v1/{v}", dict(v="a?b"), "/v1/a%3Fb"),
+        ("/v1/{v}", dict(v="a#b"), "/v1/a%23b"),
+        # same var encoded differently by component
+        (
+            "/v1/{v}?q={v}#{v}",
+            dict(v="a/b?c#d"),
+            "/v1/a%2Fb%3Fc%23d?q=a/b?c%23d#a/b?c%23d",
+        ),
+        ("/v1/{val}", dict(val="x?admin=true"), "/v1/x%3Fadmin=true"),  # query injection
+        ("/v1/{val}", dict(val="x#admin"), "/v1/x%23admin"),  # fragment injection
+    ],
+)
+def test_interpolation(template: str, kwargs: dict[str, Any], expected: str) -> None:
+    assert path_template(template, **kwargs) == expected
+
+
+def test_missing_kwarg_raises_key_error() -> None:
+    with pytest.raises(KeyError, match="org_id"):
+        path_template("/v1/{org_id}")
+
+
+@pytest.mark.parametrize(
+    "template, kwargs",
+    [
+        ("{a}/path", dict(a=".")),
+        ("{a}/path", dict(a="..")),
+        ("/v1/{a}", dict(a=".")),
+        ("/v1/{a}", dict(a="..")),
+        ("/v1/{a}/path", dict(a=".")),
+        ("/v1/{a}/path", dict(a="..")),
+        ("/v1/{a}{b}", dict(a=".", b=".")),  # adjacent vars → ".."
+        ("/v1/{a}.", dict(a=".")),  # var + static → ".."
+        ("/v1/{a}{b}", dict(a="", b=".")),  # empty + dot → "."
+        ("/v1/%2e/{x}", dict(x="ok")),  # encoded dot in static text
+        ("/v1/%2e./{x}", dict(x="ok")),  # mixed encoded ".." in static
+        ("/v1/.%2E/{x}", dict(x="ok")),  # mixed encoded ".." in static
+        ("/v1/{v}?q=1", dict(v="..")),
+        ("/v1/{v}#frag", dict(v="..")),
+    ],
+)
+def test_dot_segment_rejected(template: str, kwargs: dict[str, Any]) -> None:
+    with pytest.raises(ValueError, match="dot-segment"):
+        path_template(template, **kwargs)
