diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,12 @@
+# v7.5.0 (2026-05-04)
+
+## ✨ New Features
+- [`abdc3a0`](https://gitlab.com/krakrjak/restman/-/commit/abdc3a0) ✨   Add --oneshot Mode to RESTman 
+
+## 🐛 Bug Fixes
+
+## 🏗️ Architecture Changes
+
 # v7.4.0 (2026-05-03)
 
 ## ✨ New Features
diff --git a/app/Main.hs b/app/Main.hs
--- a/app/Main.hs
+++ b/app/Main.hs
@@ -2,20 +2,23 @@
 
 -- base
 import Control.Applicative (many, optional, (<|>))
+import Control.Exception (SomeException, try)
 import Data.Bits (Bits, unsafeShiftL)
-import System.IO (IOMode(ReadMode), withFile)
+import System.Exit (exitFailure)
+import System.IO (IOMode(ReadMode), hPutStrLn, stderr, stdout, withFile)
 
 -- bytestring
 import Data.ByteString (ByteString, hGetSome)
 import qualified Data.ByteString as BS
 import qualified Data.ByteString.Char8 as CS
-import Data.ByteString.Lazy (fromChunks, fromStrict)
+import Data.ByteString.Lazy (fromChunks, fromStrict, hPut)
 import qualified Data.ByteString.Lazy as Lazy (ByteString)
 
 -- case-insensitive
 import qualified Data.CaseInsensitive as CI
 
 -- microlens
+import Lens.Micro ((%~), (&), (.~))
 import Lens.Micro.Extras (view)
 
 -- optparse-applicatve
@@ -39,15 +42,22 @@
   , showDefault
   , strArgument
   , strOption
+  , switch
   )
 import qualified Options.Applicative as OptParse
 
 -- text
-import Data.Text (Text)
+import Data.Text (Text, unpack)
 
 -- utf8-string
 import qualified Data.ByteString.UTF8 as UTF8
 
+-- http-client
+import qualified Network.HTTP.Client as HC
+
+-- wreq
+import qualified Network.Wreq as W
+
 -- local imports
 import HTTP.Client
 import UI
@@ -134,8 +144,16 @@
   , customHeaders :: [Header]
   , payload :: Maybe Payload
   , uri :: Maybe Text
+  , oneshot :: Bool
   }
 
+-- | Handles @--oneshot@: make one request, print the body, and exit without starting the TUI.
+oneshotParser :: Parser Bool
+oneshotParser = switch
+  (  long "oneshot"
+  <> help "Make the request, print the response body to stdout, and exit without starting the TUI."
+  )
+
 helpParser :: Parser (a -> a)
 helpParser = abortOption (ShowHelpText mempty)
   (  long "help"
@@ -152,7 +170,7 @@
   )
  where
   optionsParser =
-    MkOptions <$> methodParser <*> useDefaultParser <*> customHeadersParser <*> payloadParser <*> uriPositionalParser
+    MkOptions <$> methodParser <*> useDefaultParser <*> customHeadersParser <*> payloadParser <*> uriPositionalParser <*> oneshotParser
 
 -- | Lens for the method in the options.
 optMethod :: Functor f => (Method -> f Method) -> Options -> f Options
@@ -177,9 +195,34 @@
         chunks <- go
         return $ chunk:chunks
 
+-- | Non-TUI entry point: perform one request, write the response body to stdout, and exit.
+--
+-- Exits with failure if no URI was supplied or the request throws an exception.
+runOneshot :: Options -> Maybe Lazy.ByteString -> IO ()
+runOneshot opts payloadLbs = case uri opts of
+  Nothing -> do
+    hPutStrLn stderr "restman: --oneshot requires a URI argument"
+    exitFailure
+  Just u -> do
+    manager <- HC.newManager robustSettings
+    let baseOpts = W.defaults & W.manager .~ Right manager
+        wreqOpts = case useDefaultHeaders opts of
+          AppendCustomToDefaultHeaders -> baseOpts & headers %~ (customHeaders opts ++)
+          ReplaceDefaultHeaders -> baseOpts & headers .~ customHeaders opts
+        url = unpack u
+        doReq = case payloadLbs of
+          Nothing -> customMethodWith  (method opts) wreqOpts url
+          Just lbs -> customPayloadMethodWith (method opts) wreqOpts url lbs
+    result <- try doReq :: IO (Either SomeException (W.Response Lazy.ByteString))
+    case result of
+      Left ex -> hPutStrLn stderr ("restman: " <> show ex) >> exitFailure
+      Right resp -> hPut stdout (view responseBody resp)
+
 -- | Application entry point.
 main :: IO ()
 main = do
   options <- execParser appParser
   payloadLbs <- traverse loadPayload $ payload options
