diff --git a/.flake8 b/.flake8
index 9705b0356a1934a128a512b20e40ec680472b87c..f5883816b62a531fb55b5fd3db72470b6ab57e49 100644
--- a/.flake8
+++ b/.flake8
@@ -2,5 +2,5 @@
 max-line-length = 99
 max-doc-length = 99
 docstring-convention=google
-exclude = python, tests/*, __init__.py, src/aura_engine/client/*
+exclude = python, tests/*, __init__.py, src/aura_engine_api/*, src/aura_steering_api/*, src/aura_tank_api/*
 ignore = E121,E123,E126,E203,E226,E24,E704,W503,N802,D105,D107,D200,D202,D212,D417
diff --git a/.openapi-client-tank.yml b/.openapi-client-tank.yml
new file mode 100644
index 0000000000000000000000000000000000000000..56571b0cf2baaa9648f13a681763ed47841b2579
--- /dev/null
+++ b/.openapi-client-tank.yml
@@ -0,0 +1,2 @@
+project_name_override: .build
+package_name_override: aura_tank_api
\ No newline at end of file
diff --git a/Makefile b/Makefile
index b780cccd4dace5d521ac8d6566d4f19413c52b9d..857dace859d1d270b279c512b47fd6060bd1ced6 100644
--- a/Makefile
+++ b/Makefile
@@ -5,6 +5,7 @@ help::
 	@echo "$(APP_NAME) targets:"
 	@echo "    init.app        - init application environment"
 	@echo "    init.dev        - init development environment"
+	@echo "    build.models    - build models for API requests/responses"
 	@echo "    lint            - verify code style"
 	@echo "    spell           - check spelling of text"
 	@echo "    format          - apply automatic formatting"
@@ -59,6 +60,14 @@ init.dev:: pyproject.toml
 	cp -n config/sample.engine.ini config/engine.ini
 	mkdir -p .cache
 
+build.models::
+	rm -rf .build
+	poetry run openapi-python-client generate --path schemas/openapi-tank.json --config .openapi-client-tank.yml
+	cp -r .build/aura_tank_api/models src/aura_tank_api
+	cp .build/aura_tank_api/py.typed src/aura_tank_api
+	cp .build/aura_tank_api/types.py src/aura_tank_api
+
+
 lint::
 	poetry run python3 -m flake8 .
 
diff --git a/pyproject.toml b/pyproject.toml
index 16fe726500760dbe37804d21508a628d9c47d6ad..62c5958659f586f99f412ab9601dec05a2e885b7 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -57,7 +57,7 @@ coverage = {extras = ["toml"], version = "^7.2.5"}
 
 [tool.coverage.run]
 source = ["src"]
-omit = []
+omit = ["types.py"]
 [tool.coverage.report]
 # TODO Increase after we have more test cases
 fail_under = 25
diff --git a/schemas/openapi-tank.json b/schemas/openapi-tank.json
new file mode 100644
index 0000000000000000000000000000000000000000..6673c07b6932f80f054d285059cf064ebc22dfab
--- /dev/null
+++ b/schemas/openapi-tank.json
@@ -0,0 +1,144 @@
+{
+    "openapi" : "3.0.1",
+    "info" : {
+      "contact" : {
+        "name" : "aura.radio",
+        "url" : "https://aura.radio"
+      },
+      "description" : "Import & Playlist Daemon",
+      "license" : {
+        "name" : "AGPLv3",
+        "url" : "https://www.gnu.org/licenses/agpl-3.0"
+      },
+      "title" : "AURA Tank API",
+      "version" : "1.0"
+    },
+    "servers" : [ {
+      "url" : "/"
+    } ],
+    "paths" : {
+    },
+    "components" : {
+      "schemas" : {
+        "File" : {
+            "properties" : {
+                "created" : {
+                "type" : "string"
+                },
+                "duration" : {
+                "type" : "integer"
+                },
+                "id" : {
+                "type" : "integer"
+                },
+                "metadata" : {
+                "$ref" : "#/components/schemas/FileMetadata"
+                },
+                "show" : {
+                "type" : "string"
+                },
+                "size" : {
+                "type" : "integer"
+                },
+                "source" : {
+                "$ref" : "#/components/schemas/FileSource"
+                },
+                "updated" : {
+                "type" : "string"
+                }
+            },
+            "type" : "object"
+            },
+            "FileMetadata" : {
+            "properties" : {
+                "album" : {
+                "type" : "string"
+                },
+                "artist" : {
+                "description" : "actually a full-text index would be nice here...",
+                "type" : "string"
+                },
+                "isrc" : {
+                "type" : "string"
+                },
+                "organization" : {
+                "type" : "string"
+                },
+                "title" : {
+                "type" : "string"
+                }
+            },
+            "type" : "object"
+            },
+            "FileSource" : {
+            "properties" : {
+                "hash" : {
+                "type" : "string"
+                },
+                "import" : {
+                "$ref" : "#/components/schemas/Import"
+                },
+                "uri" : {
+                "type" : "string"
+                }
+            },
+            "type" : "object"
+                },
+            "Import" : {
+            "properties" : {
+                "error" : {
+                "type" : "string"
+                },
+                "state" : {
+                "type" : "string"
+                }
+            },
+            "type" : "object"
+            },
+            "Playlist" : {
+            "properties" : {
+                "created" : {
+                "type" : "string"
+                },
+                "description" : {
+                "type" : "string"
+                },
+                "entries" : {
+                "items" : {
+                    "$ref" : "#/components/schemas/PlaylistEntry"
+                },
+                "type" : "array"
+                },
+                "id" : {
+                "type" : "integer"
+                },
+                "playout-mode" : {
+                "type" : "string"
+                },
+                "show" : {
+                "type" : "string"
+                },
+                "updated" : {
+                "type" : "string"
+                }
+            },
+            "type" : "object"
+            },
+            "PlaylistEntry" : {
+            "properties" : {
+                "duration" : {
+                "type" : "integer"
+                },
+                "file" : {
+                "$ref" : "#/components/schemas/File"
+                },
+                "uri" : {
+                "type" : "string"
+                }
+            },
+            "type" : "object"
+            }
+        },
+        "x-original-swagger-version" : "2.0"
+    }
+}
\ No newline at end of file
diff --git a/src/aura_tank_api/__init__.py b/src/aura_tank_api/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/src/aura_tank_api/models/__init__.py b/src/aura_tank_api/models/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..d29566926351ce07f8880624e467dd73e05121fd
--- /dev/null
+++ b/src/aura_tank_api/models/__init__.py
@@ -0,0 +1,17 @@
+""" Contains all the data models used in inputs/outputs """
+
+from .file import File
+from .file_metadata import FileMetadata
+from .file_source import FileSource
+from .import_ import Import
+from .playlist import Playlist
+from .playlist_entry import PlaylistEntry
+
+__all__ = (
+    "File",
+    "FileMetadata",
+    "FileSource",
+    "Import",
+    "Playlist",
+    "PlaylistEntry",
+)
diff --git a/src/aura_tank_api/models/file.py b/src/aura_tank_api/models/file.py
new file mode 100644
index 0000000000000000000000000000000000000000..b408d50d2110fd027d0c1edd8337887a3b60579c
--- /dev/null
+++ b/src/aura_tank_api/models/file.py
@@ -0,0 +1,137 @@
+from typing import TYPE_CHECKING, Any, Dict, List, Type, TypeVar, Union
+
+import attr
+
+from ..types import UNSET, Unset
+
+if TYPE_CHECKING:
+    from ..models.file_metadata import FileMetadata
+    from ..models.file_source import FileSource
+
+
+T = TypeVar("T", bound="File")
+
+
+@attr.s(auto_attribs=True)
+class File:
+    """
+    Attributes:
+        created (Union[Unset, str]):
+        duration (Union[Unset, int]):
+        id (Union[Unset, int]):
+        metadata (Union[Unset, FileMetadata]):
+        show (Union[Unset, str]):
+        size (Union[Unset, int]):
+        source (Union[Unset, FileSource]):
+        updated (Union[Unset, str]):
+    """
+
+    created: Union[Unset, str] = UNSET
+    duration: Union[Unset, int] = UNSET
+    id: Union[Unset, int] = UNSET
+    metadata: Union[Unset, "FileMetadata"] = UNSET
+    show: Union[Unset, str] = UNSET
+    size: Union[Unset, int] = UNSET
+    source: Union[Unset, "FileSource"] = UNSET
+    updated: Union[Unset, str] = UNSET
+    additional_properties: Dict[str, Any] = attr.ib(init=False, factory=dict)
+
+    def to_dict(self) -> Dict[str, Any]:
+        created = self.created
+        duration = self.duration
+        id = self.id
+        metadata: Union[Unset, Dict[str, Any]] = UNSET
+        if not isinstance(self.metadata, Unset):
+            metadata = self.metadata.to_dict()
+
+        show = self.show
+        size = self.size
+        source: Union[Unset, Dict[str, Any]] = UNSET
+        if not isinstance(self.source, Unset):
+            source = self.source.to_dict()
+
+        updated = self.updated
+
+        field_dict: Dict[str, Any] = {}
+        field_dict.update(self.additional_properties)
+        field_dict.update({})
+        if created is not UNSET:
+            field_dict["created"] = created
+        if duration is not UNSET:
+            field_dict["duration"] = duration
+        if id is not UNSET:
+            field_dict["id"] = id
+        if metadata is not UNSET:
+            field_dict["metadata"] = metadata
+        if show is not UNSET:
+            field_dict["show"] = show
+        if size is not UNSET:
+            field_dict["size"] = size
+        if source is not UNSET:
+            field_dict["source"] = source
+        if updated is not UNSET:
+            field_dict["updated"] = updated
+
+        return field_dict
+
+    @classmethod
+    def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T:
+        from ..models.file_metadata import FileMetadata
+        from ..models.file_source import FileSource
+
+        d = src_dict.copy()
+        created = d.pop("created", UNSET)
+
+        duration = d.pop("duration", UNSET)
+
+        id = d.pop("id", UNSET)
+
+        _metadata = d.pop("metadata", UNSET)
+        metadata: Union[Unset, FileMetadata]
+        if isinstance(_metadata, Unset):
+            metadata = UNSET
+        else:
+            metadata = FileMetadata.from_dict(_metadata)
+
+        show = d.pop("show", UNSET)
+
+        size = d.pop("size", UNSET)
+
+        _source = d.pop("source", UNSET)
+        source: Union[Unset, FileSource]
+        if isinstance(_source, Unset):
+            source = UNSET
+        else:
+            source = FileSource.from_dict(_source)
+
+        updated = d.pop("updated", UNSET)
+
+        file = cls(
+            created=created,
+            duration=duration,
+            id=id,
+            metadata=metadata,
+            show=show,
+            size=size,
+            source=source,
+            updated=updated,
+        )
+
+        file.additional_properties = d
+        return file
+
+    @property
+    def additional_keys(self) -> List[str]:
+        return list(self.additional_properties.keys())
+
+    def __getitem__(self, key: str) -> Any:
+        return self.additional_properties[key]
+
+    def __setitem__(self, key: str, value: Any) -> None:
+        self.additional_properties[key] = value
+
+    def __delitem__(self, key: str) -> None:
+        del self.additional_properties[key]
+
+    def __contains__(self, key: str) -> bool:
+        return key in self.additional_properties
diff --git a/src/aura_tank_api/models/file_metadata.py b/src/aura_tank_api/models/file_metadata.py
new file mode 100644
index 0000000000000000000000000000000000000000..4bd06f46e60ea3795c7162ec4082dabb716406e6
--- /dev/null
+++ b/src/aura_tank_api/models/file_metadata.py
@@ -0,0 +1,89 @@
+from typing import Any, Dict, List, Type, TypeVar, Union
+
+import attr
+
+from ..types import UNSET, Unset
+
+T = TypeVar("T", bound="FileMetadata")
+
+
+@attr.s(auto_attribs=True)
+class FileMetadata:
+    """
+    Attributes:
+        album (Union[Unset, str]):
+        artist (Union[Unset, str]): actually a full-text index would be nice here...
+        isrc (Union[Unset, str]):
+        organization (Union[Unset, str]):
+        title (Union[Unset, str]):
+    """
+
+    album: Union[Unset, str] = UNSET
+    artist: Union[Unset, str] = UNSET
+    isrc: Union[Unset, str] = UNSET
+    organization: Union[Unset, str] = UNSET
+    title: Union[Unset, str] = UNSET
+    additional_properties: Dict[str, Any] = attr.ib(init=False, factory=dict)
+
+    def to_dict(self) -> Dict[str, Any]:
+        album = self.album
+        artist = self.artist
+        isrc = self.isrc
+        organization = self.organization
+        title = self.title
+
+        field_dict: Dict[str, Any] = {}
+        field_dict.update(self.additional_properties)
+        field_dict.update({})
+        if album is not UNSET:
+            field_dict["album"] = album
+        if artist is not UNSET:
+            field_dict["artist"] = artist
+        if isrc is not UNSET:
+            field_dict["isrc"] = isrc
+        if organization is not UNSET:
+            field_dict["organization"] = organization
+        if title is not UNSET:
+            field_dict["title"] = title
+
+        return field_dict
+
+    @classmethod
+    def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T:
+        d = src_dict.copy()
+        album = d.pop("album", UNSET)
+
+        artist = d.pop("artist", UNSET)
+
+        isrc = d.pop("isrc", UNSET)
+
+        organization = d.pop("organization", UNSET)
+
+        title = d.pop("title", UNSET)
+
+        file_metadata = cls(
+            album=album,
+            artist=artist,
+            isrc=isrc,
+            organization=organization,
+            title=title,
+        )
+
+        file_metadata.additional_properties = d
+        return file_metadata
+
+    @property
+    def additional_keys(self) -> List[str]:
+        return list(self.additional_properties.keys())
+
+    def __getitem__(self, key: str) -> Any:
+        return self.additional_properties[key]
+
+    def __setitem__(self, key: str, value: Any) -> None:
+        self.additional_properties[key] = value
+
+    def __delitem__(self, key: str) -> None:
+        del self.additional_properties[key]
+
+    def __contains__(self, key: str) -> bool:
+        return key in self.additional_properties
diff --git a/src/aura_tank_api/models/file_source.py b/src/aura_tank_api/models/file_source.py
new file mode 100644
index 0000000000000000000000000000000000000000..97032b69b93018a1eaacae2d3a6a17370420cb33
--- /dev/null
+++ b/src/aura_tank_api/models/file_source.py
@@ -0,0 +1,87 @@
+from typing import TYPE_CHECKING, Any, Dict, List, Type, TypeVar, Union
+
+import attr
+
+from ..types import UNSET, Unset
+
+if TYPE_CHECKING:
+    from ..models.import_ import Import
+
+
+T = TypeVar("T", bound="FileSource")
+
+
+@attr.s(auto_attribs=True)
+class FileSource:
+    """
+    Attributes:
+        hash_ (Union[Unset, str]):
+        import_ (Union[Unset, Import]):
+        uri (Union[Unset, str]):
+    """
+
+    hash_: Union[Unset, str] = UNSET
+    import_: Union[Unset, "Import"] = UNSET
+    uri: Union[Unset, str] = UNSET
+    additional_properties: Dict[str, Any] = attr.ib(init=False, factory=dict)
+
+    def to_dict(self) -> Dict[str, Any]:
+        hash_ = self.hash_
+        import_: Union[Unset, Dict[str, Any]] = UNSET
+        if not isinstance(self.import_, Unset):
+            import_ = self.import_.to_dict()
+
+        uri = self.uri
+
+        field_dict: Dict[str, Any] = {}
+        field_dict.update(self.additional_properties)
+        field_dict.update({})
+        if hash_ is not UNSET:
+            field_dict["hash"] = hash_
+        if import_ is not UNSET:
+            field_dict["import"] = import_
+        if uri is not UNSET:
+            field_dict["uri"] = uri
+
+        return field_dict
+
+    @classmethod
+    def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T:
+        from ..models.import_ import Import
+
+        d = src_dict.copy()
+        hash_ = d.pop("hash", UNSET)
+
+        _import_ = d.pop("import", UNSET)
+        import_: Union[Unset, Import]
+        if isinstance(_import_, Unset):
+            import_ = UNSET
+        else:
+            import_ = Import.from_dict(_import_)
+
+        uri = d.pop("uri", UNSET)
+
+        file_source = cls(
+            hash_=hash_,
+            import_=import_,
+            uri=uri,
+        )
+
+        file_source.additional_properties = d
+        return file_source
+
+    @property
+    def additional_keys(self) -> List[str]:
+        return list(self.additional_properties.keys())
+
+    def __getitem__(self, key: str) -> Any:
+        return self.additional_properties[key]
+
+    def __setitem__(self, key: str, value: Any) -> None:
+        self.additional_properties[key] = value
+
+    def __delitem__(self, key: str) -> None:
+        del self.additional_properties[key]
+
+    def __contains__(self, key: str) -> bool:
+        return key in self.additional_properties
diff --git a/src/aura_tank_api/models/import_.py b/src/aura_tank_api/models/import_.py
new file mode 100644
index 0000000000000000000000000000000000000000..7490f941558e2528ff244ee43566f94be3b06370
--- /dev/null
+++ b/src/aura_tank_api/models/import_.py
@@ -0,0 +1,65 @@
+from typing import Any, Dict, List, Type, TypeVar, Union
+
+import attr
+
+from ..types import UNSET, Unset
+
+T = TypeVar("T", bound="Import")
+
+
+@attr.s(auto_attribs=True)
+class Import:
+    """
+    Attributes:
+        error (Union[Unset, str]):
+        state (Union[Unset, str]):
+    """
+
+    error: Union[Unset, str] = UNSET
+    state: Union[Unset, str] = UNSET
+    additional_properties: Dict[str, Any] = attr.ib(init=False, factory=dict)
+
+    def to_dict(self) -> Dict[str, Any]:
+        error = self.error
+        state = self.state
+
+        field_dict: Dict[str, Any] = {}
+        field_dict.update(self.additional_properties)
+        field_dict.update({})
+        if error is not UNSET:
+            field_dict["error"] = error
+        if state is not UNSET:
+            field_dict["state"] = state
+
+        return field_dict
+
+    @classmethod
+    def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T:
+        d = src_dict.copy()
+        error = d.pop("error", UNSET)
+
+        state = d.pop("state", UNSET)
+
+        import_ = cls(
+            error=error,
+            state=state,
+        )
+
+        import_.additional_properties = d
+        return import_
+
+    @property
+    def additional_keys(self) -> List[str]:
+        return list(self.additional_properties.keys())
+
+    def __getitem__(self, key: str) -> Any:
+        return self.additional_properties[key]
+
+    def __setitem__(self, key: str, value: Any) -> None:
+        self.additional_properties[key] = value
+
+    def __delitem__(self, key: str) -> None:
+        del self.additional_properties[key]
+
+    def __contains__(self, key: str) -> bool:
+        return key in self.additional_properties
diff --git a/src/aura_tank_api/models/playlist.py b/src/aura_tank_api/models/playlist.py
new file mode 100644
index 0000000000000000000000000000000000000000..426ed62b51e51f5b19fb25576027478d8de65cbf
--- /dev/null
+++ b/src/aura_tank_api/models/playlist.py
@@ -0,0 +1,123 @@
+from typing import TYPE_CHECKING, Any, Dict, List, Type, TypeVar, Union
+
+import attr
+
+from ..types import UNSET, Unset
+
+if TYPE_CHECKING:
+    from ..models.playlist_entry import PlaylistEntry
+
+
+T = TypeVar("T", bound="Playlist")
+
+
+@attr.s(auto_attribs=True)
+class Playlist:
+    """
+    Attributes:
+        created (Union[Unset, str]):
+        description (Union[Unset, str]):
+        entries (Union[Unset, List['PlaylistEntry']]):
+        id (Union[Unset, int]):
+        playout_mode (Union[Unset, str]):
+        show (Union[Unset, str]):
+        updated (Union[Unset, str]):
+    """
+
+    created: Union[Unset, str] = UNSET
+    description: Union[Unset, str] = UNSET
+    entries: Union[Unset, List["PlaylistEntry"]] = UNSET
+    id: Union[Unset, int] = UNSET
+    playout_mode: Union[Unset, str] = UNSET
+    show: Union[Unset, str] = UNSET
+    updated: Union[Unset, str] = UNSET
+    additional_properties: Dict[str, Any] = attr.ib(init=False, factory=dict)
+
+    def to_dict(self) -> Dict[str, Any]:
+        created = self.created
+        description = self.description
+        entries: Union[Unset, List[Dict[str, Any]]] = UNSET
+        if not isinstance(self.entries, Unset):
+            entries = []
+            for entries_item_data in self.entries:
+                entries_item = entries_item_data.to_dict()
+
+                entries.append(entries_item)
+
+        id = self.id
+        playout_mode = self.playout_mode
+        show = self.show
+        updated = self.updated
+
+        field_dict: Dict[str, Any] = {}
+        field_dict.update(self.additional_properties)
+        field_dict.update({})
+        if created is not UNSET:
+            field_dict["created"] = created
+        if description is not UNSET:
+            field_dict["description"] = description
+        if entries is not UNSET:
+            field_dict["entries"] = entries
+        if id is not UNSET:
+            field_dict["id"] = id
+        if playout_mode is not UNSET:
+            field_dict["playout-mode"] = playout_mode
+        if show is not UNSET:
+            field_dict["show"] = show
+        if updated is not UNSET:
+            field_dict["updated"] = updated
+
+        return field_dict
+
+    @classmethod
+    def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T:
+        from ..models.playlist_entry import PlaylistEntry
+
+        d = src_dict.copy()
+        created = d.pop("created", UNSET)
+
+        description = d.pop("description", UNSET)
+
+        entries = []
+        _entries = d.pop("entries", UNSET)
+        for entries_item_data in _entries or []:
+            entries_item = PlaylistEntry.from_dict(entries_item_data)
+
+            entries.append(entries_item)
+
+        id = d.pop("id", UNSET)
+
+        playout_mode = d.pop("playout-mode", UNSET)
+
+        show = d.pop("show", UNSET)
+
+        updated = d.pop("updated", UNSET)
+
+        playlist = cls(
+            created=created,
+            description=description,
+            entries=entries,
+            id=id,
+            playout_mode=playout_mode,
+            show=show,
+            updated=updated,
+        )
+
+        playlist.additional_properties = d
+        return playlist
+
+    @property
+    def additional_keys(self) -> List[str]:
+        return list(self.additional_properties.keys())
+
+    def __getitem__(self, key: str) -> Any:
+        return self.additional_properties[key]
+
+    def __setitem__(self, key: str, value: Any) -> None:
+        self.additional_properties[key] = value
+
+    def __delitem__(self, key: str) -> None:
+        del self.additional_properties[key]
+
+    def __contains__(self, key: str) -> bool:
+        return key in self.additional_properties
diff --git a/src/aura_tank_api/models/playlist_entry.py b/src/aura_tank_api/models/playlist_entry.py
new file mode 100644
index 0000000000000000000000000000000000000000..c575c632048a06c3101227b0592c05f3e72fb34d
--- /dev/null
+++ b/src/aura_tank_api/models/playlist_entry.py
@@ -0,0 +1,87 @@
+from typing import TYPE_CHECKING, Any, Dict, List, Type, TypeVar, Union
+
+import attr
+
+from ..types import UNSET, Unset
+
+if TYPE_CHECKING:
+    from ..models.file import File
+
+
+T = TypeVar("T", bound="PlaylistEntry")
+
+
+@attr.s(auto_attribs=True)
+class PlaylistEntry:
+    """
+    Attributes:
+        duration (Union[Unset, int]):
+        file (Union[Unset, File]):
+        uri (Union[Unset, str]):
+    """
+
+    duration: Union[Unset, int] = UNSET
+    file: Union[Unset, "File"] = UNSET
+    uri: Union[Unset, str] = UNSET
+    additional_properties: Dict[str, Any] = attr.ib(init=False, factory=dict)
+
+    def to_dict(self) -> Dict[str, Any]:
+        duration = self.duration
+        file: Union[Unset, Dict[str, Any]] = UNSET
+        if not isinstance(self.file, Unset):
+            file = self.file.to_dict()
+
+        uri = self.uri
+
+        field_dict: Dict[str, Any] = {}
+        field_dict.update(self.additional_properties)
+        field_dict.update({})
+        if duration is not UNSET:
+            field_dict["duration"] = duration
+        if file is not UNSET:
+            field_dict["file"] = file
+        if uri is not UNSET:
+            field_dict["uri"] = uri
+
+        return field_dict
+
+    @classmethod
+    def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T:
+        from ..models.file import File
+
+        d = src_dict.copy()
+        duration = d.pop("duration", UNSET)
+
+        _file = d.pop("file", UNSET)
+        file: Union[Unset, File]
+        if isinstance(_file, Unset):
+            file = UNSET
+        else:
+            file = File.from_dict(_file)
+
+        uri = d.pop("uri", UNSET)
+
+        playlist_entry = cls(
+            duration=duration,
+            file=file,
+            uri=uri,
+        )
+
+        playlist_entry.additional_properties = d
+        return playlist_entry
+
+    @property
+    def additional_keys(self) -> List[str]:
+        return list(self.additional_properties.keys())
+
+    def __getitem__(self, key: str) -> Any:
+        return self.additional_properties[key]
+
+    def __setitem__(self, key: str, value: Any) -> None:
+        self.additional_properties[key] = value
+
+    def __delitem__(self, key: str) -> None:
+        del self.additional_properties[key]
+
+    def __contains__(self, key: str) -> bool:
+        return key in self.additional_properties
diff --git a/src/aura_tank_api/py.typed b/src/aura_tank_api/py.typed
new file mode 100644
index 0000000000000000000000000000000000000000..1aad32711f3d86135c552ae38665f4dcc73f3ce2
--- /dev/null
+++ b/src/aura_tank_api/py.typed
@@ -0,0 +1 @@
+# Marker file for PEP 561
\ No newline at end of file
diff --git a/src/aura_tank_api/types.py b/src/aura_tank_api/types.py
new file mode 100644
index 0000000000000000000000000000000000000000..599eeb9f5eef454afb0f9db35e08ef57de14c3a7
--- /dev/null
+++ b/src/aura_tank_api/types.py
@@ -0,0 +1,44 @@
+""" Contains some shared types for properties """
+from http import HTTPStatus
+from typing import BinaryIO, Generic, Literal, MutableMapping, Optional, Tuple, TypeVar
+
+import attr
+
+
+class Unset:
+    def __bool__(self) -> Literal[False]:
+        return False
+
+
+UNSET: Unset = Unset()
+
+FileJsonType = Tuple[Optional[str], BinaryIO, Optional[str]]
+
+
+@attr.s(auto_attribs=True)
+class File:
+    """Contains information for file uploads"""
+
+    payload: BinaryIO
+    file_name: Optional[str] = None
+    mime_type: Optional[str] = None
+
+    def to_tuple(self) -> FileJsonType:
+        """Return a tuple representation that httpx will accept for multipart/form-data"""
+        return self.file_name, self.payload, self.mime_type
+
+
+T = TypeVar("T")
+
+
+@attr.s(auto_attribs=True)
+class Response(Generic[T]):
+    """A response from an endpoint"""
+
+    status_code: HTTPStatus
+    content: bytes
+    headers: MutableMapping[str, str]
+    parsed: Optional[T]
+
+
+__all__ = ["File", "Response", "FileJsonType"]