diff --git a/all.sas b/all.sas
index 82e9cd8..3d00f66 100644
--- a/all.sas
+++ b/all.sas
@@ -10105,8 +10105,9 @@ filename &tempref clear;
&prefix._INIT_NUM /* initialisation time as numeric */
&prefix._INIT_DTTM /* initialisation time in E8601DT26.6 format */
&prefix.WORK /* avoid typing %sysfunc(pathname(work)) every time */
+ &prefix.PROCESSMODE
+ &prefix._STPSRV_HEADER_LOC
;
-
%let sasjs_prefix=&prefix;
data _null_;
@@ -24525,6 +24526,198 @@ quit;
%end;
%mend mv_castabload;/**
+ @file mv_castabsave.sas
+ @brief Saves an in-memory CAS table back to persistent storage
+ @details Runs in SPRE against an active CAS session. Accepts a
+ SAS libref, derives the CAS caslib and session UUID from
+ sashelp.vlibnam, locates the owning CAS server via the
+ casManagement REST API, then queries the table endpoint to
+ discover the original source file and saves back to that path.
+ CASUTIL infers the file type from the output file extension.
+
+ A CAS session must already be established by the caller, eg:
+
+ cas mysess;
+ libname mylib cas caslib=Public;
+ %mv_castabsave(lib=mylib, table=BASEBALL)
+
+ @param [in] lib= SAS libref for the CAS caslib
+ @param [in] table= Name of the in-memory CAS table to save
+ @param [in] mdebug= (0) Set to 1 to enable verbose logging:
+ - echoes resolved parameters
+ - prints HTTP response body
+ - enables mprint/notes during PROC calls
+
+
SAS Macros
+ @li mf_getplatform.sas
+ @li mf_getuniquefileref.sas
+ @li mf_getuniquelibref.sas
+ @li mp_abort.sas
+
+**/
+
+%macro mv_castabsave(
+ lib=
+ ,table=
+ ,mdebug=0
+);
+
+%local _sysopts base_uri caslib uuid server
+ srcfile srccaslib fname1 libref1 ftmp i _svcount;
+%let _sysopts=%sysfunc(getoption(mprint)) %sysfunc(getoption(notes));
+
+/* ---- input validation -------------------------------------------------- */
+%mp_abort(
+ iftrue=("&lib"="" or "&table"=""),
+ msg=%str(lib= and table= are required)
+)
+
+%if &mdebug=1 %then %do;
+ %put &=lib;
+ %put &=table;
+ options mprint notes;
+%end;
+
+/* ---- derive caslib and session UUID from sashelp.vlibnam --------------- */
+data _null_;
+ set sashelp.vlibnam(
+ where=(libname="%upcase(&lib)"
+ and sysname in ("Caslib","Session UUID"))
+ );
+ if sysname="Caslib" then call symputx('caslib',sysvalue,'L');
+ else call symputx('uuid',sysvalue,'L');
+run;
+
+%mp_abort(
+ iftrue=("&caslib"=""),
+ msg=%str(&lib is not an assigned CAS libref)
+)
+
+%mp_abort(
+ iftrue=("&uuid"=""),
+ msg=%str(No session UUID found for libref &lib)
+)
+
+%if &mdebug=1 %then %do;
+ %put &=caslib;
+ %put &=uuid;
+%end;
+
+%let base_uri=%mf_getplatform(VIYARESTAPI);
+
+/* ---- get list of CAS servers ------------------------------------------- */
+%let fname1=%mf_getuniquefileref();
+%let libref1=%mf_getuniquelibref();
+
+proc http method='GET' out=&fname1 oauth_bearer=sas_services
+ url="&base_uri/casManagement/servers";
+run;
+
+%mp_abort(
+ iftrue=(&SYS_PROCHTTP_STATUS_CODE ne 200),
+ msg=%str(&SYS_PROCHTTP_STATUS_CODE &SYS_PROCHTTP_STATUS_PHRASE)
+)
+
+libname &libref1 JSON fileref=&fname1;
+
+data _null_;
+ set &libref1..items;
+ call symputx(cats('_sv_', _n_), name, 'L');
+ call symputx('_svcount', _n_, 'L');
+run;
+
+libname &libref1 clear;
+filename &fname1 clear;
+
+/* ---- find which server owns this session ------------------------------- */
+%do i=1 %to &_svcount;
+ %if "&server"="" %then %do;
+ %if &mdebug=1 %then %put checking server: &&_sv_&i;
+ %let ftmp=%mf_getuniquefileref();
+ proc http method='GET' out=&ftmp oauth_bearer=sas_services
+ url="&base_uri/casManagement/servers/&&_sv_&i/sessions/&uuid";
+ run;
+ %if &SYS_PROCHTTP_STATUS_CODE=200
+ %then %let server=&&_sv_&i;
+ filename &ftmp clear;
+ %end;
+%end;
+
+%mp_abort(
+ iftrue=("&server"=""),
+ msg=%str(Could not find owning server for CAS session &uuid)
+)
+
+%if &mdebug=1 %then %put &=server;
+
+/* ---- discover srcfile from REST endpoint ------------------------------- */
+%let fname1=%mf_getuniquefileref();
+%let libref1=%mf_getuniquelibref();
+
+proc http method='GET' out=&fname1 oauth_bearer=sas_services
+ url="&base_uri/casManagement/servers/&server/caslibs/&caslib/tables/&table";
+run;
+
+%if &mdebug=1 %then %do;
+ %put &=SYS_PROCHTTP_STATUS_CODE &=SYS_PROCHTTP_STATUS_PHRASE;
+ data _null_;
+ infile &fname1;
+ input;
+ putlog _infile_;
+ run;
+%end;
+
+%mp_abort(
+ iftrue=(&SYS_PROCHTTP_STATUS_CODE=404),
+ msg=%str(&caslib..&table not found - is it loaded in memory?)
+)
+%mp_abort(
+ iftrue=(&SYS_PROCHTTP_STATUS_CODE ne 200),
+ msg=%str(&SYS_PROCHTTP_STATUS_CODE &SYS_PROCHTTP_STATUS_PHRASE)
+)
+
+libname &libref1 JSON fileref=&fname1;
+
+data _null_;
+ set &libref1..tablereference;
+ call symputx('srcfile', sourceTableName, 'L');
+ call symputx('srccaslib', sourceCaslibName, 'L');
+ stop;
+run;
+
+libname &libref1 clear;
+filename &fname1 clear;
+
+%mp_abort(
+ iftrue=("&srcfile"="" or "&srccaslib"=""),
+ msg=%str(No sourceTableName/sourceCaslibName for &caslib..&table)
+)
+
+%if &mdebug=1 %then %put &=srcfile;
+
+/* ---- save to disk ------------------------------------------------------- */
+proc casutil;
+ save casdata="&table"
+ incaslib="&caslib"
+ casout="&srcfile"
+ outcaslib="&srccaslib"
+ replace;
+quit;
+
+%mp_abort(
+ iftrue=(&syscc ne 0),
+ msg=%str(Save failed for &caslib..&table)
+)
+
+%put NOTE: Table &caslib..&table saved to &srcfile;
+
+/* ---- restore options --------------------------------------------------- */
+%if &mdebug=1 %then %do;
+ options &_sysopts;
+%end;
+
+%mend mv_castabsave;
+/**
@file
@brief Creates a file in SAS Drive using the API method
@details Creates a file in SAS Drive using the API interface.