-  startUI (view optMethod options) (useDefaultHeaders options) (customHeaders options) payloadLbs (uri options)
+  if oneshot options
+    then runOneshot options payloadLbs
+    else startUI (view optMethod options) (useDefaultHeaders options) (customHeaders options) payloadLbs (uri options)
diff --git a/restman.cabal b/restman.cabal
--- a/restman.cabal
+++ b/restman.cabal
@@ -1,6 +1,6 @@
 cabal-version:      1.12
 name:               restman
-version:            0.7.4.0
+version:            0.7.5.0
 license:            BSD2
 license-file:       LICENSE
 copyright:          Zac Slade or Boyd Stephen Smith Jr, 2024
@@ -73,6 +73,7 @@
         brick >=2.9 && <2.10,
         bytestring >=0.12.2.0 && <0.13,
         case-insensitive >=1.2.1.0 && <1.3,
+        http-client >=0.7.19 && <0.8,
         http-types >=0.12.4 && <0.13,
         microlens >=0.4.14.0 && <0.5,
         optparse-applicative >=0.18.1.0 && <0.19,
@@ -86,6 +87,7 @@
     main-is:          Spec.hs
     hs-source-dirs:   test
     other-modules:
+        BinarySpec
         HTTP.ClientIntegrationSpec
         HTTP.ClientSpec
         HTTP.EchoServer
@@ -107,9 +109,11 @@
         bytestring >=0.12.2.0 && <0.13,
         case-insensitive >=1.2.1.0 && <1.3,
         containers >=0.7 && <0.8,
+        directory >=1.3.8.5 && <1.4,
         hedgehog >=1.5 && <1.6,
         http-client >=0.7.19 && <0.8,
         http-types >=0.12.4 && <0.13,
+        process >=1.6.26.1 && <1.7,
         restman,
         skylighting >=0.14.7 && <0.15,
         tasty >=1.5.4 && <1.6,
