diff --git a/ChangeLog.md b/ChangeLog.md
--- a/ChangeLog.md
+++ b/ChangeLog.md
@@ -1,3 +1,7 @@
+## 0.2.0.0
+### Changed
+- Functions now return FilePath instead of Text, where the value is always a file URI.
+
 ## 0.1.1.0
 ### Added
 - Add Settings portal support.
diff --git a/README.md b/README.md
--- a/README.md
+++ b/README.md
@@ -25,6 +25,7 @@
 - Functions should take records called `...Options` and return records called `...Results`.
 - `...Options` records should have a `Default` instance where all fields have a reasonable empty value.
 - Record fields should not have unique prefixes.
+- API methods that return URIs that are known to always be `file:` URIs should be wrapped with functions returning [`Prelude.FilePath`](https://hackage.haskell.org/package/base-4.18.0.0/docs/Prelude.html#t:FilePath) (at some point this can be replaced with [`System.OsPath`](https://hackage.haskell.org/package/filepath-1.4.100.3/docs/System-OsPath.html#t:OsPath)). If they are not known to be file URIs, then [`Text.URI.URI`](https://hackage.haskell.org/package/modern-uri-0.3.6.0/docs/Text-URI.html#t:URI) should be returned.
 
 ### To format the source code
 ```bash
diff --git a/desktop-portal.cabal b/desktop-portal.cabal
--- a/desktop-portal.cabal
+++ b/desktop-portal.cabal
@@ -1,6 +1,6 @@
 cabal-version:      1.12
 name:               desktop-portal
-version:            0.1.1.0
+version:            0.2.0.0
 license:            MIT
 license-file:       LICENSE
 maintainer:         garethdanielsmith@gmail.com
@@ -50,6 +50,7 @@
         containers <0.7,
         data-default-class <0.2,
         dbus >=1.2.28 && <1.3,
+        modern-uri <0.4,
         random <1.3,
         text <1.3
 
@@ -88,6 +89,7 @@
         desktop-portal,
         hspec >=2 && <3,
         hspec-expectations <0.9,
+        modern-uri <0.4,
         process <1.7,
         random <1.3,
         text <1.3
diff --git a/src/Desktop/Portal/Account.hs b/src/Desktop/Portal/Account.hs
--- a/src/Desktop/Portal/Account.hs
+++ b/src/Desktop/Portal/Account.hs
@@ -15,7 +15,7 @@
 import Data.Maybe (catMaybes, fromMaybe)
 import Data.Text (Text)
 import Desktop.Portal.Internal (Client, Request, sendRequest)
-import Desktop.Portal.Util (optionalFromVariant, toVariantPair)
+import Desktop.Portal.Util (decodeFileUri, mapJust, optionalFromVariant, toVariantPair)
 
 data GetUserInformationOptions = GetUserInformationOptions
   { window :: Maybe Text,
@@ -33,7 +33,7 @@
 data GetUserInformationResults = GetUserInformationResults
   { id :: Text,
     name :: Text,
-    image :: Maybe Text
+    image :: Maybe FilePath
   }
   deriving (Eq, Show)
 
@@ -54,7 +54,7 @@
       resMap
         | Just id' <- DBus.fromVariant =<< Map.lookup "id" resMap,
           Just name <- DBus.fromVariant =<< Map.lookup "name" resMap,
-          Just image <- optionalFromVariant "image" resMap ->
+          Just image <- mapJust decodeFileUri =<< optionalFromVariant "image" resMap -> do
             pure GetUserInformationResults {id = id', name, image}
       resMap ->
         throwIO . DBus.clientError $ "getUserInformation: could not parse response: " <> show resMap
diff --git a/src/Desktop/Portal/FileChooser.hs b/src/Desktop/Portal/FileChooser.hs
--- a/src/Desktop/Portal/FileChooser.hs
+++ b/src/Desktop/Portal/FileChooser.hs
@@ -26,10 +26,10 @@
 import Data.Map (Map)
 import Data.Map.Strict qualified as Map
 import Data.Maybe (catMaybes, fromMaybe)
-import Data.Text (Text)
+import Data.Text (Text, pack)
 import Data.Word (Word32)
 import Desktop.Portal.Internal (Client, Request, sendRequest)
-import Desktop.Portal.Util (encodeNullTerminatedUtf8, mapJust, optionalFromVariant, toVariantPair, toVariantPair')
+import Desktop.Portal.Util (decodeFileUris, encodeNullTerminatedUtf8, mapJust, optionalFromVariant, toVariantPair, toVariantPair')
 
 data Filter = Filter
   { name :: Text,
@@ -90,7 +90,7 @@
       }
 
 data OpenFileResults = OpenFileResults
-  { uris :: [Text],
+  { uris :: [FilePath],
     choices :: Maybe [ChoiceComboSelection],
     currentFilter :: Maybe Filter
   }
@@ -105,8 +105,8 @@
     currentFilter :: Maybe Filter,
     choices :: Maybe [ChoiceCombo],
     currentName :: Maybe Text,
-    currentFolder :: Maybe Text,
-    currentFile :: Maybe Text
+    currentFolder :: Maybe FilePath,
+    currentFile :: Maybe FilePath
   }
   deriving (Eq, Show)
 
@@ -126,7 +126,7 @@
       }
 
 data SaveFileResults = SaveFileResults
-  { uris :: [Text],
+  { uris :: [FilePath],
     choices :: Maybe [ChoiceComboSelection],
     currentFilter :: Maybe Filter
   }
@@ -168,8 +168,8 @@
           toVariantPair' encodeFilter "current_filter" options.currentFilter,
           toVariantPair' (fmap encodeCombo) "choices" options.choices,
           toVariantPair "current_name" options.currentName,
-          toVariantPair "current_folder" (encodeNullTerminatedUtf8 <$> options.currentFolder),
-          toVariantPair "current_file" (encodeNullTerminatedUtf8 <$> options.currentFile)
+          toVariantPair "current_folder" (encodeNullTerminatedUtf8 . pack <$> options.currentFolder),
+          toVariantPair "current_file" (encodeNullTerminatedUtf8 . pack <$> options.currentFile)
         ]
 
     parseResponse resMap = do
@@ -179,7 +179,7 @@
 parseOpenFileResponse :: Map Text Variant -> IO OpenFileResults
 parseOpenFileResponse = \case
   resMap
-    | Just uris <- DBus.fromVariant =<< Map.lookup "uris" resMap,
+    | Just uris <- decodeFileUris =<< DBus.fromVariant =<< Map.lookup "uris" resMap,
       Just choicesRaw <- optionalFromVariant "choices" resMap,
       choices <- fmap decodeChoiceComboSelection <$> choicesRaw,
       Just currentFilterRaw <- optionalFromVariant "current_filter" resMap,
diff --git a/src/Desktop/Portal/Util.hs b/src/Desktop/Portal/Util.hs
--- a/src/Desktop/Portal/Util.hs
+++ b/src/Desktop/Portal/Util.hs
@@ -1,9 +1,13 @@
+{-# LANGUAGE ViewPatterns #-}
+
 module Desktop.Portal.Util
   ( optionalFromVariant,
     mapJust,
     toVariantPair,
     toVariantPair',
     encodeNullTerminatedUtf8,
+    decodeFileUri,
+    decodeFileUris,
   )
 where
 
@@ -13,8 +17,10 @@
 import Data.ByteString.Lazy (ByteString)
 import Data.Map (Map)
 import Data.Map qualified as Map
-import Data.Text (Text)
+import Data.Text (Text, unpack)
 import Data.Text.Encoding qualified as Encoding
+import Text.URI (Authority (..), URI (..))
+import Text.URI qualified as URI
 
 -- | Returns @Just Nothing@ if the field does not exist, @Just (Just x)@ if it does exist and
 -- can be turned into the expected type, or @Nothing@ if the field exists with the wrong type.
@@ -38,3 +44,30 @@
 encodeNullTerminatedUtf8 :: Text -> ByteString
 encodeNullTerminatedUtf8 txt =
   Binary.toLazyByteString (Encoding.encodeUtf8Builder txt <> Binary.singleton 0)
+
+decodeFileUri :: Text -> Maybe FilePath
+decodeFileUri uri =
+  case URI.mkURI uri of
+    Just
+      URI
+        { uriScheme = Just (URI.unRText -> "file"),
+          uriAuthority = (validAuthority -> True),
+          uriPath = Just (_trailingSlash, parts),
+          uriQuery = [],
+          uriFragment = Nothing
+        } -> Just . unpack $ foldMap (("/" <>) . URI.unRText) parts
+    _ ->
+      Nothing
+  where
+    validAuthority = \case
+      Left True -> True
+      Right
+        Authority
+          { authUserInfo = Nothing,
+            authHost = (URI.unRText -> ""),
+            authPort = Nothing
+          } -> True
+      _ -> False
+
+decodeFileUris :: [Text] -> Maybe [FilePath]
+decodeFileUris = traverse decodeFileUri
diff --git a/test/Desktop/Portal/AccountSpec.hs b/test/Desktop/Portal/AccountSpec.hs
--- a/test/Desktop/Portal/AccountSpec.hs
+++ b/test/Desktop/Portal/AccountSpec.hs
@@ -36,11 +36,11 @@
               successResponse
                 [ ("id", toVariantText "_id"),
                   ("name", toVariantText "_name"),
-                  ("image", toVariantText "_img")
+                  ("image", toVariantText "file:///some/path")
                 ]
         withRequestResponse handle accountInterface "GetUserInformation" responseBody $ do
           (Portal.getUserInformation (client handle) def >>= Portal.await)
-            `shouldReturn` Just (GetUserInformationResults "_id" "_name" (Just "_img"))
+            `shouldReturn` Just (GetUserInformationResults "_id" "_name" (Just "/some/path"))
 
       it "should decode response without image" $ \handle -> do
         let responseBody =
diff --git a/test/Desktop/Portal/FileChooserSpec.hs b/test/Desktop/Portal/FileChooserSpec.hs
--- a/test/Desktop/Portal/FileChooserSpec.hs
+++ b/test/Desktop/Portal/FileChooserSpec.hs
@@ -103,7 +103,7 @@
         withRequestResponse handle fileChooserInterface "OpenFile" responseBody $ do
           (Portal.openFile (client handle) def >>= Portal.await)
             `shouldReturn` Just
-              (OpenFileResults {uris = ["file:///a/b/c"], choices = Nothing, currentFilter = Nothing})
+              (OpenFileResults {uris = ["/a/b/c"], choices = Nothing, currentFilter = Nothing})
 
       it "should decode response with all Justs" $ \handle -> do
         let responseBody =
@@ -116,7 +116,7 @@
           (Portal.openFile (client handle) def >>= Portal.await)
             `shouldReturn` Just
               ( OpenFileResults
-                  { uris = ["file:///a/b/c"],
+                  { uris = ["/a/b/c"],
                     choices = Just [ChoiceComboSelection {comboId = "_comboId", optionId = "_optionId"}],
                     currentFilter = Just Filter {name = "_filterId", fileTypes = [GlobFilter "*.md"]}
                   }
@@ -214,7 +214,7 @@
         withRequestResponse handle fileChooserInterface "SaveFile" responseBody $ do
           (Portal.saveFile (client handle) def >>= Portal.await)
             `shouldReturn` Just
-              (SaveFileResults {uris = ["file:///a/b/c"], choices = Nothing, currentFilter = Nothing})
+              (SaveFileResults {uris = ["/a/b/c"], choices = Nothing, currentFilter = Nothing})
 
       it "should decode response with all Justs" $ \handle -> do
         let responseBody =
@@ -227,7 +227,7 @@
           (Portal.saveFile (client handle) def >>= Portal.await)
             `shouldReturn` Just
               ( SaveFileResults
-                  { uris = ["file:///a/b/c"],
+                  { uris = ["/a/b/c"],
                     choices = Just [ChoiceComboSelection {comboId = "_comboId", optionId = "_optionId"}],
                     currentFilter = Just Filter {name = "_filterId", fileTypes = [GlobFilter "*.md"]}
                   }