diff --git a/test/BinarySpec.hs b/test/BinarySpec.hs
new file mode 100644
--- /dev/null
+++ b/test/BinarySpec.hs
@@ -0,0 +1,161 @@
+{-# language OverloadedStrings #-}
+-- | Acceptance tests that spawn the @restman@ binary and inspect its
+-- behaviour via the local echo server.
+--
+-- These tests require the @restman@ executable to be present on @PATH@.
+-- When running via @stack test@, Stack builds the executable first and adds
+-- the package bin-dir to @PATH@ automatically.
+module BinarySpec (tests) where
+
+-- base
+import Data.List (isInfixOf)
+import System.Exit (ExitCode(..))
+import System.Process (readProcessWithExitCode)
+
+-- aeson
+import Data.Aeson (FromJSON(..), eitherDecode, withObject, (.:))
+
+-- bytestring
+import qualified Data.ByteString.Lazy.Char8 as BL8
+
+-- containers
+import Data.Map.Strict (Map)
+import qualified Data.Map.Strict as Map
+
+-- directory
+import System.Directory (findExecutable)
+
+-- text
+import qualified Data.Text as T
+
+-- tasty
+import Test.Tasty
+import Test.Tasty.HUnit
+
+-- warp
+import Network.Wai.Handler.Warp (Port)
+
+-- local
+import HTTP.EchoServer (withEchoServer)
+
+-- ---------------------------------------------------------------------------
+-- EchoResponse — mirrors the JSON produced by HTTP.EchoServer
+-- ---------------------------------------------------------------------------
+
+data EchoResponse = EchoResponse
+    { echoMethod :: T.Text
+    , echoHeaders :: Map T.Text T.Text
+    , echoBody :: T.Text
+    } deriving (Eq, Show)
+
+instance FromJSON EchoResponse where
+    parseJSON = withObject "EchoResponse" $ \o ->
+        EchoResponse
+            <$> o .: "method"
+            <*> o .: "headers"
+            <*> o .: "body"
+
+-- ---------------------------------------------------------------------------
+-- Helpers
+-- ---------------------------------------------------------------------------
+
+-- | Locate the @restman@ binary on @PATH@, failing the test if it is absent.
+findRestmanExe :: IO FilePath
+findRestmanExe = do
+    mPath <- findExecutable "restman"
+    case mPath of
+        Nothing -> assertFailure
+            "restman binary not found in PATH; run acceptance tests via 'stack test'"
+        Just p -> pure p
+
+-- | Run @restman@ with the supplied arguments, returning
+-- @(exitCode, stdout, stderr)@.
+runRestman :: [String] -> IO (ExitCode, String, String)
+runRestman args = do
+    exe <- findRestmanExe
+    readProcessWithExitCode exe args ""
+
+echoURL :: Port -> String -> String
+echoURL port path = "http://localhost:" <> show port <> path
+
+-- | Run @restman --oneshot@ against the local echo server with optional
+-- extra arguments, decode the JSON response body, and return the result.
+oneshotEcho :: Port -> [String] -> IO EchoResponse
+oneshotEcho port extraArgs = do
+    let args = extraArgs ++ ["--oneshot", echoURL port "/"]
+    (code, out, err) <- runRestman args
+    case code of
+        ExitFailure n ->
+            assertFailure $ "restman exited with code " <> show n
+                          <> "; stderr: " <> err
+        ExitSuccess ->
+            case eitherDecode (BL8.pack out) of
+                Left e -> assertFailure
+                    $ "Failed to decode echo response: " <> e
+                   <> "\nstdout was: " <> out
+                Right r -> pure r
+
+-- ---------------------------------------------------------------------------
+-- Tests
+-- ---------------------------------------------------------------------------
+
+tests :: TestTree
+tests = testGroup "Application (acceptance)"
+    [ cliTests
+    , fetchTests
+    ]
+
+cliTests :: TestTree
+cliTests = testGroup "CLI"
+    [ testCase "--help exits with success and mentions RESTman" $ do
+        (code, out, _) <- runRestman ["--help"]
+        code @?= ExitSuccess
+        assertBool ("expected 'RESTman' in help output\ngot: " <> out)
+                   ("RESTman" `isInfixOf` out)
+
+    , testCase "unrecognised flag exits with failure" $ do
+        (code, _, _) <- runRestman ["--not-a-real-flag"]
+        case code of
+            ExitFailure _ -> pure ()
+            ExitSuccess -> assertFailure
+                "expected non-zero exit for unrecognised flag"
+
+    , testCase "--oneshot without URI exits with failure" $ do
+        (code, _, err) <- runRestman ["--oneshot"]
+        case code of
+            ExitFailure _ ->
+                assertBool ("expected 'requires' in error output\ngot: " <> err)
+                           ("requires" `isInfixOf` err)
+            ExitSuccess ->
+                assertFailure
+                    "expected non-zero exit when --oneshot has no URI"
+    ]
+
+fetchTests :: TestTree
+fetchTests = testGroup "fetch (--oneshot)"
+    [ testCase "default method is GET" $ withEchoServer $ \port -> do
+        er <- oneshotEcho port []
+        echoMethod er @?= "GET"
+
+    , testCase "-m DELETE sends DELETE" $ withEchoServer $ \port -> do
+        er <- oneshotEcho port ["-m", "DELETE"]
+        echoMethod er @?= "DELETE"
+
+    , testCase "-m POST with --payload sends request body" $ withEchoServer $ \port -> do
+        er <- oneshotEcho port ["-m", "POST", "--payload", "ping"]
+        echoBody er @?= "ping"
+
+    , testCase "-h sends a custom header" $ withEchoServer $ \port -> do
+        er <- oneshotEcho port ["-h", "X-Smoke:hello"]
+        Map.lookup "x-smoke" (echoHeaders er) @?= Just "hello"
+
+    , testCase "default headers include user-agent" $ withEchoServer $ \port -> do
+        er <- oneshotEcho port []
+        assertBool "expected 'user-agent' header with default settings"
+                   (Map.member "user-agent" (echoHeaders er))
+
+    , testCase "--no-default-headers omits user-agent" $ withEchoServer $ \port -> do
+        er <- oneshotEcho port ["--no-default-headers"]
+        assertBool "expected 'user-agent' to be absent with --no-default-headers"
+                   (Map.notMember "user-agent" (echoHeaders er))
+    ]
diff --git a/test/Spec.hs b/test/Spec.hs
--- a/test/Spec.hs
+++ b/test/Spec.hs
@@ -24,6 +24,7 @@
 import Skylighting.Format.Vty
   (attrColor, attrStyle, colorDistance, formatVty, vtyStyleAttr)
 
+import qualified BinarySpec
 import qualified HTTP.ClientIntegrationSpec
 import qualified HTTP.ClientSpec
 import qualified LibSpec
@@ -35,7 +36,7 @@
 import qualified UISpec
 
 main :: IO ()
-main = defaultMainWithIngredients [antXMLRunner] tests
+main = defaultMainWithIngredients (antXMLRunner : defaultIngredients) tests
 
 -- | A minimal Style with no token styles and no colors.
 emptyStyle :: Style
@@ -51,6 +52,7 @@
 tests = testGroup "restman"
   [ HTTP.ClientSpec.tests
   , HTTP.ClientIntegrationSpec.tests
+  , BinarySpec.tests
   , LibSpec.tests
   , TypesSpec.tests
   , UISpec.tests
