<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>https://docs.analytica.com/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Lchrisman</id>
	<title>Analytica Docs - User contributions [en]</title>
	<link rel="self" type="application/atom+xml" href="https://docs.analytica.com/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Lchrisman"/>
	<link rel="alternate" type="text/html" href="https://docs.analytica.com/index.php/Special:Contributions/Lchrisman"/>
	<updated>2026-10-02T15:08:48Z</updated>
	<subtitle>User contributions</subtitle>
	<generator>MediaWiki 1.43.9</generator>
	<entry>
		<id>https://docs.analytica.com/index.php?title=Salesforce_REST_library&amp;diff=64639</id>
		<title>Salesforce REST library</title>
		<link rel="alternate" type="text/html" href="https://docs.analytica.com/index.php?title=Salesforce_REST_library&amp;diff=64639"/>
		<updated>2026-09-30T19:26:51Z</updated>

		<summary type="html">&lt;p&gt;Lchrisman: Document SFLogin(): interactive browser sign-in&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:Function libraries]]&lt;br /&gt;
[[Category:Integration Functions]]&lt;br /&gt;
&lt;br /&gt;
{{ReleaseBar}}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Requires [[Analytica 7.1]] {{Analytica Developer}} edition or better.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The &#039;&#039;&#039;Salesforce REST library&#039;&#039;&#039; lets your model authenticate with [https://www.salesforce.com/ Salesforce] and read Salesforce records and metadata through the Salesforce REST API, version 67.0. Use it to pull live data -- accounts, contacts, opportunities, or your own custom objects -- straight into an Analytica model, so that your analysis works from current CRM data instead of an exported snapshot.&lt;br /&gt;
&lt;br /&gt;
It provides:&lt;br /&gt;
* Interactive sign-in through your web browser, as yourself, for desktop use&lt;br /&gt;
* OAuth 2.0 client-credentials authentication for unattended, server-to-server use&lt;br /&gt;
* A legacy SOAP-login option as a temporary migration bridge&lt;br /&gt;
* SOQL query execution, including automatic pagination&lt;br /&gt;
* Record-selection and record-retrieval helpers&lt;br /&gt;
* Salesforce object and field metadata&lt;br /&gt;
* SOSL search&lt;br /&gt;
&lt;br /&gt;
This library reads from Salesforce. It has no functions to create, update, or delete Salesforce records.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Download:&#039;&#039;&#039; [[media:Salesforce REST library.ana|Salesforce REST library.ana]] (v. 1.02)&lt;br /&gt;
&lt;br /&gt;
== Requirements ==&lt;br /&gt;
&lt;br /&gt;
To use this library, you need:&lt;br /&gt;
* [[Analytica 7.1]] or later, {{Analytica Developer}} edition or better. The library is built on [[ReadFromUrl]](), which is not available in the Professional or Player editions.&lt;br /&gt;
* A Salesforce account. [[#SFLogin|SFLogin]]() needs nothing more. [[#ClientCredentialsAuth|ClientCredentialsAuth]]() also needs an OAuth client configured in Salesforce (see [[#Setting up Salesforce for client-credentials authentication|Setting up Salesforce]] below).&lt;br /&gt;
* Internet access allowing Analytica to reach your Salesforce login and instance domains over HTTPS. If you have a strong firewall, you may need to add a rule to allow this.&lt;br /&gt;
&lt;br /&gt;
== Getting started ==&lt;br /&gt;
&lt;br /&gt;
# [[media:Salesforce REST library.ana|Download the library]] and save it into your &amp;lt;code&amp;gt;&amp;quot;C:\Program Files\Lumina\Analytica 7.1\Libraries&amp;quot;&amp;lt;/code&amp;gt; folder.&lt;br /&gt;
# Launch Analytica and open your model, or start a new one.&lt;br /&gt;
# Select &#039;&#039;&#039;Add Library...&#039;&#039;&#039; from the &#039;&#039;&#039;File&#039;&#039;&#039; menu, select &amp;lt;code&amp;gt;Salesforce REST library.ana&amp;lt;/code&amp;gt;, click &#039;&#039;&#039;OK&#039;&#039;&#039;, select &#039;&#039;&#039;Link&#039;&#039;&#039;, then click &#039;&#039;&#039;OK&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
The simplest way to connect is to sign in as yourself. [[#SFLogin|SFLogin]]() opens your web browser at the Salesforce login page and returns the connection when you&#039;ve signed in:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre style=&amp;quot;background:white; border:white; margin-left: 1em;&amp;quot;&amp;gt;&lt;br /&gt;
Variable Sf ::= SFLogin()&lt;br /&gt;
&lt;br /&gt;
Variable Active_accounts ::= SalesforceSelect( Sf, &amp;quot;Account&amp;quot;, &amp;quot;Id, Name, Industry&amp;quot;,&lt;br /&gt;
        where: &amp;quot;IsDeleted = false&amp;quot;,&lt;br /&gt;
        order_by: &amp;quot;Name&amp;quot;,&lt;br /&gt;
        row_limit: 100 )&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For unattended use, such as a model that runs on a server, authenticate with client credentials instead. A model then uses three Variables -- an authentication Struct, a connection Struct, and one or more query results:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre style=&amp;quot;background:white; border:white; margin-left: 1em;&amp;quot;&amp;gt;&lt;br /&gt;
Variable Sf_auth ::= ClientCredentialsAuth(&lt;br /&gt;
        &amp;quot;https://login.salesforce.com&amp;quot;,&lt;br /&gt;
        Sf_client_id,&lt;br /&gt;
        Sf_client_secret )&lt;br /&gt;
&lt;br /&gt;
Variable Sf ::= Salesforce( Sf_auth )&lt;br /&gt;
&lt;br /&gt;
Variable Active_accounts ::= SalesforceSelect( Sf, &amp;quot;Account&amp;quot;, &amp;quot;Id, Name, Industry&amp;quot;,&lt;br /&gt;
        where: &amp;quot;IsDeleted = false&amp;quot;,&lt;br /&gt;
        order_by: &amp;quot;Name&amp;quot;,&lt;br /&gt;
        row_limit: 100 )&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;Active_accounts&amp;lt;/code&amp;gt; is a query-result [[Struct]]. Its list of records is:&lt;br /&gt;
:&amp;lt;code&amp;gt;Active_accounts -&amp;gt; records&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To read a field from the first record:&lt;br /&gt;
:&amp;lt;code&amp;gt;SalesforceField( Active_accounts -&amp;gt; records[@=1], &amp;quot;Name&amp;quot; )&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
When you authenticate to a sandbox, use &amp;lt;code&amp;gt;&amp;quot;https://test.salesforce.com&amp;quot;&amp;lt;/code&amp;gt; in place of &amp;lt;code&amp;gt;&amp;quot;https://login.salesforce.com&amp;quot;&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== A brief introduction to SOQL ==&lt;br /&gt;
&lt;br /&gt;
Salesforce Object Query Language (SOQL) resembles SQL, but it queries Salesforce objects and fields by their &#039;&#039;&#039;API names&#039;&#039;&#039; rather than by table and column names. A basic query looks like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre style=&amp;quot;background:white; border:white; margin-left: 1em;&amp;quot;&amp;gt;&lt;br /&gt;
SELECT Id, Name, Industry&lt;br /&gt;
FROM Account&lt;br /&gt;
WHERE Industry = &#039;Technology&#039;&lt;br /&gt;
ORDER BY Name&lt;br /&gt;
LIMIT 100&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Standard object names include &amp;lt;code&amp;gt;Account&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Contact&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;Opportunity&amp;lt;/code&amp;gt;. API names of custom objects and custom fields usually end in &amp;lt;code&amp;gt;__c&amp;lt;/code&amp;gt; -- for example &amp;lt;code&amp;gt;License__c&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;Expiration_Date__c&amp;lt;/code&amp;gt;. An object&#039;s API name is often not the same as the label you see in the Salesforce user interface, so use [[#SalesforceObjects|SalesforceObjects]]() and [[#SalesforceFields|SalesforceFields]]() to discover the names you need.&lt;br /&gt;
&lt;br /&gt;
Whenever you insert a text value into a SOQL expression, quote it with [[#SalesforceSoqlQuote|SalesforceSoqlQuote]](). Don&#039;t use that function for object or field API names.&lt;br /&gt;
&lt;br /&gt;
For the full language, see the Salesforce [https://developer.salesforce.com/docs/atlas.en-us.soql_sosl.meta/soql_sosl/ SOQL and SOSL Reference].&lt;br /&gt;
&lt;br /&gt;
== Setting up Salesforce for client-credentials authentication ==&lt;br /&gt;
&lt;br /&gt;
[[#ClientCredentialsAuth|ClientCredentialsAuth]]() is meant for unattended, server-to-server access. It needs an OAuth client configured in Salesforce, plus a dedicated Salesforce user whose permissions the integration runs under.&lt;br /&gt;
&lt;br /&gt;
Salesforce terminology and Setup screens vary by edition and release. In current releases the OAuth client is normally an &#039;&#039;&#039;External Client App&#039;&#039;&#039;; some organizations still use a &#039;&#039;&#039;Connected App&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
A Salesforce administrator generally needs to:&lt;br /&gt;
# Create an &#039;&#039;&#039;External Client App&#039;&#039;&#039; in Salesforce Setup.&lt;br /&gt;
# Enable &#039;&#039;&#039;OAuth settings&#039;&#039;&#039; and the &#039;&#039;&#039;OAuth 2.0 Client Credentials Flow&#039;&#039;&#039;.&lt;br /&gt;
# Grant the OAuth scope needed for REST access, normally &#039;&#039;&#039;Manage user data via APIs&#039;&#039;&#039; (&amp;lt;code&amp;gt;api&amp;lt;/code&amp;gt;). Avoid broader scopes unless you need them.&lt;br /&gt;
# Save the app, then copy its &#039;&#039;&#039;Consumer Key&#039;&#039;&#039; and &#039;&#039;&#039;Consumer Secret&#039;&#039;&#039;. These become the «client_id» and «client_secret» parameters in Analytica.&lt;br /&gt;
# In the app&#039;s OAuth policies, select a dedicated &#039;&#039;&#039;Run As&#039;&#039;&#039; integration user for the client-credentials flow. Depending on the app type, Salesforce may also require &#039;&#039;&#039;Admin approved users are pre-authorized&#039;&#039;&#039;.&lt;br /&gt;
# Give the integration user the permissions your model needs -- API access, plus read access to the required objects and fields. Prefer permission sets and least privilege.&lt;br /&gt;
# If the app uses pre-authorization, assign the app&#039;s permission set or profile authorization to the integration user.&lt;br /&gt;
&lt;br /&gt;
The library sends the token request to:&lt;br /&gt;
:&amp;lt;code&amp;gt;https://login.salesforce.com/services/oauth2/token&amp;lt;/code&amp;gt;&lt;br /&gt;
or, for a sandbox:&lt;br /&gt;
:&amp;lt;code&amp;gt;https://test.salesforce.com/services/oauth2/token&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Salesforce returns an access token and the instance URL that the library uses for all later requests.&lt;br /&gt;
&lt;br /&gt;
For current Salesforce-side details, see the Salesforce [https://developer.salesforce.com/docs/platform/mobile-sdk/guide/oauth-client-credentials-flow.html OAuth 2.0 Client Credentials Flow] documentation.&lt;br /&gt;
&lt;br /&gt;
=== Protect the client secret ===&lt;br /&gt;
&lt;br /&gt;
The consumer secret grants access as the configured integration user. Don&#039;t save it in a model that you distribute to people who shouldn&#039;t have the credential, and don&#039;t commit it to source control. Where you can, obtain it at deployment time from your organization&#039;s approved secret-management mechanism.&lt;br /&gt;
&lt;br /&gt;
== Authentication and connection ==&lt;br /&gt;
&lt;br /&gt;
The library offers three ways to authenticate:&lt;br /&gt;
* [[#SFLogin|SFLogin]]() -- you sign in through your browser as yourself. Best for interactive, desktop use.&lt;br /&gt;
* [[#ClientCredentialsAuth|ClientCredentialsAuth]]() -- unattended, server-to-server access as a dedicated integration user.&lt;br /&gt;
* [[#SoapLoginAuth|SoapLoginAuth]]() -- legacy username-and-password login, which stops working on 1-Jun-2027.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;span id=&amp;quot;SFLogin&amp;quot;&amp;gt;SFLogin()&amp;lt;/span&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Signs you in to Salesforce interactively and returns a connection Struct, ready to pass to the query, record, search, and metadata functions.&lt;br /&gt;
&lt;br /&gt;
When it evaluates, it opens your default web browser at the Salesforce login page. Sign in as usual -- including any multi-factor authentication your organization requires -- and, the first time, approve access for &#039;&#039;&#039;Desktop Analytica&#039;&#039;&#039;. The browser then says you may close the window, and the evaluation completes. Your model reads Salesforce with your own permissions: it sees the objects, fields, and records that you can see.&lt;br /&gt;
&lt;br /&gt;
You don&#039;t need to set anything up in Salesforce, and there is no client ID or secret for you to supply or protect. It uses the OAuth 2.0 authorization-code flow with PKCE, through an external client app that Lumina registered with Salesforce. Your password goes only to Salesforce, never to Analytica or your model.&lt;br /&gt;
&lt;br /&gt;
Example:&lt;br /&gt;
:&amp;lt;code&amp;gt;Variable Sf ::= SFLogin()&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The Struct it returns contains the same members as the one from [[#Salesforce|Salesforce]](), so you use it directly -- don&#039;t pass it to &amp;lt;code&amp;gt;Salesforce()&amp;lt;/code&amp;gt;:&lt;br /&gt;
* &amp;lt;code&amp;gt;api_version&amp;lt;/code&amp;gt; -- &amp;lt;code&amp;gt;&amp;quot;67.0&amp;quot;&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;instance_url&amp;lt;/code&amp;gt; -- your organization&#039;s instance, from Salesforce&#039;s user-information service&lt;br /&gt;
* &amp;lt;code&amp;gt;access_token&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;rest_base_url&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Things to know:&lt;br /&gt;
* It signs in to production Salesforce, at &amp;lt;code&amp;gt;login.salesforce.com&amp;lt;/code&amp;gt;. It doesn&#039;t support sandbox logins.&lt;br /&gt;
* During sign-in, Salesforce sends the result back to Analytica at &amp;lt;code&amp;gt;http://localhost:18756&amp;lt;/code&amp;gt;. Port 18756 must be free on your computer, and your firewall must allow Analytica to listen on it for that local connection.&lt;br /&gt;
* It doesn&#039;t renew the access token. When your Salesforce session expires, requests fail with HTTP 401; recompute &amp;lt;code&amp;gt;SFLogin()&amp;lt;/code&amp;gt; to sign in again.&lt;br /&gt;
* Because it needs a person at a browser, don&#039;t use it in a model that runs unattended. Use [[#ClientCredentialsAuth|ClientCredentialsAuth]]() for that.&lt;br /&gt;
&lt;br /&gt;
[[Parameter types]]: &amp;lt;code&amp;gt;SFLogin()&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;span id=&amp;quot;ClientCredentialsAuth&amp;quot;&amp;gt;ClientCredentialsAuth(login_url, client_id, client_secret)&amp;lt;/span&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Authenticates using the OAuth 2.0 client-credentials flow and returns a [[Struct]] holding the access token.&lt;br /&gt;
&lt;br /&gt;
:{| class=wikitable&lt;br /&gt;
! Parameter&lt;br /&gt;
! Meaning&lt;br /&gt;
|-&lt;br /&gt;
| «login_url»&lt;br /&gt;
| &amp;lt;code&amp;gt;&amp;quot;https://login.salesforce.com&amp;quot;&amp;lt;/code&amp;gt; for production, or &amp;lt;code&amp;gt;&amp;quot;https://test.salesforce.com&amp;quot;&amp;lt;/code&amp;gt; for a sandbox&lt;br /&gt;
|-&lt;br /&gt;
| «client_id»&lt;br /&gt;
| Consumer key from the Salesforce External Client App or Connected App&lt;br /&gt;
|-&lt;br /&gt;
| «client_secret»&lt;br /&gt;
| Consumer secret from the same app&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The Struct it returns contains these members:&lt;br /&gt;
* &amp;lt;code&amp;gt;access_token&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;instance_url&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;status_code&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;status_text&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;raw_response&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Example:&lt;br /&gt;
:&amp;lt;code&amp;gt;ClientCredentialsAuth( &amp;quot;https://login.salesforce.com&amp;quot;, Sf_client_id, Sf_client_secret )&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
When authentication fails, it reports an Analytica error that includes the HTTP status and the Salesforce response.&lt;br /&gt;
&lt;br /&gt;
[[Parameter types]]: &amp;lt;code&amp;gt;ClientCredentialsAuth(login_url: Text; client_id: Text; client_secret: Text)&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;span id=&amp;quot;Salesforce&amp;quot;&amp;gt;Salesforce(auth)&amp;lt;/span&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Creates the connection Struct that you pass to every query, record, search, and metadata function. «auth» is the Struct returned by [[#ClientCredentialsAuth|ClientCredentialsAuth]]() or [[#SoapLoginAuth|SoapLoginAuth]](). You don&#039;t need this function with [[#SFLogin|SFLogin]](), which returns a connection itself.&lt;br /&gt;
&lt;br /&gt;
The Struct it returns contains these members:&lt;br /&gt;
* &amp;lt;code&amp;gt;api_version&amp;lt;/code&amp;gt; -- &amp;lt;code&amp;gt;&amp;quot;67.0&amp;quot;&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;instance_url&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;access_token&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;rest_base_url&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Example:&lt;br /&gt;
:&amp;lt;code&amp;gt;Salesforce( Sf_auth )&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
It&#039;s usually clearer to define the authentication and the connection as separate Variables, as shown in [[#Getting started|Getting started]], so that you can inspect an authentication failure on its own. You can also nest the calls:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre style=&amp;quot;background:white; border:white; margin-left: 1em;&amp;quot;&amp;gt;&lt;br /&gt;
Salesforce(&lt;br /&gt;
    ClientCredentialsAuth(&lt;br /&gt;
        &amp;quot;https://login.salesforce.com&amp;quot;,&lt;br /&gt;
        Sf_client_id,&lt;br /&gt;
        Sf_client_secret ) )&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
All REST calls use API version 67.0, including calls authenticated through [[#SoapLoginAuth|SoapLoginAuth]]().&lt;br /&gt;
&lt;br /&gt;
[[Parameter types]]: &amp;lt;code&amp;gt;Salesforce(auth)&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;span id=&amp;quot;SoapLoginAuth&amp;quot;&amp;gt;SoapLoginAuth(login_url, username, password, security_token)&amp;lt;/span&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Logs in with the legacy Salesforce SOAP Partner API &amp;lt;code&amp;gt;login()&amp;lt;/code&amp;gt; call and returns a Struct whose session ID the REST functions use as a bearer token.&lt;br /&gt;
&lt;br /&gt;
«security_token» is the Salesforce security token for that user account. The function concatenates it onto the password, as SOAP login requires.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;This authentication method is a migration bridge only.&#039;&#039;&#039; Salesforce retires the SOAP &amp;lt;code&amp;gt;login()&amp;lt;/code&amp;gt; mechanism on &#039;&#039;&#039;1-Jun-2027&#039;&#039;&#039;, after which this function stops working. Write new models against [[#SFLogin|SFLogin]]() or [[#ClientCredentialsAuth|ClientCredentialsAuth]](), and migrate existing ones before that date.&lt;br /&gt;
&lt;br /&gt;
[[Parameter types]]: &amp;lt;code&amp;gt;SoapLoginAuth(login_url: Text; username: Text; password: Text; security_token: Text)&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Query functions ==&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;span id=&amp;quot;SalesforceSelect&amp;quot;&amp;gt;SalesforceSelect(sf, sobject, fields, &#039;&#039;where, order_by, row_limit&#039;&#039;)&amp;lt;/span&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Builds and runs a common SOQL &amp;lt;code&amp;gt;SELECT&amp;lt;/code&amp;gt;. This is the easiest entry point when your query has a straightforward &amp;lt;code&amp;gt;WHERE&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;ORDER BY&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;LIMIT&amp;lt;/code&amp;gt; structure. It returns the same query-result Struct as [[#SalesforceQuery|SalesforceQuery]]().&lt;br /&gt;
&lt;br /&gt;
:{| class=wikitable&lt;br /&gt;
! Parameter&lt;br /&gt;
! Meaning&lt;br /&gt;
|-&lt;br /&gt;
| «sf»&lt;br /&gt;
| The connection returned by [[#Salesforce|Salesforce]]()&lt;br /&gt;
|-&lt;br /&gt;
| «sobject»&lt;br /&gt;
| Salesforce object API name, such as &amp;lt;code&amp;gt;&amp;quot;Account&amp;quot;&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| «fields»&lt;br /&gt;
| Comma-separated field API names&lt;br /&gt;
|-&lt;br /&gt;
| «where»&lt;br /&gt;
| Optional SOQL condition, without the &amp;lt;code&amp;gt;WHERE&amp;lt;/code&amp;gt; keyword&lt;br /&gt;
|-&lt;br /&gt;
| «order_by»&lt;br /&gt;
| Optional ordering expression, without the &amp;lt;code&amp;gt;ORDER BY&amp;lt;/code&amp;gt; keywords&lt;br /&gt;
|-&lt;br /&gt;
| «row_limit»&lt;br /&gt;
| Optional maximum number of records. &amp;lt;code&amp;gt;0&amp;lt;/code&amp;gt;, the default, applies no explicit limit&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The three optional parameters are declared &amp;lt;code&amp;gt;Named&amp;lt;/code&amp;gt;, so pass them by name:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre style=&amp;quot;background:white; border:white; margin-left: 1em;&amp;quot;&amp;gt;&lt;br /&gt;
SalesforceSelect( Sf, &amp;quot;Contact&amp;quot;, &amp;quot;Id, Name, Email, Account.Name&amp;quot;,&lt;br /&gt;
        where: &amp;quot;Email = &amp;quot; &amp;amp; SalesforceSoqlQuote( Target_email ),&lt;br /&gt;
        order_by: &amp;quot;Name&amp;quot;,&lt;br /&gt;
        row_limit: 20 )&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
It validates the object and field API names, but «where» and «order_by» are SOQL fragments that you supply. Quote any text value you interpolate into them with [[#SalesforceSoqlQuote|SalesforceSoqlQuote]](), as in the example above.&lt;br /&gt;
&lt;br /&gt;
[[Parameter types]]: &amp;lt;code&amp;gt;SalesforceSelect(sf; sobject: Text; fields: Text; where: Named Text := &amp;quot;&amp;quot;; order_by: Named Text := &amp;quot;&amp;quot;; row_limit: Named Number := 0)&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;span id=&amp;quot;SalesforceQuery&amp;quot;&amp;gt;SalesforceQuery(sf, soql, &#039;&#039;include_deleted, max_pages&#039;&#039;)&amp;lt;/span&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Runs a complete SOQL expression and follows Salesforce pagination until it has retrieved every page.&lt;br /&gt;
&lt;br /&gt;
It returns a query-result Struct with these members:&lt;br /&gt;
&lt;br /&gt;
:{| class=wikitable&lt;br /&gt;
! Member&lt;br /&gt;
! Meaning&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;records&amp;lt;/code&amp;gt;&lt;br /&gt;
| List of parsed Salesforce record Structs&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;total_size&amp;lt;/code&amp;gt;&lt;br /&gt;
| Total matching records, as reported by Salesforce&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;pages&amp;lt;/code&amp;gt;&lt;br /&gt;
| Number of pages retrieved&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;done&amp;lt;/code&amp;gt;&lt;br /&gt;
| Whether Salesforce reported the query complete&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;next_records_url&amp;lt;/code&amp;gt;&lt;br /&gt;
| URL of a subsequent page. Normally &amp;lt;code&amp;gt;Null&amp;lt;/code&amp;gt; once the query is complete&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;raw_response&amp;lt;/code&amp;gt;&lt;br /&gt;
| Raw response for the final page&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Set «include_deleted» to &amp;lt;code&amp;gt;True&amp;lt;/code&amp;gt; to use Salesforce&#039;s query-all behavior, which can include deleted and archived records where Salesforce supports it.&lt;br /&gt;
&lt;br /&gt;
«max_pages», 100 by default, protects your model from an unexpectedly large or insufficiently bounded query. If the result needs more pages than this, it raises an error rather than quietly returning an incomplete result.&lt;br /&gt;
&lt;br /&gt;
Example:&lt;br /&gt;
&amp;lt;pre style=&amp;quot;background:white; border:white; margin-left: 1em;&amp;quot;&amp;gt;&lt;br /&gt;
Local soql := &amp;quot;SELECT Id, Name, Amount, CloseDate &amp;quot; &amp;amp;&lt;br /&gt;
              &amp;quot;FROM Opportunity &amp;quot; &amp;amp;&lt;br /&gt;
              &amp;quot;WHERE IsClosed = false &amp;quot; &amp;amp;&lt;br /&gt;
              &amp;quot;ORDER BY CloseDate&amp;quot;;&lt;br /&gt;
SalesforceQuery( Sf, soql )&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Parameter types]]: &amp;lt;code&amp;gt;SalesforceQuery(sf; soql: Text; include_deleted := False; max_pages: Number := 100)&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;span id=&amp;quot;SalesforceQueryFirst&amp;quot;&amp;gt;SalesforceQueryFirst(sf, soql)&amp;lt;/span&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Runs a SOQL expression and returns the first matching record Struct, or &amp;lt;code&amp;gt;Null&amp;lt;/code&amp;gt; when nothing matches. Include an &amp;lt;code&amp;gt;ORDER BY&amp;lt;/code&amp;gt; clause whenever more than one record could match, so that you get a predictable result.&lt;br /&gt;
&lt;br /&gt;
Example:&lt;br /&gt;
&amp;lt;pre style=&amp;quot;background:white; border:white; margin-left: 1em;&amp;quot;&amp;gt;&lt;br /&gt;
SalesforceQueryFirst( Sf,&lt;br /&gt;
    &amp;quot;SELECT Id, Name FROM Account WHERE Name = &amp;quot; &amp;amp;&lt;br /&gt;
    SalesforceSoqlQuote( Account_name ) &amp;amp;&lt;br /&gt;
    &amp;quot; ORDER BY LastModifiedDate DESC LIMIT 1&amp;quot; )&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Parameter types]]: &amp;lt;code&amp;gt;SalesforceQueryFirst(sf; soql: Text)&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;span id=&amp;quot;SalesforceCount&amp;quot;&amp;gt;SalesforceCount(sf, soql, &#039;&#039;include_deleted&#039;&#039;)&amp;lt;/span&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Returns the query&#039;s &amp;lt;code&amp;gt;totalSize&amp;lt;/code&amp;gt; as a number, so you don&#039;t have to retrieve and inspect the record list to count matches.&lt;br /&gt;
&lt;br /&gt;
Example:&lt;br /&gt;
:&amp;lt;code&amp;gt;SalesforceCount( Sf, &amp;quot;SELECT Id FROM Opportunity WHERE IsClosed = false&amp;quot; )&amp;lt;/code&amp;gt; &amp;amp;rarr; &amp;lt;code&amp;gt;42&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Parameter types]]: &amp;lt;code&amp;gt;SalesforceCount(sf; soql: Text; include_deleted := False)&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;span id=&amp;quot;SalesforceQueryPage&amp;quot;&amp;gt;SalesforceQueryPage(sf, soql_or_next_url, &#039;&#039;include_deleted&#039;&#039;)&amp;lt;/span&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Retrieves exactly one query page. Pass a SOQL expression to get the first page, or the &amp;lt;code&amp;gt;next_records_url&amp;lt;/code&amp;gt; from a previous result to get the page after it.&lt;br /&gt;
&lt;br /&gt;
Most models should use [[#SalesforceQuery|SalesforceQuery]](), which handles pagination for you. Use this function when your model deliberately needs page-by-page control.&lt;br /&gt;
&lt;br /&gt;
[[Parameter types]]: &amp;lt;code&amp;gt;SalesforceQueryPage(sf; soql_or_next_url: Text; include_deleted := False)&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Record functions ==&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;span id=&amp;quot;SalesforceGetRecord&amp;quot;&amp;gt;SalesforceGetRecord(sf, sobject, record_id, fields)&amp;lt;/span&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Retrieves one record, given its Salesforce object API name and record ID. It returns the record Struct itself, not a query-result wrapper.&lt;br /&gt;
&lt;br /&gt;
* «record_id» must be a 15- or 18-character Salesforce ID.&lt;br /&gt;
* «fields» is a comma-separated list of field API names.&lt;br /&gt;
&lt;br /&gt;
Example:&lt;br /&gt;
:&amp;lt;code&amp;gt;SalesforceGetRecord( Sf, &amp;quot;Account&amp;quot;, Account_id, &amp;quot;Id, Name, Industry, BillingCountry&amp;quot; )&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Parameter types]]: &amp;lt;code&amp;gt;SalesforceGetRecord(sf; sobject: Text; record_id: Text; fields: Text)&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;span id=&amp;quot;SalesforceGetRecords&amp;quot;&amp;gt;SalesforceGetRecords(sf, sobject, record_ids, fields)&amp;lt;/span&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Retrieves several records from a list of Salesforce record IDs, and returns a list of record Structs. Internally it builds a SOQL &amp;lt;code&amp;gt;IN (...)&amp;lt;/code&amp;gt; condition from the IDs.&lt;br /&gt;
&lt;br /&gt;
Example:&lt;br /&gt;
:&amp;lt;code&amp;gt;SalesforceGetRecords( Sf, &amp;quot;Contact&amp;quot;, Contact_ids, &amp;quot;Id, Name, Email&amp;quot; )&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Parameter types]]: &amp;lt;code&amp;gt;SalesforceGetRecords(sf; sobject: Text; record_ids: List; fields: Text)&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;span id=&amp;quot;SalesforceField&amp;quot;&amp;gt;SalesforceField(record, field_path, &#039;&#039;default_value&#039;&#039;)&amp;lt;/span&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Reads a field from a Salesforce record Struct when the field name is a text value. A dotted «field_path» traverses relationship Structs.&lt;br /&gt;
&lt;br /&gt;
Examples:&lt;br /&gt;
:&amp;lt;code&amp;gt;SalesforceField( Contact_record, &amp;quot;Email&amp;quot; )&amp;lt;/code&amp;gt;&lt;br /&gt;
:&amp;lt;code&amp;gt;SalesforceField( Contact_record, &amp;quot;Account.Name&amp;quot;, &amp;quot;No account&amp;quot; )&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
It returns «default_value», &amp;lt;code&amp;gt;Null&amp;lt;/code&amp;gt; by default, when the member is absent or it can&#039;t traverse the path.&lt;br /&gt;
&lt;br /&gt;
When you already know the field name as you write the Definition, direct Struct access is simpler:&lt;br /&gt;
:&amp;lt;code&amp;gt;Contact_record -&amp;gt; Email&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Use &amp;lt;code&amp;gt;SalesforceField&amp;lt;/code&amp;gt; for dynamic field names, dotted paths, or when you want a controlled default.&lt;br /&gt;
&lt;br /&gt;
[[Parameter types]]: &amp;lt;code&amp;gt;SalesforceField(record; field_path: Text; default_value := Null)&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Quoting text values ==&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;span id=&amp;quot;SalesforceSoqlQuote&amp;quot;&amp;gt;SalesforceSoqlQuote(value)&amp;lt;/span&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Escapes a text value as a SOQL string literal and surrounds it with single quotes, handling quotes, backslashes, and control characters.&lt;br /&gt;
&lt;br /&gt;
Example:&lt;br /&gt;
:&amp;lt;code&amp;gt;&amp;quot;Name = &amp;quot; &amp;amp; SalesforceSoqlQuote( Customer_name )&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If &amp;lt;code&amp;gt;Customer_name&amp;lt;/code&amp;gt; is &amp;lt;code&amp;gt;O&#039;Reilly&amp;lt;/code&amp;gt;, this produces a valid quoted SOQL literal instead of letting the apostrophe terminate the value early.&lt;br /&gt;
&lt;br /&gt;
Use this function for &#039;&#039;&#039;values only&#039;&#039;&#039;. You can&#039;t safely turn Salesforce object or field API names into identifiers by quoting them -- the library validates those names instead.&lt;br /&gt;
&lt;br /&gt;
[[Intelligent Arrays|Array abstraction]] applies, so it also quotes a list of values. [[#SalesforceGetRecords|SalesforceGetRecords]]() relies on this when it builds its &amp;lt;code&amp;gt;IN (...)&amp;lt;/code&amp;gt; condition.&lt;br /&gt;
&lt;br /&gt;
[[Parameter types]]: &amp;lt;code&amp;gt;SalesforceSoqlQuote(value: Text)&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Metadata functions ==&lt;br /&gt;
&lt;br /&gt;
These functions help you discover the API names and properties available to the authenticated integration user.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;span id=&amp;quot;SalesforceObjects&amp;quot;&amp;gt;SalesforceObjects(sf)&amp;lt;/span&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Returns a list of summary Structs for the Salesforce objects that the authenticated user can see.&lt;br /&gt;
&lt;br /&gt;
Example:&lt;br /&gt;
:&amp;lt;code&amp;gt;SalesforceObjects( Sf )&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Useful members usually include the object&#039;s API &amp;lt;code&amp;gt;name&amp;lt;/code&amp;gt;, its display &amp;lt;code&amp;gt;label&amp;lt;/code&amp;gt;, and capability flags. Salesforce supplies the exact members, which vary by API release and object type.&lt;br /&gt;
&lt;br /&gt;
[[Parameter types]]: &amp;lt;code&amp;gt;SalesforceObjects(sf)&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;span id=&amp;quot;SalesforceDescribe&amp;quot;&amp;gt;SalesforceDescribe(sf, sobject)&amp;lt;/span&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Returns Salesforce&#039;s full metadata description for an object, including its fields, relationships, picklist entries, and access capabilities.&lt;br /&gt;
&lt;br /&gt;
Example:&lt;br /&gt;
:&amp;lt;code&amp;gt;SalesforceDescribe( Sf, &amp;quot;Opportunity&amp;quot; )&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Parameter types]]: &amp;lt;code&amp;gt;SalesforceDescribe(sf; sobject: Text)&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;span id=&amp;quot;SalesforceFields&amp;quot;&amp;gt;SalesforceFields(sf, sobject)&amp;lt;/span&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Returns just the list of field metadata Structs from [[#SalesforceDescribe|SalesforceDescribe]](). Use it to find field API names, types, labels, whether a field can be filtered, and its allowed picklist values.&lt;br /&gt;
&lt;br /&gt;
Example:&lt;br /&gt;
:&amp;lt;code&amp;gt;SalesforceFields( Sf, &amp;quot;Account&amp;quot; )&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Parameter types]]: &amp;lt;code&amp;gt;SalesforceFields(sf; sobject: Text)&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Search ==&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;span id=&amp;quot;SalesforceSearch&amp;quot;&amp;gt;SalesforceSearch(sf, sosl)&amp;lt;/span&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Runs a Salesforce Object Search Language (SOSL) expression and returns its &amp;lt;code&amp;gt;searchRecords&amp;lt;/code&amp;gt; list.&lt;br /&gt;
&lt;br /&gt;
SOQL queries known objects and fields; SOSL searches text across one or more objects. Use &amp;lt;code&amp;gt;SalesforceSearch&amp;lt;/code&amp;gt; when your search spans object types, or when it resembles a text search more than a structured query.&lt;br /&gt;
&lt;br /&gt;
Example:&lt;br /&gt;
&amp;lt;pre style=&amp;quot;background:white; border:white; margin-left: 1em;&amp;quot;&amp;gt;&lt;br /&gt;
SalesforceSearch( Sf,&lt;br /&gt;
    &amp;quot;FIND {Acme} IN NAME FIELDS &amp;quot; &amp;amp;&lt;br /&gt;
    &amp;quot;RETURNING Account(Id, Name), Contact(Id, Name, Email)&amp;quot; )&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For the full SOSL syntax, see the Salesforce [https://developer.salesforce.com/docs/atlas.en-us.soql_sosl.meta/soql_sosl/ SOQL and SOSL Reference].&lt;br /&gt;
&lt;br /&gt;
[[Parameter types]]: &amp;lt;code&amp;gt;SalesforceSearch(sf; sosl: Text)&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Working with returned records ==&lt;br /&gt;
&lt;br /&gt;
The library represents Salesforce JSON objects as Analytica [[Struct]]s, and collections as lists. Given:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre style=&amp;quot;background:white; border:white; margin-left: 1em;&amp;quot;&amp;gt;&lt;br /&gt;
Variable Result ::= SalesforceSelect( Sf, &amp;quot;Account&amp;quot;, &amp;quot;Id, Name, Owner.Name&amp;quot;,&lt;br /&gt;
        where: &amp;quot;BillingCountry = &amp;quot; &amp;amp; SalesforceSoqlQuote( Country ),&lt;br /&gt;
        order_by: &amp;quot;Name&amp;quot;,&lt;br /&gt;
        row_limit: 100 )&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
the records are:&lt;br /&gt;
:&amp;lt;code&amp;gt;Result -&amp;gt; records&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
the first record is:&lt;br /&gt;
:&amp;lt;code&amp;gt;Result -&amp;gt; records[@=1]&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
and you can read a field directly:&lt;br /&gt;
:&amp;lt;code&amp;gt;Result -&amp;gt; records[@=1] -&amp;gt; Name&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
or through the helper, which also handles the dotted relationship path:&lt;br /&gt;
:&amp;lt;code&amp;gt;SalesforceField( Result -&amp;gt; records[@=1], &amp;quot;Owner.Name&amp;quot; )&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To build an array from the record list, introduce an [[Index]] for the record position and extract the members you want over that Index.&lt;br /&gt;
&lt;br /&gt;
== Error handling and troubleshooting ==&lt;br /&gt;
&lt;br /&gt;
The library raises an Analytica error when authentication fails, when Salesforce returns an unsuccessful HTTP status, when a record ID is malformed, when an object or field API name is invalid, or when a query exceeds «max_pages».&lt;br /&gt;
&lt;br /&gt;
Common causes:&lt;br /&gt;
; Authentication fails&lt;br /&gt;
: With [[#ClientCredentialsAuth|ClientCredentialsAuth]](), check the login URL, the consumer key and secret, the client-credentials setting, the &#039;&#039;&#039;Run As&#039;&#039;&#039; user, and the app authorization.&lt;br /&gt;
; [[#SFLogin|SFLogin]]() fails&lt;br /&gt;
: Make sure you completed the sign-in in the browser window it opened, and that nothing else on your computer is using port 18756. If your organization restricts which connected or external client apps its users may authorize, ask your Salesforce administrator to allow &#039;&#039;&#039;Desktop Analytica&#039;&#039;&#039;.&lt;br /&gt;
; &amp;lt;code&amp;gt;INVALID_FIELD&amp;lt;/code&amp;gt;, or an unknown field&lt;br /&gt;
: Use [[#SalesforceFields|SalesforceFields]]() to check the field&#039;s API name. The display label is often not the API name.&lt;br /&gt;
; Insufficient access&lt;br /&gt;
: Check the integration user&#039;s API permission, object permissions, field-level security, sharing access, and permission-set assignments.&lt;br /&gt;
; No records returned&lt;br /&gt;
: Try a simpler query, confirm the integration user can see the records, and inspect &amp;lt;code&amp;gt;total_size&amp;lt;/code&amp;gt; in the query-result Struct.&lt;br /&gt;
; Query exceeds «max_pages»&lt;br /&gt;
: Add a selective &amp;lt;code&amp;gt;WHERE&amp;lt;/code&amp;gt; clause or a &amp;lt;code&amp;gt;LIMIT&amp;lt;/code&amp;gt;. Raise «max_pages» only when you intend the larger transfer.&lt;br /&gt;
; Sandbox authentication fails&lt;br /&gt;
: Use &amp;lt;code&amp;gt;&amp;quot;https://test.salesforce.com&amp;quot;&amp;lt;/code&amp;gt;, unless your organization requires a specific My Domain login URL. [[#SFLogin|SFLogin]]() signs in to production only.&lt;br /&gt;
; The network request fails&lt;br /&gt;
: Confirm that the computer running Analytica reaches the Salesforce login and instance domains over HTTPS, and that any proxy or firewall permits the requests.&lt;br /&gt;
&lt;br /&gt;
An HTTP 401 gets its own message asking you to recalculate the authentication and the connection, then retry -- the access token has expired or been revoked. With [[#SFLogin|SFLogin]](), recalculating it signs you in again.&lt;br /&gt;
&lt;br /&gt;
== Function summary ==&lt;br /&gt;
&lt;br /&gt;
:{| class=wikitable&lt;br /&gt;
! Function&lt;br /&gt;
! Purpose&lt;br /&gt;
|-&lt;br /&gt;
| [[#SFLogin|SFLogin]]()&lt;br /&gt;
| Sign in through your browser, and return the connection Struct&lt;br /&gt;
|-&lt;br /&gt;
| [[#ClientCredentialsAuth|ClientCredentialsAuth]]()&lt;br /&gt;
| Authenticate with the OAuth 2.0 client-credentials flow&lt;br /&gt;
|-&lt;br /&gt;
| [[#Salesforce|Salesforce]]()&lt;br /&gt;
| Create the REST connection Struct&lt;br /&gt;
|-&lt;br /&gt;
| [[#SoapLoginAuth|SoapLoginAuth]]()&lt;br /&gt;
| Legacy SOAP authentication. Migrate before 1-Jun-2027&lt;br /&gt;
|-&lt;br /&gt;
| [[#SalesforceSelect|SalesforceSelect]]()&lt;br /&gt;
| Build and run a common SOQL &amp;lt;code&amp;gt;SELECT&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| [[#SalesforceQuery|SalesforceQuery]]()&lt;br /&gt;
| Run complete SOQL and retrieve all pages&lt;br /&gt;
|-&lt;br /&gt;
| [[#SalesforceQueryFirst|SalesforceQueryFirst]]()&lt;br /&gt;
| Return the first matching record, or &amp;lt;code&amp;gt;Null&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| [[#SalesforceCount|SalesforceCount]]()&lt;br /&gt;
| Return the total matching-record count&lt;br /&gt;
|-&lt;br /&gt;
| [[#SalesforceQueryPage|SalesforceQueryPage]]()&lt;br /&gt;
| Retrieve one query page manually&lt;br /&gt;
|-&lt;br /&gt;
| [[#SalesforceGetRecord|SalesforceGetRecord]]()&lt;br /&gt;
| Retrieve one record by ID&lt;br /&gt;
|-&lt;br /&gt;
| [[#SalesforceGetRecords|SalesforceGetRecords]]()&lt;br /&gt;
| Retrieve several records from a list of IDs&lt;br /&gt;
|-&lt;br /&gt;
| [[#SalesforceField|SalesforceField]]()&lt;br /&gt;
| Read a dynamic or dotted field path from a record&lt;br /&gt;
|-&lt;br /&gt;
| [[#SalesforceSoqlQuote|SalesforceSoqlQuote]]()&lt;br /&gt;
| Quote a text value safely for SOQL&lt;br /&gt;
|-&lt;br /&gt;
| [[#SalesforceObjects|SalesforceObjects]]()&lt;br /&gt;
| List the objects visible to the authenticated user&lt;br /&gt;
|-&lt;br /&gt;
| [[#SalesforceDescribe|SalesforceDescribe]]()&lt;br /&gt;
| Retrieve full metadata for an object&lt;br /&gt;
|-&lt;br /&gt;
| [[#SalesforceFields|SalesforceFields]]()&lt;br /&gt;
| List the field metadata for an object&lt;br /&gt;
|-&lt;br /&gt;
| [[#SalesforceSearch|SalesforceSearch]]()&lt;br /&gt;
| Run SOSL and return the search records&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== History ==&lt;br /&gt;
&lt;br /&gt;
The Salesforce REST library was introduced in August 2026, and requires [[Analytica 7.1]] or later. [[#SFLogin|SFLogin]]() was added in September 2026. The Constant &amp;lt;code&amp;gt;Salesforce_library_version&amp;lt;/code&amp;gt; in the library holds its version number, currently &amp;lt;code&amp;gt;1.01&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== See also ==&lt;br /&gt;
&lt;br /&gt;
* [[ReadFromUrl]]() -- the function this library is built on&lt;br /&gt;
* [[ParseJSON]]() -- parses the JSON that Salesforce returns&lt;br /&gt;
* [[Struct]] -- the data structure used for connections, records, and query results&lt;br /&gt;
* [[Additional libraries]]&lt;br /&gt;
* [[OpenAI API library]]&lt;br /&gt;
* [[Database library]]&lt;br /&gt;
* Salesforce [https://developer.salesforce.com/docs/atlas.en-us.soql_sosl.meta/soql_sosl/ SOQL and SOSL Reference]&lt;br /&gt;
* Salesforce [https://developer.salesforce.com/docs/platform/mobile-sdk/guide/oauth-client-credentials-flow.html OAuth 2.0 Client Credentials Flow]&lt;/div&gt;</summary>
		<author><name>Lchrisman</name></author>
	</entry>
	<entry>
		<id>https://docs.analytica.com/index.php?title=Salesforce_REST_library&amp;diff=64638</id>
		<title>Salesforce REST library</title>
		<link rel="alternate" type="text/html" href="https://docs.analytica.com/index.php?title=Salesforce_REST_library&amp;diff=64638"/>
		<updated>2026-09-30T19:21:58Z</updated>

		<summary type="html">&lt;p&gt;Lchrisman: Changed version #&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:Function libraries]]&lt;br /&gt;
[[Category:Integration Functions]]&lt;br /&gt;
&lt;br /&gt;
{{ReleaseBar}}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Requires [[Analytica 7.1]] {{Analytica Developer}} edition or better.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The &#039;&#039;&#039;Salesforce REST library&#039;&#039;&#039; lets your model authenticate with [https://www.salesforce.com/ Salesforce] and read Salesforce records and metadata through the Salesforce REST API, version 67.0. Use it to pull live data -- accounts, contacts, opportunities, or your own custom objects -- straight into an Analytica model, so that your analysis works from current CRM data instead of an exported snapshot.&lt;br /&gt;
&lt;br /&gt;
It provides:&lt;br /&gt;
* OAuth 2.0 client-credentials authentication for unattended, server-to-server use&lt;br /&gt;
* A legacy SOAP-login option as a temporary migration bridge&lt;br /&gt;
* SOQL query execution, including automatic pagination&lt;br /&gt;
* Record-selection and record-retrieval helpers&lt;br /&gt;
* Salesforce object and field metadata&lt;br /&gt;
* SOSL search&lt;br /&gt;
&lt;br /&gt;
This library reads from Salesforce. It has no functions to create, update, or delete Salesforce records.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Download:&#039;&#039;&#039; [[media:Salesforce REST library.ana|Salesforce REST library.ana]] (v. 1.02)&lt;br /&gt;
&lt;br /&gt;
== Requirements ==&lt;br /&gt;
&lt;br /&gt;
To use this library, you need:&lt;br /&gt;
* [[Analytica 7.1]] or later, {{Analytica Developer}} edition or better. The library is built on [[ReadFromUrl]](), which is not available in the Professional or Player editions.&lt;br /&gt;
* A Salesforce account, and an OAuth client configured in Salesforce (see [[#Setting up Salesforce for client-credentials authentication|Setting up Salesforce]] below).&lt;br /&gt;
* Internet access allowing Analytica to reach your Salesforce login and instance domains over HTTPS. If you have a strong firewall, you may need to add a rule to allow this.&lt;br /&gt;
&lt;br /&gt;
== Getting started ==&lt;br /&gt;
&lt;br /&gt;
# [[media:Salesforce REST library.ana|Download the library]] and save it into your &amp;lt;code&amp;gt;&amp;quot;C:\Program Files\Lumina\Analytica 7.1\Libraries&amp;quot;&amp;lt;/code&amp;gt; folder.&lt;br /&gt;
# Launch Analytica and open your model, or start a new one.&lt;br /&gt;
# Select &#039;&#039;&#039;Add Library...&#039;&#039;&#039; from the &#039;&#039;&#039;File&#039;&#039;&#039; menu, select &amp;lt;code&amp;gt;Salesforce REST library.ana&amp;lt;/code&amp;gt;, click &#039;&#039;&#039;OK&#039;&#039;&#039;, select &#039;&#039;&#039;Link&#039;&#039;&#039;, then click &#039;&#039;&#039;OK&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
A typical model then uses three Variables -- an authentication Struct, a connection Struct, and one or more query results:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre style=&amp;quot;background:white; border:white; margin-left: 1em;&amp;quot;&amp;gt;&lt;br /&gt;
Variable Sf_auth ::= ClientCredentialsAuth(&lt;br /&gt;
        &amp;quot;https://login.salesforce.com&amp;quot;,&lt;br /&gt;
        Sf_client_id,&lt;br /&gt;
        Sf_client_secret )&lt;br /&gt;
&lt;br /&gt;
Variable Sf ::= Salesforce( Sf_auth )&lt;br /&gt;
&lt;br /&gt;
Variable Active_accounts ::= SalesforceSelect( Sf, &amp;quot;Account&amp;quot;, &amp;quot;Id, Name, Industry&amp;quot;,&lt;br /&gt;
        where: &amp;quot;IsDeleted = false&amp;quot;,&lt;br /&gt;
        order_by: &amp;quot;Name&amp;quot;,&lt;br /&gt;
        row_limit: 100 )&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;Active_accounts&amp;lt;/code&amp;gt; is a query-result [[Struct]]. Its list of records is:&lt;br /&gt;
:&amp;lt;code&amp;gt;Active_accounts -&amp;gt; records&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To read a field from the first record:&lt;br /&gt;
:&amp;lt;code&amp;gt;SalesforceField( Active_accounts -&amp;gt; records[@=1], &amp;quot;Name&amp;quot; )&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
When you authenticate to a sandbox, use &amp;lt;code&amp;gt;&amp;quot;https://test.salesforce.com&amp;quot;&amp;lt;/code&amp;gt; in place of &amp;lt;code&amp;gt;&amp;quot;https://login.salesforce.com&amp;quot;&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== A brief introduction to SOQL ==&lt;br /&gt;
&lt;br /&gt;
Salesforce Object Query Language (SOQL) resembles SQL, but it queries Salesforce objects and fields by their &#039;&#039;&#039;API names&#039;&#039;&#039; rather than by table and column names. A basic query looks like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre style=&amp;quot;background:white; border:white; margin-left: 1em;&amp;quot;&amp;gt;&lt;br /&gt;
SELECT Id, Name, Industry&lt;br /&gt;
FROM Account&lt;br /&gt;
WHERE Industry = &#039;Technology&#039;&lt;br /&gt;
ORDER BY Name&lt;br /&gt;
LIMIT 100&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Standard object names include &amp;lt;code&amp;gt;Account&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Contact&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;Opportunity&amp;lt;/code&amp;gt;. API names of custom objects and custom fields usually end in &amp;lt;code&amp;gt;__c&amp;lt;/code&amp;gt; -- for example &amp;lt;code&amp;gt;License__c&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;Expiration_Date__c&amp;lt;/code&amp;gt;. An object&#039;s API name is often not the same as the label you see in the Salesforce user interface, so use [[#SalesforceObjects|SalesforceObjects]]() and [[#SalesforceFields|SalesforceFields]]() to discover the names you need.&lt;br /&gt;
&lt;br /&gt;
Whenever you insert a text value into a SOQL expression, quote it with [[#SalesforceSoqlQuote|SalesforceSoqlQuote]](). Don&#039;t use that function for object or field API names.&lt;br /&gt;
&lt;br /&gt;
For the full language, see the Salesforce [https://developer.salesforce.com/docs/atlas.en-us.soql_sosl.meta/soql_sosl/ SOQL and SOSL Reference].&lt;br /&gt;
&lt;br /&gt;
== Setting up Salesforce for client-credentials authentication ==&lt;br /&gt;
&lt;br /&gt;
[[#ClientCredentialsAuth|ClientCredentialsAuth]]() is meant for unattended, server-to-server access. It needs an OAuth client configured in Salesforce, plus a dedicated Salesforce user whose permissions the integration runs under.&lt;br /&gt;
&lt;br /&gt;
Salesforce terminology and Setup screens vary by edition and release. In current releases the OAuth client is normally an &#039;&#039;&#039;External Client App&#039;&#039;&#039;; some organizations still use a &#039;&#039;&#039;Connected App&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
A Salesforce administrator generally needs to:&lt;br /&gt;
# Create an &#039;&#039;&#039;External Client App&#039;&#039;&#039; in Salesforce Setup.&lt;br /&gt;
# Enable &#039;&#039;&#039;OAuth settings&#039;&#039;&#039; and the &#039;&#039;&#039;OAuth 2.0 Client Credentials Flow&#039;&#039;&#039;.&lt;br /&gt;
# Grant the OAuth scope needed for REST access, normally &#039;&#039;&#039;Manage user data via APIs&#039;&#039;&#039; (&amp;lt;code&amp;gt;api&amp;lt;/code&amp;gt;). Avoid broader scopes unless you need them.&lt;br /&gt;
# Save the app, then copy its &#039;&#039;&#039;Consumer Key&#039;&#039;&#039; and &#039;&#039;&#039;Consumer Secret&#039;&#039;&#039;. These become the «client_id» and «client_secret» parameters in Analytica.&lt;br /&gt;
# In the app&#039;s OAuth policies, select a dedicated &#039;&#039;&#039;Run As&#039;&#039;&#039; integration user for the client-credentials flow. Depending on the app type, Salesforce may also require &#039;&#039;&#039;Admin approved users are pre-authorized&#039;&#039;&#039;.&lt;br /&gt;
# Give the integration user the permissions your model needs -- API access, plus read access to the required objects and fields. Prefer permission sets and least privilege.&lt;br /&gt;
# If the app uses pre-authorization, assign the app&#039;s permission set or profile authorization to the integration user.&lt;br /&gt;
&lt;br /&gt;
The library sends the token request to:&lt;br /&gt;
:&amp;lt;code&amp;gt;https://login.salesforce.com/services/oauth2/token&amp;lt;/code&amp;gt;&lt;br /&gt;
or, for a sandbox:&lt;br /&gt;
:&amp;lt;code&amp;gt;https://test.salesforce.com/services/oauth2/token&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Salesforce returns an access token and the instance URL that the library uses for all later requests.&lt;br /&gt;
&lt;br /&gt;
For current Salesforce-side details, see the Salesforce [https://developer.salesforce.com/docs/platform/mobile-sdk/guide/oauth-client-credentials-flow.html OAuth 2.0 Client Credentials Flow] documentation.&lt;br /&gt;
&lt;br /&gt;
=== Protect the client secret ===&lt;br /&gt;
&lt;br /&gt;
The consumer secret grants access as the configured integration user. Don&#039;t save it in a model that you distribute to people who shouldn&#039;t have the credential, and don&#039;t commit it to source control. Where you can, obtain it at deployment time from your organization&#039;s approved secret-management mechanism.&lt;br /&gt;
&lt;br /&gt;
== Authentication and connection ==&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;span id=&amp;quot;ClientCredentialsAuth&amp;quot;&amp;gt;ClientCredentialsAuth(login_url, client_id, client_secret)&amp;lt;/span&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Authenticates using the OAuth 2.0 client-credentials flow and returns a [[Struct]] holding the access token.&lt;br /&gt;
&lt;br /&gt;
:{| class=wikitable&lt;br /&gt;
! Parameter&lt;br /&gt;
! Meaning&lt;br /&gt;
|-&lt;br /&gt;
| «login_url»&lt;br /&gt;
| &amp;lt;code&amp;gt;&amp;quot;https://login.salesforce.com&amp;quot;&amp;lt;/code&amp;gt; for production, or &amp;lt;code&amp;gt;&amp;quot;https://test.salesforce.com&amp;quot;&amp;lt;/code&amp;gt; for a sandbox&lt;br /&gt;
|-&lt;br /&gt;
| «client_id»&lt;br /&gt;
| Consumer key from the Salesforce External Client App or Connected App&lt;br /&gt;
|-&lt;br /&gt;
| «client_secret»&lt;br /&gt;
| Consumer secret from the same app&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The Struct it returns contains these members:&lt;br /&gt;
* &amp;lt;code&amp;gt;access_token&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;instance_url&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;status_code&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;status_text&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;raw_response&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Example:&lt;br /&gt;
:&amp;lt;code&amp;gt;ClientCredentialsAuth( &amp;quot;https://login.salesforce.com&amp;quot;, Sf_client_id, Sf_client_secret )&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
When authentication fails, it reports an Analytica error that includes the HTTP status and the Salesforce response.&lt;br /&gt;
&lt;br /&gt;
[[Parameter types]]: &amp;lt;code&amp;gt;ClientCredentialsAuth(login_url: Text; client_id: Text; client_secret: Text)&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;span id=&amp;quot;Salesforce&amp;quot;&amp;gt;Salesforce(auth)&amp;lt;/span&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Creates the connection Struct that you pass to every query, record, search, and metadata function. «auth» is the Struct returned by [[#ClientCredentialsAuth|ClientCredentialsAuth]]() or [[#SoapLoginAuth|SoapLoginAuth]]().&lt;br /&gt;
&lt;br /&gt;
The Struct it returns contains these members:&lt;br /&gt;
* &amp;lt;code&amp;gt;api_version&amp;lt;/code&amp;gt; -- &amp;lt;code&amp;gt;&amp;quot;67.0&amp;quot;&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;instance_url&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;access_token&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;rest_base_url&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Example:&lt;br /&gt;
:&amp;lt;code&amp;gt;Salesforce( Sf_auth )&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
It&#039;s usually clearer to define the authentication and the connection as separate Variables, as shown in [[#Getting started|Getting started]], so that you can inspect an authentication failure on its own. You can also nest the calls:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre style=&amp;quot;background:white; border:white; margin-left: 1em;&amp;quot;&amp;gt;&lt;br /&gt;
Salesforce(&lt;br /&gt;
    ClientCredentialsAuth(&lt;br /&gt;
        &amp;quot;https://login.salesforce.com&amp;quot;,&lt;br /&gt;
        Sf_client_id,&lt;br /&gt;
        Sf_client_secret ) )&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
All REST calls use API version 67.0, including calls authenticated through [[#SoapLoginAuth|SoapLoginAuth]]().&lt;br /&gt;
&lt;br /&gt;
[[Parameter types]]: &amp;lt;code&amp;gt;Salesforce(auth)&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;span id=&amp;quot;SoapLoginAuth&amp;quot;&amp;gt;SoapLoginAuth(login_url, username, password, security_token)&amp;lt;/span&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Logs in with the legacy Salesforce SOAP Partner API &amp;lt;code&amp;gt;login()&amp;lt;/code&amp;gt; call and returns a Struct whose session ID the REST functions use as a bearer token.&lt;br /&gt;
&lt;br /&gt;
«security_token» is the Salesforce security token for that user account. The function concatenates it onto the password, as SOAP login requires.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;This authentication method is a migration bridge only.&#039;&#039;&#039; Salesforce retires the SOAP &amp;lt;code&amp;gt;login()&amp;lt;/code&amp;gt; mechanism on &#039;&#039;&#039;1-Jun-2027&#039;&#039;&#039;, after which this function stops working. Write new models against [[#ClientCredentialsAuth|ClientCredentialsAuth]](), and migrate existing ones before that date.&lt;br /&gt;
&lt;br /&gt;
[[Parameter types]]: &amp;lt;code&amp;gt;SoapLoginAuth(login_url: Text; username: Text; password: Text; security_token: Text)&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Query functions ==&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;span id=&amp;quot;SalesforceSelect&amp;quot;&amp;gt;SalesforceSelect(sf, sobject, fields, &#039;&#039;where, order_by, row_limit&#039;&#039;)&amp;lt;/span&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Builds and runs a common SOQL &amp;lt;code&amp;gt;SELECT&amp;lt;/code&amp;gt;. This is the easiest entry point when your query has a straightforward &amp;lt;code&amp;gt;WHERE&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;ORDER BY&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;LIMIT&amp;lt;/code&amp;gt; structure. It returns the same query-result Struct as [[#SalesforceQuery|SalesforceQuery]]().&lt;br /&gt;
&lt;br /&gt;
:{| class=wikitable&lt;br /&gt;
! Parameter&lt;br /&gt;
! Meaning&lt;br /&gt;
|-&lt;br /&gt;
| «sf»&lt;br /&gt;
| The connection returned by [[#Salesforce|Salesforce]]()&lt;br /&gt;
|-&lt;br /&gt;
| «sobject»&lt;br /&gt;
| Salesforce object API name, such as &amp;lt;code&amp;gt;&amp;quot;Account&amp;quot;&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| «fields»&lt;br /&gt;
| Comma-separated field API names&lt;br /&gt;
|-&lt;br /&gt;
| «where»&lt;br /&gt;
| Optional SOQL condition, without the &amp;lt;code&amp;gt;WHERE&amp;lt;/code&amp;gt; keyword&lt;br /&gt;
|-&lt;br /&gt;
| «order_by»&lt;br /&gt;
| Optional ordering expression, without the &amp;lt;code&amp;gt;ORDER BY&amp;lt;/code&amp;gt; keywords&lt;br /&gt;
|-&lt;br /&gt;
| «row_limit»&lt;br /&gt;
| Optional maximum number of records. &amp;lt;code&amp;gt;0&amp;lt;/code&amp;gt;, the default, applies no explicit limit&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The three optional parameters are declared &amp;lt;code&amp;gt;Named&amp;lt;/code&amp;gt;, so pass them by name:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre style=&amp;quot;background:white; border:white; margin-left: 1em;&amp;quot;&amp;gt;&lt;br /&gt;
SalesforceSelect( Sf, &amp;quot;Contact&amp;quot;, &amp;quot;Id, Name, Email, Account.Name&amp;quot;,&lt;br /&gt;
        where: &amp;quot;Email = &amp;quot; &amp;amp; SalesforceSoqlQuote( Target_email ),&lt;br /&gt;
        order_by: &amp;quot;Name&amp;quot;,&lt;br /&gt;
        row_limit: 20 )&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
It validates the object and field API names, but «where» and «order_by» are SOQL fragments that you supply. Quote any text value you interpolate into them with [[#SalesforceSoqlQuote|SalesforceSoqlQuote]](), as in the example above.&lt;br /&gt;
&lt;br /&gt;
[[Parameter types]]: &amp;lt;code&amp;gt;SalesforceSelect(sf; sobject: Text; fields: Text; where: Named Text := &amp;quot;&amp;quot;; order_by: Named Text := &amp;quot;&amp;quot;; row_limit: Named Number := 0)&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;span id=&amp;quot;SalesforceQuery&amp;quot;&amp;gt;SalesforceQuery(sf, soql, &#039;&#039;include_deleted, max_pages&#039;&#039;)&amp;lt;/span&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Runs a complete SOQL expression and follows Salesforce pagination until it has retrieved every page.&lt;br /&gt;
&lt;br /&gt;
It returns a query-result Struct with these members:&lt;br /&gt;
&lt;br /&gt;
:{| class=wikitable&lt;br /&gt;
! Member&lt;br /&gt;
! Meaning&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;records&amp;lt;/code&amp;gt;&lt;br /&gt;
| List of parsed Salesforce record Structs&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;total_size&amp;lt;/code&amp;gt;&lt;br /&gt;
| Total matching records, as reported by Salesforce&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;pages&amp;lt;/code&amp;gt;&lt;br /&gt;
| Number of pages retrieved&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;done&amp;lt;/code&amp;gt;&lt;br /&gt;
| Whether Salesforce reported the query complete&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;next_records_url&amp;lt;/code&amp;gt;&lt;br /&gt;
| URL of a subsequent page. Normally &amp;lt;code&amp;gt;Null&amp;lt;/code&amp;gt; once the query is complete&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;raw_response&amp;lt;/code&amp;gt;&lt;br /&gt;
| Raw response for the final page&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Set «include_deleted» to &amp;lt;code&amp;gt;True&amp;lt;/code&amp;gt; to use Salesforce&#039;s query-all behavior, which can include deleted and archived records where Salesforce supports it.&lt;br /&gt;
&lt;br /&gt;
«max_pages», 100 by default, protects your model from an unexpectedly large or insufficiently bounded query. If the result needs more pages than this, it raises an error rather than quietly returning an incomplete result.&lt;br /&gt;
&lt;br /&gt;
Example:&lt;br /&gt;
&amp;lt;pre style=&amp;quot;background:white; border:white; margin-left: 1em;&amp;quot;&amp;gt;&lt;br /&gt;
Local soql := &amp;quot;SELECT Id, Name, Amount, CloseDate &amp;quot; &amp;amp;&lt;br /&gt;
              &amp;quot;FROM Opportunity &amp;quot; &amp;amp;&lt;br /&gt;
              &amp;quot;WHERE IsClosed = false &amp;quot; &amp;amp;&lt;br /&gt;
              &amp;quot;ORDER BY CloseDate&amp;quot;;&lt;br /&gt;
SalesforceQuery( Sf, soql )&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Parameter types]]: &amp;lt;code&amp;gt;SalesforceQuery(sf; soql: Text; include_deleted := False; max_pages: Number := 100)&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;span id=&amp;quot;SalesforceQueryFirst&amp;quot;&amp;gt;SalesforceQueryFirst(sf, soql)&amp;lt;/span&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Runs a SOQL expression and returns the first matching record Struct, or &amp;lt;code&amp;gt;Null&amp;lt;/code&amp;gt; when nothing matches. Include an &amp;lt;code&amp;gt;ORDER BY&amp;lt;/code&amp;gt; clause whenever more than one record could match, so that you get a predictable result.&lt;br /&gt;
&lt;br /&gt;
Example:&lt;br /&gt;
&amp;lt;pre style=&amp;quot;background:white; border:white; margin-left: 1em;&amp;quot;&amp;gt;&lt;br /&gt;
SalesforceQueryFirst( Sf,&lt;br /&gt;
    &amp;quot;SELECT Id, Name FROM Account WHERE Name = &amp;quot; &amp;amp;&lt;br /&gt;
    SalesforceSoqlQuote( Account_name ) &amp;amp;&lt;br /&gt;
    &amp;quot; ORDER BY LastModifiedDate DESC LIMIT 1&amp;quot; )&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Parameter types]]: &amp;lt;code&amp;gt;SalesforceQueryFirst(sf; soql: Text)&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;span id=&amp;quot;SalesforceCount&amp;quot;&amp;gt;SalesforceCount(sf, soql, &#039;&#039;include_deleted&#039;&#039;)&amp;lt;/span&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Returns the query&#039;s &amp;lt;code&amp;gt;totalSize&amp;lt;/code&amp;gt; as a number, so you don&#039;t have to retrieve and inspect the record list to count matches.&lt;br /&gt;
&lt;br /&gt;
Example:&lt;br /&gt;
:&amp;lt;code&amp;gt;SalesforceCount( Sf, &amp;quot;SELECT Id FROM Opportunity WHERE IsClosed = false&amp;quot; )&amp;lt;/code&amp;gt; &amp;amp;rarr; &amp;lt;code&amp;gt;42&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Parameter types]]: &amp;lt;code&amp;gt;SalesforceCount(sf; soql: Text; include_deleted := False)&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;span id=&amp;quot;SalesforceQueryPage&amp;quot;&amp;gt;SalesforceQueryPage(sf, soql_or_next_url, &#039;&#039;include_deleted&#039;&#039;)&amp;lt;/span&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Retrieves exactly one query page. Pass a SOQL expression to get the first page, or the &amp;lt;code&amp;gt;next_records_url&amp;lt;/code&amp;gt; from a previous result to get the page after it.&lt;br /&gt;
&lt;br /&gt;
Most models should use [[#SalesforceQuery|SalesforceQuery]](), which handles pagination for you. Use this function when your model deliberately needs page-by-page control.&lt;br /&gt;
&lt;br /&gt;
[[Parameter types]]: &amp;lt;code&amp;gt;SalesforceQueryPage(sf; soql_or_next_url: Text; include_deleted := False)&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Record functions ==&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;span id=&amp;quot;SalesforceGetRecord&amp;quot;&amp;gt;SalesforceGetRecord(sf, sobject, record_id, fields)&amp;lt;/span&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Retrieves one record, given its Salesforce object API name and record ID. It returns the record Struct itself, not a query-result wrapper.&lt;br /&gt;
&lt;br /&gt;
* «record_id» must be a 15- or 18-character Salesforce ID.&lt;br /&gt;
* «fields» is a comma-separated list of field API names.&lt;br /&gt;
&lt;br /&gt;
Example:&lt;br /&gt;
:&amp;lt;code&amp;gt;SalesforceGetRecord( Sf, &amp;quot;Account&amp;quot;, Account_id, &amp;quot;Id, Name, Industry, BillingCountry&amp;quot; )&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Parameter types]]: &amp;lt;code&amp;gt;SalesforceGetRecord(sf; sobject: Text; record_id: Text; fields: Text)&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;span id=&amp;quot;SalesforceGetRecords&amp;quot;&amp;gt;SalesforceGetRecords(sf, sobject, record_ids, fields)&amp;lt;/span&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Retrieves several records from a list of Salesforce record IDs, and returns a list of record Structs. Internally it builds a SOQL &amp;lt;code&amp;gt;IN (...)&amp;lt;/code&amp;gt; condition from the IDs.&lt;br /&gt;
&lt;br /&gt;
Example:&lt;br /&gt;
:&amp;lt;code&amp;gt;SalesforceGetRecords( Sf, &amp;quot;Contact&amp;quot;, Contact_ids, &amp;quot;Id, Name, Email&amp;quot; )&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Parameter types]]: &amp;lt;code&amp;gt;SalesforceGetRecords(sf; sobject: Text; record_ids: List; fields: Text)&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;span id=&amp;quot;SalesforceField&amp;quot;&amp;gt;SalesforceField(record, field_path, &#039;&#039;default_value&#039;&#039;)&amp;lt;/span&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Reads a field from a Salesforce record Struct when the field name is a text value. A dotted «field_path» traverses relationship Structs.&lt;br /&gt;
&lt;br /&gt;
Examples:&lt;br /&gt;
:&amp;lt;code&amp;gt;SalesforceField( Contact_record, &amp;quot;Email&amp;quot; )&amp;lt;/code&amp;gt;&lt;br /&gt;
:&amp;lt;code&amp;gt;SalesforceField( Contact_record, &amp;quot;Account.Name&amp;quot;, &amp;quot;No account&amp;quot; )&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
It returns «default_value», &amp;lt;code&amp;gt;Null&amp;lt;/code&amp;gt; by default, when the member is absent or it can&#039;t traverse the path.&lt;br /&gt;
&lt;br /&gt;
When you already know the field name as you write the Definition, direct Struct access is simpler:&lt;br /&gt;
:&amp;lt;code&amp;gt;Contact_record -&amp;gt; Email&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Use &amp;lt;code&amp;gt;SalesforceField&amp;lt;/code&amp;gt; for dynamic field names, dotted paths, or when you want a controlled default.&lt;br /&gt;
&lt;br /&gt;
[[Parameter types]]: &amp;lt;code&amp;gt;SalesforceField(record; field_path: Text; default_value := Null)&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Quoting text values ==&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;span id=&amp;quot;SalesforceSoqlQuote&amp;quot;&amp;gt;SalesforceSoqlQuote(value)&amp;lt;/span&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Escapes a text value as a SOQL string literal and surrounds it with single quotes, handling quotes, backslashes, and control characters.&lt;br /&gt;
&lt;br /&gt;
Example:&lt;br /&gt;
:&amp;lt;code&amp;gt;&amp;quot;Name = &amp;quot; &amp;amp; SalesforceSoqlQuote( Customer_name )&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If &amp;lt;code&amp;gt;Customer_name&amp;lt;/code&amp;gt; is &amp;lt;code&amp;gt;O&#039;Reilly&amp;lt;/code&amp;gt;, this produces a valid quoted SOQL literal instead of letting the apostrophe terminate the value early.&lt;br /&gt;
&lt;br /&gt;
Use this function for &#039;&#039;&#039;values only&#039;&#039;&#039;. You can&#039;t safely turn Salesforce object or field API names into identifiers by quoting them -- the library validates those names instead.&lt;br /&gt;
&lt;br /&gt;
[[Intelligent Arrays|Array abstraction]] applies, so it also quotes a list of values. [[#SalesforceGetRecords|SalesforceGetRecords]]() relies on this when it builds its &amp;lt;code&amp;gt;IN (...)&amp;lt;/code&amp;gt; condition.&lt;br /&gt;
&lt;br /&gt;
[[Parameter types]]: &amp;lt;code&amp;gt;SalesforceSoqlQuote(value: Text)&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Metadata functions ==&lt;br /&gt;
&lt;br /&gt;
These functions help you discover the API names and properties available to the authenticated integration user.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;span id=&amp;quot;SalesforceObjects&amp;quot;&amp;gt;SalesforceObjects(sf)&amp;lt;/span&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Returns a list of summary Structs for the Salesforce objects that the authenticated user can see.&lt;br /&gt;
&lt;br /&gt;
Example:&lt;br /&gt;
:&amp;lt;code&amp;gt;SalesforceObjects( Sf )&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Useful members usually include the object&#039;s API &amp;lt;code&amp;gt;name&amp;lt;/code&amp;gt;, its display &amp;lt;code&amp;gt;label&amp;lt;/code&amp;gt;, and capability flags. Salesforce supplies the exact members, which vary by API release and object type.&lt;br /&gt;
&lt;br /&gt;
[[Parameter types]]: &amp;lt;code&amp;gt;SalesforceObjects(sf)&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;span id=&amp;quot;SalesforceDescribe&amp;quot;&amp;gt;SalesforceDescribe(sf, sobject)&amp;lt;/span&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Returns Salesforce&#039;s full metadata description for an object, including its fields, relationships, picklist entries, and access capabilities.&lt;br /&gt;
&lt;br /&gt;
Example:&lt;br /&gt;
:&amp;lt;code&amp;gt;SalesforceDescribe( Sf, &amp;quot;Opportunity&amp;quot; )&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Parameter types]]: &amp;lt;code&amp;gt;SalesforceDescribe(sf; sobject: Text)&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;span id=&amp;quot;SalesforceFields&amp;quot;&amp;gt;SalesforceFields(sf, sobject)&amp;lt;/span&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Returns just the list of field metadata Structs from [[#SalesforceDescribe|SalesforceDescribe]](). Use it to find field API names, types, labels, whether a field can be filtered, and its allowed picklist values.&lt;br /&gt;
&lt;br /&gt;
Example:&lt;br /&gt;
:&amp;lt;code&amp;gt;SalesforceFields( Sf, &amp;quot;Account&amp;quot; )&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Parameter types]]: &amp;lt;code&amp;gt;SalesforceFields(sf; sobject: Text)&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Search ==&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;span id=&amp;quot;SalesforceSearch&amp;quot;&amp;gt;SalesforceSearch(sf, sosl)&amp;lt;/span&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Runs a Salesforce Object Search Language (SOSL) expression and returns its &amp;lt;code&amp;gt;searchRecords&amp;lt;/code&amp;gt; list.&lt;br /&gt;
&lt;br /&gt;
SOQL queries known objects and fields; SOSL searches text across one or more objects. Use &amp;lt;code&amp;gt;SalesforceSearch&amp;lt;/code&amp;gt; when your search spans object types, or when it resembles a text search more than a structured query.&lt;br /&gt;
&lt;br /&gt;
Example:&lt;br /&gt;
&amp;lt;pre style=&amp;quot;background:white; border:white; margin-left: 1em;&amp;quot;&amp;gt;&lt;br /&gt;
SalesforceSearch( Sf,&lt;br /&gt;
    &amp;quot;FIND {Acme} IN NAME FIELDS &amp;quot; &amp;amp;&lt;br /&gt;
    &amp;quot;RETURNING Account(Id, Name), Contact(Id, Name, Email)&amp;quot; )&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For the full SOSL syntax, see the Salesforce [https://developer.salesforce.com/docs/atlas.en-us.soql_sosl.meta/soql_sosl/ SOQL and SOSL Reference].&lt;br /&gt;
&lt;br /&gt;
[[Parameter types]]: &amp;lt;code&amp;gt;SalesforceSearch(sf; sosl: Text)&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Working with returned records ==&lt;br /&gt;
&lt;br /&gt;
The library represents Salesforce JSON objects as Analytica [[Struct]]s, and collections as lists. Given:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre style=&amp;quot;background:white; border:white; margin-left: 1em;&amp;quot;&amp;gt;&lt;br /&gt;
Variable Result ::= SalesforceSelect( Sf, &amp;quot;Account&amp;quot;, &amp;quot;Id, Name, Owner.Name&amp;quot;,&lt;br /&gt;
        where: &amp;quot;BillingCountry = &amp;quot; &amp;amp; SalesforceSoqlQuote( Country ),&lt;br /&gt;
        order_by: &amp;quot;Name&amp;quot;,&lt;br /&gt;
        row_limit: 100 )&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
the records are:&lt;br /&gt;
:&amp;lt;code&amp;gt;Result -&amp;gt; records&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
the first record is:&lt;br /&gt;
:&amp;lt;code&amp;gt;Result -&amp;gt; records[@=1]&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
and you can read a field directly:&lt;br /&gt;
:&amp;lt;code&amp;gt;Result -&amp;gt; records[@=1] -&amp;gt; Name&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
or through the helper, which also handles the dotted relationship path:&lt;br /&gt;
:&amp;lt;code&amp;gt;SalesforceField( Result -&amp;gt; records[@=1], &amp;quot;Owner.Name&amp;quot; )&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To build an array from the record list, introduce an [[Index]] for the record position and extract the members you want over that Index.&lt;br /&gt;
&lt;br /&gt;
== Error handling and troubleshooting ==&lt;br /&gt;
&lt;br /&gt;
The library raises an Analytica error when authentication fails, when Salesforce returns an unsuccessful HTTP status, when a record ID is malformed, when an object or field API name is invalid, or when a query exceeds «max_pages».&lt;br /&gt;
&lt;br /&gt;
Common causes:&lt;br /&gt;
; Authentication fails&lt;br /&gt;
: Check the login URL, the consumer key and secret, the client-credentials setting, the &#039;&#039;&#039;Run As&#039;&#039;&#039; user, and the app authorization.&lt;br /&gt;
; &amp;lt;code&amp;gt;INVALID_FIELD&amp;lt;/code&amp;gt;, or an unknown field&lt;br /&gt;
: Use [[#SalesforceFields|SalesforceFields]]() to check the field&#039;s API name. The display label is often not the API name.&lt;br /&gt;
; Insufficient access&lt;br /&gt;
: Check the integration user&#039;s API permission, object permissions, field-level security, sharing access, and permission-set assignments.&lt;br /&gt;
; No records returned&lt;br /&gt;
: Try a simpler query, confirm the integration user can see the records, and inspect &amp;lt;code&amp;gt;total_size&amp;lt;/code&amp;gt; in the query-result Struct.&lt;br /&gt;
; Query exceeds «max_pages»&lt;br /&gt;
: Add a selective &amp;lt;code&amp;gt;WHERE&amp;lt;/code&amp;gt; clause or a &amp;lt;code&amp;gt;LIMIT&amp;lt;/code&amp;gt;. Raise «max_pages» only when you intend the larger transfer.&lt;br /&gt;
; Sandbox authentication fails&lt;br /&gt;
: Use &amp;lt;code&amp;gt;&amp;quot;https://test.salesforce.com&amp;quot;&amp;lt;/code&amp;gt;, unless your organization requires a specific My Domain login URL.&lt;br /&gt;
; The network request fails&lt;br /&gt;
: Confirm that the computer running Analytica reaches the Salesforce login and instance domains over HTTPS, and that any proxy or firewall permits the requests.&lt;br /&gt;
&lt;br /&gt;
An HTTP 401 gets its own message asking you to recalculate the authentication and the connection, then retry -- the access token has expired or been revoked.&lt;br /&gt;
&lt;br /&gt;
== Function summary ==&lt;br /&gt;
&lt;br /&gt;
:{| class=wikitable&lt;br /&gt;
! Function&lt;br /&gt;
! Purpose&lt;br /&gt;
|-&lt;br /&gt;
| [[#ClientCredentialsAuth|ClientCredentialsAuth]]()&lt;br /&gt;
| Authenticate with the OAuth 2.0 client-credentials flow&lt;br /&gt;
|-&lt;br /&gt;
| [[#Salesforce|Salesforce]]()&lt;br /&gt;
| Create the REST connection Struct&lt;br /&gt;
|-&lt;br /&gt;
| [[#SoapLoginAuth|SoapLoginAuth]]()&lt;br /&gt;
| Legacy SOAP authentication. Migrate before 1-Jun-2027&lt;br /&gt;
|-&lt;br /&gt;
| [[#SalesforceSelect|SalesforceSelect]]()&lt;br /&gt;
| Build and run a common SOQL &amp;lt;code&amp;gt;SELECT&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| [[#SalesforceQuery|SalesforceQuery]]()&lt;br /&gt;
| Run complete SOQL and retrieve all pages&lt;br /&gt;
|-&lt;br /&gt;
| [[#SalesforceQueryFirst|SalesforceQueryFirst]]()&lt;br /&gt;
| Return the first matching record, or &amp;lt;code&amp;gt;Null&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| [[#SalesforceCount|SalesforceCount]]()&lt;br /&gt;
| Return the total matching-record count&lt;br /&gt;
|-&lt;br /&gt;
| [[#SalesforceQueryPage|SalesforceQueryPage]]()&lt;br /&gt;
| Retrieve one query page manually&lt;br /&gt;
|-&lt;br /&gt;
| [[#SalesforceGetRecord|SalesforceGetRecord]]()&lt;br /&gt;
| Retrieve one record by ID&lt;br /&gt;
|-&lt;br /&gt;
| [[#SalesforceGetRecords|SalesforceGetRecords]]()&lt;br /&gt;
| Retrieve several records from a list of IDs&lt;br /&gt;
|-&lt;br /&gt;
| [[#SalesforceField|SalesforceField]]()&lt;br /&gt;
| Read a dynamic or dotted field path from a record&lt;br /&gt;
|-&lt;br /&gt;
| [[#SalesforceSoqlQuote|SalesforceSoqlQuote]]()&lt;br /&gt;
| Quote a text value safely for SOQL&lt;br /&gt;
|-&lt;br /&gt;
| [[#SalesforceObjects|SalesforceObjects]]()&lt;br /&gt;
| List the objects visible to the authenticated user&lt;br /&gt;
|-&lt;br /&gt;
| [[#SalesforceDescribe|SalesforceDescribe]]()&lt;br /&gt;
| Retrieve full metadata for an object&lt;br /&gt;
|-&lt;br /&gt;
| [[#SalesforceFields|SalesforceFields]]()&lt;br /&gt;
| List the field metadata for an object&lt;br /&gt;
|-&lt;br /&gt;
| [[#SalesforceSearch|SalesforceSearch]]()&lt;br /&gt;
| Run SOSL and return the search records&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== History ==&lt;br /&gt;
&lt;br /&gt;
The Salesforce REST library was introduced in August 2026, and requires [[Analytica 7.1]] or later. The Constant &amp;lt;code&amp;gt;Salesforce_library_version&amp;lt;/code&amp;gt; in the library holds its version number, currently &amp;lt;code&amp;gt;1.01&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== See also ==&lt;br /&gt;
&lt;br /&gt;
* [[ReadFromUrl]]() -- the function this library is built on&lt;br /&gt;
* [[ParseJSON]]() -- parses the JSON that Salesforce returns&lt;br /&gt;
* [[Struct]] -- the data structure used for connections, records, and query results&lt;br /&gt;
* [[Additional libraries]]&lt;br /&gt;
* [[OpenAI API library]]&lt;br /&gt;
* [[Database library]]&lt;br /&gt;
* Salesforce [https://developer.salesforce.com/docs/atlas.en-us.soql_sosl.meta/soql_sosl/ SOQL and SOSL Reference]&lt;br /&gt;
* Salesforce [https://developer.salesforce.com/docs/platform/mobile-sdk/guide/oauth-client-credentials-flow.html OAuth 2.0 Client Credentials Flow]&lt;/div&gt;</summary>
		<author><name>Lchrisman</name></author>
	</entry>
	<entry>
		<id>https://docs.analytica.com/index.php?title=File:Salesforce_REST_library.ana&amp;diff=64637</id>
		<title>File:Salesforce REST library.ana</title>
		<link rel="alternate" type="text/html" href="https://docs.analytica.com/index.php?title=File:Salesforce_REST_library.ana&amp;diff=64637"/>
		<updated>2026-09-30T19:20:35Z</updated>

		<summary type="html">&lt;p&gt;Lchrisman: Lchrisman uploaded a new version of File:Salesforce REST library.ana&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Summary ==&lt;br /&gt;
The [[Salesforce REST library]] for Analytica -- a REST + JSON client for the Salesforce API v67.0. Supports OAuth 2.0 client-credentials authentication (and legacy SOAP login), paginated SOQL queries, record retrieval, SOSL search, and sObject metadata. Library version 1.01. Requires [[Analytica 7.1]] {{Analytica Developer}} edition or better.&lt;/div&gt;</summary>
		<author><name>Lchrisman</name></author>
	</entry>
	<entry>
		<id>https://docs.analytica.com/index.php?title=File:Salesforce_REST_library.ana&amp;diff=64636</id>
		<title>File:Salesforce REST library.ana</title>
		<link rel="alternate" type="text/html" href="https://docs.analytica.com/index.php?title=File:Salesforce_REST_library.ana&amp;diff=64636"/>
		<updated>2026-09-30T19:19:31Z</updated>

		<summary type="html">&lt;p&gt;Lchrisman: Lchrisman reverted File:Salesforce REST library.ana to an old version&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Summary ==&lt;br /&gt;
The [[Salesforce REST library]] for Analytica -- a REST + JSON client for the Salesforce API v67.0. Supports OAuth 2.0 client-credentials authentication (and legacy SOAP login), paginated SOQL queries, record retrieval, SOSL search, and sObject metadata. Library version 1.01. Requires [[Analytica 7.1]] {{Analytica Developer}} edition or better.&lt;/div&gt;</summary>
		<author><name>Lchrisman</name></author>
	</entry>
	<entry>
		<id>https://docs.analytica.com/index.php?title=File:Salesforce_REST_library.ana&amp;diff=64635</id>
		<title>File:Salesforce REST library.ana</title>
		<link rel="alternate" type="text/html" href="https://docs.analytica.com/index.php?title=File:Salesforce_REST_library.ana&amp;diff=64635"/>
		<updated>2026-09-30T19:18:06Z</updated>

		<summary type="html">&lt;p&gt;Lchrisman: Lchrisman uploaded a new version of File:Salesforce REST library.ana&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Summary ==&lt;br /&gt;
The [[Salesforce REST library]] for Analytica -- a REST + JSON client for the Salesforce API v67.0. Supports OAuth 2.0 client-credentials authentication (and legacy SOAP login), paginated SOQL queries, record retrieval, SOSL search, and sObject metadata. Library version 1.01. Requires [[Analytica 7.1]] {{Analytica Developer}} edition or better.&lt;/div&gt;</summary>
		<author><name>Lchrisman</name></author>
	</entry>
	<entry>
		<id>https://docs.analytica.com/index.php?title=What%27s_new_in_Analytica_7.2%3F&amp;diff=64630</id>
		<title>What&#039;s new in Analytica 7.2?</title>
		<link rel="alternate" type="text/html" href="https://docs.analytica.com/index.php?title=What%27s_new_in_Analytica_7.2%3F&amp;diff=64630"/>
		<updated>2026-09-28T13:42:14Z</updated>

		<summary type="html">&lt;p&gt;Lchrisman: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;!--Analytica 7.2 is currently under development, and will be a future release of Analytica. The current official release is [[Analytica 7.1]]. This page is under construction, and will list enhancements that are new to Analytica or ADE 7.2.--&amp;gt;&lt;br /&gt;
Analytica 7.2 is the next release of Analytica and is currently in [[Beta Tester Page|beta testing]]. The current official release is [[Analytica 7.1]]. This page lists enhancements that are now to Analytica or ADE 7.2. To use Analytica 7.2 beta, please visit the [[Beta Tester Page]].&lt;br /&gt;
&lt;br /&gt;
== Secrets ==&lt;br /&gt;
The [[Secret]] object class is a first-class home for the credentials your model uses: database passwords, API keys, bearer tokens.&lt;br /&gt;
&lt;br /&gt;
A Secret keeps the credential itself out of the language entirely. The Secret&#039;s identifier evaluates to a &#039;&#039;placeholder&#039;&#039; text, e.g. &amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;{{secret:AcmeKey}}&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt;, which you compose into a connection string or URL like any other text:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;code&amp;gt;DbQuery(f&amp;quot;DSN=AcmeWarehouse;UID=svc_reports;PWD={AcmeKey}&amp;quot;, sql)&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The real value is substituted only inside allow-listed built-in functions ([[DbQuery]], [[ReadFromUrl|ReadFromURL]], [[OAuth2Authorize]], and the other database functions), in compiled code, just before the text goes to ODBC or to the web -- and only when the call&#039;s destination matches the Secret&#039;s declared policy. Definitions stay fully readable, with no cloaking or locked modules -- yet the credential never appears in any result, attribute, error message, or saved file, and model code cannot exfiltrate it (a &amp;lt;code&amp;gt;WriteTextFile&amp;lt;/code&amp;gt; or a &amp;lt;code&amp;gt;ReadFromURL&amp;lt;/code&amp;gt; aimed at some other server never sees the real value).&lt;br /&gt;
&lt;br /&gt;
Create one with &#039;&#039;&#039;[[Object menu]] / New / Secret&#039;&#039;&#039; (requires the Developer edition; models &#039;&#039;containing&#039;&#039; secrets load and run in every edition). See [[Secret]] for the full story, including an honest account of what each storage kind does and does not protect against.&lt;br /&gt;
&lt;br /&gt;
== Custom File Providers ==&lt;br /&gt;
(&#039;&#039;advanced, esoteric&#039;&#039;)&lt;br /&gt;
A [[Custom file system providers|file system provider]] makes a URI-style path, &amp;lt;code&amp;gt;«scheme»://«root»/«path»&amp;lt;/code&amp;gt;, for example &amp;lt;code&amp;gt;&amp;quot;repo://Sales/Q3.csv&amp;quot;&amp;lt;/code&amp;gt;, work anywhere the Analytica engine accepts a file name. The file provider can be a source other than a file system (for example, a git repo, cloud storage, a web-based document management system, etc.), but from Analytica and within your models, it can be used anywhere a file path would be used, as if it were a file. You have to configure the available file providers, and you can implement your own (e.g., to wrap an existing service) according to a document [[Custom file system providers/API spec|API specification]].&lt;br /&gt;
&lt;br /&gt;
== GUI ==&lt;br /&gt;
* The [[Object menu]] has a new submenu named &#039;&#039;&#039;New&#039;&#039;&#039;, which you can use to add a new object of any class. This makes it easier and more convenient to add more esoteric class instances that aren&#039;t on the toolbar (like Frame node, Struct, Callable, Secret, etc.) Plus it is easier to add a new node without using a mouse.&lt;br /&gt;
** &#039;&#039;&#039;[[Object menu]] / New / Picture&#039;&#039;&#039; prompts for an image filename. (similar to &#039;&#039;&#039;[File menu] / Import...&#039;&#039;&#039;.&lt;br /&gt;
* Enhancements to the [[Outline window]]&lt;br /&gt;
** There are now hover icons on line items.&lt;br /&gt;
** You can Alt+Click on an item in the Outline to insert its identifier while editing an expression elsewhere.&lt;br /&gt;
** The &#039;&#039;&#039;Module only&#039;&#039;&#039; checkbox has been removed from the header, and a new icon button added to each module line item to show or hide non-module children, allowing this to be controlled at the level of each module or library.&lt;br /&gt;
** Key strokes now have an effect. Up/Down arrows move between line items. Right/Left arrows from a module expand or collapse that item. Shift+Right/Left from a module show or hide non-module children. Home/End keys jump to the begging or end. And typing characters incrementally search among the items visible in the window.&lt;br /&gt;
* Input and Output popups are now present on Module nodes in a diagram. Input popups appear when you click to the immediately left of a node, and output popups appear when you click to the immediate right of a node. These have long existed for variables enabling you to quickly navigate dependencies, but for a Module node the situation is much more complex. A dependency exists from Module A to Module B when there exists a dependency between variables &amp;lt;code&amp;gt;X&amp;amp;rarr;Y&amp;lt;/code&amp;gt; where X is within Module A and Y is within Module B. With modules, there can be a very large number of such dependencies, so the new module input/output popups are organized hierarchically with expanding outline views.&lt;br /&gt;
* There is a new [[Publish To Cloud dialog]]. It handles both ACP3 (the current version at https://acp.analytica.com at the time of this release) as well as ACP4 (coming soon).&lt;br /&gt;
* A user input node with a &#039;&#039;&#039;[List]&#039;&#039;&#039; control pops up a new list-editor dialog, similar to the one in ACP. Previously this opened the Object Window where you would edit the list. The new dialog focuses solely on the list without all the other attributes.&lt;br /&gt;
* Added Drag-and-drop of selected text within and between textual attribute values. Formerly required Cut-Paste, but differs in that Drag-and-drop doesn&#039;t alter the clipboard.&lt;br /&gt;
* The [[Preferences dialog]] exposes some new preference settings:&lt;br /&gt;
** The method used when generating identifiers from titles. (Which Assista will also use when naming objects)&lt;br /&gt;
** Whether display-only arrows should be show as dashed.&lt;br /&gt;
** The time zone used for new date-time values (such as when parsing dates).&lt;br /&gt;
&lt;br /&gt;
== Built-in functions or structs ==&lt;br /&gt;
* The [[Functions To Read Excel Worksheets|Spreadsheet functions]] now work directly with live Google sheets.&lt;br /&gt;
* A new option to &amp;lt;code&amp;gt;[[SpreadsheetInfo]]( wb, &#039;Backend&#039;)&amp;lt;/code&amp;gt;, to programatically detect which backend the workbook uses (&amp;lt;code&amp;gt;&#039;LibXl&#039;&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;&#039;Excel&#039;&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;&#039;GoogleSheets&#039;&amp;lt;/code&amp;gt;). &lt;br /&gt;
* [[FileExists]]( filepath&#039;&#039;, files, folders&#039;&#039; ) tests whether a file or folder exists, without the [[FileSystemListing]] idiom that this used to require. Requires {{Analytica Developer}} or better.&lt;br /&gt;
* New parameters, «map» and «includeNull» added to [[Flatten]] (...). Makes it easy and efficient to concatenate a collection of lists when the lists are behind [[references]] or in [[Struct]] members.&lt;br /&gt;
* [[SortIndex]], [[Sort]] and [[Rank]] can now accept a «lessThat» function for custom comparisons. See [[Using an ordering function when sorting]].&lt;br /&gt;
* A new optional «timeZone» parameter added to [[ParseDate]], [[ParseCSV]], [[NumberToText]], and [[Today]]. Controls which time zone a newly created date-time number (such as when parsed) is created in. [[ParseDate]] and [[ParseCSV]] now recognize ISO 8601 and RFC 3339 date-time format; These are used by many web services, and may include an explicit time zone, which gets converted into the active or specified time zone. &lt;br /&gt;
* Added new built-in functions for [[Encrypting and decrypting|encryption and decryption]] using strong, standard authenticated encryption. (requires [[Analytica Developer]] edition or better).&lt;br /&gt;
* (Esoteric) A new parameter, «sensitive», to [[AskMsgText]] and [[MsgBox]]. Used with the new &amp;lt;code&amp;gt;/AutomationTrace&amp;lt;/code&amp;gt; [[Analytica Command Line|command line]] option to prevent sensitive info (like a credential or API key) from being logged.&lt;br /&gt;
* (&#039;&#039;Experimental&#039;&#039;) New [[Timer]] struct for scheduling an evaluation.&lt;br /&gt;
* New options for [[GetProcessInfo]] to detect non-Windows host version (e.g., when running on Linux), as well as installed fonts.&lt;br /&gt;
* (Esoteric) A new Struct, [[ArrowDependencies]], returns detailed information on dependencies into, or out of, nodes. Most useful for finding all the dependencies between two modules (i.e., which variables deep inside are responsible for dependencies between them). Introduce to enable Assista to better answer questions about module dependencies.&lt;br /&gt;
* The [[UncertainLMH]] distribution now allows «xLow»=«lb» or «xHigh»=«ub». This means that 10% (or more generally «pLow») of the probability sits at the «lb» or «ub» value creating a discontinuity in the CDF (an infinite spike in the PDF) at that value.&lt;br /&gt;
* Added an «asIndex» parameter to [[HandleFromIdentifier]]. The same already existed on [[Handle]]. Used to disambiguate in the case of a self-indexed array-valued variable. &lt;br /&gt;
* When &amp;lt;code&amp;gt;[[Area]](y,x)&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;[[Integrate]](y,x)&amp;lt;/code&amp;gt; appear in a [[DefineOptimization]] formulation where &amp;lt;code&amp;gt;y&amp;lt;/code&amp;gt; depends on decision variables, the analyzer recognizes this as a linear or quadratic relationship when deciding whether the problem is an «LP», «QP» or «NLP».&lt;br /&gt;
&lt;br /&gt;
== Dates and time zones ==&lt;br /&gt;
* ISO 8601 / RFC 3339 date-times with a UTC offset, such as &amp;lt;code&amp;gt;2025-08-25T00:07:05.000+0000&amp;lt;/code&amp;gt; (the form most web APIs return), are now parsed by [[ParseDate]], [[ParseCSV]], table cells and definitions, and can be written as literals inside expressions, e.g., &amp;lt;code&amp;gt;Sequence(2025-08-25T00:00:00, 2025-08-27T00:00:00, dateUnit: &#039;D&#039;)&amp;lt;/code&amp;gt;. See [[Date and Time Values]].&lt;br /&gt;
* A new model preference, &#039;&#039;&#039;Time zone&#039;&#039;&#039; ([[Preferences dialog]]; system variable [[System variables#Sys_TimeZone|Sys_TimeZone]], default &amp;lt;code&amp;gt;&#039;Local&#039;&amp;lt;/code&amp;gt;), says which time zone the model&#039;s date-time values are in. Text with an explicit offset is converted into it, and [[Today]]() reports the time in it.&lt;br /&gt;
* A new «timeZone» parameter on [[ParseDate]], [[ParseCSV]], [[Today]] and [[NumberToText]]: &amp;lt;code&amp;gt;&#039;Model&#039;&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;&#039;Local&#039;&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;&#039;UTC&#039;&amp;lt;/code&amp;gt; or a fixed offset such as &amp;lt;code&amp;gt;&#039;+05:30&#039;&amp;lt;/code&amp;gt;.&lt;br /&gt;
* A new date format, &amp;lt;code&amp;gt;&#039;ISO8601&#039;&amp;lt;/code&amp;gt; (equivalent to &amp;lt;code&amp;gt;yyyy-MM-ddTHH:mm:ss.sssZ&amp;lt;/code&amp;gt;), and date template codes &amp;lt;code&amp;gt;Z&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;ZZ&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;ZZZ&amp;lt;/code&amp;gt; for the UTC offset, so timestamps can be sent back to an API: &amp;lt;code&amp;gt;NumberToText(x, dateFormat: &#039;ISO8601&#039;, timeZone: &#039;UTC&#039;)&amp;lt;/code&amp;gt;. See [[Date formats]].&lt;br /&gt;
* &#039;&#039;&#039;Behavior change&#039;&#039;&#039;: a trailing &amp;lt;code&amp;gt;Z&amp;lt;/code&amp;gt; on a parsed date-time, e.g. &amp;lt;code&amp;gt;ParseDate(&#039;2025-08-25T00:07:05Z&#039;)&amp;lt;/code&amp;gt;, was accepted but ignored in 7.0 and 7.1. It now means UTC, so such values shift by your zone&#039;s offset from UTC. A literal &amp;lt;code&amp;gt;Z&amp;lt;/code&amp;gt; in a custom date template must now be quoted (&amp;lt;code&amp;gt;&#039;Z&#039;&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
== New command line options ==&lt;br /&gt;
See [[Analytica Command Line]].&lt;br /&gt;
*; &amp;lt;code&amp;gt;/evalThenExit:«expression»&amp;lt;/code&amp;gt;: Like &amp;lt;code&amp;gt;/eval:&amp;lt;/code&amp;gt;, but Analytica exits as soon as «expression» has finished, without asking whether to save changes. It replaces the &amp;lt;code&amp;gt;/eval:&amp;quot;«expression»;EvaluateScript(&#039;Bye -&#039;)&amp;quot;&amp;lt;/code&amp;gt; idiom that a [[Running a model in a command line workflow|command line workflow]] used to need, and that was easy to forget.&lt;br /&gt;
&lt;br /&gt;
*; &amp;lt;code&amp;gt;/lib:«YourLib.ana»&amp;lt;/code&amp;gt;: Loads an Analytica library into [[SysLib_Customizations]], which exists concurrently with your model (in a different [[Namespace]]). It survives across model closures, so it can appear as new built-in functionality. Useful for [[MCP server in Analytica|MCP Servers]], QA testing, benchmarking, [[Running a model in a command line workflow|batch processing]], GUI extensions, General (model-independent) [[Assista - Analytica AI Assistant/Custom user skills for Assista|Custom Assista skill libraries]], etc.&lt;br /&gt;
&lt;br /&gt;
*; &amp;lt;code&amp;gt;/Automation&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;/AutomationTrace:«file»&amp;lt;/code&amp;gt;: Run in an [[Analytica_Command_Line/Automation|automation]] mode so that blocking modal dialogs don&#039;t appear waiting for a user input.&lt;br /&gt;
&lt;br /&gt;
*; &amp;lt;code&amp;gt;/WindowXY&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;/WindowSize&amp;lt;/code&amp;gt;: Explicit control over the initial location of the Analytica application window on your screen.&lt;br /&gt;
&lt;br /&gt;
*; &amp;lt;code&amp;gt;/stores:«path to FileProvider.config»&amp;lt;/code&amp;gt;: Location of a [[Custom file system providers|Custom file system provider]] configuration file.&lt;br /&gt;
&lt;br /&gt;
*; &amp;lt;code&amp;gt;/remote-debugging-port:«port»&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;/remote-allow-origins:«origins»&amp;lt;/code&amp;gt;: Hooks used by Lumina for Assista benchmarking and AI-assisted debugging during GUI development.&lt;br /&gt;
&lt;br /&gt;
*; &amp;lt;code&amp;gt;/Autosave=0&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;/Autosave=1&amp;lt;/code&amp;gt;: Turns the &#039;&#039;Maintain recovery info&#039;&#039; preference off or on for that run only, without changing the stored preference (and &amp;lt;code&amp;gt;/Automation&amp;lt;/code&amp;gt; now implies &amp;lt;code&amp;gt;/Autosave=0&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
== AI Integration ==&lt;br /&gt;
* You can now use an [[MCP server in Analytica|MCP server implemented in desktop Analytica]] from Claude.ai, Claude CoWork and other AI clients that are running in the Cloud (when used in conjunction with a tunnel such as &amp;lt;code&amp;gt;cloudflared&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;ngrok&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
== Evaluation engine ==&lt;br /&gt;
* [[Null]] can be specified to a repeated index parameter, which is a way to specify the implicit dimension (aka the null index). All such repeated index parameters for all built-in functions handle this. This enables &amp;lt;code&amp;gt;...[[IndexesOf]](x)&amp;lt;/code&amp;gt; to work when &amp;lt;code&amp;gt;x&amp;lt;/code&amp;gt; has an implicit dimension without any extra surrounding code. &lt;br /&gt;
* Introduced a new function parameter qualifier, &amp;lt;code&amp;gt;nullIndexOk&amp;lt;/code&amp;gt;, for a non-repeated index parameter. When this is specified, the caller can specify [[Null]] for the index with the meaning that the function should operate over the implicit dimension. &lt;br /&gt;
&lt;br /&gt;
== Misc ==&lt;br /&gt;
* The limit on the maximum number of objects (which was 65500) has been removed. There is no longer any fixed limit.&lt;br /&gt;
* Dynamic calculations now use new attributes, &amp;lt;code&amp;gt;dynValue&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;dynProbValue&amp;lt;/code&amp;gt; for partial results, and the usual &amp;lt;code&amp;gt;Value&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;probValue&amp;lt;/code&amp;gt; only for fully computed results. Previously partial results during a dynamic calculation were tracked in &amp;lt;code&amp;gt;Value&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;probValue&amp;lt;/code&amp;gt;.&lt;br /&gt;
* The identifier &amp;lt;code&amp;gt;Date&amp;lt;/code&amp;gt; is no longer a reserved identifier, and hence can be used as an identifier in your own models. The previous attribute, which encodes &amp;quot;Created date&amp;quot; for models, modules, and libraries, is still present as &amp;lt;code&amp;gt;SysLib_Internal::Date&amp;lt;/code&amp;gt;. Model file format is unchanged by this.&lt;/div&gt;</summary>
		<author><name>Lchrisman</name></author>
	</entry>
	<entry>
		<id>https://docs.analytica.com/index.php?title=Functions_to_Write_Data_to_Excel_Worksheets&amp;diff=64623</id>
		<title>Functions to Write Data to Excel Worksheets</title>
		<link rel="alternate" type="text/html" href="https://docs.analytica.com/index.php?title=Functions_to_Write_Data_to_Excel_Worksheets&amp;diff=64623"/>
		<updated>2026-09-24T19:17:52Z</updated>

		<summary type="html">&lt;p&gt;Lchrisman: Custom file system providers: URI paths now supported here (write/file-management/spreadsheet support)&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:Excel to Analytica mappings]]&lt;br /&gt;
[[Category:Integration Functions]]&lt;br /&gt;
&lt;br /&gt;
__TOC__&lt;br /&gt;
{{ReleaseBar}}&lt;br /&gt;
&lt;br /&gt;
These functions write directly to an Excel spreadsheet.  They complement the [[SpreadsheetCell]] and [[SpreadsheetRange]] functions for [[Functions To Read Excel Worksheets|reading data from Excel worksheets]].  They are a simpler alternative to [[OLE Linking]] or [[DbQuery|ODBC]], two other methods for sending data to spreadsheets.  They are available only from the {{Developer}} edition or higher, including [[ADE]]. For the functions that read from Excel, see [[Functions To Read Excel Worksheets]].&lt;br /&gt;
&lt;br /&gt;
These functions write the data to a spreadsheet when the function is evaluated.  If upstream Analytica variables change, they won&#039;t  automatically update the data in Excel until the variable (or button) that calls them is re-evaluated.  In this way, they are different from outgoing [[OLE linking|OLE links]] which do update automatically if &#039;&#039;Auto recompute outgoing OLE links&#039;&#039; is set and the spreadsheet is currently loaded in Excel.  These updates become permantent only when [[#SpreadsheetSave|SpreadsheetSave]] is called.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;SpreadsheetSave&amp;quot; &amp;gt;&lt;br /&gt;
== SpreadsheetSave(workbook&#039;&#039;, filename&#039;&#039;) ==&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Saves any changes to the «workbook» to the indicated filename, or to the original file if «filename» is omitted.  The «filename» is interpreted relative to the [[CurrentDataFolder]]. The «workbook» must be an object obtained from [[SpreadsheetOpen]](). {{Release|1=7.2|2=|3=The original file, or «filename», can be a [[Custom file system providers|custom file system provider]] path such as &amp;lt;code&amp;gt;repo://Sales/Q3.xlsx&amp;lt;/code&amp;gt;; the workbook is then uploaded to that store in one request.}}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;SpreadsheetSetCell&amp;quot; &amp;gt;&lt;br /&gt;
== SpreadsheetSetCell(workbook, sheet, col, row, value) ==&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Writes «value» (either a number or text) to the cell identified by «sheet», «col» and «row».  You can write multiple values via array abstraction when «col», «row» and «value» share one or more indexes.  The «workbook» must be an object obtained from [[SpreadsheetOpen]]().&lt;br /&gt;
&lt;br /&gt;
For writing large arrays, [[#SpreadsheetSetRange|SpreadsheetSetRange]] runs faster.&lt;br /&gt;
&lt;br /&gt;
It writes «value» into the formula of each cell.  Thus, you can set an actual cell formula such as &amp;lt;code&amp;gt;&amp;quot;=Sum(D4:D24)&amp;quot;&amp;lt;/code&amp;gt; by writing a &lt;br /&gt;
text values starting with &amp;lt;code&amp;gt;=&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
{{Release|1=7.1|2=|3=&lt;br /&gt;
=== Adding and removing sheets ===&lt;br /&gt;
You can add a new sheet or remove an existing sheet by using a &amp;lt;code&amp;gt;+&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;-&amp;lt;/code&amp;gt; prefix on the «sheet» parameter:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;code&amp;gt;SpreadsheetSetCell(wb, &amp;quot;+NewSheet&amp;quot;, col, row, value)&amp;lt;/code&amp;gt; &amp;amp;mdash; Adds a sheet named &amp;quot;NewSheet&amp;quot; to the workbook if it does not already exist (but uses the existing sheet if it does), then writes «value» to the specified cell. If «value» is Null, the sheet is added without writing any cell.&lt;br /&gt;
:&amp;lt;code&amp;gt;SpreadsheetSetCell(wb, &amp;quot;-OldSheet&amp;quot;, col, row, Null)&amp;lt;/code&amp;gt; &amp;amp;mdash; Removes the sheet named &amp;quot;OldSheet&amp;quot; from the workbook. The «col» and «row» parameters are required but ignored; «value» must be Null.&lt;br /&gt;
&lt;br /&gt;
It is an error to add a sheet whose name already exists, or to remove the last remaining sheet from a workbook.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== Examples ===&lt;br /&gt;
&lt;br /&gt;
For these examples we assume that, &amp;lt;code&amp;gt;Wb&amp;lt;/code&amp;gt; is a variable defined as:&lt;br /&gt;
:&amp;lt;code&amp;gt;Variable Wb := SpreadsheetOpen(&amp;quot;MyWorkbook.xls&amp;quot;)&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Either of the following two calls write the value &amp;lt;code&amp;gt;3.5&amp;lt;/code&amp;gt; to cell &amp;lt;code&amp;gt;Sheet1!C5&amp;lt;/code&amp;gt;.  Note that «col» can use either the character label or the numeric column position:&lt;br /&gt;
:&amp;lt;code&amp;gt;SpreadsheetSetCell(Wb , &amp;quot;Sheet1&amp;quot;, &amp;quot;C&amp;quot;, 5, 3.5)&amp;lt;/code&amp;gt;&lt;br /&gt;
:&amp;lt;code&amp;gt;SpreadsheetSetCell(Wb , &amp;quot;Sheet1&amp;quot;, 3, 5, 3.5)&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Here A is a 1-D array indexed by &amp;lt;code&amp;gt;I&amp;lt;/code&amp;gt;.  Writes the array as a column-vector starting at cell &amp;lt;code&amp;gt;&amp;quot;D5&amp;quot;&amp;lt;/code&amp;gt;:&lt;br /&gt;
:&amp;lt;code&amp;gt;SpreadsheetSetCell(Wb , &amp;quot;Sheet1&amp;quot;, 4, 4+@I, A)&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Writes the same 1-D array as a row-vector starting at cell &amp;lt;code&amp;gt;&amp;quot;E1&amp;quot;&amp;lt;/code&amp;gt;:&lt;br /&gt;
:&amp;lt;code&amp;gt;SpreadsheetSetCell(Wb , &amp;quot;Sheet1&amp;quot;, &amp;quot;E&amp;quot;, @I, A)&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Writes a 2-D array &amp;lt;code&amp;gt;B&amp;lt;/code&amp;gt;, indexed by &amp;lt;code&amp;gt;I&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;J&amp;lt;/code&amp;gt;, to the sheet with the upper-left corner at cell &amp;lt;code&amp;gt;B7&amp;lt;/code&amp;gt;, with the &amp;lt;code&amp;gt;I&amp;lt;/code&amp;gt; dimension on the horizontal, the &amp;lt;code&amp;gt;J&amp;lt;/code&amp;gt; on the vertical:&lt;br /&gt;
:&amp;lt;code&amp;gt;SpreadsheetSetCell(Wb , &amp;quot;Sheet1&amp;quot;, 1+@I, 6+@J, B)&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;SpreadsheetSetRange&amp;quot; &amp;gt;&lt;br /&gt;
&lt;br /&gt;
== SpreadsheetSetRange(workbook, range, value&#039;&#039;, colIndex, rowIndex, sheet&#039;&#039;) ==&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Writes «value» to «range» in spreadsheet «workbook». «range» may be a named range, or a cell coordinate range, such as &amp;lt;code&amp;gt;&#039;C6:F9&#039;&amp;lt;/code&amp;gt; or single cell &amp;lt;code&amp;gt;&#039;C9&#039;&amp;lt;/code&amp;gt;. If it&#039;s a coordinate range, you must specify the «sheet», either within the coordinate range, such as &amp;lt;code&amp;gt;&#039;Sheet1!C6: F9&#039;&amp;lt;/code&amp;gt; or using the optional «sheet» parameter , with sheet name as text or sheet number as a number from 1 to n.&lt;br /&gt;
&lt;br /&gt;
«value» may be [[atomic]] -- i.e., a single number or text value -- or an array with 1 or 2 dimensions.  If 1-D, you should specify either , «colIndex» or «rowIndex» as an index parameter.  If2-D, you must specify both «colIndex» and «rowIndex».&lt;br /&gt;
&lt;br /&gt;
Ideally, the target range matches the data with the same number of rows and columns.  If the number of columns in «colIndex» exceeds the number of columns in «range» or «rowIndex» exceeds the number of rows, it will not write the extra columns or rows.  If «value» has only one column, it repeats the same data for all rows in «range».  If «value» has more than 1 column but less than the number of columns in «range»(or more than 1 row, but fewer than the number of rows in  «range»), it writes &amp;lt;code&amp;gt;#N/A&amp;lt;/code&amp;gt; into the extra cells in «range».&lt;br /&gt;
&lt;br /&gt;
If value is text and begins with the &amp;lt;code&amp;gt;=&amp;lt;/code&amp;gt; character, the spreadsheet will treat it as a formula, just as if you had typed the formula directly into the spreadsheet.&lt;br /&gt;
&lt;br /&gt;
{{Release|1=7.1|2=|3=&lt;br /&gt;
=== Adding and removing sheets ===&lt;br /&gt;
You can add or remove sheets using a &amp;lt;code&amp;gt;+&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;-&amp;lt;/code&amp;gt; prefix, either in the «range» parameter or in the «sheet» parameter.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Adding a sheet via «range»:&#039;&#039;&#039;&lt;br /&gt;
:&amp;lt;code&amp;gt;SpreadsheetSetRange(wb, &amp;quot;+NewSheet!A1:C3&amp;quot;, data)&amp;lt;/code&amp;gt; &amp;amp;mdash; Adds &amp;quot;NewSheet&amp;quot; and writes «data» to the range A1:C3 on it.&lt;br /&gt;
:&amp;lt;code&amp;gt;SpreadsheetSetRange(wb, &amp;quot;+NewSheet!&amp;quot;, Null)&amp;lt;/code&amp;gt; &amp;amp;mdash; Adds &amp;quot;NewSheet&amp;quot; without writing any data.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Removing a sheet via «range»:&#039;&#039;&#039;&lt;br /&gt;
:&amp;lt;code&amp;gt;SpreadsheetSetRange(wb, &amp;quot;-OldSheet!&amp;quot;, Null)&amp;lt;/code&amp;gt; &amp;amp;mdash; Removes &amp;quot;OldSheet&amp;quot; from the workbook.&lt;br /&gt;
:&amp;lt;code&amp;gt;SpreadsheetSetRange(wb, &amp;quot;-OldSheet&amp;quot;, Null)&amp;lt;/code&amp;gt; &amp;amp;mdash; Same (the trailing &amp;lt;code&amp;gt;!&amp;lt;/code&amp;gt; is optional for removal).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Using the «sheet» parameter:&#039;&#039;&#039;&lt;br /&gt;
:&amp;lt;code&amp;gt;SpreadsheetSetRange(wb, &amp;quot;A1:C3&amp;quot;, data, sheet:&amp;quot;+NewSheet&amp;quot;)&amp;lt;/code&amp;gt; &amp;amp;mdash; Adds &amp;quot;NewSheet&amp;quot; and writes data.&lt;br /&gt;
:&amp;lt;code&amp;gt;SpreadsheetSetRange(wb, &amp;quot;A1&amp;quot;, Null, sheet:&amp;quot;-OldSheet&amp;quot;)&amp;lt;/code&amp;gt; &amp;amp;mdash; Removes &amp;quot;OldSheet&amp;quot;; «value» must be Null.&lt;br /&gt;
&lt;br /&gt;
The sheet name cannot be specified with a &amp;lt;code&amp;gt;+&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;-&amp;lt;/code&amp;gt; prefix in both «range» and «sheet» simultaneously. It is an error to add a sheet that already exists, or to remove the last remaining sheet.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== Examples ===&lt;br /&gt;
&lt;br /&gt;
Writes the value &amp;lt;code&amp;gt;6.0&amp;lt;/code&amp;gt; to the cell &amp;lt;code&amp;gt;Sheet1!B5&amp;lt;/code&amp;gt;:&lt;br /&gt;
:&amp;lt;code&amp;gt;SpreadsheetSetRange(Wb , &amp;quot;Sheet1!B5&amp;quot;, 6.0)&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Writes the 1-D array &amp;lt;code&amp;gt;Cash_flow&amp;lt;/code&amp;gt; as a column-vector to a named range, already labelled as &amp;lt;code&amp;gt;&amp;quot;Cash_flow&amp;quot;&amp;lt;/code&amp;gt;:&lt;br /&gt;
:&amp;lt;code&amp;gt;SpreadsheetSetRange(Wb , &amp;quot;Cash_flow&amp;quot;, Cash_flow, Time)&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Writes a 1-D array &amp;lt;code&amp;gt;B&amp;lt;/code&amp;gt;, indexed by &amp;lt;code&amp;gt;I&amp;lt;/code&amp;gt;, as a row-vector, here in the third worksheet:&lt;br /&gt;
:&amp;lt;code&amp;gt;SpreadsheetSetRange(Wb , &amp;quot;D5:D15&amp;quot;, B, , I, sheet: 3)&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Writes a 2-D array &amp;lt;code&amp;gt;ShippingCosts&amp;lt;/code&amp;gt;, indexed by &amp;lt;code&amp;gt;Destination&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;Origin&amp;lt;/code&amp;gt;, to the range labelled &amp;lt;code&amp;gt;&amp;quot;Shipping_costs&amp;quot;&amp;lt;/code&amp;gt;:&lt;br /&gt;
:&amp;lt;code&amp;gt;SpreadsheetSetRange(Wb , &amp;quot;Shipping_costs&amp;quot;, ShippingCosts, Destination, Origin)&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The next expression includes the row and column header labels with the data written by concatenating them to the data before the data is written to the range. The original array is in Variable &amp;lt;code&amp;gt;A&amp;lt;/code&amp;gt; indexed by indexes &amp;lt;code&amp;gt;Row&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;Col&amp;lt;/code&amp;gt;. Temporary index &amp;lt;code&amp;gt;R&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;C&amp;lt;/code&amp;gt; prepend one item for the header row and column.&lt;br /&gt;
:&amp;lt;code&amp;gt;Index R:=Concat(&amp;quot;&amp;quot;,Row);&amp;lt;/code&amp;gt;&lt;br /&gt;
:&amp;lt;code&amp;gt;Index C:= Concat(&amp;quot;&amp;quot;,Col);&amp;lt;/code&amp;gt;&lt;br /&gt;
:&amp;lt;code&amp;gt;var temp := Concat([Row],A,,Col,C);&amp;lt;/code&amp;gt;&lt;br /&gt;
:&amp;lt;code&amp;gt;var data := Concat([C],d1,,Row,R);&amp;lt;/code&amp;gt;&lt;br /&gt;
:&amp;lt;code&amp;gt;SpreadsheetSetRange(Wb ,range,data,C,R)&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== SpreadsheetSetInfo(workbook, item, value) ==&lt;br /&gt;
&lt;br /&gt;
SpreadsheetSetInfo lets you change properties of the workbook itself, as opposed to [[SpreadsheetSetCell]] and [[SpreadsheetSetRange]] which change properties or contents of cells.  The «item» parameter must be one of these:&lt;br /&gt;
* &amp;lt;code&amp;gt;&amp;quot;ActiveSheet&amp;quot;&amp;lt;/code&amp;gt;          { Changes the selected sheet to the be one with the name «value» }&lt;br /&gt;
* &amp;lt;code&amp;gt;&amp;quot;Author&amp;quot;&amp;lt;/code&amp;gt;&lt;br /&gt;
*&amp;lt;code&amp;gt; &amp;quot;CalculationMode&amp;quot;&amp;lt;/code&amp;gt;      { and «value» must be &amp;quot;Automatic&amp;quot;, &amp;quot;Manual&amp;quot; or &amp;quot;Semiautomatic&amp;quot; }&lt;br /&gt;
* &amp;lt;code&amp;gt;&amp;quot;Date1904&amp;quot;&amp;lt;/code&amp;gt;&lt;br /&gt;
*&amp;lt;code&amp;gt; &amp;quot;SelectedRange&amp;quot;&amp;lt;/code&amp;gt;        { makes the workbook and sheet visible if they aren&#039;t already, and selects the indicated cell range }&lt;br /&gt;
* &amp;lt;code&amp;gt;&amp;quot;Title&amp;quot;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Examples ===&lt;br /&gt;
:&amp;lt;code&amp;gt;SpreadsheetSetInfo(wb, &amp;quot;ActiveSheet&amp;quot;, &amp;quot;Sheet3&amp;quot;)&amp;lt;/code&amp;gt;&lt;br /&gt;
:&amp;lt;code&amp;gt;SpreadsheetSetInfo(wb, &amp;quot;CalculationMode&amp;quot;, &amp;quot;Manual&amp;quot;)&amp;lt;/code&amp;gt;&lt;br /&gt;
:&amp;lt;code&amp;gt;SpreadsheetSetInfo(wb, &amp;quot;SelectedRange&amp;quot;, &amp;quot;Sheet2!B3:E9&amp;quot;)&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== History ==&lt;br /&gt;
In early 4.2 beta builds, before 4.2.0.21, these functions were present as [[SaveExcelWorkbook]], [[WriteWorksheetCell]] and [[WriteWorksheetRange]].  Those names have now been deprecated.  They will still work for a while, but may be removed in a future Analytica build.  Also, the parameters of [[SpreadsheetSetRange]] differ slightly from [[WriteWorksheetRange]] -- the «sheet» parameter has been made optional and moved from being the 2nd parameter to being the last parameter.  It is now not necessary for named ranges.&lt;br /&gt;
&lt;br /&gt;
== See Also ==&lt;br /&gt;
* [[Read and Write Spreadsheets]]&lt;br /&gt;
* [[Excel spreadsheets read and write]]&lt;br /&gt;
* [[Functions To Read Excel Worksheets]]&lt;br /&gt;
* [[Excel Functions from ADE]]&lt;br /&gt;
* [[Excel to Analytica Translation]]&lt;br /&gt;
* [[Excel to Analytica Mappings]]&lt;br /&gt;
* [[DbQuery|ODBC]]&lt;br /&gt;
* [[OLE Linking]]&lt;/div&gt;</summary>
		<author><name>Lchrisman</name></author>
	</entry>
	<entry>
		<id>https://docs.analytica.com/index.php?title=Functions_To_Read_Excel_Worksheets&amp;diff=64622</id>
		<title>Functions To Read Excel Worksheets</title>
		<link rel="alternate" type="text/html" href="https://docs.analytica.com/index.php?title=Functions_To_Read_Excel_Worksheets&amp;diff=64622"/>
		<updated>2026-09-24T19:17:52Z</updated>

		<summary type="html">&lt;p&gt;Lchrisman: Custom file system providers: URI paths now supported here (write/file-management/spreadsheet support)&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:Excel to Analytica mappings]]&lt;br /&gt;
[[Category:Integration Functions]]&lt;br /&gt;
&lt;br /&gt;
__TOC__&lt;br /&gt;
{{ReleaseBar}}&lt;br /&gt;
&lt;br /&gt;
These functions let you open an Excel spreadsheet file, and read cells and ranges from it. For writing to a spreadsheet, see [[Functions to Write Data to Excel Worksheets]].&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;SpreadsheetOpen&amp;quot; &amp;gt;&lt;br /&gt;
== SpreadsheetOpen(filename&#039;&#039;, showDialog, title{{Release|7.0||, backend}}{{Release|7.2||, account}}&#039;&#039;) ==&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Opens a spreadsheet file and returns a workbook object for use by other functions (such as [[SpreadsheetCell]] or [[SpreadsheetRange]]) to read from or write to the file.&lt;br /&gt;
&lt;br /&gt;
The returned object displays in a result table as &amp;lt;code&amp;gt;«ExcelWorkbook»&amp;lt;/code&amp;gt;{{Release|7.2||, &amp;lt;code&amp;gt;«GoogleSheetsWorkbook»&amp;lt;/code&amp;gt; }}{{Release|7.0|| or &amp;lt;code&amp;gt;«LibXlWorkbook»&amp;lt;/code&amp;gt;, depending on the «backend» used.}} {{Release|1=7.2|2=|3=A workbook opened from a native Google Sheet displays as &amp;lt;code&amp;gt;«GoogleSheetsWorkbook»,&amp;lt;/code&amp;gt; but an Excel file stored in Google Drive displays as &amp;lt;code&amp;gt;«LibXlWorkbook»&amp;lt;/code&amp;gt;.}}&lt;br /&gt;
&lt;br /&gt;
{{Release|1=7.2|2=|3=«filename» can also be a [[Custom file system providers|custom file system provider]] path, such as &amp;lt;code&amp;gt;repo://Sales/Q3.xlsx&amp;lt;/code&amp;gt;. The workbook is then read from that store and the LibXL backend is always used -- &amp;lt;code&amp;gt;backend: &#039;Excel&#039;&amp;lt;/code&amp;gt; is refused for such a path -- and [[SpreadsheetSave]] writes it back to the store.}}&lt;br /&gt;
&lt;br /&gt;
Unless you include a complete file path in «filename», Analytica looks for the file in the [[CurrentDataFolder]]. You can also provide the name of a workbook that is currently open in Excel, even if it has not yet been saved to disk.&lt;br /&gt;
&lt;br /&gt;
If you omit the optional parameter «showDialog», the file browser dialog opens only if the specified file cannot be found.&lt;br /&gt;
* Set «showDialog» to True (&amp;lt;code&amp;gt;1&amp;lt;/code&amp;gt;) to force the file browser even if the file exists.&lt;br /&gt;
* Set «showDialog» to False (&amp;lt;code&amp;gt;0&amp;lt;/code&amp;gt;) to suppress the dialog entirely.&lt;br /&gt;
&lt;br /&gt;
If no file is successfully opened, the function flags an error. You can customize the file dialog caption by passing text to the optional «title» parameter.&lt;br /&gt;
&lt;br /&gt;
[[SpreadsheetOpen]] can return two values: the workbook object and the full path to the file that was opened. This is particularly useful when the user selects a file via the dialog: &lt;br /&gt;
::&amp;lt;code&amp;gt;Local (wb, filePath) := SpreadsheetOpen(&amp;quot;Data.xlsx&amp;quot;);&amp;lt;/code&amp;gt;&lt;br /&gt;
{{Release|1=7.0|2=|3=&lt;br /&gt;
{{Release|1=7.1|2=|3=&lt;br /&gt;
=== Creating a new workbook ===&lt;br /&gt;
If you pass &amp;lt;code&amp;gt;&amp;quot;New&amp;quot;&amp;lt;/code&amp;gt; as the «filename», SpreadsheetOpen creates a new blank workbook with a single empty sheet, without saving to any file. This is useful for building a workbook from scratch before saving it with [[Functions_to_Write_Data_to_Excel_Worksheets#SpreadsheetSave|SpreadsheetSave]].&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;code&amp;gt;SpreadsheetOpen(&amp;quot;New&amp;quot;)&amp;lt;/code&amp;gt; — Creates a new workbook using the Excel backend.&lt;br /&gt;
:&amp;lt;code&amp;gt;SpreadsheetOpen(&amp;quot;New&amp;quot;, backend:&#039;LibXl&#039;)&amp;lt;/code&amp;gt; — Creates a new workbook using the LibXl backend.&lt;br /&gt;
&lt;br /&gt;
You can then add sheets using the &amp;lt;code&amp;gt;+&amp;lt;/code&amp;gt; prefix in [[Functions_to_Write_Data_to_Excel_Worksheets#SpreadsheetSetRange|SpreadsheetSetRange]] or [[Functions_to_Write_Data_to_Excel_Worksheets#SpreadsheetSetCell|SpreadsheetSetCell]], and save with SpreadsheetSave when done.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== Backend === &lt;br /&gt;
&#039;&#039;(New to [[Analytica 7.0]])&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The optional «backend» parameter determines which underlying engine Analytica uses to handle the spreadsheet.&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;&#039;Excel&#039;&amp;lt;/code&amp;gt;: (Default) Uses the Microsoft Excel COM interface. &lt;br /&gt;
*;Requirements: Requires Microsoft Excel to be installed locally.  &lt;br /&gt;
*;Capabilities: This backend includes the full Excel calculation engine. If you change cell values using [[SpreadsheetSetCell]] or [[SpreadsheetSetRange]], formulas within the workbook will be recalculated, allowing you to read back computed results. It supports all standard Excel file formats and features. &lt;br /&gt;
*;Return type: Returns an «ExcelWorkbook» object.&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;&#039;LibXl&#039;&amp;lt;/code&amp;gt;: Uses a built-in library for direct file access.&lt;br /&gt;
*; Requirements: Does not require Microsoft Excel to be installed. &lt;br /&gt;
*; Capabilities: Offers high performance for reading and writing raw data. It is ideal for automated environments (like servers) where Excel might not be present. Note that it does &#039;&#039;&#039;&#039;&#039;not&#039;&#039;&#039;&#039;&#039; include a calculation engine; it reads literal values and formulas from the file but cannot &amp;quot;re-calc&amp;quot; a workbook after data is changed. &lt;br /&gt;
*; Return type: Returns a «LibXlWorkbook» object.&lt;br /&gt;
{{Release|1=7.2|2=|3=&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;&#039;GoogleSheets&#039;&amp;lt;/code&amp;gt;: Opens a Google Sheets spreadsheet, or an Excel workbook stored in Google Drive, from its link. It is selected automatically when «filename» is a &amp;lt;code&amp;gt;https://docs.google.com/spreadsheets/d/...&amp;lt;/code&amp;gt; link (copy it from your browser&#039;s address bar while the sheet is open), so «backend» can be omitted.&lt;br /&gt;
*; Requirements: A Google account with access to the spreadsheet. The first time, Analytica opens your web browser so you can sign in to Google and select the spreadsheet in Google&#039;s file chooser; the connection is remembered on your computer under your Windows account, never in the model, and you can revoke it from your Google account settings. Analytica can open only the spreadsheets you select in that chooser (plus ones it creates itself), so a link to a spreadsheet the chooser did not show cannot be opened. Pass an empty «filename» (or «showDialog»: True) to browse for a spreadsheet.&lt;br /&gt;
*; Capabilities: Reads come from a snapshot taken when the workbook is opened. Writes made with [[SpreadsheetSetCell]] and [[SpreadsheetSetRange]], and sheets added or removed, are sent to Google when the computation finishes, when you call [[Functions_to_Write_Data_to_Excel_Worksheets#SpreadsheetSave|SpreadsheetSave]](wb), or when the workbook is refreshed: &amp;lt;code&amp;gt;SpreadsheetSetInfo(wb, &#039;Refresh&#039;, true)&amp;lt;/code&amp;gt; sends the pending writes and re-downloads the spreadsheet, so the values Google computed (and other people&#039;s edits) are seen. A native Google Sheet is exported by Google as an .xlsx snapshot (Google limits the export to 10 MB); an Excel workbook stored in Drive is downloaded as-is and written back as a whole file. The tab names the model sees (&amp;lt;code&amp;gt;SpreadsheetInfo(wb, &#039;Sheets&#039;)&amp;lt;/code&amp;gt;, and «sheet» given by name) are the names in the exported snapshot, truncated to 31 characters, while writes are addressed to Google&#039;s real tab titles. &amp;lt;code&amp;gt;SpreadsheetOpen(&amp;quot;New&amp;quot;, backend:&#039;GoogleSheets&#039;, title: &amp;quot;My sheet&amp;quot;)&amp;lt;/code&amp;gt; creates a new Google Sheet in your Drive. The second return value is the spreadsheet&#039;s link.&lt;br /&gt;
*; Return type: Returns a «GoogleSheetsWorkbook» object for a native Google Sheet, or a «LibXlWorkbook» object for an Excel file stored in Drive. &amp;lt;code&amp;gt;SpreadsheetInfo(wb, &#039;Backend&#039;)&amp;lt;/code&amp;gt; returns &amp;lt;code&amp;gt;&#039;GoogleSheets&#039;&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;&#039;LibXl&#039;&amp;lt;/code&amp;gt; accordingly, and &amp;lt;code&amp;gt;SpreadsheetInfo(wb, &#039;URL&#039;)&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;SpreadsheetInfo(wb, &#039;Account&#039;)&amp;lt;/code&amp;gt; give its link and the Google account it was opened with.&lt;br /&gt;
*; Advanced: Analytica identifies itself to Google as an application registered by Lumina. An organization that would rather it identified itself as an application of their own -- because their Google Workspace administrator controls which outside applications may reach their data, for example -- can arrange that; see [[Using your own Google OAuth client]], which also covers asking Google for access to every spreadsheet so that the chooser is never shown.&lt;br /&gt;
*; Unattended use: A server, a scheduled job or [[Analytica Decision Engine|ADE]] on a machine where nobody is present cannot answer a browser. Give «account» a Google service account instead, and share the spreadsheet with its e-mail address -- see [[Using a Google service account]].&lt;br /&gt;
*; Managing the connection: &amp;lt;code&amp;gt;SysLib_Internal::GoogleAccountEmail()&amp;lt;/code&amp;gt; answers the Google account Analytica is connected to, or Null when there is none. &amp;lt;code&amp;gt;SysLib_Internal::DisconnectGoogleAccount()&amp;lt;/code&amp;gt; revokes that access at Google and forgets the stored connection, returning the address it disconnected; it is a side effect, so evaluate it from a button&#039;s OnClick or the [[Typescript Window|Typescript window]] rather than in a Definition. Opening a spreadsheet again then reconnects, and &amp;lt;code&amp;gt;showDialog: True&amp;lt;/code&amp;gt; forces the connect dialog if you want to switch accounts. You can also revoke Analytica&#039;s access from [https://myaccount.google.com/permissions your Google account&#039;s permissions page].&lt;br /&gt;
}}&lt;br /&gt;
}}&lt;br /&gt;
=== Example ===&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;code&amp;gt;SpreadsheetOpen(&amp;quot;C:\MyModels\Sales Numbers.xlsx&amp;quot;) &amp;amp;rarr; &#039;&#039;«ExcelWorkbook»&#039;&#039;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;SpreadsheetCell&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Getting the file name actually opened ===&lt;br /&gt;
Your code may want to know the file path for which file was actually opened. This may differ from «filename» when the specified file is not found, or when  «showDialog» forces a dialog, allowing the user to select a different file. [[SpreadsheetOpen]] returns the file path as a second return value, which you can optionally capture using, e.g.,&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;code&amp;gt;[[Local]] (contents, filepath) := [[SpreadsheetOpen]]( ... );&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A common pattern is that you may want to save the filename in a variable such that when the evaluation is repeated in the future, it can supply the file selected by the user to the «filename» parameter. This can be coded by supplying first a global variable to hold the filename defined using [[ComputedBy]] with the default filename as follows:&lt;br /&gt;
&lt;br /&gt;
:Variable TheFilename ::= &lt;br /&gt;
::&amp;lt;code&amp;gt;[[ComputedBy]]( TheFileContetns, &amp;quot;defaultFilename.xlsx&amp;quot; ) &amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
:Variable TheWorkbook::= &lt;br /&gt;
::&amp;lt;code&amp;gt;( , TheFilename ) := [[SpreadsheetOpen]]( TheFilename )&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The assignment to &amp;lt;code&amp;gt;( , TheFilename )&amp;lt;/code&amp;gt; passes through the first parameter as the result of the assignment expression, but assigns the second return value the &amp;lt;code&amp;gt;TheFilename&amp;lt;/code&amp;gt;. The assignment to &amp;lt;code&amp;gt;TheFilename&amp;lt;/code&amp;gt; is a [[side-effect]] that is allowed only because &amp;lt;code&amp;gt;TheFilename&amp;lt;/code&amp;gt; is defined as a [[ComputedBy]]. The assignment changes the value, but also rewrites the second parameter of the call to [[ComputedBy]], thus permanently preserving the filename selected. The one line definition of &amp;lt;code&amp;gt;TheWorkbook&amp;lt;/code&amp;gt; is locally equivalent to:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;code&amp;gt;[[Local]] (wb, filename ) := [[SpreadsheetOpen]]( TheFilename );&amp;lt;/code&amp;gt;&lt;br /&gt;
:&amp;lt;code&amp;gt;TheFilename := filename;&amp;lt;/code&amp;gt;&lt;br /&gt;
:&amp;lt;code&amp;gt;wb&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Use with Office 2010 ===&lt;br /&gt;
&lt;br /&gt;
If you have installed the &amp;quot;Click-to-Run&amp;quot; version of Office 2010 from a web download, these spreadsheet functions may not work, due to a &amp;quot;feature&amp;quot; introduced in Office 2010 that apparently disables several common operations.  In this case, you may need to re-install Office using the MSI-based edition.  See how to do this at:&lt;br /&gt;
&lt;br /&gt;
[http://office.microsoft.com/en-gb/excel-help/click-to-run-switch-to-using-a-full-office-edition-HA101850538.aspx http://office.microsoft.com/en-gb/excel-help/click-to-run-switch-to-using-a-full-office-edition-HA101850538.aspx]&lt;br /&gt;
&lt;br /&gt;
=== Excel 64-bit requires Analytica 64-bit ===&lt;br /&gt;
&lt;br /&gt;
Analytica 32-bit cannot launch Excel 64-bit. (The other way around works). Thus, if you have installed Excel 64-bit (which we recommend), make sure you have installed Analytica 64-bit.  If you are a [[Free Edition]] user, you probably have 32-bit installed, but you can install Analytica 64-bit from the [https://www.lumina.com/support/downloads/ Analytica Downloads page].&lt;br /&gt;
&lt;br /&gt;
=== Remembering the selected filename ===&lt;br /&gt;
&lt;br /&gt;
When [[SpreadsheetOpen]]() shows the file dialog and you select a file, it does not save the file name. So, the next time you load the model, you&#039;ll have to select the file again.  If you want the model to remember the selected file, so it will just load it without asking, prompt using that file name as the default, you can use the &#039;&#039;&#039;SpreadsheetOpenEx&#039;&#039;&#039; function in the [[Analytica_Libraries_and_Templates#Spreadsheet_Helper_Library|Spreadsheet Helper Library]].&lt;br /&gt;
&lt;br /&gt;
=== Having same spreadsheet open in Excel at the same time ===&lt;br /&gt;
&lt;br /&gt;
It is often useful to have the spreadsheet you are working with open in Excel at the same time your model is working with it. When you want to do this, is it best to open it Excel first, before evaluating [[SpreadsheetOpen]], in which case [[SpreadsheetOpen]] connects to the existing Excel process and to the currently open spreadsheet. If you change cells in Excel, then evaluate a spreadsheet read functions, you&#039;ll read the new values, and if your model writes to the spreadsheet, you&#039;ll see those values reflected immediately in the Excel interface.&lt;br /&gt;
&lt;br /&gt;
When you call [[SpreadsheetOpen]] before opening the model in Excel, the situation is more complex. To understand what happens and how to view the same model in the Excel UI at the same time, see [[Simultaneously opening a spreadsheet in Excel and Analytica]].&lt;br /&gt;
&lt;br /&gt;
=== SpreadsheetOpenFlags ===&lt;br /&gt;
A registry setting named &amp;lt;code&amp;gt;SpreadsheetOpenFlags&amp;lt;/code&amp;gt; can be set to alter how [[SpreadsheetOpen]] connects to Excel and the initial settings in Excel. There is usually no reason to fiddle with these flags unless you encounter a specific problem. It has been more common to set these flags in server-based applications using ADE than from desktop Analytica.&lt;br /&gt;
&lt;br /&gt;
You&#039;ll need to modify the sitting from RegEdit.  You can set it in either&lt;br /&gt;
:&amp;lt;code&amp;gt;HKEY_LOCAL_MACHINE/Software/Lumina Decision Systems/Analytica/{{#svarget:anarelease|6.1}}&amp;lt;/code&amp;gt;&lt;br /&gt;
or&lt;br /&gt;
:&amp;lt;code&amp;gt;HKEY_CURRENT_USER/Software/Lumina Decision Systems/Analytica/{{#svarget:anarelease|6.1}}&amp;lt;/code&amp;gt;&lt;br /&gt;
For ADE, set it in one of these hives:&lt;br /&gt;
:&amp;lt;code&amp;gt;HKEY_LOCAL_MACHINE/Software/Lumina Decision Systems/Analytica/{{#svarget:anarelease|6.1}}&amp;lt;/code&amp;gt;&lt;br /&gt;
:&amp;lt;code&amp;gt;HKEY_CURRENT_USER/Software/Lumina Decision Systems/Analytica/{{#svarget:anarelease|6.1}}&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Setting it in HKLM causes it to apply from any account on your computer, while setting it from HKCU causes it to apply only to your own account.  A setting in HKCU takes precedence over the same setting in HKLM.&lt;br /&gt;
&lt;br /&gt;
Initially the value &amp;lt;code&amp;gt;SpreadsheetOpenFlags&amp;lt;/code&amp;gt; will not be present. Create a new 32-bit DWORD with this name.  The set the numeric value to an addition of any of these flags that you want:&lt;br /&gt;
* 1 = Launch using a COMCreateObject mechanism.  (unset)=Launch using a BindToObject method. &lt;br /&gt;
*: A BindToObject method (the default for Desktop Analytica) makes it possible to connect to a Workbook running in an active Excel UI. A COMCreateObject mechanism launches a separate instance of Excel every time.   &lt;br /&gt;
* 2 = Turn off Excel&#039;s Interactive flag.&lt;br /&gt;
* 4 = Turn off Excel&#039;s &amp;quot;Ask to update OLE links&amp;quot; flag.&lt;br /&gt;
* 8 = Turn off Excel&#039;s &amp;quot;Display Alerts&amp;quot;&lt;br /&gt;
* 16 = Disable Excel macros (for security)&lt;br /&gt;
* 32 = Close when visible. &lt;br /&gt;
*:Normally, if the workbook is currently visible in an Excel UI, Analytica simply disconnects from it, but doesn&#039;t force the workbook to close.  The Excel UI is then responsible for eventually closing it.  This overrides this and forces the workbook to close when the model releases it, even if it is visible.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== From ADE ===&lt;br /&gt;
When using from ADE on a Web Server, we strongly advise against using Excel 2016 on the server. Excel 2010 works fairly well, but Excel 2016 is extremely unstable and has a tendency to fail unpredictably and lock up all other Excel instances. Microsoft responds by saying that Excel 2016 is not supported nor licensed for use on a web server.&lt;br /&gt;
&lt;br /&gt;
When [[SpreadsheetOpen]] is evaluated in [[ADE|the Analytica Decision Engine (ADE)]] and a dialog needs to be shown to the end-user, it calls [[IAdeUICallbacks::GetFilename]](...). From within that callback, the parent application can interact with the end-user to resolve the file path, and a web applications can instruct the end-user to upload a file. Once complete, the callback returns the full path to the file which is then read. To receive this callback, the parent application must have previously registered the callback with ADE using [[CAEngine::SetCallbackObject]]( ). If it has not registered a callback and the file doesn&#039;t exist, returns an empty text.&lt;br /&gt;
&lt;br /&gt;
Once the open completes, it calls [[IAdeUICallbacks::FileOpenCompleted]]().&lt;br /&gt;
&lt;br /&gt;
=== Debugging Errors ===&lt;br /&gt;
This section documents failures when &#039;&#039;&#039;SpreadsheetOpen&#039;&#039;&#039; has been unable to open Excel, and solutions.&lt;br /&gt;
* &#039;&#039;&#039;&#039;&#039;Library not registered&#039;&#039;&#039;&#039;&#039;: &lt;br /&gt;
*:If this error occurs when &#039;&#039;&#039;SpreadsheetOpen&#039;&#039;&#039; is evaluated...&lt;br /&gt;
** The article [https://excel.tips.net/T002952_Library_Not_Registered_Error.html Library not registered error] explains how to solve this problem when it is caused by an Excel plug-in. It may be caused by a bad Excel add-in library.  You should also run &amp;lt;code&amp;gt;excel.exe /regserver&amp;lt;/code&amp;gt;.&lt;br /&gt;
** In one case, an Analytica user concluded that an older version of Excel was interfering with his newer 32-bit version of Excel. He uninstalled both and re-installed Excel 64-bit and the problem corrected itself.  But, for a different user with this problem, these steps did not correct the problem.&lt;br /&gt;
** A common cause of this problem is when stray registry settings from Excel versions that had been installed and uninstalled interfere with your current version of Excel. This is most common after you roll back to an earlier release after uninstalling a later release. To test for this cause, start Power Shell and run:&lt;br /&gt;
**::&amp;lt;code&amp;gt;get-childitem -Path &amp;quot;HKLM:\Software\Classes\TypeLib\{00020813-0000-0000-C000-000000000046}&amp;quot;&amp;lt;/code&amp;gt;&lt;br /&gt;
::If you see more than one version listed, with the most recent version number missing its mapping to Excel, then this is probably the cause. To fix, use &amp;lt;code&amp;gt;RegEdit&amp;lt;/code&amp;gt; to delete the hive for the later version number.&lt;br /&gt;
&lt;br /&gt;
== SpreadsheetCell(workbook, sheet, column, row&#039;&#039;, what&#039;&#039;) ==&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Returns the value (or other information) of a cell of a worksheet given its coordinates.  The function fully array abstracts, so you can get a range of cells by specifying the column and/or row as an array.&lt;br /&gt;
&lt;br /&gt;
Parameters:&lt;br /&gt;
;«workbook»: A workbook object returned by [[SpreadsheetOpen]]()&lt;br /&gt;
;«sheet»: The name or number of a worksheet from the workbook. Number 1 is the first worksheet, etc.&lt;br /&gt;
::  If you specify &amp;lt;code&amp;gt;sheet: &amp;quot;*&amp;quot;&amp;lt;/code&amp;gt;, it returns the cell value from &#039;&#039;column, row&#039;&#039; for all sheets in the workbook, indexed by &amp;lt;code&amp;gt;.Sheet&amp;lt;/code&amp;gt;, a local index containing the names of the worksheets. This is a way to get a list of all the worksheets in the workbook. If you specify column and/or rows as arrays, you can also use this to get a 3D array for a range over all worksheets.&lt;br /&gt;
;«column»: The column label, e.g., &amp;lt;code&amp;gt;&amp;quot;A&amp;quot;, &amp;quot;B&amp;quot;&amp;lt;/code&amp;gt;, or &amp;lt;code&amp;gt;&amp;quot;AB&amp;quot;&amp;lt;/code&amp;gt;, or the column number as an integer.&lt;br /&gt;
;«row»: The row number as an integer&lt;br /&gt;
;«what»: optional. Let&#039;s you get the formula or format information from the cell. See below under [[SpreadsheetRange]] for details. &lt;br /&gt;
&lt;br /&gt;
If the worksheet cell is empty, it returns [[Null]]. It flags an error if «workbook» is not a valid workbook, if it does not contain «sheet», or if the coordinates are invalid.&lt;br /&gt;
&lt;br /&gt;
=== Examples ===&lt;br /&gt;
These expressions are different ways to get the same result, the value from cell &#039;&#039;C7&#039;&#039; in the first sheet, &amp;lt;code&amp;gt;&amp;quot;Sheet1&amp;quot;&amp;lt;/code&amp;gt; of workbook:&lt;br /&gt;
:&amp;lt;code&amp;gt;SpreadsheetCell(workbook, &amp;quot;Sheet1&amp;quot;, &amp;quot;C&amp;quot;, 7)&amp;lt;/code&amp;gt;&lt;br /&gt;
:&amp;lt;code&amp;gt;SpreadsheetCell(workbook, &amp;quot;Sheet1&amp;quot;, 3, 7)&amp;lt;/code&amp;gt;&lt;br /&gt;
:&amp;lt;code&amp;gt;SpreadsheetCell(workbook, 1, 3, 7)&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Suppose the spreadsheet contains a 2-D table in the region &#039;&#039;C4:J19&#039;&#039;.  The columns of this table correspond to the years 2008..2015.  The rows correspond to different assets.  It is easier to refer to the columns by number, so that the columns &amp;quot;C&amp;quot; thru &amp;quot;J&amp;quot; are columns 3 thru 10.  To hold this 2-D table, we need two indexes in Analytica, &amp;lt;code&amp;gt;Time&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;Asset&amp;lt;/code&amp;gt;.  &lt;br /&gt;
&lt;br /&gt;
:&amp;lt;code&amp;gt;Index Time := 2008..2015&amp;lt;/code&amp;gt;&lt;br /&gt;
:&amp;lt;code&amp;gt;Index Asset := 1..16&amp;lt;/code&amp;gt;&lt;br /&gt;
:&amp;lt;code&amp;gt;Variable Workbook := SpreadsheetOpen(&amp;quot;C:\Asset Data.xls&amp;quot;)&amp;lt;/code&amp;gt;&lt;br /&gt;
:&amp;lt;code&amp;gt;Variable Data := SpreadsheetCell( workbook, &amp;quot;Sheet1&amp;quot;, @Time+2, @Asset+3)&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;SpreadsheetRange&amp;quot;&amp;gt;&lt;br /&gt;
== SpreadsheetRange(workbook, range&#039;&#039;, colIndex, rowIndex, howToIndex, sheet, what&#039;&#039;) ==&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Returns the values (or other information) for a range of cells from an Excel worksheet.  The  «range» can be can be a cell address such as &amp;lt;code&amp;gt;&amp;quot;C7&amp;quot;&amp;lt;/code&amp;gt; or cell range &amp;lt;code&amp;gt;&amp;quot;C7:F12&amp;quot;&amp;lt;/code&amp;gt;, or the name of a range defined in the spreadsheet.  If you want to read or write several cells or ranges in a spreadsheet, it is often convenient to use Excel&#039;s name mechanism and refer to them by name in Analytica.&lt;br /&gt;
&lt;br /&gt;
If the range has multiple columns, the result has local index &amp;lt;code&amp;gt;.Column&amp;lt;/code&amp;gt; unless you specify «colindex» as a parameter. Similarly, if the range has multiple rows, the result has local index &amp;lt;code&amp;gt;.Row&amp;lt;/code&amp;gt; unless you specify «rowindex» as a parameter. Flags in «howToIndex» let you control whether the first row (column) should be used as labels for local index  &amp;lt;code&amp;gt;.Row&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;.Column&amp;lt;/code&amp;gt;.  &lt;br /&gt;
&lt;br /&gt;
If you specify a sheet name with no cells, e.g.  &amp;lt;code&amp;gt;&amp;quot;Inputs!&amp;quot;&amp;lt;/code&amp;gt;, it returns a table that includes all cells from that sheet that contain anything.&lt;br /&gt;
&lt;br /&gt;
By default, it returns the number or text values from the range (or &amp;lt;code&amp;gt;NULL&amp;lt;/code&amp;gt; if the cell is empty). You can use the «what» parameter to obtain the cell formula, address, format, styles, precedent, and dependent cells for each cell.&lt;br /&gt;
&lt;br /&gt;
=== SpreadsheetRange Parameters ===&lt;br /&gt;
&lt;br /&gt;
SpreadsheetRange has two required parameters:&lt;br /&gt;
&lt;br /&gt;
;«workbook»: A workbook object returned by [[SpreadsheetOpen]]()&lt;br /&gt;
; «range»: A cell range.  It may be a single cell address, e.g. &amp;lt;code&amp;gt;&amp;quot;B10&amp;quot;&amp;lt;/code&amp;gt;, a range, e.g. &amp;lt;code&amp;gt;&amp;quot;A1:BC99&amp;quot;&amp;lt;/code&amp;gt;, optionally with sheet name, e.g.  &amp;lt;code&amp;gt;&amp;quot;Sheet1!A1:BC99&amp;quot;&amp;lt;/code&amp;gt;, or a named range, e.g. &amp;lt;code&amp;gt;&amp;quot;Discount_rate&amp;quot;&amp;lt;/code&amp;gt; defined in the spreadsheet. If the «range» doesn&#039;t mention the sheet name, you must specify «sheet» as a separate parameter.&lt;br /&gt;
:: If you specify the range as &amp;lt;code&amp;gt;&amp;quot;Sheet1!&amp;quot;&amp;lt;/code&amp;gt;, with nothing after the &amp;quot;!&amp;quot;, or omit «range» and specify only «sheet», it returns the smallest rectangular range that includes all used cells within the sheet. &lt;br /&gt;
&lt;br /&gt;
SpreadsheetRange has four optional parameters relating to the indexes for a range with multiple columns or rows, or over multiple sheets:&lt;br /&gt;
;«colIndex»: (optional) An index to use for the column dimension of the result.&lt;br /&gt;
;«rowIndex»: (optional) An index to use for the row dimension of the result.&lt;br /&gt;
;«howToIndex»: (optional) Flags controlling how to index the result when «colIndex» or «rowIndex» are not specified.  You can add any of these values to combine their effects:&lt;br /&gt;
::&amp;lt;code&amp;gt;1&amp;lt;/code&amp;gt;: Force a column index even if the range spans only a single column. Has no effect if you specify «colIndex».&lt;br /&gt;
::&amp;lt;code&amp;gt; 2&amp;lt;/code&amp;gt;: Force a row index even if the range spans only a single row.  Has no effect if you specify «rowIndex».&lt;br /&gt;
::&amp;lt;code&amp;gt;4&amp;lt;/code&amp;gt;: Use the first row of «range» as column labels in the local index &amp;lt;code&amp;gt;.Column&amp;lt;/code&amp;gt;. Exclude this first row in the result returned.&lt;br /&gt;
::&amp;lt;code&amp;gt;8&amp;lt;/code&amp;gt;: Use the first column of «range» as labels in the local index &amp;lt;code&amp;gt;.Row&amp;lt;/code&amp;gt;. Exclude this first column in the result returned..&lt;br /&gt;
::&amp;lt;code&amp;gt;16&amp;lt;/code&amp;gt;: Suppress the error message that is otherwise given if the sizes of «colIndex» or «rowIndex» do not match the size of the range.&lt;br /&gt;
;«sheet»: (optional) The name or number of a worksheet inside the workbook. It can be a list of sheets, in which case, the function will return a 3D table, indexed by this list as the third dimension.&lt;br /&gt;
;«what»: (optional)  See below for details on this parameter.&lt;br /&gt;
&lt;br /&gt;
=== Indexes of a cell range ===&lt;br /&gt;
&lt;br /&gt;
The result may be a scalar (single cell), a column vector, a row vector, or a 2-D array, depending on the dimensions of the cell range.  If the range has more than one row (or column),  it will use a local index .Row (.Column) by default. By default, the elements of the .Row index contain the range&#039;s row numbers and elements of the column index contain its column labels.  For example, if the range is &amp;lt;code&amp;gt;&amp;quot;C7:E12&amp;quot;&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;.Row&amp;lt;/code&amp;gt; would contain the elements &amp;lt;code&amp;gt;[7, 8, 9, 10, 11, 12]&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;.Column&amp;lt;/code&amp;gt; would contain &amp;lt;code&amp;gt;[&#039;C&#039;, &#039;D&#039;, &#039;E&#039;]&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Or, you can use the first column (row) of the range as the values for the local index .Row (.Column), by specifying &amp;lt;code&amp;gt;howToIndex: 8&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;howToIndex: 4&amp;lt;/code&amp;gt;, or &amp;lt;code&amp;gt;howToIndex: 12&amp;lt;/code&amp;gt; for both .Row and .Column.)   If you use, the first row (column) of the range as values of the local indexe(es), they will not be included in the value of the array returned. So, in that case, the range must have at least two rows (columns).  &lt;br /&gt;
&lt;br /&gt;
Alternatively, if you already have index(es), you can supply them to the  «rowIndex» («colIndex») parameters.  If you specify a «rowIndex» or «colIndex», that is shorter than the number of rows (columns) in the range, it  truncates the result. If an index is too long, it pads the result with [[Null]].  In these cases, it gives a warning message unless you set flag &amp;lt;code&amp;gt;&#039;&#039;howToIndex: 16&#039;&#039;&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
If the range has just one  column, the result normally will not have a local .Column index. But, you can force it to use a .Column with one element by setting &amp;lt;code&amp;gt;howToIndex: 1&amp;lt;/code&amp;gt;.  If you are using a named range and don&#039;t know how many columns it has, you might use this option to prevent an error occurring if you use [[Dot_operator::A.I|result.Column]] in an expression. Similarly, you can force it to use local &amp;lt;code&amp;gt;.Row&amp;lt;/code&amp;gt; index even when the result has only a single row by specifying &amp;lt;code&amp;gt;howToIndex: 2&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
You can obtain the entire range of a worksheet with all cells that contain anything named &amp;lt;code&amp;gt;&amp;quot;Sheet1&amp;quot;&amp;lt;/code&amp;gt; by specifying the «range» as &amp;lt;code&amp;gt;&amp;quot;Sheet1!&amp;quot;&amp;lt;/code&amp;gt; or by omitting the «range» parameter and specifying just the «sheet» parameter.&lt;br /&gt;
&lt;br /&gt;
=== Examples ===&lt;br /&gt;
&lt;br /&gt;
The following examples use this spreadsheet:&lt;br /&gt;
&lt;br /&gt;
:[[Image:WorksheetRange ExcelShot.jpg]]&lt;br /&gt;
&lt;br /&gt;
This spreadsheet contains these named ranges:&lt;br /&gt;
:{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Label !! Range &lt;br /&gt;
|-&lt;br /&gt;
| Rate || B1&lt;br /&gt;
|-&lt;br /&gt;
| Year || B3:F3&lt;br /&gt;
|-&lt;br /&gt;
| Cash_flow || B4:F4&lt;br /&gt;
|-&lt;br /&gt;
| Divisions || A7:A9&lt;br /&gt;
|-&lt;br /&gt;
| Employee_count || B7:F9&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;code&amp;gt;SpreadsheetRange(wb, &amp;quot;Rate&amp;quot;) &amp;amp;rarr; 0.08&amp;lt;/code&amp;gt;&lt;br /&gt;
:&amp;lt;code&amp;gt;SpreadsheetRange(wb, &amp;quot;Sheet1!B1&amp;quot;) &amp;amp;rarr; 0.08&amp;lt;/code&amp;gt;&lt;br /&gt;
:&amp;lt;code&amp;gt;SpreadsheetRange(wb, &amp;quot;Sheet1!B3:F3&amp;quot;) &amp;amp;rarr;&amp;lt;/code&amp;gt;&lt;br /&gt;
:{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
 ! .Column &amp;amp;rarr; !! &#039;B&#039; !! &#039;C&#039; !! &#039;D&#039; !! &#039;E&#039; !! &#039;F&#039;&lt;br /&gt;
 |-&lt;br /&gt;
 | || 2008 || 2009 || 2010 || 2011 || 2012&lt;br /&gt;
 |}&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;code&amp;gt;Index Year := CopyIndex( SpreadsheetRange(wb, &amp;quot;Year&amp;quot;, howToIndex: 1));&amp;lt;/code&amp;gt;&lt;br /&gt;
:&amp;lt;code&amp;gt;SpreadsheetRange(wb, &amp;quot;Cash_flow&amp;quot;, colIndex: Year) &amp;amp;rarr;&amp;lt;/code&amp;gt;&lt;br /&gt;
:{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
 ! .Year &amp;amp;rarr; !! 2008 !! 2009 !! 2010 !! 2011 !! 2012&lt;br /&gt;
 |-&lt;br /&gt;
 | || -100 || 10 || 30 || 50 || 60&lt;br /&gt;
 |}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Note&#039;&#039;: &amp;lt;code&amp;gt;howToIndex: 1&amp;lt;/code&amp;gt; was specified for &amp;lt;code&amp;gt;Year&amp;lt;/code&amp;gt; here so that we would have a 1-D array even if only one year were present in the spreadsheet.&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;code&amp;gt;SpreadsheetRange(wb, &amp;quot;Employee_count&amp;quot;) &amp;amp;rarr;&amp;lt;/code&amp;gt;&lt;br /&gt;
:{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
 ! !! .Column &amp;amp;rarr; !! &#039;B&#039; !! &#039;C&#039; !! &#039;D&#039; !! &#039;E&#039; !! &#039;F&#039;&lt;br /&gt;
 |-&lt;br /&gt;
 ! rowspan=&amp;quot;3&amp;quot; | .Row&amp;lt;br&amp;gt;&amp;amp;darr; !! 7 &lt;br /&gt;
 | 24 || 27 || 28 || 32 || 35&lt;br /&gt;
 |-&lt;br /&gt;
 ! 8 &lt;br /&gt;
 | 13 || 13 || 13 || 13 || 13&lt;br /&gt;
 |-&lt;br /&gt;
 ! 9 &lt;br /&gt;
 | 25 || 22 || 21 || 19 || 16&lt;br /&gt;
 |}&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;code&amp;gt;Index Time := [2008, 2009, 2010, 2011, 2012];&amp;lt;/code&amp;gt;&lt;br /&gt;
:&amp;lt;code&amp;gt;SpreadsheetRange(wb, &amp;quot;A7:F9&amp;quot;, colIndex: Time, howToIndex: 8, sheet: 1)  &amp;amp;rarr;&amp;lt;/code&amp;gt;&lt;br /&gt;
:{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
 ! !! Time &amp;amp;rarr; !! 2008 !! 2009 !! 2010 !! 2011 !! 2012&lt;br /&gt;
 |-&lt;br /&gt;
 ! rowspan=&amp;quot;3&amp;quot; | .Row&amp;lt;br&amp;gt;&amp;amp;darr; !! &amp;quot;Div A&amp;quot; &lt;br /&gt;
 | 24 || 27 || 28 || 32 || 35&lt;br /&gt;
 |-&lt;br /&gt;
 ! &amp;quot;Div B&amp;quot; &lt;br /&gt;
 | 13 || 13 || 13 || 13 || 13&lt;br /&gt;
 |-&lt;br /&gt;
 ! &amp;quot;Div C&amp;quot; &lt;br /&gt;
 | 25 || 22 || 21 || 19 || 16&lt;br /&gt;
 |}&lt;br /&gt;
&lt;br /&gt;
To obtain the list of worksheet names:&lt;br /&gt;
:&amp;lt;code&amp;gt;SpreadsheetCell(wb, &amp;quot;*&amp;quot;, 1, 1).Sheet&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To obtain all used cells in sheet named &amp;quot;Sheet2&amp;quot;:&lt;br /&gt;
:&amp;lt;code&amp;gt;SpreadsheetRange(wb, sheet:&amp;quot;Sheet2&amp;quot;)&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To obtain the number format of all cells in &amp;quot;Sheet2&amp;quot;:&lt;br /&gt;
:&amp;lt;code&amp;gt;SpreadsheetRange(wb, sheet:&amp;quot;Sheet2&amp;quot;, what:&amp;quot;NumberFormat&amp;quot;)&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===  SpreadsheetRange «what» parameter === &lt;br /&gt;
&lt;br /&gt;
By default, SpreadsheetRange() returns the value of the cell(s) in the range, but you can use the «what» parameter to obtain the formula,  cell style and formats, cell address, predecessor or dependent cells of each cell:&lt;br /&gt;
;«what»: (optional). By default, SpreadsheetRange returns the value of the range, but you can use this parameter to obtain its formula, or cell style parameters.  Possible values: &lt;br /&gt;
::&amp;lt;code&amp;gt;&amp;quot;Value&amp;quot;&amp;lt;/code&amp;gt;: (Default) The computed value.  Excel dates become Analytica date-time numbers, which display as dates.&lt;br /&gt;
::&amp;lt;code&amp;gt;&amp;quot;NumericValue&amp;quot;&amp;lt;/code&amp;gt;: The computed value, but dates are returned as numbers.&lt;br /&gt;
::&amp;lt;code&amp;gt;&amp;quot;Formula&amp;quot;&amp;lt;/code&amp;gt;: The cell formula as a text value in the normal Excel format starting with &amp;quot;=&amp;quot;, e.g., &amp;lt;code&amp;gt;&amp;quot;=Sum(D4:D10)&amp;quot;&amp;lt;/code&amp;gt;&lt;br /&gt;
::&amp;lt;code&amp;gt;&amp;quot;RelativeFormula&amp;quot;&amp;lt;/code&amp;gt;: The cell formula using relative offset format, e.g., &amp;lt;code&amp;gt;&amp;quot;=Sum(RC[-9]:R[+6]C[-9])&amp;quot;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== «what» parameter cell formats  ==== &lt;br /&gt;
::&amp;lt;code&amp;gt;&amp;quot;NumberFormat&amp;quot;&amp;lt;/code&amp;gt;: The cell number format as text.&lt;br /&gt;
::&amp;lt;code&amp;gt;&amp;quot;BackColor&amp;quot;&amp;lt;/code&amp;gt;: Cell background color as integer: &#039;&#039;red*65536 + green*256 + blue&#039;&#039;&lt;br /&gt;
::&amp;lt;code&amp;gt;&amp;quot;Text Color&amp;quot;&amp;lt;/code&amp;gt;: Font color as an integer: &#039;&#039;red*65536 + green*256 + blue&#039;&#039;&lt;br /&gt;
::&amp;lt;code&amp;gt;&amp;quot;FontName&amp;quot;&amp;lt;/code&amp;gt;: Name of the font used to display the cell&lt;br /&gt;
::&amp;lt;code&amp;gt;&amp;quot;FontSize&amp;quot;&amp;lt;/code&amp;gt;: Point size of the font displayed in the cell&lt;br /&gt;
::&amp;lt;code&amp;gt;&amp;quot;FontStyle&amp;quot;&amp;lt;/code&amp;gt;: Special font styles for cell separated by spaces, may include &amp;quot;bold italic underline strikethrough subscript superscript outline shadow&amp;quot;&lt;br /&gt;
::&amp;lt;code&amp;gt;&amp;quot;HorizontalAlignment&amp;quot;&amp;lt;/code&amp;gt;: Text justification, one of: &amp;lt;code&amp;gt;&#039;Left&#039;, &#039;Center&#039;, &#039;Right&#039;, &#039;Justify&#039;, &#039;Distributed&#039;, &#039;Fill&#039;&amp;lt;/code&amp;gt;.  (&#039;&#039;new to [[Analytica 5.0]]&#039;&#039;)&lt;br /&gt;
::&amp;lt;code&amp;gt;&amp;quot;VerticalAlignment&amp;quot;&amp;lt;/code&amp;gt;: Text vertical justification, one of: &amp;lt;code&amp;gt;&#039;Top&#039;, &#039;Middle&#039;, &#039;Bottom&#039;, &#039;Justify&#039;, &#039;Distributed&#039;&amp;lt;/code&amp;gt;.  (&#039;&#039;new to [[Analytica 5.0]]&#039;&#039;)&lt;br /&gt;
::&amp;lt;code&amp;gt;&amp;quot;WrapText&amp;quot;&amp;lt;/code&amp;gt;: &amp;lt;code&amp;gt;0&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;1&amp;lt;/code&amp;gt; controls whether text is word wrapped to fit in the cell.&lt;br /&gt;
::&amp;lt;code&amp;gt;&amp;quot;Border(Left|Right|Up|Down)&amp;quot;&amp;lt;/code&amp;gt; show a border to left, right, above, or below the cell.&lt;br /&gt;
::&amp;lt;code&amp;gt;&amp;quot;Border(Left|Right|Up|Down)Color&amp;quot;&amp;lt;/code&amp;gt;: Return the color of the specified side of the border as an RGB number --  E.g., &amp;lt;code&amp;gt;&amp;quot;BorderLeftColor&amp;quot;&amp;lt;/code&amp;gt; returns an integer equal to &#039;&#039;red*65535+green*256+blue&#039;&#039;&lt;br /&gt;
::&amp;lt;code&amp;gt;&amp;quot;Border (Left|Right|Up|Down) Style&amp;quot;&amp;lt;/code&amp;gt;: Style of indicated border, or [[Null]] if not set. May be &amp;lt;code&amp;gt;&amp;quot;Solid&amp;quot;, &amp;quot;Dash&amp;quot;, &amp;quot;DashDot&amp;quot;, &amp;quot;DashDotDot&amp;quot;, &amp;quot;Dot&amp;quot;, &amp;quot;Double&amp;quot;&amp;lt;/code&amp;gt;, or &amp;lt;code&amp;gt;&amp;quot;SlantDashDot&amp;quot;&amp;lt;/code&amp;gt;.&lt;br /&gt;
::&amp;lt;code&amp;gt;&amp;quot;Border (Left|Right|Up|Down) Weight&amp;quot;&amp;lt;/code&amp;gt;: Thickness of indicated border, usually between 1 and 4&lt;br /&gt;
&lt;br /&gt;
==== «what» parameter cell addresses  ====&lt;br /&gt;
::&amp;lt;code&amp;gt;&amp;quot;Address&amp;quot;&amp;lt;/code&amp;gt;: The address of the cell range, e.g., &amp;lt;code&amp;gt;&amp;quot;B12:C13&amp;quot;&amp;lt;/code&amp;gt;&lt;br /&gt;
::&amp;lt;code&amp;gt;&amp;quot;AddressR1C1&amp;quot;&amp;lt;/code&amp;gt;: The address of the cell range in R1C1 format, e.g., &amp;lt;code&amp;gt;&amp;quot;R12C2:R13C3&amp;quot;&amp;lt;/code&amp;gt;&lt;br /&gt;
::&amp;lt;code&amp;gt;&amp;quot;Sheet&amp;quot;&amp;lt;/code&amp;gt;: The sheet name where the cell range exists.&lt;br /&gt;
::&amp;lt;code&amp;gt;&amp;quot;RangeName&amp;quot;&amp;lt;/code&amp;gt;: The name of the range, if it is a named range. &lt;br /&gt;
&lt;br /&gt;
==== «what» parameter cell precedents and dependents  ==== &lt;br /&gt;
&lt;br /&gt;
::&amp;lt;code&amp;gt;&amp;quot;DirectPrecedents&amp;quot;&amp;lt;/code&amp;gt;: Addresses of all cells mentioned in the cell formula, separated by commas.  Unfortunately, Excel lists only cells in the same sheet, but not precedents in other sheets.  &lt;br /&gt;
::&amp;lt;code&amp;gt;&amp;quot;DirectPrecedentsRelative&amp;quot;&amp;lt;/code&amp;gt;: Same as &amp;quot;DirectPrecedents&amp;quot;, but cells are given by their offset relative to the current cell, e.g., &amp;lt;code&amp;gt;R[-3]C[6]&amp;lt;/code&amp;gt;.&lt;br /&gt;
::&amp;lt;code&amp;gt;&amp;quot;DirectDependents&amp;quot;&amp;lt;/code&amp;gt;: Addresses of all cells whose formula mentions this cell, separated by commas.  Unfortunately, Excel lists only cells in the same sheet, but not dependents in other sheets. &lt;br /&gt;
::&amp;lt;code&amp;gt;&amp;quot;DirectDependentsRelative&amp;quot;&amp;lt;/code&amp;gt;: Same as &amp;lt;code&amp;gt;&amp;quot;DirectDependents&amp;quot;&amp;lt;/code&amp;gt;, but cells are identified by their offset relative to the current cell, e.g., &amp;lt;code&amp;gt;R[-3]C[6]&amp;lt;/code&amp;gt;.&lt;br /&gt;
::&amp;lt;code&amp;gt;&amp;quot;Precedents&amp;quot;&amp;lt;/code&amp;gt;:  Addresses of all cells in the current worksheet mentioned in the formula of this cell and the formulas of its direct precedents.  It does not include cells reached by paths passing through other sheets.&lt;br /&gt;
::&amp;lt;code&amp;gt;&amp;quot;PrecedentsRelative&amp;quot;&amp;lt;/code&amp;gt;: Same as &amp;lt;code&amp;gt;&amp;quot;Precedents&amp;quot;&amp;lt;/code&amp;gt;, but cells are identified by their offset relative to the current cell, e.g., &amp;lt;code&amp;gt;R[-3]C[6]&amp;lt;/code&amp;gt;.&lt;br /&gt;
::&amp;lt;code&amp;gt;&amp;quot;Descendants&amp;quot;&amp;lt;/code&amp;gt;: Description of all cells in the current worksheet that depend directly or indirectly on the given cell.  It does not include cells reached by paths passing through other sheets.&lt;br /&gt;
::&amp;lt;code&amp;gt;&amp;quot;DirectDescendantsRelative&amp;quot;&amp;lt;/code&amp;gt;: Same as &amp;lt;code&amp;gt;&amp;quot;DirectDescendants&amp;quot;&amp;lt;/code&amp;gt;, but cells are identified by their offset relative to the current cell, e.g., &amp;lt;code&amp;gt;R[-3]C[6]&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Errors in SpreadsheetRange parameters ===&lt;br /&gt;
In a call to SpreadsheetRange(wb, range):&lt;br /&gt;
* If range refers to a sheet, e.g. &amp;quot;sheet!x&amp;quot; that does not exist in workbook wb, it gives an error message saying &amp;quot;the worksheet &#039;sheet&#039; was not found.&amp;quot;&lt;br /&gt;
* If range refers to a named range, e.g. &amp;quot;sheet!x&amp;quot; that does not exist in workbook wb, it gives an error message saying &amp;quot;the indicated named cell range, &#039;x&#039;, was not found.&amp;quot;&lt;br /&gt;
* If range refers to a cell address with bad syntax, e.g. &amp;quot;ted!A1:R3C6&amp;quot;, it gives an error message saying &amp;quot;the range named A1:R3C6 was not found in Excel worksheet &#039;ted&#039;.&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;SpreadsheetInfo&amp;quot;&amp;gt;&lt;br /&gt;
== SpreadsheetInfo(workbook, item) == &lt;br /&gt;
&amp;lt;/div&amp;gt;  &lt;br /&gt;
&lt;br /&gt;
SpreadsheetInfo gets various kinds of information about the spreadsheet («workbook») specified by parameter «item»:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! item !! Description&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;&amp;quot;AcceptLabelsInFormulas&amp;quot;&amp;lt;/code&amp;gt; || True when you can use labels in worksheet formulas. This is usually false.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;&amp;quot;Account&amp;quot;&amp;lt;/code&amp;gt; || &#039;&#039;([[Analytica 7.2|7.2]])&#039;&#039; The Google account a workbook opened from Google Sheets or Google Drive is connected with. Null for other workbooks.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;&amp;quot;ActiveSheet&amp;quot;&amp;lt;/code&amp;gt; || The number of the active (displayed) worksheet.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;&amp;quot;Author&amp;quot;&amp;lt;/code&amp;gt; || The name of the author, usually the name of the person who created the spreadsheet as recorded by Windows OS.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;&amp;quot;Backend&amp;quot;&amp;lt;/code&amp;gt; || &#039;&#039;([[Analytica 7.2|7.2]])&#039;&#039; Which engine holds the workbook: &amp;lt;code&amp;gt;&#039;Excel&#039;&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;&#039;LibXl&#039;&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;&#039;GoogleSheets&#039;&amp;lt;/code&amp;gt; (see the «backend» parameter of [[SpreadsheetOpen]]).&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;&amp;quot;CalculationMode&amp;quot;&amp;lt;/code&amp;gt;   || The calculation mode set for the workbook, which may be &amp;quot;Automatic&amp;quot;, &amp;quot;Manual&amp;quot; or &amp;quot;Semiautomatic&amp;quot;, meaning automatic except for data tables.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;&amp;quot;CalculationState&amp;quot;&amp;lt;/code&amp;gt; || &#039;&#039;([[Analytica 5.0|5.0]])&#039;&#039; The current state of Excel&#039;s calculation engine, either &amp;lt;code&amp;gt;&amp;quot;Calculating&amp;quot;&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;&amp;quot;Pending&amp;quot;&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;&amp;quot;Done&amp;quot;&amp;lt;/code&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;&amp;quot;CalculationVersion&amp;quot;&amp;lt;/code&amp;gt; || &#039;&#039;([[Analytica 5.0|5.0]])&#039;&#039; Number equal to 10000 * major + minor, encoding the version of the Excel calculation engine that the current workbook was last calculated in. If it was saved in an earlier version of Excel and hasn&#039;t yet been fully calculated, the value is 0. You can compare this to the &amp;quot;Excel.CalculationVersion&amp;quot; to determine whether it was last re-calculated using the same calculation engine as your current installed Excel.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;&amp;quot;CodeName&amp;quot;&amp;lt;/code&amp;gt; ||&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;&amp;quot;Date1904&amp;quot;&amp;lt;/code&amp;gt; || The base for dates used in the workbook.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;&amp;quot;DecimalSeparator&amp;quot;&amp;lt;/code&amp;gt; || &#039;&#039;([[Analytica 5.0|5.0]])&#039;&#039; The character used to separate a whole number from its fractional part. In English-speaking countries this is &#039;.&#039; (a dot).&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;&amp;quot;Excel.CalculationVersion&amp;quot;&amp;lt;/code&amp;gt; || &#039;&#039;([[Analytica 5.0|5.0]])&#039;&#039; Number equal to 10000 * major + minor, encoding the version of calculation engine for your installed version of Excel. &lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;&amp;quot;Filename&amp;quot;&amp;lt;/code&amp;gt;   || The name of the file, including the full file path.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;&amp;quot;Name&amp;quot;&amp;lt;/code&amp;gt;         || The name of the file, without the file path.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;&amp;quot;Names&amp;quot;&amp;lt;/code&amp;gt;          || A list of all the named ranges.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;&amp;quot;OperatingSystem&amp;quot;&amp;lt;/code&amp;gt; || &#039;&#039;([[Analytica 5.0|5.0]])&#039;&#039; The name of the operating system that your Excel instance is running on, as reported by Excel. &lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;&amp;quot;ReadOnly&amp;quot;&amp;lt;/code&amp;gt; || True (1) if the file is saved as Readonly.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;&amp;quot;Saved&amp;quot;&amp;lt;/code&amp;gt;      || False (0) if it has unsaved changes.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;&amp;quot;SelectedRange&amp;quot;&amp;lt;/code&amp;gt; || The currently selected Range.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;&amp;quot;SelectedRangeR1C1&amp;quot;&amp;lt;/code&amp;gt; || The currently selected Range specified by row and column number.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;&amp;quot;Sheets&amp;quot;&amp;lt;/code&amp;gt;     || A list of the names of all the worksheets&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;&amp;quot;ThousandsSeparator&amp;quot;&amp;lt;/code&amp;gt; || &#039;&#039;([[Analytica 5.0|5.0]])&#039;&#039; The character Excel uses to group thousands when displaying a large number. In English-speaking countries this is &#039;,&#039; (a comma). For example, in the number &amp;lt;code&amp;gt;1,234,456.78&amp;lt;/code&amp;gt;, groups of thousands are separated by commas.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;&amp;quot;Title&amp;quot;&amp;lt;/code&amp;gt; || The title of the spreadsheet&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;&amp;quot;URL&amp;quot;&amp;lt;/code&amp;gt; || &#039;&#039;([[Analytica 7.2|7.2]])&#039;&#039; The link of a workbook opened from Google Sheets or Google Drive. Null for other workbooks.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;&amp;quot;UseSystemSeparators&amp;quot;&amp;lt;/code&amp;gt; || &#039;&#039;([[Analytica 5.0|5.0]])&#039;&#039; True when Excel uses &amp;lt;code&amp;gt;&amp;quot;DecimalSeparator&amp;quot;&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;&amp;quot;ThousandsSeparator&amp;quot;&amp;lt;/code&amp;gt; for displaying numbers.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;&amp;quot;Version&amp;quot;&amp;lt;/code&amp;gt; || &#039;&#039;([[Analytica 5.0|5.0]])&#039;&#039; The version number (text) for the installed release of Excel. Excel 2010 is &amp;quot;14.0&amp;quot;, Excel 2013 is &amp;quot;15.0&amp;quot; and Excel 2016 is &amp;quot;16.0&amp;quot;.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;&amp;quot;Visible&amp;quot;&amp;lt;/code&amp;gt; || True when the Excel UI is visible.&lt;br /&gt;
|}  The items above marked with &#039;&#039;([[Analytica 5.0|5.0]])&#039;&#039;  require [[Analytica 5.0]] or better; those marked &#039;&#039;([[Analytica 7.2|7.2]])&#039;&#039; require [[Analytica 7.2]].&lt;br /&gt;
&lt;br /&gt;
== History== &lt;br /&gt;
&lt;br /&gt;
Functions for reading cells from Excel were first present in Analytica 4.1 with functions named [[OpenExcelFile]], [[WorksheetCell]] and [[WorksheetRange]], although these were labelled as &#039;&#039;experimental&#039;&#039;, and the present functions were not officially available until 4.2.0.    The old names are now deprecated, replaced with [[SpreadsheetOpen]], [[SpreadsheetCell]] and [[SpreadsheetRange]].  The old functions still work, but may be removed in future Analytica releases.  The parameters have changed slightly from [[WorksheetRange]] to [[SpreadsheetRange]], with the sheet parameter moved from being the second to being the last parameter and now optional -- no longer required for named ranges or ranges of the form &amp;lt;code&amp;gt;&amp;quot;Sheet1!A1:Z99&amp;quot;&amp;lt;/code&amp;gt;.  &lt;br /&gt;
&lt;br /&gt;
[[SpreadsheetInfo]] was introduced in [[Analytica 4.5]]. These options to [[SpreadsheetInfo]] were added in [[Analytica 5.0]]: &amp;quot;Version&amp;quot;, &amp;quot;CalculationVersion&amp;quot;, &amp;quot;Excel.CalculationVersion&amp;quot;, &amp;quot;UseSystemSeparators&amp;quot;, &amp;quot;DecimalSeparator&amp;quot;, &amp;quot;ThousandsSeparator&amp;quot;, and &amp;quot;CalculationState&amp;quot;.  &lt;br /&gt;
&lt;br /&gt;
The color options for «what» incorrectly returned numbers in 0x00bbggrr order, instead of 0x00rrggbb order prior to [[Analytica 5.0]]. (This was a bug -- the documentation stated it should be 0x00rrggbb).  Various options to the «what» parameter of [[SpreadsheetCell]] and [[SpreadsheetRange]] have appeared at different releases. The options &amp;lt;code&amp;gt;&#039;HorizontalAlignment&#039;&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;&#039;VerticalAlignment&#039;&amp;lt;/code&amp;gt; appeared in [[Analytica 5.0]]. Options &amp;lt;code&amp;gt;&#039;Address&#039;&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;&#039;AddressR1C1&#039;&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;&#039;Sheet&#039;&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;&#039;RangeName&#039;&amp;lt;/code&amp;gt; appeared in [[Analytica 4.6]]. The remaining options appeared in [[Analytica 4.4]], except for &amp;lt;code&amp;gt;&#039;Value&#039;&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;&#039;NumericValue&#039;&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;&#039;Formula&#039;&amp;lt;/code&amp;gt;&#039; and &amp;lt;code&amp;gt;&#039;RelativeFormula&#039;&amp;lt;/code&amp;gt;, which appeared when the «what» parameter was introduced in [[Analytica 4.3]].&lt;br /&gt;
&lt;br /&gt;
{{Release|1=7.2|2=|3=The &amp;lt;code&amp;gt;&#039;GoogleSheets&#039;&amp;lt;/code&amp;gt; «backend» of [[SpreadsheetOpen]] (Google Sheets spreadsheets and Excel workbooks stored in Google Drive, opened from their link), and the &amp;quot;Backend&amp;quot;, &amp;quot;URL&amp;quot; and &amp;quot;Account&amp;quot; items of [[SpreadsheetInfo]], were added in [[Analytica 7.2]].}}&lt;br /&gt;
&lt;br /&gt;
== See Also == &lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;column-count:2;-moz-column-count:2;-webkit-column-count:2&amp;quot;&amp;gt;&lt;br /&gt;
*  [[media:Spreadsheet Helper lib.ana|Spreadsheet Helper lib.ana]]&lt;br /&gt;
* [[Analytica_Libraries_and_Templates#Spreadsheet_Helper_Library|Spreadsheet Helper Library]]&lt;br /&gt;
* [[Media:Functions for Reading Excel Worksheets.ana|Reading Excel Worksheets.ana]]&lt;br /&gt;
* [[Read and Write Spreadsheets]]&lt;br /&gt;
* [[Excel spreadsheets read and write]]&lt;br /&gt;
* [[Functions to Write Data to Excel Worksheets]] -- [[SpreadsheetSetCell]], [[SpreadsheetSetRange]] and [[SpreadsheetSave]]&lt;br /&gt;
* {{Release|1=7.2|2=|3=[[Using your own Google OAuth client]] -- connecting to Google Sheets through your own Google Cloud registration}}&lt;br /&gt;
* {{Release|1=7.2|2=|3=[[Using a Google service account]] -- opening a Google Sheet with no browser and nobody present}}&lt;br /&gt;
* You can also use [[DbQuery| ODBC]] -- a standard database access method to read from Excel spreadsheets.&lt;br /&gt;
* [[SuppressExcelAlerts]]&lt;br /&gt;
* [[Excel to Analytica Translation]]&lt;br /&gt;
* [[Excel to Analytica Mappings]]&lt;br /&gt;
* [[ReadTextFile]]&lt;br /&gt;
* [[ReadCsvFile]]&lt;br /&gt;
* You can use these functions from  [[Excel Functions from ADE| ADE]].&lt;br /&gt;
* These spreadsheet functions above are more flexible than [[OLE linking]] which is also available.&lt;br /&gt;
* [[OLE linking]] &amp;lt;/div&amp;gt;&lt;/div&gt;</summary>
		<author><name>Lchrisman</name></author>
	</entry>
	<entry>
		<id>https://docs.analytica.com/index.php?title=WriteTextFile&amp;diff=64621</id>
		<title>WriteTextFile</title>
		<link rel="alternate" type="text/html" href="https://docs.analytica.com/index.php?title=WriteTextFile&amp;diff=64621"/>
		<updated>2026-09-24T19:17:52Z</updated>

		<summary type="html">&lt;p&gt;Lchrisman: Custom file system providers: URI paths now supported here (write/file-management/spreadsheet support)&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[category:System Functions]]&lt;br /&gt;
[[category:Database Functions]]&lt;br /&gt;
[[category:File system functions]]&lt;br /&gt;
[[Category:Doc Status D]] &amp;lt;!-- For Lumina use, do not change --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{{ReleaseBar}}&lt;br /&gt;
&lt;br /&gt;
== WriteTextFile(filename, text&#039;&#039;, append, warn, sep, showDialog, encoding{{Release|6.0||, download}}{{Release|7.1||, extensionFilter}}&#039;&#039;) ==&lt;br /&gt;
&lt;br /&gt;
Write «text» value to a file with name «filename».&lt;br /&gt;
&lt;br /&gt;
=== Optional parameters ===&lt;br /&gt;
&lt;br /&gt;
«append»: Default &amp;lt;code&amp;gt;False&amp;lt;/code&amp;gt;. If the file exists and «append» is &amp;lt;code&amp;gt;True&amp;lt;/code&amp;gt;, it appends the text to the end of the file. If it doesn&#039;t exist and «warn» is &amp;lt;code&amp;gt;True&amp;lt;/code&amp;gt; , it asks whether to create the file.&lt;br /&gt;
&lt;br /&gt;
«warn»: Default &amp;lt;code&amp;gt;True&amp;lt;/code&amp;gt;. If «warn» is &amp;lt;code&amp;gt;False&amp;lt;/code&amp;gt;, it suppresses the warning that the file already exists when «append» is false or that it does not exist when «append» is True.&lt;br /&gt;
&lt;br /&gt;
«showDialog»: Default &amp;lt;code&amp;gt;Undefined&amp;lt;/code&amp;gt; -- i.e. it shows a file browser dialog only if the indicated filename is a folder, a read-only file, or if it exists and «warn» is &amp;lt;code&amp;gt;True&amp;lt;/code&amp;gt;, or if it does not exist and «append» is &amp;lt;code&amp;gt;True&amp;lt;/code&amp;gt;. When «showDialog» is &amp;lt;code&amp;gt;True&amp;lt;/code&amp;gt;, it always opens a dialog before with file name as the default to let the user change file name or folder. When &amp;lt;code&amp;gt;False&amp;lt;/code&amp;gt;, it never shows a dialog but may generate an error when trying to overwrite an existing file with «append» False.&lt;br /&gt;
&lt;br /&gt;
«sep»: (Default is a new line.) Character to use as a separator between elements when «text» is an array.&lt;br /&gt;
&lt;br /&gt;
«encoding» (Default &amp;lt;code&amp;gt;&amp;quot;ANSI&amp;quot;&amp;lt;/code&amp;gt;.) Specifies how to encode characters in the file:&lt;br /&gt;
* &amp;lt;code&amp;gt;&amp;quot;ANSI&amp;quot;&amp;lt;/code&amp;gt; : One byte per character, [http://en.wikipedia.org/wiki/8859 ISO-8859-1]. Extended characters (above ascii 255) are written as ?.&lt;br /&gt;
* &amp;lt;code&amp;gt;&amp;quot;UTF-8&amp;quot;&amp;lt;/code&amp;gt; : [http://en.wikipedia.org/wiki/UTF-8 UTF-8 encoding] with a [http://en.wikipedia.org/wiki/Byte_order_mark byte order mark] ([http://en.wikipedia.org/wiki/Byte_order_mark BOM]). The UTF-8 encoding uses one byte for comman characters, and 2-3 bytes for characters with ascii values above 127.&lt;br /&gt;
*&amp;lt;code&amp;gt; &amp;quot;-UTF-8&amp;quot;&amp;lt;/code&amp;gt; : [http://en.wikipedia.org/wiki/UTF-8 UTF-8 encoding] without a BOM.&lt;br /&gt;
* &amp;lt;code&amp;gt;&amp;quot;UTF-16&amp;quot;&amp;lt;/code&amp;gt; : Two bytes per character, big endian, with BOM.&lt;br /&gt;
* &amp;lt;code&amp;gt;&amp;quot;UTF-16le&amp;quot;&amp;lt;/code&amp;gt; : Two bytes per character, little endian, with BOM.&lt;br /&gt;
&lt;br /&gt;
{{Release|6.0||&lt;br /&gt;
«download»: This is only used when running the model in [[ACP]]. When set to &amp;lt;code&amp;gt;True&amp;lt;/code&amp;gt;, the «text» is downloaded onto the end-user&#039;s computer as a text file with the given «filename». }}&lt;br /&gt;
&lt;br /&gt;
{{Release|1=7.1|2=|3=&lt;br /&gt;
«extensionFilter»: (Optional) Overrides the file types offered in the file-selector dialog. Specify the filter(s) as a text in any of these forms:&lt;br /&gt;
* &#039;&#039;Display form&#039;&#039; &amp;amp;mdash; one or more filters separated by semicolons, each a description with its patterns in parentheses, e.g. &amp;lt;code&amp;gt;&amp;quot;Text Files (*.txt); CSV Files (*.csv); All Files (*.*)&amp;quot;&amp;lt;/code&amp;gt;.&lt;br /&gt;
* &#039;&#039;A list of&#039;&#039; &amp;lt;code&amp;gt;&amp;quot;Description{{!}}patterns&amp;quot;&amp;lt;/code&amp;gt; &#039;&#039;filters&#039;&#039; (description first, patterns separated by &amp;lt;code&amp;gt;;&amp;lt;/code&amp;gt;), e.g. &amp;lt;code&amp;gt;[&amp;quot;Text files{{!}}*.txt;*.csv&amp;quot;, &amp;quot;All files{{!}}*.*&amp;quot;]&amp;lt;/code&amp;gt;.&lt;br /&gt;
* &#039;&#039;Just patterns&#039;&#039;, e.g. &amp;lt;code&amp;gt;&amp;quot;*.txt;*.csv&amp;quot;&amp;lt;/code&amp;gt; (a description is generated automatically).&lt;br /&gt;
An &amp;lt;code&amp;gt;&amp;quot;All files (*.*)&amp;quot;&amp;lt;/code&amp;gt; entry is appended automatically unless your list already includes one. When omitted, the default file types are shown.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
==History==&lt;br /&gt;
Introduced in [[What&#039;s new in Analytica 4.0?|Analytica 4.0]].&lt;br /&gt;
&lt;br /&gt;
«encoding» parameter introduced in [[Analytica 4.5]].&lt;br /&gt;
&lt;br /&gt;
«download» parameter introduced in [[Analytica 6.0]].&lt;br /&gt;
&lt;br /&gt;
«extensionFilter» parameter introduced in [[Analytica 7.1]].&lt;br /&gt;
&lt;br /&gt;
== See also ==&lt;br /&gt;
* [[Custom file system providers]] -- paths such as &amp;lt;code&amp;gt;repo://Sales/Q3&amp;lt;/code&amp;gt; in a file store&lt;br /&gt;
* [[ReadTextFile]]&lt;br /&gt;
* [[Read and write text files]]&lt;br /&gt;
* [[MakeCSV]]&lt;br /&gt;
* [[MakeJSON]]&lt;br /&gt;
* [[Text functions]]&lt;br /&gt;
* [[WriteBinaryFile]]&lt;br /&gt;
* [[CurrentDataFolder]]&lt;br /&gt;
* [[Model File Character Encoding]]&lt;br /&gt;
* [[Files and Editing]]&lt;/div&gt;</summary>
		<author><name>Lchrisman</name></author>
	</entry>
	<entry>
		<id>https://docs.analytica.com/index.php?title=FileSystemDelete&amp;diff=64620</id>
		<title>FileSystemDelete</title>
		<link rel="alternate" type="text/html" href="https://docs.analytica.com/index.php?title=FileSystemDelete&amp;diff=64620"/>
		<updated>2026-09-24T19:17:51Z</updated>

		<summary type="html">&lt;p&gt;Lchrisman: Custom file system providers: URI paths now supported here (write/file-management/spreadsheet support)&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[category:File system functions]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;New to [[Analytica 6.0]]&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Requires {{Analytica Developer}} edition or better.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== FileSystemDelete( filepath&#039;&#039;, deleteReadOnly, deleteNonEmptyFolder&#039;&#039; ) ==&lt;br /&gt;
&lt;br /&gt;
Deletes a file or folder.&lt;br /&gt;
&lt;br /&gt;
There are other ways to delete a file or folder by using [[RunConsoleProcess]] or [[COM Integration|system COM objects]], but these other methods may be unavailable for security reasons when you run your model on the public [https://Acp.analytica.com Analytica Cloud Platform (ACP)] server, whereas this one is available and secure on ACP.&lt;br /&gt;
&lt;br /&gt;
=== Parameters ===&lt;br /&gt;
&lt;br /&gt;
* «filepath»: The file or folder to be deleted. When this is a relative path, it is interpreted relative to [[CurrentDataFolder]]. Wildcards are not accepted.&lt;br /&gt;
&lt;br /&gt;
* «deleteReadOnly» (Optional, default False): If omited or False, files with the read-only attribute set will not be deleted.&lt;br /&gt;
&lt;br /&gt;
* «deleteNonEmptyFolder» (Optional, default False): If omited or False, a non-empty folder will not be deleted.&lt;br /&gt;
&lt;br /&gt;
=== Return value ===&lt;br /&gt;
&lt;br /&gt;
The (previous) full absolute path to the file or folder that was removed.&lt;br /&gt;
&lt;br /&gt;
== Notes ==&lt;br /&gt;
• Requires {{Analytica Developer}} edition or better.&lt;br /&gt;
• Can be used (securely) on Analytica Cloud Platform (ACP). You must have write access to the folder containing «filepath», i.e., only in your own project folder.&lt;br /&gt;
&lt;br /&gt;
== See Also ==&lt;br /&gt;
* [[Custom file system providers]] -- paths such as &amp;lt;code&amp;gt;repo://Sales/Q3&amp;lt;/code&amp;gt; in a file store&lt;br /&gt;
* [[:category:File system functions|]&lt;br /&gt;
* [[FileFullPath]]&lt;br /&gt;
* [[FileSystemMove]], [[FileSystemCopy]]&lt;br /&gt;
* [[FileSystemListing]]&lt;/div&gt;</summary>
		<author><name>Lchrisman</name></author>
	</entry>
	<entry>
		<id>https://docs.analytica.com/index.php?title=FileSystemMove&amp;diff=64619</id>
		<title>FileSystemMove</title>
		<link rel="alternate" type="text/html" href="https://docs.analytica.com/index.php?title=FileSystemMove&amp;diff=64619"/>
		<updated>2026-09-24T19:17:51Z</updated>

		<summary type="html">&lt;p&gt;Lchrisman: Custom file system providers: URI paths now supported here (write/file-management/spreadsheet support)&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[category:File system functions]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;New to [[Analytica 6.0]]&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Requires [[Analytica Enterprise]] edition or better.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== FileSystemMove( source, dest ) ==&lt;br /&gt;
&lt;br /&gt;
Renames a file or folder, moves a file to a new location, or moves a folder and all its contents to a new location.&lt;br /&gt;
&lt;br /&gt;
=== Parameters ===&lt;br /&gt;
&lt;br /&gt;
* «source»: A file path to an existing file or folder to be moved or renamed. If this is a relative path, it is interpreted relative to the CurrentDataFolder(). Wildcards are not accepted.  If «source» is a folder, then the entire contents will be moved.&lt;br /&gt;
&lt;br /&gt;
* «dest»: The destination name. If this is a relative path, it is interpreted relative to the CurrentDataFolder(). When moving a folder, suffix «dest» with &#039;\&#039; if you want to copy into «toPath» and don&#039;t suffix with &#039;\&#039; if you want «dest» to be the final name. See the examples below.&lt;br /&gt;
&lt;br /&gt;
===Return value===&lt;br /&gt;
The full path of the file or folder after it has been moved.&lt;br /&gt;
&lt;br /&gt;
==Examples==&lt;br /&gt;
* When both «source» and «dest» are filenames but in different folders, the file is moved AND renamed.&lt;br /&gt;
*:&amp;lt;code&amp;gt;[[FileSystemMove]]( &amp;quot;C:\MyTemplates\blank.xls&amp;quot;, &amp;quot;results.xlsx&amp;quot; )&amp;lt;/code&amp;gt;&lt;br /&gt;
*::==&amp;gt; Moves it to your [[CurrentDataFolder]], same as where your model resides unless you&#039;ve changed it.&lt;br /&gt;
&lt;br /&gt;
* When «source» is a file and «dest» is an existing folder, it moves it into the «dest» folder.&lt;br /&gt;
*:&amp;lt;code&amp;gt;[[FileSystemMove]]( &amp;quot;C:\MyModels\cashFlow.ana&amp;quot;, &amp;quot;D:\Backups\Models&amp;quot; )&amp;lt;/code&amp;gt;&lt;br /&gt;
*:==&amp;gt; moves to &amp;lt;code&amp;gt;&amp;quot;C:\Backups\Models\cashFlow.ana&amp;quot;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* When «source» is a folder and «dest» ends with \, then it moved it to inside the «dest» folder.&lt;br /&gt;
*:[[FileSystemMove]]( &amp;quot;C:\MyModels&amp;quot;, &amp;quot;D:\Backups\&amp;quot; )&amp;lt;/code&amp;gt;&lt;br /&gt;
*:==&amp;gt; moves the entire folder tree to &amp;lt;code&amp;gt;&amp;quot;D:\Backups\MyModels&amp;quot;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* When «source» is a folder and «dest» does not end with \, then «dest» is the name of the folder after it is moved.&lt;br /&gt;
*:[[FileSystemMove]]( &amp;quot;C:\MyModels&amp;quot;, &amp;quot;D:\Models2&amp;quot; )&amp;lt;/code&amp;gt;&lt;br /&gt;
*:==&amp;gt; The folder &amp;lt;code&amp;gt;&amp;quot;D:\Models2&amp;quot;&amp;lt;/code&amp;gt; is the resulting folder.	&lt;br /&gt;
	&lt;br /&gt;
== Notes==&lt;br /&gt;
* Requires Analytica Enterprise edition or better.&lt;br /&gt;
* Can be used (securely) on Analytica Cloud Platform (ACP). You must have read access to the «source» folder and write access to the «dest» folder.&lt;br /&gt;
&lt;br /&gt;
==See Also==&lt;br /&gt;
* [[Custom file system providers]] -- paths such as &amp;lt;code&amp;gt;repo://Sales/Q3&amp;lt;/code&amp;gt; in a file store&lt;br /&gt;
* [[:category:File system functions|File system functions]]&lt;br /&gt;
* [[FileSystemCopy]]&lt;br /&gt;
* [[FileFullPath]]&lt;br /&gt;
* [[FileSystemDelete]], [[FileSystemListing]]&lt;br /&gt;
* [[FileSystemNewFolder]]&lt;/div&gt;</summary>
		<author><name>Lchrisman</name></author>
	</entry>
	<entry>
		<id>https://docs.analytica.com/index.php?title=FileSystemCopy&amp;diff=64618</id>
		<title>FileSystemCopy</title>
		<link rel="alternate" type="text/html" href="https://docs.analytica.com/index.php?title=FileSystemCopy&amp;diff=64618"/>
		<updated>2026-09-24T19:17:51Z</updated>

		<summary type="html">&lt;p&gt;Lchrisman: Custom file system providers: URI paths now supported here (write/file-management/spreadsheet support)&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[category:File system functions]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;New to [[Analytica 6.0]]&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Requires {{Analytica Developer}} edition or better.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== FileSystemCopy( source, dest&#039;&#039;, replace, copyLinks&#039;&#039; ) ==&lt;br /&gt;
&lt;br /&gt;
Copies a file, or recursively copies a folder and its contents.&lt;br /&gt;
&lt;br /&gt;
Note: There are other ways to copy files or folders by using [[RunConsoleProcess]] or [[COM Integration|system COM objects]], but these other methods may be unavailable for security reasons when you run your model on the public [https://Acp.analytica.com Analytica Cloud Platform (ACP)] server, whereas this one is available and secure on ACP.&lt;br /&gt;
&lt;br /&gt;
=== Parameters:===&lt;br /&gt;
&lt;br /&gt;
* «source»: A file path to an existing file or folder to be copied. If this is a relative path, it is interpreted relative to the [[CurrentDataFolder]]. Wildcards are not accepted.  If the «source» is a folder, then the entire contents will be recursively copied.&lt;br /&gt;
&lt;br /&gt;
* «dest»: Where the file or folder should be copied to. If this is a relative path, it is interpreted relative to the [[CurrentDataFolder]]. When copying a folder, suffix «dest» with &#039;\&#039; if you want to copy into «dest» and don&#039;t suffix with &#039;\&#039; if you want «dest» to be the final name. See the examples below.&lt;br /&gt;
&lt;br /&gt;
* «replace»: (optional, default False) Set to True to replace (without asking) if the destination file exists. False to throw an error if a destination file or folder exists. &lt;br /&gt;
&lt;br /&gt;
* «copyLinks»: (optional, default false): Set to true if you want symbolic links to be copied.&lt;br /&gt;
&lt;br /&gt;
=== Return value: ===&lt;br /&gt;
The number of files copied or new folders created.&lt;br /&gt;
&lt;br /&gt;
== Examples ==&lt;br /&gt;
*Both «source» and «dest» can be filenames.&lt;br /&gt;
*:&amp;lt;code&amp;gt;[[FileSystemCopy]]( &amp;quot;C:\MyTemplates\blank.xls&amp;quot;, &amp;quot;results.xlsx&amp;quot; )&amp;lt;/code&amp;gt;&lt;br /&gt;
*:==&amp;gt; Copies to your [[CurrentDataFolder]], same as where your model resides unless you&#039;ve changed it.&lt;br /&gt;
&lt;br /&gt;
* When «source» is a file and «dest» is an existing folder, it makes a copy with the same filename in the «dest» folder.&lt;br /&gt;
*:&amp;lt;code&amp;gt;[[FileSystemCopy]]( &amp;quot;C:\MyModels\cashFlow.ana&amp;quot;, &amp;quot;D:\Backups\Models&amp;quot;, replace:true )&amp;lt;/code&amp;gt;&lt;br /&gt;
*:==&amp;gt; copies to &amp;lt;code&amp;gt;&amp;quot;C:\Backups\Models\cashFlow.ana&amp;quot;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* When «source» is a folder and «dest» ends with \, then it makes a copy as the same name as «source» inside the «dest» folder.&lt;br /&gt;
*:&amp;lt;code&amp;gt;[[FileSystemCopy]]( &amp;quot;C:\MyModels&amp;quot;, &amp;quot;D:\Backups\&amp;quot;, replace:true )&amp;lt;/code&amp;gt;&lt;br /&gt;
*:==&amp;gt; copies the entire folder tree to &amp;lt;code&amp;gt;&amp;quot;D:\Backups\MyModels&amp;quot;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* When «source» is a folder and «dest» does not end with \, then «dest» is the name of the copied folder.&lt;br /&gt;
*:&amp;lt;code&amp;gt;[[FileSystemCopy]]( &amp;quot;C:\MyModels&amp;quot;, &amp;quot;D:\Models2&amp;quot; )&amp;lt;/code&amp;gt;&lt;br /&gt;
*:==&amp;gt; The folder &amp;quot;D:\Models2&amp;quot; becomes a replica of &amp;quot;C:\MyModels&amp;quot;	&lt;br /&gt;
	&lt;br /&gt;
==Notes==&lt;br /&gt;
* Requires {{Analytica Developer}} edition or better.&lt;br /&gt;
* Can be used (securely) on Analytica Cloud Platform (ACP). You must have read access to the «source» folder and write access to the «dest» folder.&lt;br /&gt;
&lt;br /&gt;
== See Also ==&lt;br /&gt;
* [[Custom file system providers]] -- paths such as &amp;lt;code&amp;gt;repo://Sales/Q3&amp;lt;/code&amp;gt; in a file store&lt;br /&gt;
* [[:category:File system functions|File system functions]]&lt;br /&gt;
* [[FileSystemMove]]&lt;br /&gt;
* [[FileFullPath]]&lt;br /&gt;
* [[FileSystemListing]]&lt;br /&gt;
* [[FileSystemDelete]]&lt;/div&gt;</summary>
		<author><name>Lchrisman</name></author>
	</entry>
	<entry>
		<id>https://docs.analytica.com/index.php?title=FileSystemNewFolder&amp;diff=64617</id>
		<title>FileSystemNewFolder</title>
		<link rel="alternate" type="text/html" href="https://docs.analytica.com/index.php?title=FileSystemNewFolder&amp;diff=64617"/>
		<updated>2026-09-24T19:17:51Z</updated>

		<summary type="html">&lt;p&gt;Lchrisman: Custom file system providers: URI paths now supported here (write/file-management/spreadsheet support)&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[category:File system functions]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;New to [[Analytica 6.0]]&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Requires {{Analytica Developer}} edition or better.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== FileSystemNewFolder( name, parentFolder ) ==&lt;br /&gt;
&lt;br /&gt;
Creates a new folder with the name «name», if it doesn&#039;t already exist, and returns its full path.&lt;br /&gt;
&lt;br /&gt;
The optional «parentFolder» specifies the location where the new folder is created. When this is specified, the new folder must be located in the «parentFolder».&lt;br /&gt;
&lt;br /&gt;
When «parentFolder» is omitted and «name» is not a full absolute file path, then «name» is interpreted relative to the [[CurrentDataFolder]].&lt;br /&gt;
&lt;br /&gt;
«name» can be a full path to the folder. All folders along the path that don&#039;t already exist are created.&lt;br /&gt;
&lt;br /&gt;
== Examples ==&lt;br /&gt;
* &amp;lt;code&amp;gt;[[FileSystemNewFolder]]( &amp;quot;c:\DataFeeds\ISO&amp;quot; )&amp;lt;/code&amp;gt;&lt;br /&gt;
*:This might create two nested folders: &amp;lt;code&amp;gt;&amp;quot;C:\DataFeeds&amp;quot;&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;&amp;quot;C:\DataFeeds\ISO&amp;quot;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;[[FileSystemNewFolder]]( &amp;quot;MyData&amp;quot; )&amp;lt;/code&amp;gt;&lt;br /&gt;
*:Creates a folder named &amp;lt;code&amp;gt;&amp;quot;MyData&amp;quot;&amp;lt;/code&amp;gt; inside the ]]CurrentDataFolder]]. Returns the full path.&lt;br /&gt;
	&lt;br /&gt;
* &amp;lt;code&amp;gt;[[FileSystemNewFolder]]( &amp;quot;Data&amp;quot;, &amp;quot;D:\Documents\Models&amp;quot; )&amp;lt;/code&amp;gt;&lt;br /&gt;
*:Creates the folder &amp;lt;code&amp;gt;&amp;quot;D:\Documents\Models\Data&amp;quot;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Use in ACP ==&lt;br /&gt;
There are other ways to create a new folder using [[RunConsoleProcess]] or [[COM Integration|system COM objects]], but these other methods may be unavailable for security reasons when you run your model on the public [https://Acp.analytica.com Analytica Cloud Platform (ACP)] server, whereas this one is available and secure on ACP.&lt;br /&gt;
&lt;br /&gt;
In general, you  will only be able to create a new folder using this function inside your model&#039;s own project directory.&lt;br /&gt;
	&lt;br /&gt;
== See Also ==&lt;br /&gt;
* [[Custom file system providers]] -- paths such as &amp;lt;code&amp;gt;repo://Sales/Q3&amp;lt;/code&amp;gt; in a file store&lt;br /&gt;
* [[:category:File system functions|File system functions]]&lt;br /&gt;
* [[FileSystemListing]]&lt;br /&gt;
* [[FileFullPath]]&lt;/div&gt;</summary>
		<author><name>Lchrisman</name></author>
	</entry>
	<entry>
		<id>https://docs.analytica.com/index.php?title=Analytica_Command_Line&amp;diff=64616</id>
		<title>Analytica Command Line</title>
		<link rel="alternate" type="text/html" href="https://docs.analytica.com/index.php?title=Analytica_Command_Line&amp;diff=64616"/>
		<updated>2026-09-24T19:17:50Z</updated>

		<summary type="html">&lt;p&gt;Lchrisman: Custom file system providers: URI paths now supported here (write/file-management/spreadsheet support)&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt; [[category:File Commands]]&lt;br /&gt;
[[Category:Analytica User Guide]]&lt;br /&gt;
&lt;br /&gt;
{{ReleaseBar}}&lt;br /&gt;
&lt;br /&gt;
The desktop Analytica.exe process is launched with a command line having the format:&lt;br /&gt;
&lt;br /&gt;
 Analytica.exe [options] [filename]&lt;br /&gt;
&lt;br /&gt;
The brackets mean that these are optional. When &#039;&#039;filename&#039;&#039; has one or more spaces, you need to put double quotes around the filename. Or, you can put double quotes around the &#039;&#039;filename&#039;&#039; even if it doesn&#039;t have spaces.  The &#039;&#039;filename&#039;&#039; will usually have the extension *.ana.  When no filename is specified, Analytica launches to the intro screen.  When a filename is specified, it loads that model file.&lt;br /&gt;
&lt;br /&gt;
== Options ==&lt;br /&gt;
&lt;br /&gt;
Each option can be prefixed either with a forward slash, &amp;lt;code&amp;gt;/&amp;lt;/code&amp;gt;, or with a minus, &amp;lt;code&amp;gt;-&amp;lt;/code&amp;gt;. Option values appear after a colon. There can be no spaces within the option or its value. When an option value contains a space, the option value must be quoted using double quotes. &lt;br /&gt;
&lt;br /&gt;
{{Release|1=7.2|2=|3=As of [[Analytica 7.2]], an option may also be prefixed with a double minus, &amp;lt;code&amp;gt;--&amp;lt;/code&amp;gt;, and an option value may be introduced by an equal sign, &amp;lt;code&amp;gt;=&amp;lt;/code&amp;gt;, in place of the colon. So &amp;lt;code&amp;gt;/nosplash&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;-nosplash&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;--nosplash&amp;lt;/code&amp;gt; are all the same option, and &amp;lt;code&amp;gt;/mcp:8080&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;--mcp=8080&amp;lt;/code&amp;gt; are the same option with the same value. This makes the spellings that are conventional for Chromium and for command lines on other operating systems work as expected.}}&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;/rlmDiag:&#039;&#039;filename&#039;&#039;&amp;lt;/code&amp;gt;: Writes diagnostic information about licenses to &#039;&#039;filename&#039;&#039;. This is very useful if you are encountering problems with a license that you believe has been activated, but is not working.  After launching Analytica with this option, exit Analytica and either review the log file in a text editor, or email the diagnostic file to support@lumina.com for assistance. The diagnostic file logs the information about licenses that it finds on your computer, and hence is very useful for debugging license problems. &lt;br /&gt;
*: Example usage: &amp;lt;code&amp;gt;Analytica.exe /rlmDiag:&amp;quot;c:\Temp\rlmDiag.log&amp;quot;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;/roam:&#039;&#039;days&#039;&#039;&amp;lt;/code&amp;gt;: Specifies the number of days to roam a floating license.  Or, if &#039;&#039;days&#039;&#039; is -1, it releases a roamed license. See [[License Roaming]].&lt;br /&gt;
*: Example usage: &amp;lt;code&amp;gt;Analytica.exe /roam:7&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;/lic:&#039;&#039;licenseName&#039;&#039;&amp;lt;/code&amp;gt;: Uses the specified license name without changing which license is selected by default. The license must already be activated (i.e., the license must be in your &amp;lt;code&amp;gt;C:\ProgramData\Lumina\Licenses&amp;lt;/code&amp;gt; folder).&lt;br /&gt;
*:Example: &amp;lt;code&amp;gt;Analytica.exe /lic:analytica_optimizer_761_2&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;/rlm:&#039;&#039;server&#039;&#039;&amp;lt;/code&amp;gt;: Specifies the server name (or port@serverName) that is running a Reprise License Manager with the desired centrally managed (e.g., floating) license.&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;/eval:&#039;&#039;expression&#039;&#039;&amp;lt;/code&amp;gt;: (new to [[Analytica 6.0]]). Specifies an Analytica expression that is evaluated immediately after the model file that is specified on the command line finishes loading. The evaluating occurs after any proactively evaluated variables or buttons in the model. It does not dirty the model. The expression can include global variable assignments, and button identifiers. You will almost certainly want to include double quotes around the expression, and you need to escape any interior quotes by proceeding the quote with a backslash. {{Release?|1=7.1|2=As of [[Analytica 7.1]], the expression is evaluated even when no &#039;&#039;filename&#039;&#039; appears on the command line; it then runs once the model that Analytica starts up with -- your startup model, or a new untitled model -- has finished loading.|3=The parameter is ignored if &#039;&#039;filename&#039;&#039; is not specified on the command line.}}  See [[Running a model in a command line workflow]].&lt;br /&gt;
&lt;br /&gt;
*: Example usage: &amp;lt;code&amp;gt;Analytica.exe /eval:&amp;quot;Claimant=\&amp;quot;JDoe\&amp;quot;;Run_Batch;Exit&amp;quot; &amp;quot;Claim analysis.ana&amp;quot;&amp;lt;/code&amp;gt;&lt;br /&gt;
*: In this example, &amp;lt;code&amp;gt;Claimant&amp;lt;/code&amp;gt; is the identifier of an input variable in the model and &amp;lt;code&amp;gt;Run_batch&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;Exit&amp;lt;/code&amp;gt; are names of a buttons in the model.&lt;br /&gt;
&lt;br /&gt;
{{Release|1=7.2|2=|3=&lt;br /&gt;
* &amp;lt;code&amp;gt;/evalThenExit:&#039;&#039;expression&#039;&#039;&amp;lt;/code&amp;gt;: (new to [[Analytica 7.2]]) The same as &amp;lt;code&amp;gt;/eval:&amp;lt;/code&amp;gt;, except that Analytica exits as soon as &#039;&#039;expression&#039;&#039; has finished, without asking whether to save changes. This is almost always what you want in a [[Running a model in a command line workflow|command line workflow]].&lt;br /&gt;
*: Example usage: &amp;lt;code&amp;gt;Analytica.exe /evalThenExit:&amp;quot;Claimant=\&amp;quot;JDoe\&amp;quot;;Run_Batch&amp;quot; &amp;quot;Claim analysis.ana&amp;quot;&amp;lt;/code&amp;gt;&lt;br /&gt;
*: Before 7.2, you had to write the exit into the expression yourself, as &amp;lt;code&amp;gt;/eval:&amp;quot;&#039;&#039;expression&#039;&#039;;EvaluateScript(&#039;[[User Interface Typescript Commands#Bye|Bye]] -&#039;)&amp;quot;&amp;lt;/code&amp;gt;. That was awkward and easy to leave out -- and when it was left out, the batch run did its work and then sat at an idle Analytica window forever, waiting for a person who was never coming.&lt;br /&gt;
*: If &#039;&#039;expression&#039;&#039; exits on its own -- because it runs &amp;lt;code&amp;gt;Bye&amp;lt;/code&amp;gt;, or presses a button that does -- that simply happens first, and this option then has nothing left to do.&lt;br /&gt;
*: If &#039;&#039;expression&#039;&#039; fails, the error dialog still appears and waits to be dismissed before Analytica exits. Add &amp;lt;code&amp;gt;[[Analytica Command Line/Automation|/Automation]]&amp;lt;/code&amp;gt; when the run must not stop for a dialog under any circumstances.&lt;br /&gt;
*: &amp;lt;code&amp;gt;/eval:&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;/evalThenExit:&amp;lt;/code&amp;gt; may both appear on the same command line, in which case the &amp;lt;code&amp;gt;/eval:&amp;lt;/code&amp;gt; expression is evaluated first.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;/comment:&#039;&#039;text&#039;&#039;&amp;lt;/code&amp;gt;: (new to [[Analytica 6.0]]) The &#039;&#039;text&#039;&#039; is a comment that is ignored by Analytica. In one example usage, ACP3 adds a comment to each spawned processes to differentiate them in task manager.&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;/NoSplash&amp;lt;/code&amp;gt;: (new to [[Analytica 6.0]]) Don&#039;t show the splash screen at startup (which normally displays for 3 seconds). &lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;/mcp:«port»&amp;lt;/code&amp;gt;: (new to [[Analytica 7.0]]) Enable Analytica as [[MCP server in Analytica|an MCP server]] (a protocol that allows A.I. language models to call functions in your model). [[UDF]]s that contain &amp;lt;code&amp;gt;@mcpTool&amp;lt;/code&amp;gt; in their description are exposed as functions that the external A.I. client can call.&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;/lang:«code»&amp;lt;/code&amp;gt; (new to [[Analytica 7.1]]) Specifies the User Interface language for Analytica (for menus, dialogs, error messages, built-in function descriptions, etc.). «code» is a language code such as &amp;lt;code&amp;gt;&#039;en&#039;&amp;lt;/code&amp;gt; (for English), &amp;lt;code&amp;gt;&#039;es&#039;&amp;lt;/code&amp;gt; (for Spanish), etc. The language is only used when the resource files exist for that language in &lt;br /&gt;
*:&amp;lt;code&amp;gt;«Analytica install folder»\Resources\«code»\&amp;lt;/code&amp;gt;&lt;br /&gt;
*: The command line overrides the registry setting &amp;lt;code&amp;gt;Language&amp;lt;/code&amp;gt; (if present) in the hive&lt;br /&gt;
*:&amp;lt;code&amp;gt;HKCU\Software\Lumina Decision Systems\Analytica&amp;lt;/code&amp;gt;&lt;br /&gt;
*:As of [[Analytica 7.1]], non-English UI options is an experimental feature and not complete. &lt;br /&gt;
&lt;br /&gt;
The remaining options exist for internal purposes and aren&#039;t generally used by end-users:&lt;br /&gt;
* &amp;lt;code&amp;gt;/embedding&amp;lt;/code&amp;gt;: The Windows operating system uses this parameter when launching Analytica as a OLE-link server. Not used by end-users. Causes Analytica to launch quietly (without a GUI), load an indicated model, and exposes key OLE interfaces to Windows enabling an external application to complete the link to the model&#039;s data.&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;/solverDevLic&amp;lt;/code&amp;gt;: This parameter is sometimes used by Analytica developers, but not generally by end-users. It indicates that the licenses from [https://solver.com Frontline Systems] found in the &amp;lt;code&amp;gt;Solver.lic&amp;lt;/code&amp;gt; file are developer licenses rather than runtime licenses. &lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;/forcerelease&amp;lt;/code&amp;gt;: This parameter is used by Analytica quality assurance engineers during beta testing periods. It causes a beta release to behave like a final release build with regard to licensing.&lt;br /&gt;
&lt;br /&gt;
*&amp;lt;code&amp;gt;/config:&#039;&#039;filename&#039;&#039;&amp;lt;/code&amp;gt;: This is an option used by ACP3 (by Suan.exe) and not in Desktop Analytica.  The specified config file contains settings that define the ACP3 server configuration.&lt;br /&gt;
&lt;br /&gt;
{{Release|1=7.2|2=|3=&lt;br /&gt;
* &amp;lt;code&amp;gt;/remote-debugging-port:«port»&amp;lt;/code&amp;gt;: (new to [[Analytica 7.2]]) Exposes the Chrome DevTools protocol (remote debugging) endpoint of Analytica&#039;s embedded Chromium (CEF) browser windows on &amp;lt;code&amp;gt;127.0.0.1:«port»&amp;lt;/code&amp;gt;, so that an external tool — a benchmark driver, Claude Code, Puppeteer, or Chrome&#039;s &amp;lt;code&amp;gt;chrome://inspect&amp;lt;/code&amp;gt; page — can inspect and drive HTML-based windows such as the Assista chat window, HTML dialogs, and windows created with the &amp;lt;code&amp;gt;CefWindow&amp;lt;/code&amp;gt; function. The Chromium spelling &amp;lt;code&amp;gt;--remote-debugging-port=«port»&amp;lt;/code&amp;gt; is also accepted. Use a «port» of 0 to have a free (ephemeral) port picked automatically. Valid values are 0 or 1024 through 65535. The endpoint listens only on 127.0.0.1, and enabling it is per-process (all CEF windows of the process share one endpoint, with one DevTools target per window).&lt;br /&gt;
*: After launch, the process reports the outcome in &amp;lt;code&amp;gt;%LOCALAPPDATA%\Lumina\Analytica\CEF\«pid»\RemoteDebug.json&amp;lt;/code&amp;gt;, where «pid» is the process id of the launched Analytica.exe. On success this contains &amp;lt;code&amp;gt;&amp;quot;status&amp;quot;:&amp;quot;listening&amp;quot;&amp;lt;/code&amp;gt; and the actual port; when the requested port is already in use by another process, or CEF fails to initialize, it contains &amp;lt;code&amp;gt;&amp;quot;status&amp;quot;:&amp;quot;failed&amp;quot;&amp;lt;/code&amp;gt; with a reason, and a warning dialog is also shown. A tool that launches Analytica.exe with this option should read this file rather than connecting blindly, so that it cannot be fooled into talking to some other process that owns the port.&lt;br /&gt;
*: While remote debugging is enabled, each page in a CEF window exposes the JavaScript global &amp;lt;code&amp;gt;window.anaCefWindow&amp;lt;/code&amp;gt; with fields &amp;lt;code&amp;gt;pid&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;handle&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;kind&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;title&amp;lt;/code&amp;gt;, identifying which window a DevTools target belongs to. The &amp;lt;code&amp;gt;handle&amp;lt;/code&amp;gt; matches the handle of the corresponding &amp;lt;code&amp;gt;CefWindow&amp;lt;/code&amp;gt; object in the model.&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;/remote-allow-origins:«origins»&amp;lt;/code&amp;gt;: (new to [[Analytica 7.2]]) A comma-separated list of origins (or &amp;lt;code&amp;gt;*&amp;lt;/code&amp;gt;) allowed to connect to the remote-debugging endpoint from a web page. This is only needed when the DevTools client is itself a web page, which sends an HTTP Origin header that Chromium rejects by default. Ordinary clients (Python or Node scripts, Puppeteer, &amp;lt;code&amp;gt;chrome://inspect&amp;lt;/code&amp;gt;) send no Origin header and connect without this option. The Chromium spelling &amp;lt;code&amp;gt;--remote-allow-origins=«origins»&amp;lt;/code&amp;gt; is also accepted.&lt;br /&gt;
}}&lt;br /&gt;
{{Release|1=7.2|2=|3=&lt;br /&gt;
* &amp;lt;code&amp;gt;/lib:&#039;&#039;filename&#039;&#039;&amp;lt;/code&amp;gt;: (new to [[Analytica 7.2]]) Loads the Analytica library in &#039;&#039;filename&#039;&#039; into the &amp;lt;code&amp;gt;SysLib_Customizations&amp;lt;/code&amp;gt; system module, before the model named on the command line is opened. The library is then present no matter which model is loaded, and it stays loaded as models are closed and opened during the session. The option can be repeated to load several libraries. They load in the order given, all of them before the model.&lt;br /&gt;
*: Example usage: &amp;lt;code&amp;gt;Analytica.exe /lib:&amp;quot;W:\Analytica\Libraries\Multivariate Distributions.ana&amp;quot; MyModel.ana&amp;lt;/code&amp;gt;&lt;br /&gt;
*: A relative &#039;&#039;filename&#039;&#039; is looked for first in the directory that the process was launched from, then in the directories on the &amp;lt;code&amp;gt;AddLibraryDir&amp;lt;/code&amp;gt; library search path, then in the Analytica installation folder, and finally in the preferences folder. Path substitutions such as &amp;lt;code&amp;gt;%appdata%&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;%installdir%&amp;lt;/code&amp;gt; are expanded.&lt;br /&gt;
*: Each library loaded this way is its own namespace. Since &amp;lt;code&amp;gt;SysLib_Customizations&amp;lt;/code&amp;gt; is a private namespace, the library is not in namespace scope for the user&#039;s model. A model that wants to use one of these libraries must name it in its [[NamespaceImports]] attribute.&lt;br /&gt;
*: When the file is not found, or when it fails to load, a warning dialog names the file and Analytica continues to start up without it.&lt;br /&gt;
*: Uses for this include loading an [[MCP server in Analytica]] that works with whatever model is subsequently loaded, a benchmark driver, or a library that adds toolbar buttons or menu items.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
{{Release|1=7.2|2=|3=&lt;br /&gt;
* &amp;lt;code&amp;gt;/stores:&#039;&#039;filename&#039;&#039;&amp;lt;/code&amp;gt;: (new to [[Analytica 7.2]]) Names the &amp;lt;code&amp;gt;FileProviders.config&amp;lt;/code&amp;gt; file that declares the [[Custom file system providers|custom file system providers]] this process should use. A file provider makes a URI-style path such as &amp;lt;code&amp;gt;repo://Sales/Q3.csv&amp;lt;/code&amp;gt; usable anywhere Analytica accepts a file name -- opening a model, [[ReadTextFile]], [[FileSystemListing]], and so on -- with the &amp;lt;code&amp;gt;repo&amp;lt;/code&amp;gt; prefix served by an HTTP storage service that you configure, or write yourself.&lt;br /&gt;
*: Example usage: &amp;lt;code&amp;gt;Analytica.exe /stores:&amp;quot;C:\ACME\FileProviders.config&amp;quot; MyModel.ana&amp;lt;/code&amp;gt;&lt;br /&gt;
*: A relative &#039;&#039;filename&#039;&#039; is resolved against the directory that the process was launched from.&lt;br /&gt;
*: This option &#039;&#039;&#039;overrides&#039;&#039;&#039; the other places the file is otherwise looked for -- the &amp;lt;code&amp;gt;ANALYTICA_FILE_PROVIDERS_CONFIG&amp;lt;/code&amp;gt; environment variable, the &amp;lt;code&amp;gt;FileProvidersConfig&amp;lt;/code&amp;gt; registry value, and a &amp;lt;code&amp;gt;FileProviders.config&amp;lt;/code&amp;gt; sitting in the same folder as the engine binary -- so it is mainly a deployment and debugging convenience. The registry value is the usual way to configure stores, and, with the environment variable, it is the only way to do it for [[ADE]] and ADEW, which have no command line of their own.&lt;br /&gt;
*: The configuration is read once, while the engine initializes, so changing it means restarting the process. When the file named is not there, no providers are registered.&lt;br /&gt;
*: The same option, with the same meaning, is accepted by &amp;lt;code&amp;gt;Suan.exe&amp;lt;/code&amp;gt; and by &amp;lt;code&amp;gt;Amp.exe&amp;lt;/code&amp;gt;.&lt;br /&gt;
*: Requires the {{Analytica Developer}} edition or better. For what to put in the file, and for the HTTP API a storage service implements, see &#039;&#039;&#039;[[Custom file system providers]]&#039;&#039;&#039;.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
{{Release|1=7.2|2=|3=&lt;br /&gt;
* &amp;lt;code&amp;gt;/Automation&amp;lt;/code&amp;gt;: (new to [[Analytica 7.2]]) Runs Analytica in &#039;&#039;automation mode&#039;&#039;, in which every modal dialog that would otherwise block is answered automatically, so that an unattended run never stops waiting for a person. Each dialog is answered either by a built-in default, or by a handler function that the model or a library registers with [[RegisterAutomationModalHandler]]. This is intended for unattended and agent-driven runs, such as a benchmark driver, an automated QA run, or a session driven by an AI coding agent through the [[MCP server in Analytica]].&lt;br /&gt;
*: This is &#039;&#039;&#039;not&#039;&#039;&#039; a headless mode. The user interface, diagram windows, HTML dialogs, and Assista all remain fully alive and usable; only the blocking dialogs are answered for you. The splash screen is suppressed, as with &amp;lt;code&amp;gt;/nosplash&amp;lt;/code&amp;gt;, and no recovery file is written, as with &amp;lt;code&amp;gt;/Autosave=0&amp;lt;/code&amp;gt; (give &amp;lt;code&amp;gt;/Autosave=1&amp;lt;/code&amp;gt; explicitly to keep auto-recovery on).&lt;br /&gt;
*: Some built-in defaults are deliberately conservative so that an unattended run cannot damage the model it is testing. In particular, &amp;quot;Save changes?&amp;quot; is answered &#039;&#039;&#039;No&#039;&#039;&#039;, so the model file on disk is never written when the process exits. File open and save prompts fail rather than choosing a file, unless a registered handler supplies a path.&lt;br /&gt;
*: For the full list of defaults, how to write and register a handler, and worked examples of driving Analytica this way, see &#039;&#039;&#039;[[Analytica Command Line/Automation]]&#039;&#039;&#039;.&lt;br /&gt;
*: Example usage: &amp;lt;code&amp;gt;Analytica.exe /Automation /lib:BenchmarkDriver.ana MyModel.ana&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;/AutomationTrace:&#039;&#039;filename&#039;&#039;&amp;lt;/code&amp;gt;: (new to [[Analytica 7.2]]) Writes a log of every modal dialog to &#039;&#039;filename&#039;&#039;, in JSON Lines format (one JSON object per line). Each dialog record names the kind of dialog, its caption and body text, the buttons it offered, the answer that was given, and where that answer came from. Records are appended, so a driver can pre-create the file and tail it while the run proceeds, and each record carries the process id so that concurrent runs remain distinguishable.&lt;br /&gt;
*: Used together with &amp;lt;code&amp;gt;/Automation&amp;lt;/code&amp;gt;, this records what automation answered. Used &#039;&#039;&#039;without&#039;&#039;&#039; &amp;lt;code&amp;gt;/Automation&amp;lt;/code&amp;gt;, it is an &#039;&#039;observe mode&#039;&#039;: dialogs appear and behave completely normally, and the log records what the user actually chose. This is a convenient way to inventory which dialogs a scenario poses before you automate it.&lt;br /&gt;
*: A relative &#039;&#039;filename&#039;&#039; is resolved against the directory the process was launched from. If the file cannot be opened, Analytica starts normally and simply produces no log.&lt;br /&gt;
*: Example usage: &amp;lt;code&amp;gt;Analytica.exe /Automation /AutomationTrace:C:\Temp\run1.jsonl MyModel.ana&amp;lt;/code&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
{{Release|1=7.2|2=|3=&lt;br /&gt;
These two options place the main Analytica application window explicitly, instead of using the size and position that Analytica saved when it last exited. They are intended for benchmark automation and automated QA, where the window needs to be the same from one run to the next, or needs to be somewhere predictable.&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;/WindowSize:«width»,«height»&amp;lt;/code&amp;gt;: (new to [[Analytica 7.2]]) Sets the size of the main application window, in screen pixels.&lt;br /&gt;
*: Example usage: &amp;lt;code&amp;gt;Analytica.exe /WindowSize:1000,600&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;/WindowXY:«x»,«y»&amp;lt;/code&amp;gt;: (new to [[Analytica 7.2]]) Sets the position of the top left corner of the main application window, in screen coordinates. On a computer with more than one monitor, these coordinates can be negative, or larger than the size of the primary monitor, to place the window on another monitor.&lt;br /&gt;
*: Example usage: &amp;lt;code&amp;gt;Analytica.exe /WindowSize:1000,600 /WindowXY:-1000,100&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Either option can be used without the other. The one you leave out keeps the value that was saved when Analytica last exited.&lt;br /&gt;
&lt;br /&gt;
The window is placed exactly where you ask for it. Unlike a position restored from a previous session, it is not nudged back onto a monitor, so a window that falls partly or entirely off screen stays where you put it. These options also take precedence over a maximized state saved from the previous session, and over the &#039;&#039;Run: Maximized&#039;&#039; setting of a Windows shortcut, so that the size you ask for is never ignored.&lt;br /&gt;
&lt;br /&gt;
The geometry given on the command line is &#039;&#039;&#039;not&#039;&#039;&#039; saved back to the registry when Analytica exits. An automated run that uses these options therefore leaves the window size and position of your own interactive sessions undisturbed.&lt;br /&gt;
&lt;br /&gt;
The size is that of the visible window frame. Windows surrounds that frame with an invisible drop shadow, so a script that measures the window using &amp;lt;code&amp;gt;GetWindowRect&amp;lt;/code&amp;gt; sees a few extra pixels on each side, while &amp;lt;code&amp;gt;DwmGetWindowAttribute&amp;lt;/code&amp;gt; with &amp;lt;code&amp;gt;DWMWA_EXTENDED_FRAME_BOUNDS&amp;lt;/code&amp;gt; reports exactly the size that was requested.&lt;br /&gt;
&lt;br /&gt;
A value that is not two whole numbers, or a width or height that is not positive, produces the &#039;&#039;Unrecognized command line parameter&#039;&#039; warning and that option is ignored.&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;/Autosave=0&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;/Autosave=1&amp;lt;/code&amp;gt;: (new to [[Analytica 7.2]]) Turns the &#039;&#039;Maintain recovery info&#039;&#039; preference off or on for this process only, without changing the stored preference. That preference controls the incremental autosave that lets Analytica offer to recover unsaved changes after a crash. With &amp;lt;code&amp;gt;/Autosave=0&amp;lt;/code&amp;gt;, no recovery file is written while the process runs, so if the process is killed rather than closed, the next launch does not stop at the &amp;quot;recover unsaved changes?&amp;quot; dialog. With &amp;lt;code&amp;gt;/Autosave=1&amp;lt;/code&amp;gt;, recovery information is maintained for the session even when the preference is off (in editions that support auto-recovery). A bare &amp;lt;code&amp;gt;/Autosave&amp;lt;/code&amp;gt; means &amp;lt;code&amp;gt;/Autosave=1&amp;lt;/code&amp;gt;; the spellings &amp;lt;code&amp;gt;/Autosave:0&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;--autosave=0&amp;lt;/code&amp;gt; are also accepted.&lt;br /&gt;
*: Example usage: &amp;lt;code&amp;gt;Analytica.exe /Autosave=0 MyModel.ana&amp;lt;/code&amp;gt;&lt;br /&gt;
*: When &amp;lt;code&amp;gt;/Automation&amp;lt;/code&amp;gt; is given and &amp;lt;code&amp;gt;/Autosave&amp;lt;/code&amp;gt; is not, &amp;lt;code&amp;gt;/Autosave=0&amp;lt;/code&amp;gt; is implied: an automated run is usually killed rather than closed, and would otherwise leave a recovery record behind for the next interactive launch to ask about. Give &amp;lt;code&amp;gt;/Autosave=1&amp;lt;/code&amp;gt; explicitly if an automated run needs auto-recovery on.&lt;br /&gt;
*: While the option is in effect, the &#039;&#039;Maintain recovery info&#039;&#039; checkbox in the &#039;&#039;&#039;Preferences&#039;&#039;&#039; dialog shows the effective state but is disabled. Recovery records left behind by earlier sessions are still offered at startup as usual; the option only controls whether this process writes one. A value other than 0 or 1 produces the &#039;&#039;Unrecognized command line parameter&#039;&#039; warning and the option is ignored.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
{{Release|1=7.2|2=|3=&lt;br /&gt;
== Command line options for libraries ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;new in [[Analytica 7.2]]&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
An option that begins with a double minus, &amp;lt;code&amp;gt;--&amp;lt;/code&amp;gt;, and that is not one of the options listed above, does &#039;&#039;&#039;not&#039;&#039;&#039; produce the &#039;&#039;Unrecognized command line parameter&#039;&#039; warning. Analytica ignores it and passes it through, so that a library or a model can read it. This lets a library define command line options of its own without having to be built into Analytica. For example:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;code&amp;gt;Analytica.exe --AssistaURL:&amp;lt;nowiki&amp;gt;&amp;quot;https://aaia.analytica.com/staging&amp;quot;&amp;lt;/nowiki&amp;gt; --OpenAssista MyModel.ana&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A library reads them with [[GetProcessInfo]]:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;code&amp;gt;GetProcessInfo(&amp;quot;Command line:--AssistaURL&amp;quot;) &amp;amp;rarr; &#039;https://aaia.analytica.com/staging&#039;&amp;lt;/code&amp;gt;&lt;br /&gt;
:&amp;lt;code&amp;gt;GetProcessInfo(&amp;quot;Command line:--OpenAssista&amp;quot;) &amp;amp;rarr; True&amp;lt;/code&amp;gt;&lt;br /&gt;
:&amp;lt;code&amp;gt;GetProcessInfo(&amp;quot;Command line:--NotGiven&amp;quot;) &amp;amp;rarr; Null&amp;lt;/code&amp;gt;&lt;br /&gt;
:&amp;lt;code&amp;gt;GetProcessInfo(&amp;quot;Command line parameters&amp;quot;) &amp;amp;rarr; [&#039;--AssistaURL&#039;, &#039;--OpenAssista&#039;]&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The value of such an option is the text that follows its colon or equal sign, and it must be quoted when it contains a space. An option that appears with no value reads as &amp;lt;code&amp;gt;True&amp;lt;/code&amp;gt;, and an option that is not on the command line at all reads as &amp;lt;code&amp;gt;Null&amp;lt;/code&amp;gt;, so a library can tell those two apart. A value always comes back as text, even when it looks like a number, so use [[ParseNumber]] when you want a number. When the same option appears more than once, the last occurrence is the one reported.&lt;br /&gt;
&lt;br /&gt;
Because Analytica accepts any unrecognized &amp;lt;code&amp;gt;--&amp;lt;/code&amp;gt; option silently, a misspelled one is simply ignored rather than reported. &amp;lt;code&amp;gt;GetProcessInfo(&amp;quot;Command line parameters&amp;quot;)&amp;lt;/code&amp;gt; returns the list of &amp;lt;code&amp;gt;--&amp;lt;/code&amp;gt; options that actually arrived, which is how a driver or library can check that it was passed what it expected.&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;--&amp;lt;/code&amp;gt; options are not copied into the file names that Analytica opens, and they are not case-sensitive. When reading one, the leading dashes are optional, so &amp;lt;code&amp;gt;GetProcessInfo(&amp;quot;Command line:AssistaURL&amp;quot;)&amp;lt;/code&amp;gt; finds it as well.&lt;br /&gt;
&lt;br /&gt;
This mechanism is available in Analytica.exe, in ACP (Suan.exe) and in [[ADE]]. In ADE the command line is the one belonging to the host process that loaded ADE, which is the same command line that &amp;lt;code&amp;gt;GetProcessInfo(&amp;quot;Command line&amp;quot;)&amp;lt;/code&amp;gt; reports.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
== Accessing the Command line from a model ==&lt;br /&gt;
&lt;br /&gt;
The expression &amp;lt;code&amp;gt;[[GetProcessInfo]](&#039;Command line&#039;)&amp;lt;/code&amp;gt; returns the command line used to launch the process.&lt;br /&gt;
&lt;br /&gt;
== See also ==&lt;br /&gt;
* [[GetProcessInfo]] -- especially &amp;lt;code&amp;gt;GetProcessInfo(&#039;Command line&#039;)&amp;lt;/code&amp;gt;&lt;br /&gt;
* [[Running a model in a command line workflow]]&lt;br /&gt;
* [[/Automation]]&lt;br /&gt;
* [[MCP server in Analytica]]&lt;br /&gt;
* [[Custom file system providers]]&lt;br /&gt;
* [[Assista]]&lt;/div&gt;</summary>
		<author><name>Lchrisman</name></author>
	</entry>
	<entry>
		<id>https://docs.analytica.com/index.php?title=Custom_file_system_providers/API_spec&amp;diff=64615</id>
		<title>Custom file system providers/API spec</title>
		<link rel="alternate" type="text/html" href="https://docs.analytica.com/index.php?title=Custom_file_system_providers/API_spec&amp;diff=64615"/>
		<updated>2026-09-24T19:16:55Z</updated>

		<summary type="html">&lt;p&gt;Lchrisman: Write and file-management endpoints (PUT/DELETE /files, PUT/DELETE /folders, POST /copy, POST /move); status codes 405/409/412/413/501&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[category:File system functions]]&lt;br /&gt;
[[Category:Analytica User Guide]]&lt;br /&gt;
&lt;br /&gt;
{{ReleaseBar}}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;New in [[Analytica 7.2]].&#039;&#039; See also the parent page, [[Custom file system providers]].&lt;br /&gt;
&lt;br /&gt;
__TOC__&lt;br /&gt;
&lt;br /&gt;
== Overview ==&lt;br /&gt;
&lt;br /&gt;
A &#039;&#039;&#039;store service&#039;&#039;&#039; is an ordinary HTTP service that Analytica calls whenever a model uses a &amp;lt;code&amp;gt;«scheme»://&amp;lt;/code&amp;gt; path. A store that is only read from needs three endpoints, and nothing else is required. A store that Analytica may also write to -- saving models, [[WriteTextFile]], [[FileSystemNewFolder]], [[FileSystemCopy]], [[FileSystemMove]], [[FileSystemDelete]], [[SpreadsheetSave]] -- implements the [[#Write and file-management endpoints|write and file-management endpoints]] as well. No Analytica code is involved, so write the service in whatever language and framework you like, and back it with whatever you like: object storage, a database, a version control system, a document management system, or a REST API of its own.&lt;br /&gt;
&lt;br /&gt;
Analytica is told about the service by one line in &amp;lt;code&amp;gt;FileProviders.config&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;code&amp;gt;FileStore repo = http://mystore.internal:8080&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
after which every path beginning &amp;lt;code&amp;gt;repo://&amp;lt;/code&amp;gt; becomes a request to that base URL. See [[Custom file system providers]] for the rest of the configuration.&lt;br /&gt;
&lt;br /&gt;
== The endpoints ==&lt;br /&gt;
&lt;br /&gt;
These three serve every read. All endpoints, including the [[#Write and file-management endpoints|write and file-management endpoints]], are relative to the configured base URL, including any path prefix in it. With a base URL of &amp;lt;code&amp;gt;http://mystore.internal:8080/api&amp;lt;/code&amp;gt;, the first endpoint is &amp;lt;code&amp;gt;http://mystore.internal:8080/api/files/...&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Request !! Purpose !! Success !! Not found&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;GET {base}/files/{root}/{path}&amp;lt;/code&amp;gt; || Fetch a file || &amp;lt;code&amp;gt;200&amp;lt;/code&amp;gt;, with the raw file bytes as the body || &amp;lt;code&amp;gt;404&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;HEAD {base}/files/{root}/{path}&amp;lt;/code&amp;gt; || Existence check, without transferring the file || &amp;lt;code&amp;gt;200&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;404&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;GET {base}/list/{root}/{path}?recursive=0&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;?recursive=1&amp;lt;/code&amp;gt; || List a folder || &amp;lt;code&amp;gt;200&amp;lt;/code&amp;gt;, with a JSON array (see below) || &amp;lt;code&amp;gt;404&amp;lt;/code&amp;gt;, taken as an empty listing&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;{root}/{path}&amp;lt;/code&amp;gt; is everything that followed &amp;lt;code&amp;gt;«scheme»://&amp;lt;/code&amp;gt; in the model&#039;s path. For &amp;lt;code&amp;gt;repo://Sales/2026/Q3.csv&amp;lt;/code&amp;gt; the engine requests &amp;lt;code&amp;gt;{base}/files/Sales/2026/Q3.csv&amp;lt;/code&amp;gt;. The engine attaches no meaning of its own to &amp;lt;code&amp;gt;{root}&amp;lt;/code&amp;gt;: it is simply the first segment. It is there so that one service can host several logical roots -- one per customer, per project or per bucket -- and decide from it which backing store to consult.&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;recursive&amp;lt;/code&amp;gt; query parameter is sent on every &amp;lt;code&amp;gt;/list&amp;lt;/code&amp;gt; request and on no &amp;lt;code&amp;gt;/files&amp;lt;/code&amp;gt; request.&lt;br /&gt;
&lt;br /&gt;
A health endpoint is a good idea for your own monitoring, but the engine never calls one and does not require it.&lt;br /&gt;
&lt;br /&gt;
== Path encoding ==&lt;br /&gt;
&lt;br /&gt;
Everything after &amp;lt;code&amp;gt;«scheme»://&amp;lt;/code&amp;gt; is the literal path -- unencoded text, as the model author wrote it. Before sending a request, the engine:&lt;br /&gt;
&lt;br /&gt;
# replaces every &amp;lt;code&amp;gt;\&amp;lt;/code&amp;gt; with &amp;lt;code&amp;gt;/&amp;lt;/code&amp;gt;, and&lt;br /&gt;
# percent-encodes every byte that is not in the unreserved set &amp;lt;code&amp;gt;A-Z a-z 0-9 - _ . ~ /&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Your service must percent-decode the path. Because the encoding is applied byte by byte to the UTF-8 form of the name, spaces and non-ASCII characters round-trip correctly: &amp;lt;code&amp;gt;repo://models/Ventas Q3.csv&amp;lt;/code&amp;gt; arrives as &amp;lt;code&amp;gt;/files/models/Ventas%20Q3.csv&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The engine refuses a &amp;lt;code&amp;gt;..&amp;lt;/code&amp;gt; segment before any request is sent, so a well-behaved client never asks for one. Reject traversal in the service as well; defence in depth is worth the three lines it costs.&lt;br /&gt;
&lt;br /&gt;
== Authentication ==&lt;br /&gt;
&lt;br /&gt;
When a token is configured, with &amp;lt;code&amp;gt;FileStoreToken&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;FileStoreTokenFile&amp;lt;/code&amp;gt;, the engine sends&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;code&amp;gt;Authorization: Bearer «token»&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
on &#039;&#039;&#039;every&#039;&#039;&#039; request, to every endpoint. When no token is configured, no &amp;lt;code&amp;gt;Authorization&amp;lt;/code&amp;gt; header is sent at all.&lt;br /&gt;
&lt;br /&gt;
Answer &amp;lt;code&amp;gt;401&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;403&amp;lt;/code&amp;gt; when the token is missing, expired or wrong. Analytica turns either one into an error that names the scheme, so the user is told the credentials were rejected rather than that the file is missing.&lt;br /&gt;
&lt;br /&gt;
== How the engine interprets your status codes ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Status !! What the engine does&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;200&amp;lt;/code&amp;gt; || Success. The body is the file, or the listing.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;404&amp;lt;/code&amp;gt; || Not found. Treated as a missing file, with no error text of its own, so the caller reports a missing file in its usual way. On &amp;lt;code&amp;gt;/list&amp;lt;/code&amp;gt;, an empty listing.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;401&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;403&amp;lt;/code&amp;gt; || A credentials error naming the scheme: &amp;lt;code&amp;gt;the repo:// file store rejected the credentials (HTTP 401)&amp;lt;/code&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;405&amp;lt;/code&amp;gt; || On a write: that store, or that part of it, is read-only. The write fails with &amp;lt;code&amp;gt;the repo:// file store is read-only and refused the write (HTTP 405)&amp;lt;/code&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;409&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;412&amp;lt;/code&amp;gt; || On a write: a conflict -- the destination already exists, a folder is not empty, or a file is in the way of a folder. The function that asked reports it in its own words (for example, that the destination exists and «replace» would allow it).&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;413&amp;lt;/code&amp;gt; || On a write: the upload is too large. &amp;lt;code&amp;gt;the repo:// file store refused the upload as too large (HTTP 413)&amp;lt;/code&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;501&amp;lt;/code&amp;gt; || On a copy or move: the service cannot do it itself. For a single file the engine then does the work with &amp;lt;code&amp;gt;GET&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;PUT&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;DELETE&amp;lt;/code&amp;gt;; for a folder the operation fails.&lt;br /&gt;
|-&lt;br /&gt;
| anything else || &amp;lt;code&amp;gt;HTTP «n» from the repo:// file store&amp;lt;/code&amp;gt;.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
A connection that cannot be made at all, or that times out, is reported as &amp;lt;code&amp;gt;could not reach the repo:// file store (...)&amp;lt;/code&amp;gt;, with the transport reason in the parentheses. Neither of those is a status code you control, so return a real status for everything you can: the message the user sees is then about your service rather than about the network.&lt;br /&gt;
&lt;br /&gt;
== The listing response ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;GET {base}/list/{root}/{path}&amp;lt;/code&amp;gt; returns a JSON &#039;&#039;&#039;array&#039;&#039;&#039; of objects, one per entry. A body that does not parse, or that is not a JSON array, is an error.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Member !! Type !! Meaning&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;name&amp;lt;/code&amp;gt; || string || The entry name only, with no path. &#039;&#039;&#039;Required&#039;&#039;&#039; -- an entry with no &amp;lt;code&amp;gt;name&amp;lt;/code&amp;gt;, or whose &amp;lt;code&amp;gt;name&amp;lt;/code&amp;gt; is not a string, is skipped.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;isFolder&amp;lt;/code&amp;gt; || boolean or number || &amp;lt;code&amp;gt;true&amp;lt;/code&amp;gt;, or a non-zero number, for a folder. Absent or false means a file.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;size&amp;lt;/code&amp;gt; || number || Size in bytes. Use 0 for folders.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;modified&amp;lt;/code&amp;gt; || string || The last-modified time in ISO-8601, UTC, for example &amp;lt;code&amp;gt;&amp;quot;2026-08-14T09:12:03Z&amp;quot;&amp;lt;/code&amp;gt;. Omit it when you do not know it.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Members other than these are ignored, so you may return extra information for your own clients.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
[&lt;br /&gt;
  {&amp;quot;name&amp;quot;: &amp;quot;Q1.csv&amp;quot;,  &amp;quot;isFolder&amp;quot;: false, &amp;quot;size&amp;quot;: 20481, &amp;quot;modified&amp;quot;: &amp;quot;2026-04-02T17:45:10Z&amp;quot;},&lt;br /&gt;
  {&amp;quot;name&amp;quot;: &amp;quot;Q2.csv&amp;quot;,  &amp;quot;isFolder&amp;quot;: false, &amp;quot;size&amp;quot;: 21118, &amp;quot;modified&amp;quot;: &amp;quot;2026-07-01T08:03:55Z&amp;quot;},&lt;br /&gt;
  {&amp;quot;name&amp;quot;: &amp;quot;archive&amp;quot;, &amp;quot;isFolder&amp;quot;: true,  &amp;quot;size&amp;quot;: 0}&lt;br /&gt;
]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;recursive=1&amp;lt;/code&amp;gt; asks for the whole subtree below the folder. When you answer one, &amp;lt;code&amp;gt;name&amp;lt;/code&amp;gt; should carry the path &#039;&#039;&#039;relative to the folder that was listed&#039;&#039;&#039; -- &amp;lt;code&amp;gt;&amp;quot;archive/2025/Q4.csv&amp;quot;&amp;lt;/code&amp;gt;, not just &amp;lt;code&amp;gt;&amp;quot;Q4.csv&amp;quot;&amp;lt;/code&amp;gt;. That is what lets [[FileSystemListing]] report the sub-path the way it does for a local recursive listing. With &amp;lt;code&amp;gt;recursive=0&amp;lt;/code&amp;gt;, return only the immediate children.&lt;br /&gt;
&lt;br /&gt;
The engine, not the service, applies any wildcard the model wrote in the last segment of the path, and applies the «files» and «folders» flags of [[FileSystemListing]]. Return the folder&#039;s whole contents and let it do that filtering.&lt;br /&gt;
&lt;br /&gt;
== Write and file-management endpoints ==&lt;br /&gt;
&lt;br /&gt;
These are optional. A store that implements none of them is a read-only store, and every function that would write to it fails cleanly. Implement them when models are to save files, or to create, copy, move and delete them, in your store. Like the read endpoints, they are relative to the base URL, receive the same &amp;lt;code&amp;gt;Authorization&amp;lt;/code&amp;gt; header and use the same path encoding.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Request !! Purpose !! Answers&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;PUT {base}/files/{root}/{path}&amp;lt;/code&amp;gt; || Write a whole file. The body is the complete file, with a &amp;lt;code&amp;gt;Content-Length&amp;lt;/code&amp;gt;. The header &amp;lt;code&amp;gt;If-None-Match: *&amp;lt;/code&amp;gt; means &amp;quot;create only -- do not replace an existing file&amp;quot;. || &amp;lt;code&amp;gt;201&amp;lt;/code&amp;gt; created · &amp;lt;code&amp;gt;200&amp;lt;/code&amp;gt; replaced · &amp;lt;code&amp;gt;412&amp;lt;/code&amp;gt; exists (create only) · &amp;lt;code&amp;gt;405&amp;lt;/code&amp;gt; read-only · &amp;lt;code&amp;gt;413&amp;lt;/code&amp;gt; too large&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;DELETE {base}/files/{root}/{path}&amp;lt;/code&amp;gt; || Delete a file || &amp;lt;code&amp;gt;200&amp;lt;/code&amp;gt; · &amp;lt;code&amp;gt;404&amp;lt;/code&amp;gt; · &amp;lt;code&amp;gt;409&amp;lt;/code&amp;gt; it is a folder · &amp;lt;code&amp;gt;405&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;PUT {base}/folders/{root}/{path}&amp;lt;/code&amp;gt; || Create a folder, and any missing parent folders. The body is empty. || &amp;lt;code&amp;gt;201&amp;lt;/code&amp;gt; created · &amp;lt;code&amp;gt;200&amp;lt;/code&amp;gt; already a folder · &amp;lt;code&amp;gt;409&amp;lt;/code&amp;gt; a file is in the way · &amp;lt;code&amp;gt;405&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;DELETE {base}/folders/{root}/{path}?recursive=0&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;?recursive=1&amp;lt;/code&amp;gt; || Delete a folder; with &amp;lt;code&amp;gt;recursive=1&amp;lt;/code&amp;gt;, everything in it as well || &amp;lt;code&amp;gt;200&amp;lt;/code&amp;gt; · &amp;lt;code&amp;gt;404&amp;lt;/code&amp;gt; · &amp;lt;code&amp;gt;409&amp;lt;/code&amp;gt; not empty (with &amp;lt;code&amp;gt;recursive=0&amp;lt;/code&amp;gt;) · &amp;lt;code&amp;gt;405&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;POST {base}/copy/{root}/{path}?to={root}/{path}&amp;amp;overwrite=0&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;&amp;amp;overwrite=1&amp;lt;/code&amp;gt; || Copy a file, or a folder and everything in it || &amp;lt;code&amp;gt;201&amp;lt;/code&amp;gt; new or &amp;lt;code&amp;gt;200&amp;lt;/code&amp;gt; replaced, with the JSON body &amp;lt;code&amp;gt;{&amp;quot;copied&amp;quot;: «n»}&amp;lt;/code&amp;gt; · &amp;lt;code&amp;gt;404&amp;lt;/code&amp;gt; · &amp;lt;code&amp;gt;412&amp;lt;/code&amp;gt; the destination exists (with &amp;lt;code&amp;gt;overwrite=0&amp;lt;/code&amp;gt;) · &amp;lt;code&amp;gt;409&amp;lt;/code&amp;gt; a file/folder clash · &amp;lt;code&amp;gt;405&amp;lt;/code&amp;gt; · &amp;lt;code&amp;gt;501&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;POST {base}/move/{root}/{path}?to={root}/{path}&amp;lt;/code&amp;gt; || Move or rename a file or a folder. Never overwrites. || &amp;lt;code&amp;gt;200&amp;lt;/code&amp;gt; · &amp;lt;code&amp;gt;404&amp;lt;/code&amp;gt; · &amp;lt;code&amp;gt;412&amp;lt;/code&amp;gt; the destination exists · &amp;lt;code&amp;gt;405&amp;lt;/code&amp;gt; · &amp;lt;code&amp;gt;501&amp;lt;/code&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
* The &amp;lt;code&amp;gt;to&amp;lt;/code&amp;gt; parameter is percent-encoded exactly like the path.&lt;br /&gt;
* A folder is whatever &amp;lt;code&amp;gt;/list&amp;lt;/code&amp;gt; reports with &amp;lt;code&amp;gt;isFolder&amp;lt;/code&amp;gt;. An object store can represent an empty folder with a placeholder object, provided &amp;lt;code&amp;gt;/list&amp;lt;/code&amp;gt; shows the folder and never the placeholder.&lt;br /&gt;
* &amp;lt;code&amp;gt;405&amp;lt;/code&amp;gt; means read-only, and it may apply to the whole store or to part of it -- one root, say. For a move, the source must be writable as well as the destination; for a copy, only the destination.&lt;br /&gt;
* &amp;lt;code&amp;gt;501&amp;lt;/code&amp;gt; from &amp;lt;code&amp;gt;/copy&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;/move&amp;lt;/code&amp;gt; means &amp;quot;not possible on the server&amp;quot; -- a copy between two roots that live in different back ends, for example. For a single file, the engine then copies it itself with &amp;lt;code&amp;gt;GET&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;PUT&amp;lt;/code&amp;gt; (and, for a move, &amp;lt;code&amp;gt;DELETE&amp;lt;/code&amp;gt;). A move whose source cannot be deleted is undone by deleting the copy it made.&lt;br /&gt;
* Refuse &amp;lt;code&amp;gt;..&amp;lt;/code&amp;gt; segments, and a bare &amp;lt;code&amp;gt;{root}&amp;lt;/code&amp;gt; as the target of any of these requests.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Implementers:&#039;&#039;&#039;&lt;br /&gt;
* Check the write policy &#039;&#039;&#039;before&#039;&#039;&#039; reading a &amp;lt;code&amp;gt;PUT&amp;lt;/code&amp;gt; body, and drain any unread body before sending an early error reply. Some proxies reset the connection otherwise, and the user then sees a network error instead of your status.&lt;br /&gt;
* Apply your access rules to &#039;&#039;&#039;both&#039;&#039;&#039; the path and the &amp;lt;code&amp;gt;to&amp;lt;/code&amp;gt; path of a copy or move.&lt;br /&gt;
* A service that predates these endpoints leaves the functions that need them failing with &amp;lt;code&amp;gt;the repo:// file store does not support this operation (the store service may need to be updated)&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Sizes, timeouts and cancellation ==&lt;br /&gt;
&lt;br /&gt;
* The engine aborts a fetch as soon as it can see that the transfer exceeds &amp;lt;code&amp;gt;FileStoreMaxMB&amp;lt;/code&amp;gt; (256 MB by default), whether it learns that from &amp;lt;code&amp;gt;Content-Length&amp;lt;/code&amp;gt; or from the bytes received so far. Send a &amp;lt;code&amp;gt;Content-Length&amp;lt;/code&amp;gt; where you can, so that an oversized file is refused before it is transferred rather than during.&lt;br /&gt;
* The connect timeout is 5 seconds and is not configurable. The read timeout is &amp;lt;code&amp;gt;FileStoreTimeoutS&amp;lt;/code&amp;gt;, 60 seconds by default. A service that has slow work to do before it can produce a file should still start responding promptly.&lt;br /&gt;
* Streaming or chunking a large body is fine, and is preferable to buffering the whole thing in the service.&lt;br /&gt;
* A user can cancel a transfer that is in progress. From the service&#039;s point of view that looks like the client closing the connection part-way through a response, which the service should handle gracefully.&lt;br /&gt;
&lt;br /&gt;
== HTTP only ==&lt;br /&gt;
&lt;br /&gt;
The engine&#039;s HTTP client has no TLS support, so the base URL must be &amp;lt;code&amp;gt;http://&amp;lt;/code&amp;gt;; an &amp;lt;code&amp;gt;https://&amp;lt;/code&amp;gt; URL is refused when the provider is registered. Bind the service to &amp;lt;code&amp;gt;127.0.0.1&amp;lt;/code&amp;gt;, or to a private network or cluster network only. The bearer token and the file bytes are both in the clear over that hop. See [[Custom file system providers#Security]].&lt;br /&gt;
&lt;br /&gt;
== An illustrative skeleton ==&lt;br /&gt;
&lt;br /&gt;
The smallest thing that works, in Python, serving a couple of directory trees. It is here to show the shape of the three read endpoints and of the listing JSON; it implements none of the write endpoints, and it is not something to deploy. It uses Python&#039;s single-threaded development server, and its traversal checking is only the obvious one.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
import datetime, json, os, urllib.parse&lt;br /&gt;
from http.server import BaseHTTPRequestHandler, HTTPServer&lt;br /&gt;
&lt;br /&gt;
ROOTS = {&amp;quot;Sales&amp;quot;: &amp;quot;/srv/data/sales&amp;quot;, &amp;quot;models&amp;quot;: &amp;quot;/srv/data/models&amp;quot;}&lt;br /&gt;
TOKEN = &amp;quot;«bearer-token»&amp;quot;&lt;br /&gt;
&lt;br /&gt;
def resolve(rest):                          # &amp;quot;Sales/2026/Q3.csv&amp;quot; -&amp;gt; a local path&lt;br /&gt;
    parts = [p for p in urllib.parse.unquote(rest).split(&amp;quot;/&amp;quot;) if p]&lt;br /&gt;
    if not parts or &amp;quot;..&amp;quot; in parts or parts[0] not in ROOTS:&lt;br /&gt;
        return None&lt;br /&gt;
    return os.path.join(ROOTS[parts[0]], *parts[1:])&lt;br /&gt;
&lt;br /&gt;
def entry(dirpath, name, rel):&lt;br /&gt;
    full = os.path.join(dirpath, name)&lt;br /&gt;
    isdir = os.path.isdir(full)&lt;br /&gt;
    when = datetime.datetime.utcfromtimestamp(os.path.getmtime(full))&lt;br /&gt;
    return {&amp;quot;name&amp;quot;: rel + name, &amp;quot;isFolder&amp;quot;: isdir,&lt;br /&gt;
            &amp;quot;size&amp;quot;: 0 if isdir else os.path.getsize(full),&lt;br /&gt;
            &amp;quot;modified&amp;quot;: when.strftime(&amp;quot;%Y-%m-%dT%H:%M:%SZ&amp;quot;)}&lt;br /&gt;
&lt;br /&gt;
class Store(BaseHTTPRequestHandler):&lt;br /&gt;
    def reply(self, code, body=b&amp;quot;&amp;quot;, ctype=&amp;quot;application/octet-stream&amp;quot;):&lt;br /&gt;
        self.send_response(code)&lt;br /&gt;
        self.send_header(&amp;quot;Content-Type&amp;quot;, ctype)&lt;br /&gt;
        self.send_header(&amp;quot;Content-Length&amp;quot;, str(len(body)))&lt;br /&gt;
        self.end_headers()&lt;br /&gt;
        if self.command != &amp;quot;HEAD&amp;quot;:&lt;br /&gt;
            self.wfile.write(body)&lt;br /&gt;
&lt;br /&gt;
    def do_GET(self):                       # HEAD arrives here too&lt;br /&gt;
        if self.headers.get(&amp;quot;Authorization&amp;quot;) != &amp;quot;Bearer &amp;quot; + TOKEN:&lt;br /&gt;
            return self.reply(401)&lt;br /&gt;
        url = urllib.parse.urlparse(self.path)&lt;br /&gt;
        kind, _, rest = url.path.lstrip(&amp;quot;/&amp;quot;).partition(&amp;quot;/&amp;quot;)&lt;br /&gt;
        target = resolve(rest)&lt;br /&gt;
        if target is None:&lt;br /&gt;
            return self.reply(404)&lt;br /&gt;
        if kind == &amp;quot;files&amp;quot; and os.path.isfile(target):&lt;br /&gt;
            with open(target, &amp;quot;rb&amp;quot;) as f:&lt;br /&gt;
                return self.reply(200, f.read())&lt;br /&gt;
        if kind == &amp;quot;list&amp;quot; and os.path.isdir(target):&lt;br /&gt;
            recurse = urllib.parse.parse_qs(url.query).get(&amp;quot;recursive&amp;quot;) == [&amp;quot;1&amp;quot;]&lt;br /&gt;
            out = []&lt;br /&gt;
            for dirpath, dirs, files in os.walk(target):&lt;br /&gt;
                rel = os.path.relpath(dirpath, target).replace(os.sep, &amp;quot;/&amp;quot;)&lt;br /&gt;
                rel = &amp;quot;&amp;quot; if rel == &amp;quot;.&amp;quot; else rel + &amp;quot;/&amp;quot;&lt;br /&gt;
                out += [entry(dirpath, n, rel) for n in dirs + files]&lt;br /&gt;
                if not recurse:&lt;br /&gt;
                    break&lt;br /&gt;
            return self.reply(200, json.dumps(out).encode(), &amp;quot;application/json&amp;quot;)&lt;br /&gt;
        return self.reply(404)&lt;br /&gt;
&lt;br /&gt;
    do_HEAD = do_GET&lt;br /&gt;
&lt;br /&gt;
HTTPServer((&amp;quot;127.0.0.1&amp;quot;, 8080), Store).serve_forever()&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Testing your provider ==&lt;br /&gt;
&lt;br /&gt;
# Write a configuration file that points at the service and gives it a scheme: a &amp;lt;code&amp;gt;FileStore repo = http://127.0.0.1:8080&amp;lt;/code&amp;gt; line, plus &amp;lt;code&amp;gt;FileStoreToken repo = «bearer-token»&amp;lt;/code&amp;gt; if the service wants one.&lt;br /&gt;
# Make Analytica find that file -- with &amp;lt;code&amp;gt;/stores:«path»&amp;lt;/code&amp;gt;, with the &amp;lt;code&amp;gt;FileProvidersConfig&amp;lt;/code&amp;gt; registry value, or by putting it beside the engine binary. See [[Custom file system providers#Where Analytica looks for FileProviders.config|Where Analytica looks for FileProviders.config]].&lt;br /&gt;
# Start Analytica and look for the registration line, &amp;lt;code&amp;gt;[FileProviders] repo:// -&amp;gt; http://127.0.0.1:8080&amp;lt;/code&amp;gt;. It is written to the engine&#039;s typescript: the [[Typescript Window|typescript window]] in Analytica, the log in [[ADE]], ADEW and [[ACP]], and &amp;lt;code&amp;gt;stderr&amp;lt;/code&amp;gt; -- the server log -- in [[AMP]]. If it is not there, the configuration file was not found, or the line in it was not understood.&lt;br /&gt;
# List the root with &amp;lt;code&amp;gt;[[FileSystemListing]]( &amp;quot;repo://&amp;quot; )&amp;lt;/code&amp;gt;. That exercises &amp;lt;code&amp;gt;/list&amp;lt;/code&amp;gt; and your JSON shape without needing any file to be readable.&lt;br /&gt;
# Read something with &amp;lt;code&amp;gt;[[ReadTextFile]]( &amp;quot;repo://Sales/Q3.csv&amp;quot; )&amp;lt;/code&amp;gt;. That exercises &amp;lt;code&amp;gt;/files&amp;lt;/code&amp;gt;, including the &amp;lt;code&amp;gt;HEAD&amp;lt;/code&amp;gt; existence check that runs first, and &amp;lt;code&amp;gt;[[FileExists]]( &amp;quot;repo://Sales/Q3.csv&amp;quot; )&amp;lt;/code&amp;gt; exercises the &amp;lt;code&amp;gt;HEAD&amp;lt;/code&amp;gt; endpoint on its own.&lt;br /&gt;
# If the service accepts writes, work in a scratch folder: &amp;lt;code&amp;gt;[[WriteTextFile]]( &amp;quot;repo://Sales/scratch/a.txt&amp;quot;, &amp;quot;hello&amp;quot; )&amp;lt;/code&amp;gt;, then [[FileSystemNewFolder]], [[FileSystemCopy]], [[FileSystemMove]] and [[FileSystemDelete]] on it, checking each result in the service&#039;s own storage rather than through Analytica. Try a write to a location that should be read-only, and expect &#039;&#039;the repo:// file store is read-only and refused the write (HTTP 405)&#039;&#039;.&lt;br /&gt;
# Then check the failure paths deliberately: a name that does not exist (expect a plain not-found), a bad token (expect a credentials error naming the scheme), and the service stopped (expect &#039;&#039;could not reach the repo:// file store&#039;&#039;).&lt;br /&gt;
&lt;br /&gt;
== See Also ==&lt;br /&gt;
* &#039;&#039;&#039;[[Custom file system providers]]&#039;&#039;&#039; -- the parent page: what providers are, and how to configure them&lt;br /&gt;
* [[Analytica Command Line]] -- the &amp;lt;code&amp;gt;/stores:«path»&amp;lt;/code&amp;gt; option&lt;br /&gt;
* [[FileSystemListing]]&lt;br /&gt;
* [[ReadTextFile]]&lt;br /&gt;
* [[ReadBinaryFile]]&lt;br /&gt;
* [[WriteTextFile]]&lt;br /&gt;
* [[FileSystemNewFolder]], [[FileSystemCopy]], [[FileSystemMove]], [[FileSystemDelete]]&lt;br /&gt;
* [[SpreadsheetOpen]], [[SpreadsheetSave]]&lt;br /&gt;
* [[:Category:File system functions]]&lt;/div&gt;</summary>
		<author><name>Lchrisman</name></author>
	</entry>
	<entry>
		<id>https://docs.analytica.com/index.php?title=Custom_file_system_providers&amp;diff=64614</id>
		<title>Custom file system providers</title>
		<link rel="alternate" type="text/html" href="https://docs.analytica.com/index.php?title=Custom_file_system_providers&amp;diff=64614"/>
		<updated>2026-09-24T19:16:08Z</updated>

		<summary type="html">&lt;p&gt;Lchrisman: Write support, FileSystemNewFolder/Copy/Move/Delete, SpreadsheetOpen/Save on URI paths; FileStoreTokenEnv; ANALYTICA_FILE_PROVIDERS_CONFIG&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[category:File system functions]]&lt;br /&gt;
&lt;br /&gt;
{{ReleaseBar}}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;New in [[Analytica 7.2]]&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Requires {{Analytica Developer}} edition or better.&#039;&#039; It is always available in [[ADE|the Analytica Decision Engine]], in [[Analytica Cloud Platform|ACP]], and in [[AMP]] (the [[Analytica MCP Platform]]). In a lesser edition, a &amp;lt;code&amp;gt;«scheme»://&amp;lt;/code&amp;gt; path reports that the feature is not available in this edition.&lt;br /&gt;
&lt;br /&gt;
__TOC__&lt;br /&gt;
&lt;br /&gt;
== What a custom file system provider is ==&lt;br /&gt;
&lt;br /&gt;
A &#039;&#039;&#039;file provider&#039;&#039;&#039; makes a URI-style path&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;code&amp;gt;«scheme»://«root»/«path»&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
for example &amp;lt;code&amp;gt;repo://Sales/Q3.csv&amp;lt;/code&amp;gt;, work anywhere the Analytica engine accepts a file name. That includes opening and saving a model and the modules that model links to or includes; reading and writing files with [[ReadTextFile]], [[ReadBinaryFile]], [[WriteTextFile]] and [[WriteBinaryFile]]; listing and managing files and folders with [[FileSystemListing]], [[FileExists]], [[FileSystemNewFolder]], [[FileSystemCopy]], [[FileSystemMove]] and [[FileSystemDelete]]; spreadsheets opened with [[SpreadsheetOpen]] and saved with [[SpreadsheetSave]]; path utilities such as [[FileFullPath]] and [[FilePathPart]]; and the internal file-exists tests that model loading uses.&lt;br /&gt;
&lt;br /&gt;
The scheme prefix -- &amp;lt;code&amp;gt;repo&amp;lt;/code&amp;gt; in that example -- is &#039;&#039;&#039;configuration, not code&#039;&#039;&#039;. Providers are registered when the engine starts up, from a plain text file named &amp;lt;code&amp;gt;FileProviders.config&amp;lt;/code&amp;gt;, so one deployment can define several prefixes, each backed by a different storage service. Behind each prefix is an HTTP service that speaks a deliberately tiny protocol. You can write that service yourself: see &#039;&#039;&#039;[[Custom file system providers/API spec]]&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Things this is used for:&lt;br /&gt;
* A &#039;&#039;&#039;cloud object store&#039;&#039;&#039;, so that models and data live in a bucket rather than on a file server or on each analyst&#039;s own disk.&lt;br /&gt;
* A &#039;&#039;&#039;document management system&#039;&#039;&#039;, so that reading a file goes through the same access control and audit trail as every other document in the organization.&lt;br /&gt;
* A &#039;&#039;&#039;version-controlled model repository&#039;&#039;&#039;, so that a run can name the exact models and data it used and those names keep meaning the same thing.&lt;br /&gt;
* A &#039;&#039;&#039;corporate content API&#039;&#039;&#039; that already knows where the authoritative copy of a data set lives.&lt;br /&gt;
&lt;br /&gt;
The point of doing it this way is that &#039;&#039;&#039;nothing else in the model changes&#039;&#039;&#039;. The model author writes&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;code&amp;gt;[[ReadTextFile]]( &amp;quot;repo://Sales/Q3.csv&amp;quot; )&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
and every other part of the model, and every function that takes a file name, behaves exactly as it does with a local path.&lt;br /&gt;
&lt;br /&gt;
== URI paths ==&lt;br /&gt;
&lt;br /&gt;
* The form is &amp;lt;code&amp;gt;«scheme»://«rest»&amp;lt;/code&amp;gt;. A scheme starts with a letter, continues with letters, digits, &amp;lt;code&amp;gt;+&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;.&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;-&amp;lt;/code&amp;gt;, and is &#039;&#039;&#039;at least two characters&#039;&#039;&#039; long. The two-character minimum is deliberate: it keeps &amp;lt;code&amp;gt;C://foo&amp;lt;/code&amp;gt; a Windows drive path rather than a URI.&lt;br /&gt;
* Scheme matching is &#039;&#039;&#039;not case-sensitive&#039;&#039;&#039;. &amp;lt;code&amp;gt;repo://&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;Repo://&amp;lt;/code&amp;gt; reach the same provider.&lt;br /&gt;
* A backslash is accepted inside a URI path and treated as &amp;lt;code&amp;gt;/&amp;lt;/code&amp;gt;. So a model at &amp;lt;code&amp;gt;repo://models/Main.ana&amp;lt;/code&amp;gt; that includes &amp;lt;code&amp;gt;sub\Child.ana&amp;lt;/code&amp;gt; resolves to &amp;lt;code&amp;gt;repo://models/sub/Child.ana&amp;lt;/code&amp;gt;.&lt;br /&gt;
* &amp;lt;code&amp;gt;.&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;..&amp;lt;/code&amp;gt; segments resolve textually when a relative path is joined onto a URI folder, and &amp;lt;code&amp;gt;..&amp;lt;/code&amp;gt; can never pop above &amp;lt;code&amp;gt;«scheme»://«root»&amp;lt;/code&amp;gt;. A &amp;lt;code&amp;gt;..&amp;lt;/code&amp;gt; segment that would still reach the provider is refused outright.&lt;br /&gt;
* A URI is treated as an &#039;&#039;&#039;absolute&#039;&#039;&#039; path, so relative-path resolution against a model that was opened from a URI works normally.&lt;br /&gt;
* The path utilities keep a syntactic URI intact even when no provider is registered for its scheme, so an unknown scheme fails with a clear message instead of being mangled into a Windows path.&lt;br /&gt;
&lt;br /&gt;
== FileProviders.config ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;FileProviders.config&amp;lt;/code&amp;gt; is a plain text file with one setting per line. A &amp;lt;code&amp;gt;#&amp;lt;/code&amp;gt; starts a comment, blank lines are ignored, and each setting has the form&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;code&amp;gt;«key» «scheme» = «value»&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Key names are not case-sensitive.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# scheme prefix -&amp;gt; store service.  Several FileStore lines define several schemes.&lt;br /&gt;
FileStore repo          = http://mystore.internal:8080&lt;br /&gt;
FileStoreToken repo     = «bearer-token»          # literal token&lt;br /&gt;
FileStoreTokenFile repo = C:\secrets\repo-token   # OR read the token from this file&lt;br /&gt;
FileStoreTokenEnv repo = REPO_STORE_TOKEN # OR from this environment variable&lt;br /&gt;
FileStoreMaxMB repo     = 256                     # optional fetch size cap (default 256)&lt;br /&gt;
FileStoreTimeoutS repo  = 60                      # optional read timeout (default 60)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Setting !! Meaning&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;FileStore «scheme» = «url»&amp;lt;/code&amp;gt; || Declares «scheme» and gives the base URL of the store service that serves it. Required, and it must be the &#039;&#039;&#039;first&#039;&#039;&#039; line for that scheme.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;FileStoreToken «scheme» = «token»&amp;lt;/code&amp;gt; || A bearer token, written literally, that is sent with every request to that store.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;FileStoreTokenFile «scheme» = «path»&amp;lt;/code&amp;gt; || Reads the bearer token from this file instead. Surrounding white space in the file is trimmed. It takes precedence over &amp;lt;code&amp;gt;FileStoreToken&amp;lt;/code&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;FileStoreTokenEnv «scheme» = «variable»&amp;lt;/code&amp;gt; || Reads the bearer token from this environment variable of the engine&#039;s process, and then &#039;&#039;&#039;removes the variable from the process&#039;s environment&#039;&#039;&#039;, so that model code -- &amp;lt;code&amp;gt;[[GetProcessInfo]](&amp;quot;env:...&amp;quot;)&amp;lt;/code&amp;gt;, or a program started with [[RunConsoleProcess]] -- cannot read the token back. Use it when a launcher, such as a container&#039;s entry point, hands the engine a per-session credential. It takes precedence over the other two. If the variable is not set, a warning is written on the typescript and the token from &amp;lt;code&amp;gt;FileStoreToken&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;FileStoreTokenFile&amp;lt;/code&amp;gt;, if there is one, is used.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;FileStoreMaxMB «scheme» = «n»&amp;lt;/code&amp;gt; || The largest file, in megabytes, that will be fetched from this store. Default 256. A fetch that exceeds it is aborted with an error that names this setting.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;FileStoreTimeoutS «scheme» = «n»&amp;lt;/code&amp;gt; || The read timeout, in seconds. Default 60. The connect timeout is fixed at 5 seconds.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;FileStore&amp;lt;/code&amp;gt; line must come first for a scheme. Any of the other keys naming a scheme that has not been declared yet is ignored, with a warning on the typescript (see [[#Diagnostics|Diagnostics]] for where that appears in each product).&lt;br /&gt;
&lt;br /&gt;
The URL may include a path prefix, as in &amp;lt;code&amp;gt;http://host:port/prefix&amp;lt;/code&amp;gt;. A trailing &amp;lt;code&amp;gt;/&amp;lt;/code&amp;gt; is dropped, and the endpoints the engine calls are appended to what remains.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;The URL must be &amp;lt;code&amp;gt;http://&amp;lt;/code&amp;gt;.&#039;&#039;&#039; An &amp;lt;code&amp;gt;https://&amp;lt;/code&amp;gt; URL is refused when the provider is registered, with a warning on the typescript, because TLS is not compiled into the engine&#039;s HTTP client. The deployments this supports are a store service on &amp;lt;code&amp;gt;localhost&amp;lt;/code&amp;gt;, or one inside the same private network or cluster. See [[#Security|Security]] below.&lt;br /&gt;
&lt;br /&gt;
The configuration is read &#039;&#039;&#039;once per process&#039;&#039;&#039;, during engine initialization. Editing &amp;lt;code&amp;gt;FileProviders.config&amp;lt;/code&amp;gt; has no effect until Analytica is restarted.&lt;br /&gt;
&lt;br /&gt;
== Where Analytica looks for FileProviders.config ==&lt;br /&gt;
&lt;br /&gt;
Four locations are consulted in this order, and the first one that names a file wins.&lt;br /&gt;
&lt;br /&gt;
# &#039;&#039;&#039;The host&#039;s own command line option&#039;&#039;&#039;, &amp;lt;code&amp;gt;/stores:«path»&amp;lt;/code&amp;gt; -- see [[Analytica Command Line]].&lt;br /&gt;
# &#039;&#039;&#039;The environment variable &amp;lt;code&amp;gt;ANALYTICA_FILE_PROVIDERS_CONFIG&amp;lt;/code&amp;gt;&#039;&#039;&#039;, naming the file. This gives one process its own configuration without touching the registry -- for example a per-session file that a container&#039;s entry point writes before it starts the engine. It works for every product, ADE and ADEW included. If the variable names a file that does not exist, no providers are registered and a warning is written on the typescript.&lt;br /&gt;
# &#039;&#039;&#039;The &amp;lt;code&amp;gt;FileProvidersConfig&amp;lt;/code&amp;gt; registry value.&#039;&#039;&#039; Two keys are consulted, and each is looked for under &amp;lt;code&amp;gt;HKEY_CURRENT_USER&amp;lt;/code&amp;gt; first and then under &amp;lt;code&amp;gt;HKEY_LOCAL_MACHINE&amp;lt;/code&amp;gt;:&lt;br /&gt;
#* &amp;lt;code&amp;gt;HKCU\Software\Lumina Decision Systems\«product»\«version»\FileProvidersConfig&amp;lt;/code&amp;gt; -- per product and per version, for example &amp;lt;code&amp;gt;...\Analytica\7.2&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;...\ADEW\7.2&amp;lt;/code&amp;gt;. Use this when you want one product to see a different set of stores from the others.&lt;br /&gt;
#* &amp;lt;code&amp;gt;HKCU\SOFTWARE\Lumina Decision Systems\FileProvidersConfig&amp;lt;/code&amp;gt; -- product-independent. One value here serves Analytica, [[ADE]] and ADEW, [[ACP]] and [[AMP]] alike. This is the usual choice, and, apart from the environment variable, it is the only way to set the location for ADE and ADEW, which have no command line of their own.&lt;br /&gt;
#: A value that names a file which does not exist produces a warning on the typescript, rather than quietly registering nothing.&lt;br /&gt;
# &#039;&#039;&#039;&amp;lt;code&amp;gt;FileProviders.config&amp;lt;/code&amp;gt; in the same folder as the engine binary&#039;&#039;&#039; -- the deployed default.&lt;br /&gt;
&lt;br /&gt;
When none of these finds a file, no providers are registered and nothing is reported.&lt;br /&gt;
&lt;br /&gt;
== Using a provider from a model ==&lt;br /&gt;
&lt;br /&gt;
Once &amp;lt;code&amp;gt;repo&amp;lt;/code&amp;gt; has been declared, use it as an ordinary file name.&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;code&amp;gt;[[ReadTextFile]]( &amp;quot;repo://data/Sales.csv&amp;quot; )&amp;lt;/code&amp;gt;&lt;br /&gt;
:&amp;lt;code&amp;gt;[[ReadBinaryFile]]( &amp;quot;repo://data/logo.png&amp;quot; )&amp;lt;/code&amp;gt;&lt;br /&gt;
:&amp;lt;code&amp;gt;[[FileExists]]( &amp;quot;repo://models/My Model.ana&amp;quot; )&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[FileSystemListing]] returns the names, sizes and modification dates that the store reports:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;code&amp;gt;[[FileSystemListing]]( &amp;quot;repo://models/&amp;quot;, recurse: true )&amp;lt;/code&amp;gt;&lt;br /&gt;
:&amp;lt;code&amp;gt;[[FileSystemListing]]( &amp;quot;repo://data/*.csv&amp;quot;, [&#039;Name&#039;, &#039;Size&#039;, &#039;DateLastModified&#039;] )&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A wildcard in the last segment of the path filters the listing, as it does for a local folder.&lt;br /&gt;
&lt;br /&gt;
You can open a model whose path is a URI. Modules that the model links to or includes resolve relative to the model&#039;s own URI, so a model at &amp;lt;code&amp;gt;repo://models/Main.ana&amp;lt;/code&amp;gt; that includes &amp;lt;code&amp;gt;sub\Child.ana&amp;lt;/code&amp;gt; loads &amp;lt;code&amp;gt;repo://models/sub/Child.ana&amp;lt;/code&amp;gt; from the same store. [[FileFullPath]] joins a relative name onto a URI folder the same way:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;code&amp;gt;[[FileFullPath]]( &amp;quot;Q3.csv&amp;quot;, &amp;quot;repo://Sales&amp;quot; ) &amp;amp;rarr; &#039;repo://Sales/Q3.csv&#039;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
An [[Obfuscated and Browse-Only Models|obfuscated model]] works over a provider scheme exactly as it does from a local file.&lt;br /&gt;
&lt;br /&gt;
A long transfer stays cancellable. Ctrl+Break, or a cancellation from an [[ACP]] or [[AMP]] client, interrupts a fetch that is in progress instead of waiting for it to finish.&lt;br /&gt;
&lt;br /&gt;
=== Existence checks ===&lt;br /&gt;
&lt;br /&gt;
The [[FileExists]] function asks the store. An object stored at that exact key is a file. A folder is recognized from the store&#039;s listing of its parent folder, since a store normally exposes a folder only as a listing prefix and answers &amp;quot;not found&amp;quot; when asked about it directly. Name matching is case-sensitive, as it is in the store itself. When the store cannot be reached, or rejects the credentials, [[FileExists]] reports that error rather than guessing an answer -- the modeler asked a question, and a wrong answer would be worse than an error.&lt;br /&gt;
&lt;br /&gt;
The existence tests the engine makes &#039;&#039;&#039;on its own&#039;&#039;&#039;, while resolving a model and the modules it links to, use a plain HTTP &amp;lt;code&amp;gt;HEAD&amp;lt;/code&amp;gt; request and take the opposite tack, deliberately: if the store cannot be reached, or rejects the credentials, the test reports &#039;&#039;&#039;exists&#039;&#039;&#039; -- it cannot prove absence. Control then reaches the code that actually opens the file, and &#039;&#039;that&#039;&#039; reports the real transport or authentication problem, instead of a misleading &amp;quot;file not found&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
=== Writing and saving ===&lt;br /&gt;
&lt;br /&gt;
[[WriteTextFile]], [[WriteBinaryFile]] and saving a model -- &#039;&#039;&#039;Save&#039;&#039;&#039;, &#039;&#039;&#039;Save As&#039;&#039;&#039; and &#039;&#039;&#039;Save a copy&#039;&#039;&#039; -- work with a URI path. The file is written to a local temporary file and uploaded whole, in one request, when it is closed. Whether a location accepts writes is up to the store service: a store, or part of one, can be read-only, and a write to it then fails with &#039;&#039;the repo:// file store is read-only and refused the write (HTTP 405)&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Creating, copying, moving and deleting ===&lt;br /&gt;
&lt;br /&gt;
[[FileSystemNewFolder]], [[FileSystemCopy]], [[FileSystemMove]] and [[FileSystemDelete]] work on URI paths, and behave as they do on a local disk: a trailing &amp;lt;code&amp;gt;/&amp;lt;/code&amp;gt; on the destination means &amp;quot;into this folder&amp;quot;, «replace» and «deleteNonEmptyFolder» mean the same thing, deleting a file that is not there returns [[Null]] rather than an error, and the error messages are the same. The differences are:&lt;br /&gt;
* [[FileSystemNewFolder]] creates any missing parent folders.&lt;br /&gt;
* A store has no read-only attribute and no links, so the «deleteReadOnly» parameter of [[FileSystemDelete]] and the «copyLinks» parameter of [[FileSystemCopy]] are ignored.&lt;br /&gt;
* Within one store, a copy or move -- of a file or of a whole folder -- is carried out by the store service itself. Between two different stores, or between a store and the local disk, a &#039;&#039;&#039;single file&#039;&#039;&#039; can be copied or moved: the engine reads it from one and writes it to the other. Copying or moving a &#039;&#039;&#039;folder&#039;&#039;&#039; between stores is refused.&lt;br /&gt;
* If a move cannot delete its source -- because the source is in a read-only part of the store, for example -- the copy it has just made is removed again and the error is reported, so a failed move never leaves the file in two places.&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;code&amp;gt;[[FileSystemNewFolder]]( &amp;quot;Q3&amp;quot;, &amp;quot;repo://Sales&amp;quot; )&amp;lt;/code&amp;gt;&lt;br /&gt;
:&amp;lt;code&amp;gt;[[FileSystemCopy]]( &amp;quot;repo://Sales/Q3/draft.csv&amp;quot;, &amp;quot;repo://Sales/Q3/final.csv&amp;quot; )&amp;lt;/code&amp;gt;&lt;br /&gt;
:&amp;lt;code&amp;gt;[[FileSystemMove]]( &amp;quot;repo://Sales/Q3&amp;quot;, &amp;quot;repo://Archive/&amp;quot; )&amp;lt;/code&amp;gt;&lt;br /&gt;
:&amp;lt;code&amp;gt;[[FileSystemDelete]]( &amp;quot;repo://Archive/Q3&amp;quot;, deleteNonEmptyFolder: true )&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Spreadsheets ===&lt;br /&gt;
&lt;br /&gt;
[[SpreadsheetOpen]] accepts a URI path, and always uses the LibXL backend for one. The Excel backend opens files through Windows and cannot reach a store, so &amp;lt;code&amp;gt;backend: &#039;Excel&#039;&amp;lt;/code&amp;gt; is refused for a URI path. The workbook is read from the store in one fetch. [[SpreadsheetSave]] writes it back in one upload -- to the path it came from when no «filename» is given, or to any other path, local or URI.&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;code&amp;gt;Local wb := [[SpreadsheetOpen]]( &amp;quot;repo://Sales/Q3.xlsx&amp;quot; );&amp;lt;/code&amp;gt;&lt;br /&gt;
:&amp;lt;code&amp;gt;[[SpreadsheetSetCell]]( wb, &amp;quot;Sheet1&amp;quot;, &amp;quot;B&amp;quot;, 2, 1250 );&amp;lt;/code&amp;gt;&lt;br /&gt;
:&amp;lt;code&amp;gt;[[SpreadsheetSave]]( wb )&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A workbook sitting next to a model that was opened from a URI can be named relative to the model, as any other file can.&lt;br /&gt;
&lt;br /&gt;
== Restrictions in Analytica 7.2 ==&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Whole-file writes.&#039;&#039;&#039; A write uploads the complete file when it is closed. There is no appending or partial update on the store side.&lt;br /&gt;
* &#039;&#039;&#039;Folders stay within one store.&#039;&#039;&#039; Only a single file can be copied or moved from one store to another, or between a store and the local disk.&lt;br /&gt;
* &#039;&#039;&#039;Atomicity is the service&#039;s.&#039;&#039;&#039; A folder copy or move inside one store is done by the store service, so whether it is atomic depends on the service. A git-backed store can make it one commit; a service over an object bucket typically copies object by object.&lt;br /&gt;
* &#039;&#039;&#039;Whole-file fetch.&#039;&#039;&#039; A read pulls the entire file, subject to &amp;lt;code&amp;gt;FileStoreMaxMB&amp;lt;/code&amp;gt;. It is not a seekable remote stream, so a provider path is not a good way to peek at a few bytes of a very large file.&lt;br /&gt;
* &#039;&#039;&#039;One short fetch cache.&#039;&#039;&#039; The URI most recently fetched is cached for 10 seconds, because opening a model reads the same file two or three times. Past that, every read goes to the store.&lt;br /&gt;
* &#039;&#039;&#039;No file browser.&#039;&#039;&#039; A URI path never poses a &amp;quot;locate the file&amp;quot; dialog. A file that is not there is reported as not found.&lt;br /&gt;
&lt;br /&gt;
== Diagnostics ==&lt;br /&gt;
&lt;br /&gt;
Everything the provider layer reports itself is written to the &#039;&#039;&#039;typescript&#039;&#039;&#039; -- the engine&#039;s typescript output. Where to look for it depends on which product you are running: in Analytica it appears in the [[Typescript Window|typescript window]]; [[ADE]] and ADEW capture the typescript as their log, and so does [[ACP]]; and in [[AMP]] the typescript is echoed to &amp;lt;code&amp;gt;stderr&amp;lt;/code&amp;gt;, which is the server log, so &amp;lt;code&amp;gt;Amp.exe&amp;lt;/code&amp;gt; users find these lines on stderr. AMP is the only product where stderr is the right place to look.&lt;br /&gt;
&lt;br /&gt;
On start-up, each registered provider prints one line:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
[FileProviders] repo:// -&amp;gt; http://mystore.internal:8080&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Unrecognized lines, unusable scheme names, an &amp;lt;code&amp;gt;https://&amp;lt;/code&amp;gt; URL, a token file that could not be read, a &amp;lt;code&amp;gt;FileProvidersConfig&amp;lt;/code&amp;gt; registry value or an &amp;lt;code&amp;gt;ANALYTICA_FILE_PROVIDERS_CONFIG&amp;lt;/code&amp;gt; environment variable naming a file that is not there, and a &amp;lt;code&amp;gt;FileStoreTokenEnv&amp;lt;/code&amp;gt; variable that is not set, each produce their own warning there. An &#039;&#039;&#039;absent&#039;&#039;&#039; configuration file is silent: no providers are registered and nothing is written.&lt;br /&gt;
&lt;br /&gt;
Errors that reach the model name the file -- for example &#039;&#039;The file &#039;...&#039; could not be opened&#039;&#039; -- and give one of these explanations:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Situation !! What it says&lt;br /&gt;
|-&lt;br /&gt;
| No provider is registered for that scheme || &amp;lt;code&amp;gt;no file provider is registered for the scheme &#039;repo&#039;&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| The edition does not include the feature || &amp;lt;code&amp;gt;&#039;repo://&#039; paths require the Developer edition or better&amp;lt;/code&amp;gt;, also naming the edition you do have&lt;br /&gt;
|-&lt;br /&gt;
| The store rejected the credentials (HTTP 401 or 403) || &amp;lt;code&amp;gt;the repo:// file store rejected the credentials (HTTP 401)&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| Any other unexpected HTTP status || &amp;lt;code&amp;gt;HTTP «n» from the repo:// file store&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| The service could not be reached || &amp;lt;code&amp;gt;could not reach the repo:// file store (...)&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| The file is larger than the cap || &amp;lt;code&amp;gt;the file exceeds the «n» MB fetch limit (FileStoreMaxMB in FileProviders.config)&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| A &amp;lt;code&amp;gt;..&amp;lt;/code&amp;gt; segment in the path || &amp;lt;code&amp;gt;&#039;..&#039; is not allowed in a repo:// path&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| The store refused a write, because that location is read-only (HTTP 405) || &amp;lt;code&amp;gt;the repo:// file store is read-only and refused the write (HTTP 405)&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| Creating a file that is already there, where it must not be overwritten || &amp;lt;code&amp;gt;&#039;Sales/Q3.csv&#039; already exists in the repo:// file store&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| An upload larger than the store accepts (HTTP 413) || &amp;lt;code&amp;gt;the repo:// file store refused the upload as too large (HTTP 413)&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| The store service does not implement the write or file-management endpoint that was needed || &amp;lt;code&amp;gt;the repo:// file store does not support this operation (the store service may need to be updated)&amp;lt;/code&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
HTTP 404 is not an error. It means the file is not there, and the caller then reports a missing file in its usual way.&lt;br /&gt;
&lt;br /&gt;
Copying or moving a folder between stores, and asking for the Excel backend on a URI path, are refused by [[FileSystemCopy]], [[FileSystemMove]] and [[SpreadsheetOpen]] with messages of their own that say why.&lt;br /&gt;
&lt;br /&gt;
== Security ==&lt;br /&gt;
&lt;br /&gt;
The connection between Analytica and a store service is &#039;&#039;&#039;plain HTTP&#039;&#039;&#039;, with no TLS. That is a deliberate limitation of this release, and you have to design around it:&lt;br /&gt;
&lt;br /&gt;
* The bearer token, and the file contents, travel &#039;&#039;&#039;in the clear&#039;&#039;&#039; over that hop. Run the store service on &amp;lt;code&amp;gt;localhost&amp;lt;/code&amp;gt;, or on a private network or cluster network that you already trust with that traffic. Do not put a store service anywhere you would not be willing to send the token in plain text.&lt;br /&gt;
* &amp;lt;code&amp;gt;FileProviders.config&amp;lt;/code&amp;gt; can hold a token, so protect it like any other file holding a secret. Restrict who can read it, and keep it out of version control and out of model archives.&lt;br /&gt;
* Prefer &amp;lt;code&amp;gt;FileStoreTokenFile&amp;lt;/code&amp;gt; to &amp;lt;code&amp;gt;FileStoreToken&amp;lt;/code&amp;gt;. It keeps the token out of the file that gets copied from machine to machine, and it lets the token live somewhere with tighter permissions -- or, in a container, be a mounted secret.&lt;br /&gt;
* Where a launcher starts the engine -- a container entry point, a session manager -- &amp;lt;code&amp;gt;FileStoreTokenEnv&amp;lt;/code&amp;gt; is better still: the token never touches a disk, and the engine removes the variable from its own environment as soon as it has read it, so the model it runs cannot read the token back.&lt;br /&gt;
* A store service should authenticate every request, and should reject path traversal itself. The engine refuses a &amp;lt;code&amp;gt;..&amp;lt;/code&amp;gt; segment before any request is sent, but a service must not rely on that.&lt;br /&gt;
&lt;br /&gt;
== See Also ==&lt;br /&gt;
* &#039;&#039;&#039;[[Custom file system providers/API spec]]&#039;&#039;&#039; -- the HTTP API that a store service must implement&lt;br /&gt;
* [[Analytica Command Line]] -- the &amp;lt;code&amp;gt;/stores:«path»&amp;lt;/code&amp;gt; option&lt;br /&gt;
* [[ReadTextFile]]&lt;br /&gt;
* [[ReadBinaryFile]]&lt;br /&gt;
* [[FileExists]]&lt;br /&gt;
* [[FileSystemListing]]&lt;br /&gt;
* [[FileFullPath]]&lt;br /&gt;
* [[FilePathPart]]&lt;br /&gt;
* [[WriteTextFile]]&lt;br /&gt;
* [[FileSystemNewFolder]], [[FileSystemCopy]], [[FileSystemMove]], [[FileSystemDelete]]&lt;br /&gt;
* [[SpreadsheetOpen]], [[SpreadsheetSave]]&lt;br /&gt;
* [[:Category:File system functions]]&lt;/div&gt;</summary>
		<author><name>Lchrisman</name></author>
	</entry>
	<entry>
		<id>https://docs.analytica.com/index.php?title=RunConsoleProcess&amp;diff=64609</id>
		<title>RunConsoleProcess</title>
		<link rel="alternate" type="text/html" href="https://docs.analytica.com/index.php?title=RunConsoleProcess&amp;diff=64609"/>
		<updated>2026-09-22T23:31:47Z</updated>

		<summary type="html">&lt;p&gt;Lchrisman: RunConsoleProcess in linux note&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt; [[Category:Integration Functions]]&lt;br /&gt;
{{ReleaseBar}}&lt;br /&gt;
&lt;br /&gt;
== RunConsoleProcess(program, &#039;&#039;cmdLine, stdIn, block, curDir, priority, showErr{{Release|7.1||, path}}&#039;&#039;) ==&lt;br /&gt;
&lt;br /&gt;
[[RunConsoleProcess]] lets an Analytica model run a &#039;&#039;console process&#039;&#039; -- that is, any Windows program. The program may be very simple with no graphical user interface that takes input from «stdIn»  and writing output to &#039;&#039;stdOut&#039;&#039; -- or it may interact with the user directly. [[RunConsoleProcess]] gives the path and name of the application in the «program» parameter. It can feed input to the program via command line parameters in «cmdLine», or as input data given as text to the «stdIn» parameter, which is piped to the &#039;&#039;StdIn&#039;&#039; input channel of the console process. Normally, when the process completes, [[RunConsoleProcess]] will return a result (as text) any information the program writes to &#039;&#039;stdOut&#039;&#039;. Analytica can also send data to a console process via a data files created with [[WriteTextFile]] or receive a data file created by the process using [[ReadTextFile]].&lt;br /&gt;
&lt;br /&gt;
==Declaration ==&lt;br /&gt;
&amp;lt;pre style=&amp;quot;background:white; border:white; margin-left: 1em;&amp;quot;&amp;gt;&lt;br /&gt;
RunConsoleProcess(&lt;br /&gt;
    program: Atomic Text,&lt;br /&gt;
    cmdLine: Optional Atomic Text,&lt;br /&gt;
    stdIn: Optional Atomic Text,&lt;br /&gt;
    block: Optional Atomic Boolean, /* default TRUE */&lt;br /&gt;
    curDir: Optional Atomic Text, /* default process directory */&lt;br /&gt;
    priority: Optional Atomic Number, /* default 0 = normal, same as Ana */&lt;br /&gt;
    showErr: Optional Numeric) /* defaults to 1 = err msg */&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
{{Release|7.1||&lt;br /&gt;
In [[Analytica 7.1]] and later, «program» and «cmdLine» are individually optional (you must supply at least one), and there is a new optional named «path» parameter:&lt;br /&gt;
&amp;lt;pre style=&amp;quot;background:white; border:white; margin-left: 1em;&amp;quot;&amp;gt;&lt;br /&gt;
RunConsoleProcess(&lt;br /&gt;
program: Optional Atomic Text,&lt;br /&gt;
cmdLine: Optional Atomic Text,&lt;br /&gt;
stdIn: Optional Atomic Text,&lt;br /&gt;
block: Optional Atomic Boolean, /* default TRUE */&lt;br /&gt;
curDir: Optional Atomic Text, /* default process directory */&lt;br /&gt;
priority: Optional Atomic Number, /* default 0 = normal, same as Ana */&lt;br /&gt;
showErr: Optional Numeric, /* defaults to 1 = err msg */&lt;br /&gt;
path: Optional Atomic Text) /* defaults to %PATH% */&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
[[RunConsoleProcess]] offers considerable flexibility through a number of other optional parameters:&lt;br /&gt;
&lt;br /&gt;
«block»: By default (or if you set «block» to &amp;lt;code&amp;gt;True&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;1&amp;lt;/code&amp;gt;),  [[RunConsoleProcess]] &#039;&#039;blocks&#039;&#039; -- that is, Analytica waits until the console process terminates and returns a result before it resumes execution. While blocked, Analytica still notices Windows events: If you press &#039;&#039;Ctrl+break&#039;&#039; (or &#039;&#039;Ctrl+.&#039;&#039;) before the process terminates, it kills the process, and ends further computation by Analytica -- just like what it does when Analytica is computing without another process.&lt;br /&gt;
&lt;br /&gt;
If you pass &amp;lt;code&amp;gt;False&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;0&amp;lt;/code&amp;gt;) to «block», [[RunConsoleProcess]] will not wait for the process to terminate: It immediately returns an empty text to Analytica. Analytica and the spawned process both continue running concurrently until they each terminate. If you press &#039;&#039;Ctrl+break&#039;&#039; (or &#039;&#039;Ctrl+.&#039;&#039;), it interrupts any computations by Analytica, but has no effect on the spawned process. An unblocked process is independent, and may continue running even after you exit Analytica. Unblocked processes are useful when you want to send data to another application for display, such as a special graphing package or GIS, or for saving selected results. It is hard to get any results or status back to Analytica from an unblocked process. It is usually best to use a blocked process if you need results back.&lt;br /&gt;
&lt;br /&gt;
The «program» and «cmdLine» parameters are separated, rather than lumped together as one parameter, to protect against a common type of virus attack. &lt;br /&gt;
&lt;br /&gt;
«curDir»:  Any relative directory path specified in the program parameter is interpreted relative to Analytica&#039;s [[CurrentDataFolder]]. «curDir» specifies the directory the spawned process should use as its default directory to read and write files. If «curDir» is not specified, it uses its own directory as its default . &lt;br /&gt;
&lt;br /&gt;
«priority» defines the priority that Windows should give the spawned process relative to the Analytica process. The default (&amp;lt;code&amp;gt;0&amp;lt;/code&amp;gt;) runs the new process at the same priority as the Analytica program. A value of &amp;lt;code&amp;gt;–1&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;-2&amp;lt;/code&amp;gt; lowers the priority, allowing other programs more of the CPU. A value of &amp;lt;code&amp;gt;+1&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;+2&amp;lt;/code&amp;gt; raises the priority, dedicating more of the CPU to the process.&lt;br /&gt;
&lt;br /&gt;
«showErr»: By default, in blocking mode, if the process writes anything to &#039;&#039;stdErr&#039;&#039;, Analytica will display it as an error message when the process terminates. If &amp;lt;code&amp;gt;showErr = 2&amp;lt;/code&amp;gt; it shows any text in &#039;&#039;stdErr&#039;&#039; as a warning message. If &amp;lt;code&amp;gt;showErr = 0&amp;lt;/code&amp;gt;, and always in non-blocking mode, it ignores anything in &#039;&#039;stdErr&#039;&#039;. {{Release|7.2||When running a Linux command in a Linux operating system, you may need &amp;lt;code&amp;gt;showError:0&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;showError:2&amp;lt;/code&amp;gt; more often since Linux tools write warnings to stderr more readily.}}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Errors&#039;&#039;&#039;: Analytica will give an error message if [[RunConsoleProcess]] cannot find or launch the specified program. &lt;br /&gt;
&lt;br /&gt;
[[RunConsoleProcess]] fully supports [[Intelligent Arrays]]: If any parameter is passed an array, it will run a separate process for each element of the array. It runs multiple blocking processes in sequence, one after another. It runs multiple non-blocking processes concurrently.&lt;br /&gt;
&lt;br /&gt;
{{Release|7.1||&lt;br /&gt;
== Combined command line and CMD built-ins ==&lt;br /&gt;
&#039;&#039;New to [[Analytica 7.1]]&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
If you specify just one of «program» or «cmdLine» (and not both), it is treated as the entire command line, and Analytica locates the executable for you. So these two calls are equivalent:&lt;br /&gt;
:&amp;lt;code&amp;gt;RunConsoleProcess(&amp;quot;ffmpeg.exe&amp;quot;, &amp;quot;ffmpeg -i input.mp4 output.mp3&amp;quot;)&amp;lt;/code&amp;gt;&lt;br /&gt;
:&amp;lt;code&amp;gt;RunConsoleProcess(&amp;quot;ffmpeg -i input.mp4 output.mp3&amp;quot;)&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You must specify at least one of «program» or «cmdLine»; calling [[RunConsoleProcess]] with neither raises an error. The original two-parameter form is still supported and recommended when you want to be explicit about which is the executable.&lt;br /&gt;
&lt;br /&gt;
In this combined-command-line form, if the first word names a built-in &amp;lt;code&amp;gt;CMD.EXE&amp;lt;/code&amp;gt; command (such as &amp;lt;code&amp;gt;DIR&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;ECHO&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;COPY&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;TYPE&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;SET&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;MOVE&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;DEL&amp;lt;/code&amp;gt;, …), Analytica automatically runs the command through CMD.EXE — that is, as &amp;lt;code&amp;gt;cmd /c «cmdLine»&amp;lt;/code&amp;gt;. For example:&lt;br /&gt;
:&amp;lt;code&amp;gt;RunConsoleProcess(&amp;quot;DIR *.txt&amp;quot;)&amp;lt;/code&amp;gt;&lt;br /&gt;
runs as &amp;lt;code&amp;gt;cmd /c DIR *.txt&amp;lt;/code&amp;gt; and returns the directory listing.&lt;br /&gt;
&lt;br /&gt;
Shell operators such as &amp;lt;code&amp;gt;&amp;amp;&amp;amp;&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;&amp;amp;#124;&amp;amp;#124;&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;&amp;amp;#124;&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;&amp;gt;&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;&amp;lt;&amp;lt;/code&amp;gt; are interpreted by CMD.EXE, not by [[RunConsoleProcess]] itself. If your command line uses them — for example, to run several commands in a single shell session so their side effects persist — put it after &amp;lt;code&amp;gt;cmd /c&amp;lt;/code&amp;gt; explicitly:&lt;br /&gt;
:&amp;lt;code&amp;gt;RunConsoleProcess(&amp;quot;cmd /c conda activate Env1 &amp;amp;&amp;amp; pip install desiredlib&amp;quot;)&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
«path»: An optional named parameter giving the &amp;lt;code&amp;gt;;&amp;lt;/code&amp;gt;-separated search path used to locate the executable when you use the combined-command-line form. It defaults to the &amp;lt;code&amp;gt;%PATH%&amp;lt;/code&amp;gt; environment variable. Use it to restrict (or extend) the search when running a tool that isn&#039;t in the system PATH:&lt;br /&gt;
:&amp;lt;code&amp;gt;RunConsoleProcess(&amp;quot;ffmpeg -i input.mp4 output.mp3&amp;quot;, path: &amp;quot;C:\Tools\ffmpeg\bin&amp;quot;)&amp;lt;/code&amp;gt;&lt;br /&gt;
:&amp;lt;code&amp;gt;RunConsoleProcess(&amp;quot;MyTool&amp;quot;, path: &amp;quot;C:\MyTools;%PATH%&amp;quot;)&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If the executable is not found in «path», [[RunConsoleProcess]] raises a &amp;quot;could not launch&amp;quot; error rather than silently falling back to the system search path. «path» has no effect when both «program» and «cmdLine» are given, since in that mode «program» already names the executable explicitly.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
{{Release|6.5||&lt;br /&gt;
== Using Environment Variables ==&lt;br /&gt;
&#039;&#039;New to [[Analytica 6.5]]&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
You can substitute the value of environment variables into the «program», «cmdLine» or «curDir» parameters using &amp;lt;code&amp;gt;%name%&amp;lt;/code&amp;gt;. For example:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;code&amp;gt;[[RunConsoleProcess]]( &amp;quot;%windir%\system32\cscript.exe&amp;quot;, &amp;quot;cscript /NoLogo HelloWorld.vbs&amp;quot;, curDir:&amp;quot;%TEMP%&amp;quot;  )&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If you need to actually pass a % character in one of those parameters, then double it, e.g., &amp;lt;code&amp;gt;&amp;quot;%%&amp;quot;&amp;lt;/code&amp;gt;.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Examples ==&lt;br /&gt;
=== VB Script ===&lt;br /&gt;
Suppose the file &amp;lt;code&amp;gt;HelloWorld.vbs&amp;lt;/code&amp;gt; is in your model directory and contains:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;code&amp;gt;WScript.Echo &amp;quot;Hello World&amp;quot;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Your call to [[RunConsoleProcess]] might look like:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;code&amp;gt;RunConsoleProcess(&amp;quot;{{Release||6.4|C:\Windows}}{{Release|6.5||%windir%}}\System32\CScript.exe&amp;quot;,&amp;lt;/code&amp;gt;&lt;br /&gt;
::&amp;lt;code&amp;gt;&amp;quot;CScript/Nologo HelloWorld.vbs&amp;quot;)&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The first parameter is the program (usually an &#039;&#039;EXE&#039;&#039; file) to be launched. You don&#039;t need to worry about quoting any spaces in the path name. The second parameter is the command line as it might appear on a command prompt. This expression will evaluate to the string &amp;quot;Hello World&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
If you need to send data to the &#039;&#039;StdIn&#039;&#039; of the process, include an optional parameter «stdIn»:&lt;br /&gt;
:&amp;lt;code&amp;gt;RunConsoleProcess(&amp;quot;{{Release||6.4|C:\Windows}}{{Release|6.5||%windir%}}\System32\CScript.exe&amp;quot;,&amp;lt;/code&amp;gt;&lt;br /&gt;
::&amp;lt;code&amp;gt;&amp;quot;CScript/Nologo HelloWorld.vbs&amp;quot;, &amp;lt;/code&amp;gt;      &lt;br /&gt;
::&amp;lt;code&amp;gt;StdIn: MyDataToSend)&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
where &amp;lt;code&amp;gt;MyDataToSend&amp;lt;/code&amp;gt; evaluates to a text.&lt;br /&gt;
&lt;br /&gt;
=== Batch File ===&lt;br /&gt;
In this example, a batch file named &amp;lt;code&amp;gt;DoIt.bat&amp;lt;/code&amp;gt; is in the directory &amp;lt;code&amp;gt;C:\Try&amp;lt;/code&amp;gt;.  Also in that directory is a file named &amp;lt;code&amp;gt;data.log&amp;lt;/code&amp;gt;.  The batch file, &amp;lt;code&amp;gt;DoIt.bat&amp;lt;/code&amp;gt; contains the following:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;code&amp;gt;# DoIt.bat -- dump the log&amp;lt;/code&amp;gt;&lt;br /&gt;
:&amp;lt;code&amp;gt;Type data.log&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This batch file assumes it is run from the directory &amp;lt;code&amp;gt;C:\Try&amp;lt;/code&amp;gt;.  {{Release||6.4|First, set up a Constant node:&lt;br /&gt;
:&amp;lt;code&amp;gt;Constant CMD := [[GetProcessInfo]](&amp;quot;env:comspec&amp;quot;)&amp;lt;/code&amp;gt;&lt;br /&gt;
which most commonly ends up with }}{{Release|6.5||This uses the &amp;lt;code&amp;gt;%comspec%&amp;lt;/code&amp;gt; environment variable to find the CMD command, which most commonly has }}the value &amp;lt;code&amp;gt;&amp;quot;C:\Windows\System32\Cmd.exe&amp;quot;&amp;lt;/code&amp;gt;. Then the call is:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;code&amp;gt;RunConsoleProcess({{Release||6.4|CMD}}{{Release|6.5||&amp;quot;%comspec%&amp;quot;, &amp;lt;/code&amp;gt;&lt;br /&gt;
::&amp;lt;code&amp;gt;&amp;quot;Cmd /C DoIt.bat&amp;quot;,&amp;lt;/code&amp;gt;&lt;br /&gt;
::&amp;lt;code&amp;gt;CurDir: &amp;quot;C:\Try&amp;quot;)&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
or you can run it directly:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;code&amp;gt;RunConsoleProcess(&amp;quot;DoIt.bat&amp;quot;, &amp;quot;DoIt.bat&amp;quot;, CurDir: &amp;quot;C:\Try&amp;quot;)&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Reading Data from a URL ===&lt;br /&gt;
&lt;br /&gt;
If you want to read data from a URL, you should use the [[ReadFromURL]] function. Nevertheless, this example from the past may be illustrative.&lt;br /&gt;
&lt;br /&gt;
To read the contents of a web page given its URL, you can use:&lt;br /&gt;
:&amp;lt;code&amp;gt;RunConsoleProcess(&amp;quot;ReadURL.exe&amp;quot;, &amp;quot;ReadURL &amp;quot; &amp;amp; url)&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
where &#039;&#039;url&#039;&#039; is a text string as would appear in the address bar of your browser.  You can download the [[media:ReadURL.exe | ReadURL.exe]] program by clicking on the link and saving.  The first parameter to [[RunConsoleProcess]] may need to be set to the full path where you placed [[media:ReadURL.exe | ReadURL.exe]] unless you put it in your [[CurrentDataFolder]].&lt;br /&gt;
&lt;br /&gt;
A step-by-step example using [[media:ReadURL.exe | ReadURL.exe]] is given here: [[Retrieving Content From the Web]]. This example includes the source code for [[media:ReadURL.exe | ReadURL.exe]], and develops an example that obtains historical stock price data from the Yahoo finance web site.&lt;br /&gt;
&lt;br /&gt;
==History==&lt;br /&gt;
This function was introduced in [[What&#039;s new in Analytica 4.0?|Analytica 4.0]].&lt;br /&gt;
&lt;br /&gt;
Ability to use environment variables in «program», «cmdLine» and «curDir» was added in [[Analytica 6.5]].&lt;br /&gt;
&lt;br /&gt;
{{Release|7.1||In [[Analytica 7.1]], «program» and «cmdLine» became individually optional. When only one is given, it is treated as a combined command line; if the first word is a CMD.EXE built-in such as &amp;lt;code&amp;gt;DIR&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;ECHO&amp;lt;/code&amp;gt;, it is run via &amp;lt;code&amp;gt;cmd /c&amp;lt;/code&amp;gt; automatically. A new optional named «path» parameter (default &amp;lt;code&amp;gt;%PATH%&amp;lt;/code&amp;gt;) can scope where the executable is searched for.}}&lt;br /&gt;
&lt;br /&gt;
== See Also ==&lt;br /&gt;
* Webinar: [https://webinars.analytica.com/Docs/2007-10-18-Calling-External-Applications.mp4 Calling-External-Applications.mp4]&lt;br /&gt;
* [[CurrentDataFolder]]&lt;br /&gt;
* [[WriteTextFile]]&lt;br /&gt;
* [[ReadTextFile]]&lt;br /&gt;
* [[GetRegistryValue]]&lt;/div&gt;</summary>
		<author><name>Lchrisman</name></author>
	</entry>
	<entry>
		<id>https://docs.analytica.com/index.php?title=File:Example_of_Encrypt_and_Decrypt.ana&amp;diff=64608</id>
		<title>File:Example of Encrypt and Decrypt.ana</title>
		<link rel="alternate" type="text/html" href="https://docs.analytica.com/index.php?title=File:Example_of_Encrypt_and_Decrypt.ana&amp;diff=64608"/>
		<updated>2026-09-22T20:15:10Z</updated>

		<summary type="html">&lt;p&gt;Lchrisman: Lchrisman uploaded a new version of File:Example of Encrypt and Decrypt.ana&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Summary ==&lt;br /&gt;
Very simple illustration of the strong encryption functions.&lt;/div&gt;</summary>
		<author><name>Lchrisman</name></author>
	</entry>
	<entry>
		<id>https://docs.analytica.com/index.php?title=File:Example_of_Encrypt_and_Decrypt.ana&amp;diff=64607</id>
		<title>File:Example of Encrypt and Decrypt.ana</title>
		<link rel="alternate" type="text/html" href="https://docs.analytica.com/index.php?title=File:Example_of_Encrypt_and_Decrypt.ana&amp;diff=64607"/>
		<updated>2026-09-22T20:10:44Z</updated>

		<summary type="html">&lt;p&gt;Lchrisman: Very simple illustration of the strong encryption functions.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Summary ==&lt;br /&gt;
Very simple illustration of the strong encryption functions.&lt;/div&gt;</summary>
		<author><name>Lchrisman</name></author>
	</entry>
	<entry>
		<id>https://docs.analytica.com/index.php?title=Encrypting_and_decrypting&amp;diff=64606</id>
		<title>Encrypting and decrypting</title>
		<link rel="alternate" type="text/html" href="https://docs.analytica.com/index.php?title=Encrypting_and_decrypting&amp;diff=64606"/>
		<updated>2026-09-22T20:10:13Z</updated>

		<summary type="html">&lt;p&gt;Lchrisman: /* See Also */  added example model&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[category:Encrypting and hashing functions]]&lt;br /&gt;
&lt;br /&gt;
{{ReleaseBar}}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;New to [[Analytica 7.2]].&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Requires [[Analytica Developer]] edition or better.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Analytica can encrypt and decrypt data using strong, standard, &#039;&#039;authenticated&#039;&#039; encryption. The four functions live in the &#039;&#039;&#039;&amp;lt;code&amp;gt;Crypto&amp;lt;/code&amp;gt;&#039;&#039;&#039; namespace:&lt;br /&gt;
&lt;br /&gt;
* [[#EncryptionKey|Crypto::EncryptionKey]] -- makes a key, which is a value of its own data type.&lt;br /&gt;
* [[#Encrypt|Crypto::Encrypt]] -- seals text or binary data under a key.&lt;br /&gt;
* [[#Decrypt|Crypto::Decrypt]] -- opens a sealed value, or reports an error if anything about it has changed.&lt;br /&gt;
* [[#RandomBytes|Crypto::RandomBytes]] -- cryptographically random bytes, for salts and keys.&lt;br /&gt;
&lt;br /&gt;
: &amp;lt;code&amp;gt;[[Variable]] Salt := Crypto::RandomBytes(16)&amp;lt;/code&amp;gt;&lt;br /&gt;
: &amp;lt;code&amp;gt;[[Variable]] MyKey := Crypto::EncryptionKey( password: &#039;correct horse battery staple&#039;, salt: Salt )&amp;lt;/code&amp;gt;&lt;br /&gt;
: &amp;lt;code&amp;gt;[[Variable]] Sealed := Crypto::Encrypt( &#039;launch codes&#039;, MyKey )&amp;lt;/code&amp;gt;&lt;br /&gt;
: &amp;lt;code&amp;gt;[[Variable]] Plain := Crypto::Decrypt( Sealed, MyKey ) &amp;amp;rarr; &#039;launch codes&#039;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== The Crypto namespace ==&lt;br /&gt;
&lt;br /&gt;
These are specialist functions, so they are not in your model&#039;s namespace to begin with. A model that never encrypts anything is not troubled by them, and they cannot collide with your own identifiers. You reach them in either of two ways.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Qualify each call&#039;&#039;&#039; with &amp;lt;code&amp;gt;Crypto::&amp;lt;/code&amp;gt;, as in the example above. Nothing is needed to make this work.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Or import the namespace once.&#039;&#039;&#039; Put &amp;lt;code&amp;gt;Crypto&amp;lt;/code&amp;gt; on a line of its own in your model&#039;s [[NamespaceImports]] attribute, and then write the names bare:&lt;br /&gt;
&lt;br /&gt;
: &amp;lt;code&amp;gt;Encrypt( &#039;launch codes&#039;, MyKey )&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The rest of this page writes &amp;lt;code&amp;gt;Crypto::&amp;lt;/code&amp;gt; explicitly so that each example works whether or not you have imported the namespace.&lt;br /&gt;
&lt;br /&gt;
== What authenticated encryption means ==&lt;br /&gt;
&lt;br /&gt;
All four algorithms here are &#039;&#039;AEAD&#039;&#039; ciphers -- Authenticated Encryption with Associated Data. Along with the encrypted data, a sealed value carries an &#039;&#039;authentication tag&#039;&#039; computed from the key. [[#Decrypt|Crypto::Decrypt]] checks the tag before it returns anything, so a sealed value that has been altered -- even by a single bit, even by someone who cannot read it -- produces an error instead of altered data.&lt;br /&gt;
&lt;br /&gt;
This is what you want, and it is not what simpler schemes give you. Encryption on its own hides data but does not stop an attacker from changing it in ways that change the decrypted result predictably. Nothing on this page lets you do unauthenticated encryption, deliberately.&lt;br /&gt;
&lt;br /&gt;
The error from a failed decryption is deliberately the same whether the key was wrong, the «aad» was wrong, or the data was altered. Telling those cases apart would help an attacker work out a key one guess at a time.&lt;br /&gt;
&lt;br /&gt;
== Keys ==&lt;br /&gt;
&lt;br /&gt;
A key is a value of its own type, made by [[#EncryptionKey|Crypto::EncryptionKey]]. It carries the algorithm along with the key material, so a key and an algorithm can never disagree, and [[#Encrypt|Crypto::Encrypt]] needs no «algorithm» parameter.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Make the key its own variable.&#039;&#039;&#039; Deriving a key from a password takes a noticeable fraction of a second by design (see [[#Passwords and salts|Passwords and salts]]). When the key is a variable, Analytica computes it once and reuses it, so encrypting a whole column of a table costs one derivation, not one per cell. Writing the derivation inline inside [[#Encrypt|Crypto::Encrypt]] would repeat it for every cell.&lt;br /&gt;
&lt;br /&gt;
The key material itself cannot be read from any expression. Printing a key shows only what it is:&lt;br /&gt;
&lt;br /&gt;
: &amp;lt;code&amp;gt;MyKey &amp;amp;rarr; «EncryptionKey AES-256-GCM #7a2f»&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A key has four readable members:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;MyKey-&amp;amp;gt;algorithm&amp;lt;/code&amp;gt; -- &amp;lt;code&amp;gt;&#039;AES-256-GCM&#039;&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;&#039;AES-192-GCM&#039;&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;&#039;AES-128-GCM&#039;&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;&#039;ChaCha20-Poly1305&#039;&amp;lt;/code&amp;gt;.&lt;br /&gt;
* &amp;lt;code&amp;gt;MyKey-&amp;amp;gt;keyBits&amp;lt;/code&amp;gt; -- 128, 192 or 256.&lt;br /&gt;
* &amp;lt;code&amp;gt;MyKey-&amp;amp;gt;fingerprint&amp;lt;/code&amp;gt; -- four hexadecimal digits that identify the key without revealing it. Two keys are the same key if and only if their fingerprints match (to a very high probability). Useful for answering &amp;quot;did this deployment get the key I think it did?&amp;quot; without ever displaying a key.&lt;br /&gt;
* &amp;lt;code&amp;gt;MyKey-&amp;amp;gt;fromSecret&amp;lt;/code&amp;gt; -- [[True]] when the key material came from a [[Secret]].&lt;br /&gt;
&lt;br /&gt;
=== Passwords and salts ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;Crypto::EncryptionKey( password: p, salt: s )&amp;lt;/code&amp;gt; stretches a password into a key using PBKDF2-HMAC-SHA-256 with «iterations» rounds, 600,000 by default. The iterations are the point: they make each guess at your password expensive for an attacker, which is the only defence a human-chosen password has.&lt;br /&gt;
&lt;br /&gt;
The «salt» is &#039;&#039;&#039;not&#039;&#039;&#039; secret. Keep it in an ordinary variable next to the key, and save it with your model -- you need the same salt to derive the same key again. Its job is to make precomputed attack tables useless, which it does even though it is public. Make one with &amp;lt;code&amp;gt;[[#RandomBytes|Crypto::RandomBytes]](16)&amp;lt;/code&amp;gt; and then leave it alone; changing the salt changes the key, and data encrypted under the old key can no longer be read.&lt;br /&gt;
&lt;br /&gt;
=== Keeping the key out of the model ===&lt;br /&gt;
&lt;br /&gt;
If the key is written in the model, anyone who has the model has the key, and the encryption protects nothing. The usual arrangement is the other way round: the &#039;&#039;sealed data&#039;&#039; travels in the &amp;lt;code&amp;gt;.ana&amp;lt;/code&amp;gt; file and the &#039;&#039;key&#039;&#039; does not.&lt;br /&gt;
&lt;br /&gt;
Give «key» or «password» a [[Secret]] to do that. The secret&#039;s value reaches the key derivation without ever entering the model as a value:&lt;br /&gt;
&lt;br /&gt;
: &amp;lt;code&amp;gt;Crypto::EncryptionKey( key: MyKeySecret )&amp;lt;/code&amp;gt;&lt;br /&gt;
: &amp;lt;code&amp;gt;Crypto::EncryptionKey( password: MyPasswordSecret, salt: Salt )&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The [[Secret]] needs two things set before Analytica will allow this:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Sinks&#039;&#039;&#039; must list &amp;lt;code&amp;gt;EncryptionKey&amp;lt;/code&amp;gt;.&lt;br /&gt;
* &#039;&#039;&#039;Destination&#039;&#039;&#039; must be the single word &amp;lt;code&amp;gt;local&amp;lt;/code&amp;gt;. Unlike a URL or a database connection string, key material never leaves the Analytica process, so there is no destination to pin -- but the setting is still required, so that allowing it is a deliberate act.&lt;br /&gt;
&lt;br /&gt;
Consider also setting the secret&#039;s &#039;&#039;&#039;Caller&#039;&#039;&#039; to the module that is allowed to build the key, which restricts the use of the secret far more tightly than any destination could.&lt;br /&gt;
&lt;br /&gt;
A key made from a [[Secret]] refuses &amp;lt;code&amp;gt;-&amp;amp;gt;Export()&amp;lt;/code&amp;gt;, and reads &amp;lt;code&amp;gt;fromSecret = True&amp;lt;/code&amp;gt;. That is deliberate: a secret&#039;s value must never come back into the model, and exporting the key it produced would be exactly that.&lt;br /&gt;
&lt;br /&gt;
=== Generating and saving a key ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;Crypto::EncryptionKey()&amp;lt;/code&amp;gt; with no arguments makes a fresh random key. To use the same key again later, export the material and store it somewhere outside the model -- a [[Secret]], a file, a password manager:&lt;br /&gt;
&lt;br /&gt;
: &amp;lt;code&amp;gt;Crypto::EncryptionKey()-&amp;amp;gt;Export() &amp;amp;rarr; a 32-byte binary value&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To see it as text you can copy, format it:&lt;br /&gt;
&lt;br /&gt;
: &amp;lt;code&amp;gt;f&amp;quot;{Crypto::EncryptionKey()-&amp;amp;gt;Export():b}&amp;quot; &amp;amp;rarr; base64&#039;8Xk2wQ...&#039;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Paste that &amp;lt;code&amp;gt;base64&#039;...&#039;&amp;lt;/code&amp;gt; literal back into an expression to rebuild the same key:&lt;br /&gt;
&lt;br /&gt;
: &amp;lt;code&amp;gt;Crypto::EncryptionKey( key: base64&#039;8Xk2wQ...&#039; )&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;A key cannot be stored in a variable&#039;&#039;&#039; the way other values can. Assigning one to a variable&#039;s definition is refused, because that would write the key material into the &amp;lt;code&amp;gt;.ana&amp;lt;/code&amp;gt; file. Build the key where it is used instead.&lt;br /&gt;
&lt;br /&gt;
== Sealed values ==&lt;br /&gt;
&lt;br /&gt;
[[#Encrypt|Crypto::Encrypt]] returns [[In-memory binary data terms|binary data]] by default. The &amp;lt;code&amp;gt;&#039;analytica&#039;&amp;lt;/code&amp;gt; container adds 36 bytes to whatever you encrypted: a marker that says this is an Analytica sealed value, which algorithm made it, whether the plaintext was text or binary, the nonce, and the authentication tag.&lt;br /&gt;
&lt;br /&gt;
Because the container records whether the plaintext was text or binary, [[#Decrypt|Crypto::Decrypt]] hands back the same kind of value you encrypted, with nothing extra to say.&lt;br /&gt;
&lt;br /&gt;
=== Every call produces a different result ===&lt;br /&gt;
&lt;br /&gt;
Each call draws a fresh random &#039;&#039;nonce&#039;&#039;, so encrypting the same text twice gives two different sealed values. That is required -- reusing a nonce with the same key breaks the encryption completely -- but it has a practical consequence in a model: a sealed value computed by a definition changes every time the definition is re-evaluated.&lt;br /&gt;
&lt;br /&gt;
So do not leave &amp;lt;code&amp;gt;Crypto::Encrypt(...)&amp;lt;/code&amp;gt; in the definition of a variable whose result you intend to keep. Compute the sealed value once, in a button script, and assign it:&lt;br /&gt;
&lt;br /&gt;
: &amp;lt;code&amp;gt;Sealed := Crypto::Encrypt( Plaintext, MyKey )&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The assignment writes the sealed value into &amp;lt;code&amp;gt;Sealed&amp;lt;/code&amp;gt;&#039;s definition as a &amp;lt;code&amp;gt;base64&#039;...&#039;&amp;lt;/code&amp;gt; literal, which is saved with the model and decrypts unchanged when it is reopened.&lt;br /&gt;
&lt;br /&gt;
=== Binding a sealed value to its context ===&lt;br /&gt;
&lt;br /&gt;
The optional «aad» parameter -- additional authenticated data -- is authenticated but not encrypted. Whoever decrypts must supply the same «aad» or the decryption fails.&lt;br /&gt;
&lt;br /&gt;
Use it to tie each sealed value to where it belongs. If you seal a column of salaries with no «aad», someone who cannot read them can still swap two rows and go undetected. Seal them with the employee as «aad» and the swap is caught:&lt;br /&gt;
&lt;br /&gt;
: &amp;lt;code&amp;gt;Crypto::Encrypt( Salary, MyKey, aad: Employee )&amp;lt;/code&amp;gt;&lt;br /&gt;
: &amp;lt;code&amp;gt;Crypto::Decrypt( SealedSalary, MyKey, aad: Employee )&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The «aad» is not secret and does not need to be hidden -- the reader has to know it anyway.&lt;br /&gt;
&lt;br /&gt;
== Encrypting a whole table ==&lt;br /&gt;
&lt;br /&gt;
«data» and «aad» are atomic parameters, so both functions [[Array Abstraction|array abstract]]. One expression seals an entire indexed table, each cell under its own fresh nonce, all under the one key:&lt;br /&gt;
&lt;br /&gt;
: &amp;lt;code&amp;gt;[[Variable]] SealedRows := Crypto::Encrypt( PlainRows, MyKey )&amp;lt;/code&amp;gt;&lt;br /&gt;
: &amp;lt;code&amp;gt;[[Variable]] PlainAgain := Crypto::Decrypt( SealedRows, MyKey )&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is where making the key its own variable pays off: the key is derived once and every cell reuses it.&lt;br /&gt;
&lt;br /&gt;
== Interoperating with other systems ==&lt;br /&gt;
&lt;br /&gt;
To exchange sealed data with something outside Analytica, you need to agree on two things: how the bytes are packaged, and how they are written as text.&lt;br /&gt;
&lt;br /&gt;
=== Layouts ===&lt;br /&gt;
&lt;br /&gt;
There is no single universal convention for packaging a nonce, a ciphertext and an authentication tag, so «layout» lets you pick the one your counterpart uses.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! «layout» !! The sealed value contains !! Used by&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;&#039;analytica&#039;&amp;lt;/code&amp;gt; (default) || A self-describing container: marker, algorithm, text-or-binary flag, nonce, tag, ciphertext || Analytica&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;&#039;nonce,ct,tag&#039;&amp;lt;/code&amp;gt; || nonce, then ciphertext, then tag || The common Go idiom, and most published examples&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;&#039;ct,tag&#039;&amp;lt;/code&amp;gt; || ciphertext, then tag; the nonce is separate || Python&#039;s &amp;lt;code&amp;gt;cryptography&amp;lt;/code&amp;gt; package, libsodium&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;&#039;nonce,tag,ct&#039;&amp;lt;/code&amp;gt; || nonce, then tag, then ciphertext ||&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;&#039;none&#039;&amp;lt;/code&amp;gt; || the bare ciphertext; nonce and tag are separate || Node.js &amp;lt;code&amp;gt;crypto&amp;lt;/code&amp;gt;, .NET &amp;lt;code&amp;gt;AesGcm&amp;lt;/code&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Whatever the layout, the nonce and the tag are &#039;&#039;&#039;also&#039;&#039;&#039; available as the second and third return values, so you can always get at them:&lt;br /&gt;
&lt;br /&gt;
: &amp;lt;code&amp;gt;[[Local]] (sealed, nonce, tag) := Crypto::Encrypt( x, MyKey );&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
and supply them again when decrypting a layout that does not carry them:&lt;br /&gt;
&lt;br /&gt;
: &amp;lt;code&amp;gt;Crypto::Decrypt( ct, MyKey, nonce: n, tag: t, layout: &#039;none&#039;, as: &#039;text&#039; )&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The raw layouts carry no record of whether the plaintext was text or binary, so «as» is required with them.&lt;br /&gt;
&lt;br /&gt;
=== Text encodings ===&lt;br /&gt;
&lt;br /&gt;
«format» writes the sealed value as text instead of binary data -- &amp;lt;code&amp;gt;&#039;base64&#039;&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;&#039;base64url&#039;&amp;lt;/code&amp;gt; (the URL-safe alphabet, no padding) or &amp;lt;code&amp;gt;&#039;hex&#039;&amp;lt;/code&amp;gt;. [[#Decrypt|Crypto::Decrypt]] reads text in the same encodings.&lt;br /&gt;
&lt;br /&gt;
: &amp;lt;code&amp;gt;Crypto::Encrypt( &#039;hello&#039;, MyKey, format: &#039;base64&#039; )&amp;lt;/code&amp;gt;&lt;br /&gt;
: &amp;lt;code&amp;gt;Crypto::Decrypt( sealedText, MyKey, format: &#039;base64&#039; )&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== A worked example ===&lt;br /&gt;
&lt;br /&gt;
This Python produces a value Analytica reads, and reads a value Analytica produced. It uses the &amp;lt;code&amp;gt;cryptography&amp;lt;/code&amp;gt; package.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
from cryptography.hazmat.primitives.ciphers.aead import AESGCM&lt;br /&gt;
import base64&lt;br /&gt;
&lt;br /&gt;
key   = bytes(range(32))        # the same 32 bytes as base64&#039;AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8=&#039;&lt;br /&gt;
nonce = bytes(range(12))&lt;br /&gt;
&lt;br /&gt;
# Seal something for Analytica&lt;br /&gt;
ct = AESGCM(key).encrypt(nonce, &#039;from python&#039;.encode(&#039;utf-8&#039;), b&#039;ctx7&#039;)&lt;br /&gt;
print(base64.b64encode(nonce + ct).decode())&lt;br /&gt;
&lt;br /&gt;
# Open something Analytica sealed with layout:&#039;ct,tag&#039;&lt;br /&gt;
plain = AESGCM(key).decrypt(nonce, bytes.fromhex(&#039;66d9d9b2da0e0c4679f3a82524f5e0499271e16f30&#039;), None)&lt;br /&gt;
print(plain.decode(&#039;utf-8&#039;))    # -&amp;gt; hello&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In Analytica:&lt;br /&gt;
&lt;br /&gt;
: &amp;lt;code&amp;gt;[[Variable]] K := Crypto::EncryptionKey( key: base64&#039;AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8=&#039; )&amp;lt;/code&amp;gt;&lt;br /&gt;
: &amp;lt;code&amp;gt;Crypto::Decrypt( base64&#039;AAECAwQFBgcICQoLIXC5duWVu2/lLvmrU3Xrc1+3jRqJx0hC/3QKjDlK2w==&#039;, K, layout: &#039;nonce,ct,tag&#039;, aad: &#039;ctx7&#039;, as: &#039;text&#039; ) &amp;amp;rarr; &#039;from python&#039;&amp;lt;/code&amp;gt;&lt;br /&gt;
: &amp;lt;code&amp;gt;Crypto::Encrypt( &#039;hello&#039;, K, nonce: base64&#039;AAAAAAAAAAAAAAAA&#039;, layout: &#039;ct,tag&#039;, format: &#039;hex&#039; ) &amp;amp;rarr; &#039;66d9d9b2da0e0c4679f3a82524f5e0499271e16f30&#039;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The «nonce» parameter is used here only to make the output reproducible so it can be checked against a published test vector. &#039;&#039;&#039;Do not supply a nonce in real use.&#039;&#039;&#039; Reusing a nonce with the same key destroys the security of the encryption completely; omitting it is always the right thing.&lt;br /&gt;
&lt;br /&gt;
=== The &#039;analytica&#039; container ===&lt;br /&gt;
&lt;br /&gt;
If you need to read Analytica&#039;s own container from another language, its bytes are:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Offset !! Length !! Field&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 4 || The four characters &amp;lt;code&amp;gt;ACRY&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| 4 || 1 || Format version, currently 1&lt;br /&gt;
|-&lt;br /&gt;
| 5 || 1 || Algorithm: 1 = AES-128-GCM, 2 = AES-192-GCM, 3 = AES-256-GCM, 4 = ChaCha20-Poly1305&lt;br /&gt;
|-&lt;br /&gt;
| 6 || 1 || Flags. Bit 0 set means the plaintext was text, encoded as UTF-8. Bit 1 set means an «aad» was supplied.&lt;br /&gt;
|-&lt;br /&gt;
| 7 || 1 || Nonce length, 12 for every algorithm above&lt;br /&gt;
|-&lt;br /&gt;
| 8 || 12 || Nonce&lt;br /&gt;
|-&lt;br /&gt;
| 20 || 16 || Authentication tag&lt;br /&gt;
|-&lt;br /&gt;
| 36 || rest || Ciphertext&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The salt and the iteration count are &#039;&#039;&#039;not&#039;&#039;&#039; in the container. They belong to the key, which you construct explicitly, so keep the salt in a variable of your own next to the key that uses it.&lt;br /&gt;
&lt;br /&gt;
= Function reference =&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;EncryptionKey&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Crypto::EncryptionKey( &#039;&#039;algorithm, key, keyFormat, password, salt, iterations, kdf&#039;&#039; ) ==&lt;br /&gt;
&#039;&#039;Requires [[Analytica Developer]] edition or better.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Creates a key for [[#Encrypt|Crypto::Encrypt]] and [[#Decrypt|Crypto::Decrypt]]. Give it either «key» or «password», or neither for a fresh random key.&lt;br /&gt;
&lt;br /&gt;
=== Parameters ===&lt;br /&gt;
&lt;br /&gt;
* «algorithm»: (optional, default &amp;lt;code&amp;gt;&#039;AES-256-GCM&#039;&amp;lt;/code&amp;gt;) One of &amp;lt;code&amp;gt;&#039;AES-256-GCM&#039;&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;&#039;AES-192-GCM&#039;&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;&#039;AES-128-GCM&#039;&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;&#039;ChaCha20-Poly1305&#039;&amp;lt;/code&amp;gt;. ChaCha20-Poly1305 requires Windows 10 version 1903 or later; on an older system it reports that it is unavailable rather than failing obscurely.&lt;br /&gt;
&lt;br /&gt;
* «key»: (optional) Raw key material, as [[In-memory binary data terms|binary data]] -- usually a &amp;lt;code&amp;gt;base64&#039;...&#039;&amp;lt;/code&amp;gt; literal. It must be exactly the algorithm&#039;s key length: 32 bytes for AES-256-GCM and ChaCha20-Poly1305, 24 for AES-192-GCM, 16 for AES-128-GCM. A key of the wrong length is an error; Analytica will not pad or hash it to fit, because that would quietly weaken it. You may also pass a [[Secret]] here, in which case the secret&#039;s text is decoded according to «keyFormat». Plain text that is not a [[Secret]] is refused -- use «password» for a passphrase.&lt;br /&gt;
&lt;br /&gt;
* «keyFormat»: (optional, default &amp;lt;code&amp;gt;&#039;base64&#039;&amp;lt;/code&amp;gt;) How to read the text a [[Secret]] holds: &amp;lt;code&amp;gt;&#039;base64&#039;&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;&#039;base64url&#039;&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;&#039;hex&#039;&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;&#039;utf8&#039;&amp;lt;/code&amp;gt;. Meaningful only when «key» is given a [[Secret]].&lt;br /&gt;
&lt;br /&gt;
* «password»: (optional) A passphrase to derive the key from. Requires «salt». May be given a [[Secret]].&lt;br /&gt;
&lt;br /&gt;
* «salt»: (optional) Text or [[In-memory binary data terms|binary data]], at least 8 bytes, required whenever «password» is given. Not secret; see [[#Passwords and salts|Passwords and salts]].&lt;br /&gt;
&lt;br /&gt;
* «iterations»: (optional, default 600000) PBKDF2 rounds. More is slower and safer. Use the same number every time, or you get a different key.&lt;br /&gt;
&lt;br /&gt;
* «kdf»: (optional, default &amp;lt;code&amp;gt;&#039;PBKDF2-HMAC-SHA-256&#039;&amp;lt;/code&amp;gt;) The key-derivation function. This release supports only that one; the parameter exists so that adding others later does not change how you write the call.&lt;br /&gt;
&lt;br /&gt;
A parameter that could not take effect is refused rather than quietly ignored -- «iterations» without «password», for example.&lt;br /&gt;
&lt;br /&gt;
=== Members ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;-&amp;amp;gt;algorithm&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;-&amp;amp;gt;keyBits&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;-&amp;amp;gt;fingerprint&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;-&amp;amp;gt;fromSecret&amp;lt;/code&amp;gt;, described under [[#Keys|Keys]]. There is no member that returns the key material.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;Export&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== key-&amp;gt;Export( ) ==&lt;br /&gt;
&lt;br /&gt;
Returns the raw key material of a key as [[In-memory binary data terms|binary data]]. This is the only way to get key material back out, and it is meant for saving a randomly generated key so the same key can be used again.&lt;br /&gt;
&lt;br /&gt;
: &amp;lt;code&amp;gt;MyKey-&amp;amp;gt;Export() &amp;amp;rarr; a 32-byte binary value&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A key derived from a [[Secret]] cannot be exported, and reports an error. A secret&#039;s value is never allowed back into the model.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;Encrypt&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Crypto::Encrypt( data, key&#039;&#039;, aad, nonce, format, layout&#039;&#039; ) ==&lt;br /&gt;
&#039;&#039;Requires [[Analytica Developer]] edition or better.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Encrypts «data» under «key» and returns a sealed value that only the same key can open. Returns [[Null]] when «data» is [[Null]].&lt;br /&gt;
&lt;br /&gt;
=== Parameters ===&lt;br /&gt;
&lt;br /&gt;
* «data»: Text or [[In-memory binary data terms|binary data]]. A number is refused rather than converted, since &amp;lt;code&amp;gt;42&amp;lt;/code&amp;gt; could reasonably mean either the text &amp;lt;code&amp;gt;&#039;42&#039;&amp;lt;/code&amp;gt; or eight bytes -- use &amp;lt;code&amp;gt;[[Text]](x)&amp;lt;/code&amp;gt; if you mean its textual form.&lt;br /&gt;
&lt;br /&gt;
* «key»: A key from [[#EncryptionKey|Crypto::EncryptionKey]]. The algorithm comes from the key.&lt;br /&gt;
&lt;br /&gt;
* «aad»: (optional) Additional authenticated data -- authenticated but not encrypted. [[#Decrypt|Crypto::Decrypt]] must be given the same value. See [[#Binding a sealed value to its context|Binding a sealed value to its context]].&lt;br /&gt;
&lt;br /&gt;
* «nonce»: (optional, named) Supplies the nonce instead of generating one. &#039;&#039;&#039;Reusing a nonce with the same key destroys the security of the encryption completely.&#039;&#039;&#039; Omit this unless you are reproducing a published test vector or another system&#039;s exact output.&lt;br /&gt;
&lt;br /&gt;
* «format»: (optional, named, default &amp;lt;code&amp;gt;&#039;binary&#039;&amp;lt;/code&amp;gt;) &amp;lt;code&amp;gt;&#039;binary&#039;&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;&#039;base64&#039;&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;&#039;base64url&#039;&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;&#039;hex&#039;&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
* «layout»: (optional, named, default &amp;lt;code&amp;gt;&#039;analytica&#039;&amp;lt;/code&amp;gt;) See [[#Layouts|Layouts]].&lt;br /&gt;
&lt;br /&gt;
=== Return value ===&lt;br /&gt;
&lt;br /&gt;
The sealed value, as [[In-memory binary data terms|binary data]] or as text according to «format». The nonce and the authentication tag are returned as the second and third return values:&lt;br /&gt;
&lt;br /&gt;
: &amp;lt;code&amp;gt;[[Local]] (sealed, nonce, tag) := Crypto::Encrypt( x, MyKey );&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
It [[Array Abstraction|array abstracts]] over «data» and «aad», sealing each cell under its own nonce.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;Decrypt&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Crypto::Decrypt( data, key&#039;&#039;, aad, nonce, tag, format, layout, as&#039;&#039; ) ==&lt;br /&gt;
&#039;&#039;Requires [[Analytica Developer]] edition or better.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Opens a value sealed by [[#Encrypt|Crypto::Encrypt]]. Returns [[Null]] when «data» is [[Null]].&lt;br /&gt;
&lt;br /&gt;
Reports an error if the value cannot be decrypted -- wrong key, wrong «aad», or data that has been altered. The message deliberately does not say which, because that distinction would help an attacker. Use [[Try]] if your model should carry on regardless.&lt;br /&gt;
&lt;br /&gt;
=== Parameters ===&lt;br /&gt;
&lt;br /&gt;
* «data»: The sealed value, as [[In-memory binary data terms|binary data]], or as text in the encoding named by «format».&lt;br /&gt;
&lt;br /&gt;
* «key»: The same key the value was sealed with. If it is a key for a different algorithm, that is reported specifically, since it is a mistake you can act on rather than a failed decryption.&lt;br /&gt;
&lt;br /&gt;
* «aad»: (optional) Must equal the «aad» the value was sealed with.&lt;br /&gt;
&lt;br /&gt;
* «nonce», «tag»: (optional, named) Required for a «layout» that does not carry them.&lt;br /&gt;
&lt;br /&gt;
* «format»: (optional, named, default &amp;lt;code&amp;gt;&#039;base64&#039;&amp;lt;/code&amp;gt;) How to read a textual «data»: &amp;lt;code&amp;gt;&#039;base64&#039;&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;&#039;base64url&#039;&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;&#039;hex&#039;&amp;lt;/code&amp;gt;. Ignored when «data» is binary.&lt;br /&gt;
&lt;br /&gt;
* «layout»: (optional, named, default &amp;lt;code&amp;gt;&#039;analytica&#039;&amp;lt;/code&amp;gt;) Must match the layout the value was sealed with. See [[#Layouts|Layouts]].&lt;br /&gt;
&lt;br /&gt;
* «as»: (optional, named) &amp;lt;code&amp;gt;&#039;text&#039;&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;&#039;binaryData&#039;&amp;lt;/code&amp;gt;. With the default &amp;lt;code&amp;gt;&#039;analytica&#039;&amp;lt;/code&amp;gt; layout it is optional, because the sealed value records which one was encrypted. For every other layout it is required. &amp;lt;code&amp;gt;&#039;text&#039;&amp;lt;/code&amp;gt; reports an error if the decrypted bytes are not valid UTF-8, which is usually a sign that you wanted &amp;lt;code&amp;gt;&#039;binaryData&#039;&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;RandomBytes&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Crypto::RandomBytes( n ) ==&lt;br /&gt;
&lt;br /&gt;
Returns «n» cryptographically random bytes as [[In-memory binary data terms|binary data]]. Use it for a «salt», or for raw key material.&lt;br /&gt;
&lt;br /&gt;
: &amp;lt;code&amp;gt;[[Variable]] Salt := Crypto::RandomBytes(16)&amp;lt;/code&amp;gt;&lt;br /&gt;
: &amp;lt;code&amp;gt;[[Variable]] NewKey := Crypto::EncryptionKey( key: Crypto::RandomBytes(32) )&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
«n» may not exceed 65536.&lt;br /&gt;
&lt;br /&gt;
Each evaluation returns different bytes. Like [[#Encrypt|Crypto::Encrypt]], this means a definition that calls it does not hold still across re-evaluations -- assign the result once if you need it to stay the same.&lt;br /&gt;
&lt;br /&gt;
== Errors ==&lt;br /&gt;
&lt;br /&gt;
Most mistakes are reported specifically, and can be caught with [[Try]]:&lt;br /&gt;
&lt;br /&gt;
* The «key» is the wrong length for the algorithm, or «key» was given plain text rather than key material or a [[Secret]].&lt;br /&gt;
* Both «key» and «password» were given, or «password» was given without a «salt», or a parameter such as «iterations» cannot take effect.&lt;br /&gt;
* The algorithm is not one of the four, or is not available on this computer.&lt;br /&gt;
* «data» is a number rather than text or binary data.&lt;br /&gt;
* The sealed value is not an Analytica sealed value, or was sealed with a different algorithm, or the text is not valid for the stated «format».&lt;br /&gt;
* A [[Secret]] was passed where a secret makes no sense, such as «aad» or «salt», neither of which is secret.&lt;br /&gt;
* &amp;lt;code&amp;gt;-&amp;amp;gt;Export()&amp;lt;/code&amp;gt; was called on a key derived from a [[Secret]].&lt;br /&gt;
&lt;br /&gt;
The one deliberately vague error is a failed decryption, described under [[#Decrypt|Crypto::Decrypt]].&lt;br /&gt;
&lt;br /&gt;
== See Also ==&lt;br /&gt;
&lt;br /&gt;
* [[Secret]] -- keeping a key out of the model&lt;br /&gt;
* [[TextCharacterEncode]] -- hashing text with SHA-1 or SHA-256, and other text encodings&lt;br /&gt;
* [[In-memory binary data terms]]&lt;br /&gt;
* [[NamespaceImports]]&lt;br /&gt;
* [[:category:Encrypting and hashing functions|Encrypting and hashing functions]]&lt;br /&gt;
* [[media:Example of Encrypt and Decrypt.ana|Example of Encrypt and Decrypt.ana]]&lt;/div&gt;</summary>
		<author><name>Lchrisman</name></author>
	</entry>
	<entry>
		<id>https://docs.analytica.com/index.php?title=What%27s_new_in_Analytica_7.2%3F&amp;diff=64605</id>
		<title>What&#039;s new in Analytica 7.2?</title>
		<link rel="alternate" type="text/html" href="https://docs.analytica.com/index.php?title=What%27s_new_in_Analytica_7.2%3F&amp;diff=64605"/>
		<updated>2026-09-22T18:23:19Z</updated>

		<summary type="html">&lt;p&gt;Lchrisman: ER 22440 - Encrypt, Decrypt&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Analytica 7.2 is currently under development, and will be a future release of Analytica. The current official release is [[Analytica 7.1]]. This page is under construction, and will list enhancements that are new to Analytica or ADE 7.2.&lt;br /&gt;
&lt;br /&gt;
== Secrets ==&lt;br /&gt;
The [[Secret]] object class is a first-class home for the credentials your model uses: database passwords, API keys, bearer tokens.&lt;br /&gt;
&lt;br /&gt;
A Secret keeps the credential itself out of the language entirely. The Secret&#039;s identifier evaluates to a &#039;&#039;placeholder&#039;&#039; text, e.g. &amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;{{secret:AcmeKey}}&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt;, which you compose into a connection string or URL like any other text:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;code&amp;gt;DbQuery(f&amp;quot;DSN=AcmeWarehouse;UID=svc_reports;PWD={AcmeKey}&amp;quot;, sql)&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The real value is substituted only inside allow-listed built-in functions ([[DbQuery]], [[ReadFromUrl|ReadFromURL]], [[OAuth2Authorize]], and the other database functions), in compiled code, just before the text goes to ODBC or to the web -- and only when the call&#039;s destination matches the Secret&#039;s declared policy. Definitions stay fully readable, with no cloaking or locked modules -- yet the credential never appears in any result, attribute, error message, or saved file, and model code cannot exfiltrate it (a &amp;lt;code&amp;gt;WriteTextFile&amp;lt;/code&amp;gt; or a &amp;lt;code&amp;gt;ReadFromURL&amp;lt;/code&amp;gt; aimed at some other server never sees the real value).&lt;br /&gt;
&lt;br /&gt;
Create one with &#039;&#039;&#039;[[Object menu]] / New / Secret&#039;&#039;&#039; (requires the Developer edition; models &#039;&#039;containing&#039;&#039; secrets load and run in every edition). See [[Secret]] for the full story, including an honest account of what each storage kind does and does not protect against.&lt;br /&gt;
&lt;br /&gt;
== Custom File Providers ==&lt;br /&gt;
(&#039;&#039;advanced, esoteric&#039;&#039;)&lt;br /&gt;
A [[Custom file system providers|file system provider]] makes a URI-style path, &amp;lt;code&amp;gt;«scheme»://«root»/«path»&amp;lt;/code&amp;gt;, for example &amp;lt;code&amp;gt;&amp;quot;repo://Sales/Q3.csv&amp;quot;&amp;lt;/code&amp;gt;, work anywhere the Analytica engine accepts a file name. The file provider can be a source other than a file system (for example, a git repo, cloud storage, a web-based document management system, etc.), but from Analytica and within your models, it can be used anywhere a file path would be used, as if it were a file. You have to configure the available file providers, and you can implement your own (e.g., to wrap an existing service) according to a document [[Custom file system providers/API spec|API specification]].&lt;br /&gt;
&lt;br /&gt;
== GUI ==&lt;br /&gt;
* The [[Object menu]] has a new submenu named &#039;&#039;&#039;New&#039;&#039;&#039;, which you can use to add a new object of any class. This makes it easier and more convenient to add more esoteric class instances that aren&#039;t on the toolbar (like Frame node, Struct, Callable, Secret, etc.) Plus it is easier to add a new node without using a mouse.&lt;br /&gt;
** &#039;&#039;&#039;[[Object menu]] / New / Picture&#039;&#039;&#039; prompts for an image filename. (similar to &#039;&#039;&#039;[File menu] / Import...&#039;&#039;&#039;.&lt;br /&gt;
* Enhancements to the [[Outline window]]&lt;br /&gt;
** There are now hover icons on line items.&lt;br /&gt;
** You can Alt+Click on an item in the Outline to insert its identifier while editing an expression elsewhere.&lt;br /&gt;
** The &#039;&#039;&#039;Module only&#039;&#039;&#039; checkbox has been removed from the header, and a new icon button added to each module line item to show or hide non-module children, allowing this to be controlled at the level of each module or library.&lt;br /&gt;
** Key strokes now have an effect. Up/Down arrows move between line items. Right/Left arrows from a module expand or collapse that item. Shift+Right/Left from a module show or hide non-module children. Home/End keys jump to the begging or end. And typing characters incrementally search among the items visible in the window.&lt;br /&gt;
* Input and Output popups are now present on Module nodes in a diagram. Input popups appear when you click to the immediately left of a node, and output popups appear when you click to the immediate right of a node. These have long existed for variables enabling you to quickly navigate dependencies, but for a Module node the situation is much more complex. A dependency exists from Module A to Module B when there exists a dependency between variables &amp;lt;code&amp;gt;X&amp;amp;rarr;Y&amp;lt;/code&amp;gt; where X is within Module A and Y is within Module B. With modules, there can be a very large number of such dependencies, so the new module input/output popups are organized hierarchically with expanding outline views.&lt;br /&gt;
* There is a new [[Publish To Cloud dialog]]. It handles both ACP3 (the current version at https://acp.analytica.com at the time of this release) as well as ACP4 (coming soon).&lt;br /&gt;
* A user input node with a &#039;&#039;&#039;[List]&#039;&#039;&#039; control pops up a new list-editor dialog, similar to the one in ACP. Previously this opened the Object Window where you would edit the list. The new dialog focuses solely on the list without all the other attributes.&lt;br /&gt;
* Added Drag-and-drop of selected text within and between textual attribute values. Formerly required Cut-Paste, but differs in that Drag-and-drop doesn&#039;t alter the clipboard.&lt;br /&gt;
* The [[Preferences dialog]] exposes some new preference settings:&lt;br /&gt;
** The method used when generating identifiers from titles. (Which Assista will also use when naming objects)&lt;br /&gt;
** Whether display-only arrows should be show as dashed.&lt;br /&gt;
** The time zone used for new date-time values (such as when parsing dates).&lt;br /&gt;
&lt;br /&gt;
== Built-in functions or structs ==&lt;br /&gt;
* The [[Functions To Read Excel Worksheets|Spreadsheet functions]] now work directly with live Google sheets.&lt;br /&gt;
* A new option to &amp;lt;code&amp;gt;[[SpreadsheetInfo]]( wb, &#039;Backend&#039;)&amp;lt;/code&amp;gt;, to programatically detect which backend the workbook uses (&amp;lt;code&amp;gt;&#039;LibXl&#039;&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;&#039;Excel&#039;&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;&#039;GoogleSheets&#039;&amp;lt;/code&amp;gt;). &lt;br /&gt;
* [[FileExists]]( filepath&#039;&#039;, files, folders&#039;&#039; ) tests whether a file or folder exists, without the [[FileSystemListing]] idiom that this used to require. Requires {{Analytica Developer}} or better.&lt;br /&gt;
* New parameters, «map» and «includeNull» added to [[Flatten]] (...). Makes it easy and efficient to concatenate a collection of lists when the lists are behind [[references]] or in [[Struct]] members.&lt;br /&gt;
* [[SortIndex]], [[Sort]] and [[Rank]] can now accept a «lessThat» function for custom comparisons. See [[Using an ordering function when sorting]].&lt;br /&gt;
* A new optional «timeZone» parameter added to [[ParseDate]], [[ParseCSV]], [[NumberToText]], and [[Today]]. Controls which time zone a newly created date-time number (such as when parsed) is created in. [[ParseDate]] and [[ParseCSV]] now recognize ISO 8601 and RFC 3339 date-time format; These are used by many web services, and may include an explicit time zone, which gets converted into the active or specified time zone. &lt;br /&gt;
* Added new built-in functions for [[Encrypting and decrypting|encryption and decryption]] using strong, standard authenticated encryption. (requires [[Analytica Developer]] edition or better).&lt;br /&gt;
* (Esoteric) A new parameter, «sensitive», to [[AskMsgText]] and [[MsgBox]]. Used with the new &amp;lt;code&amp;gt;/AutomationTrace&amp;lt;/code&amp;gt; [[Analytica Command Line|command line]] option to prevent sensitive info (like a credential or API key) from being logged.&lt;br /&gt;
* (&#039;&#039;Experimental&#039;&#039;) New [[Timer]] struct for scheduling an evaluation.&lt;br /&gt;
* New options for [[GetProcessInfo]] to detect non-Windows host version (e.g., when running on Linux), as well as installed fonts.&lt;br /&gt;
* (Esoteric) A new Struct, [[ArrowDependencies]], returns detailed information on dependencies into, or out of, nodes. Most useful for finding all the dependencies between two modules (i.e., which variables deep inside are responsible for dependencies between them). Introduce to enable Assista to better answer questions about module dependencies.&lt;br /&gt;
* The [[UncertainLMH]] distribution now allows «xLow»=«lb» or «xHigh»=«ub». This means that 10% (or more generally «pLow») of the probability sits at the «lb» or «ub» value creating a discontinuity in the CDF (an infinite spike in the PDF) at that value.&lt;br /&gt;
* Added an «asIndex» parameter to [[HandleFromIdentifier]]. The same already existed on [[Handle]]. Used to disambiguate in the case of a self-indexed array-valued variable. &lt;br /&gt;
* When &amp;lt;code&amp;gt;[[Area]](y,x)&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;[[Integrate]](y,x)&amp;lt;/code&amp;gt; appear in a [[DefineOptimization]] formulation where &amp;lt;code&amp;gt;y&amp;lt;/code&amp;gt; depends on decision variables, the analyzer recognizes this as a linear or quadratic relationship when deciding whether the problem is an «LP», «QP» or «NLP».&lt;br /&gt;
&lt;br /&gt;
== Dates and time zones ==&lt;br /&gt;
* ISO 8601 / RFC 3339 date-times with a UTC offset, such as &amp;lt;code&amp;gt;2025-08-25T00:07:05.000+0000&amp;lt;/code&amp;gt; (the form most web APIs return), are now parsed by [[ParseDate]], [[ParseCSV]], table cells and definitions, and can be written as literals inside expressions, e.g., &amp;lt;code&amp;gt;Sequence(2025-08-25T00:00:00, 2025-08-27T00:00:00, dateUnit: &#039;D&#039;)&amp;lt;/code&amp;gt;. See [[Date and Time Values]].&lt;br /&gt;
* A new model preference, &#039;&#039;&#039;Time zone&#039;&#039;&#039; ([[Preferences dialog]]; system variable [[System variables#Sys_TimeZone|Sys_TimeZone]], default &amp;lt;code&amp;gt;&#039;Local&#039;&amp;lt;/code&amp;gt;), says which time zone the model&#039;s date-time values are in. Text with an explicit offset is converted into it, and [[Today]]() reports the time in it.&lt;br /&gt;
* A new «timeZone» parameter on [[ParseDate]], [[ParseCSV]], [[Today]] and [[NumberToText]]: &amp;lt;code&amp;gt;&#039;Model&#039;&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;&#039;Local&#039;&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;&#039;UTC&#039;&amp;lt;/code&amp;gt; or a fixed offset such as &amp;lt;code&amp;gt;&#039;+05:30&#039;&amp;lt;/code&amp;gt;.&lt;br /&gt;
* A new date format, &amp;lt;code&amp;gt;&#039;ISO8601&#039;&amp;lt;/code&amp;gt; (equivalent to &amp;lt;code&amp;gt;yyyy-MM-ddTHH:mm:ss.sssZ&amp;lt;/code&amp;gt;), and date template codes &amp;lt;code&amp;gt;Z&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;ZZ&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;ZZZ&amp;lt;/code&amp;gt; for the UTC offset, so timestamps can be sent back to an API: &amp;lt;code&amp;gt;NumberToText(x, dateFormat: &#039;ISO8601&#039;, timeZone: &#039;UTC&#039;)&amp;lt;/code&amp;gt;. See [[Date formats]].&lt;br /&gt;
* &#039;&#039;&#039;Behavior change&#039;&#039;&#039;: a trailing &amp;lt;code&amp;gt;Z&amp;lt;/code&amp;gt; on a parsed date-time, e.g. &amp;lt;code&amp;gt;ParseDate(&#039;2025-08-25T00:07:05Z&#039;)&amp;lt;/code&amp;gt;, was accepted but ignored in 7.0 and 7.1. It now means UTC, so such values shift by your zone&#039;s offset from UTC. A literal &amp;lt;code&amp;gt;Z&amp;lt;/code&amp;gt; in a custom date template must now be quoted (&amp;lt;code&amp;gt;&#039;Z&#039;&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
== New command line options ==&lt;br /&gt;
See [[Analytica Command Line]].&lt;br /&gt;
*; &amp;lt;code&amp;gt;/evalThenExit:«expression»&amp;lt;/code&amp;gt;: Like &amp;lt;code&amp;gt;/eval:&amp;lt;/code&amp;gt;, but Analytica exits as soon as «expression» has finished, without asking whether to save changes. It replaces the &amp;lt;code&amp;gt;/eval:&amp;quot;«expression»;EvaluateScript(&#039;Bye -&#039;)&amp;quot;&amp;lt;/code&amp;gt; idiom that a [[Running a model in a command line workflow|command line workflow]] used to need, and that was easy to forget.&lt;br /&gt;
&lt;br /&gt;
*; &amp;lt;code&amp;gt;/lib:«YourLib.ana»&amp;lt;/code&amp;gt;: Loads an Analytica library into [[SysLib_Customizations]], which exists concurrently with your model (in a different [[Namespace]]). It survives across model closures, so it can appear as new built-in functionality. Useful for [[MCP server in Analytica|MCP Servers]], QA testing, benchmarking, [[Running a model in a command line workflow|batch processing]], GUI extensions, General (model-independent) [[Assista - Analytica AI Assistant/Custom user skills for Assista|Custom Assista skill libraries]], etc.&lt;br /&gt;
&lt;br /&gt;
*; &amp;lt;code&amp;gt;/Automation&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;/AutomationTrace:«file»&amp;lt;/code&amp;gt;: Run in an automation mode so that blocking modal dialogs don&#039;t appear waiting for a user input.&lt;br /&gt;
&lt;br /&gt;
*; &amp;lt;code&amp;gt;/WindowXY&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;/WindowSize&amp;lt;/code&amp;gt;: Explicit control over the initial location of the Analytica application window on your screen.&lt;br /&gt;
&lt;br /&gt;
*; &amp;lt;code&amp;gt;/stores:«path to FileProvider.config»&amp;lt;/code&amp;gt;: Location of a [[Custom file system providers|Custom file system provider]] configuration file.&lt;br /&gt;
&lt;br /&gt;
*; &amp;lt;code&amp;gt;/remote-debugging-port:«port»&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;/remote-allow-origins:«origins»&amp;lt;/code&amp;gt;: Hooks used by Lumina for Assista benchmarking and AI-assisted debugging during GUI development.&lt;br /&gt;
&lt;br /&gt;
*; &amp;lt;code&amp;gt;/Autosave=0&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;/Autosave=1&amp;lt;/code&amp;gt;: Turns the &#039;&#039;Maintain recovery info&#039;&#039; preference off or on for that run only, without changing the stored preference (and &amp;lt;code&amp;gt;/Automation&amp;lt;/code&amp;gt; now implies &amp;lt;code&amp;gt;/Autosave=0&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
== AI Integration ==&lt;br /&gt;
* You can now use an [[MCP server in Analytica|MCP server implemented in desktop Analytica]] from Claude.ai, Claude CoWork and other AI clients that are running in the Cloud (when used in conjunction with a tunnel such as &amp;lt;code&amp;gt;cloudflared&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;ngrok&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
== Evaluation engine ==&lt;br /&gt;
* [[Null]] can be specified to a repeated index parameter, which is a way to specify the implicit dimension (aka the null index). All such repeated index parameters for all built-in functions handle this. This enables &amp;lt;code&amp;gt;...[[IndexesOf]](x)&amp;lt;/code&amp;gt; to work when &amp;lt;code&amp;gt;x&amp;lt;/code&amp;gt; has an implicit dimension without any extra surrounding code. &lt;br /&gt;
* Introduced a new function parameter qualifier, &amp;lt;code&amp;gt;nullIndexOk&amp;lt;/code&amp;gt;, for a non-repeated index parameter. When this is specified, the caller can specify [[Null]] for the index with the meaning that the function should operate over the implicit dimension. &lt;br /&gt;
&lt;br /&gt;
== Misc ==&lt;br /&gt;
* The limit on the maximum number of objects (which was 65500) has been removed. There is no longer any fixed limit.&lt;br /&gt;
* Dynamic calculations now use new attributes, &amp;lt;code&amp;gt;dynValue&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;dynProbValue&amp;lt;/code&amp;gt; for partial results, and the usual &amp;lt;code&amp;gt;Value&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;probValue&amp;lt;/code&amp;gt; only for fully computed results. Previously partial results during a dynamic calculation were tracked in &amp;lt;code&amp;gt;Value&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;probValue&amp;lt;/code&amp;gt;.&lt;br /&gt;
* The identifier &amp;lt;code&amp;gt;Date&amp;lt;/code&amp;gt; is no longer a reserved identifier, and hence can be used as an identifier in your own models. The previous attribute, which encodes &amp;quot;Created date&amp;quot; for models, modules, and libraries, is still present as &amp;lt;code&amp;gt;SysLib_Internal::Date&amp;lt;/code&amp;gt;. Model file format is unchanged by this.&lt;/div&gt;</summary>
		<author><name>Lchrisman</name></author>
	</entry>
	<entry>
		<id>https://docs.analytica.com/index.php?title=Encrypting_and_decrypting&amp;diff=64604</id>
		<title>Encrypting and decrypting</title>
		<link rel="alternate" type="text/html" href="https://docs.analytica.com/index.php?title=Encrypting_and_decrypting&amp;diff=64604"/>
		<updated>2026-09-22T18:08:45Z</updated>

		<summary type="html">&lt;p&gt;Lchrisman: Added &amp;quot;Requires Developer edition&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[category:Encrypting and hashing functions]]&lt;br /&gt;
&lt;br /&gt;
{{ReleaseBar}}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;New to [[Analytica 7.2]].&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Requires [[Analytica Developer]] edition or better.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Analytica can encrypt and decrypt data using strong, standard, &#039;&#039;authenticated&#039;&#039; encryption. The four functions live in the &#039;&#039;&#039;&amp;lt;code&amp;gt;Crypto&amp;lt;/code&amp;gt;&#039;&#039;&#039; namespace:&lt;br /&gt;
&lt;br /&gt;
* [[#EncryptionKey|Crypto::EncryptionKey]] -- makes a key, which is a value of its own data type.&lt;br /&gt;
* [[#Encrypt|Crypto::Encrypt]] -- seals text or binary data under a key.&lt;br /&gt;
* [[#Decrypt|Crypto::Decrypt]] -- opens a sealed value, or reports an error if anything about it has changed.&lt;br /&gt;
* [[#RandomBytes|Crypto::RandomBytes]] -- cryptographically random bytes, for salts and keys.&lt;br /&gt;
&lt;br /&gt;
: &amp;lt;code&amp;gt;[[Variable]] Salt := Crypto::RandomBytes(16)&amp;lt;/code&amp;gt;&lt;br /&gt;
: &amp;lt;code&amp;gt;[[Variable]] MyKey := Crypto::EncryptionKey( password: &#039;correct horse battery staple&#039;, salt: Salt )&amp;lt;/code&amp;gt;&lt;br /&gt;
: &amp;lt;code&amp;gt;[[Variable]] Sealed := Crypto::Encrypt( &#039;launch codes&#039;, MyKey )&amp;lt;/code&amp;gt;&lt;br /&gt;
: &amp;lt;code&amp;gt;[[Variable]] Plain := Crypto::Decrypt( Sealed, MyKey ) &amp;amp;rarr; &#039;launch codes&#039;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== The Crypto namespace ==&lt;br /&gt;
&lt;br /&gt;
These are specialist functions, so they are not in your model&#039;s namespace to begin with. A model that never encrypts anything is not troubled by them, and they cannot collide with your own identifiers. You reach them in either of two ways.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Qualify each call&#039;&#039;&#039; with &amp;lt;code&amp;gt;Crypto::&amp;lt;/code&amp;gt;, as in the example above. Nothing is needed to make this work.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Or import the namespace once.&#039;&#039;&#039; Put &amp;lt;code&amp;gt;Crypto&amp;lt;/code&amp;gt; on a line of its own in your model&#039;s [[NamespaceImports]] attribute, and then write the names bare:&lt;br /&gt;
&lt;br /&gt;
: &amp;lt;code&amp;gt;Encrypt( &#039;launch codes&#039;, MyKey )&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The rest of this page writes &amp;lt;code&amp;gt;Crypto::&amp;lt;/code&amp;gt; explicitly so that each example works whether or not you have imported the namespace.&lt;br /&gt;
&lt;br /&gt;
== What authenticated encryption means ==&lt;br /&gt;
&lt;br /&gt;
All four algorithms here are &#039;&#039;AEAD&#039;&#039; ciphers -- Authenticated Encryption with Associated Data. Along with the encrypted data, a sealed value carries an &#039;&#039;authentication tag&#039;&#039; computed from the key. [[#Decrypt|Crypto::Decrypt]] checks the tag before it returns anything, so a sealed value that has been altered -- even by a single bit, even by someone who cannot read it -- produces an error instead of altered data.&lt;br /&gt;
&lt;br /&gt;
This is what you want, and it is not what simpler schemes give you. Encryption on its own hides data but does not stop an attacker from changing it in ways that change the decrypted result predictably. Nothing on this page lets you do unauthenticated encryption, deliberately.&lt;br /&gt;
&lt;br /&gt;
The error from a failed decryption is deliberately the same whether the key was wrong, the «aad» was wrong, or the data was altered. Telling those cases apart would help an attacker work out a key one guess at a time.&lt;br /&gt;
&lt;br /&gt;
== Keys ==&lt;br /&gt;
&lt;br /&gt;
A key is a value of its own type, made by [[#EncryptionKey|Crypto::EncryptionKey]]. It carries the algorithm along with the key material, so a key and an algorithm can never disagree, and [[#Encrypt|Crypto::Encrypt]] needs no «algorithm» parameter.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Make the key its own variable.&#039;&#039;&#039; Deriving a key from a password takes a noticeable fraction of a second by design (see [[#Passwords and salts|Passwords and salts]]). When the key is a variable, Analytica computes it once and reuses it, so encrypting a whole column of a table costs one derivation, not one per cell. Writing the derivation inline inside [[#Encrypt|Crypto::Encrypt]] would repeat it for every cell.&lt;br /&gt;
&lt;br /&gt;
The key material itself cannot be read from any expression. Printing a key shows only what it is:&lt;br /&gt;
&lt;br /&gt;
: &amp;lt;code&amp;gt;MyKey &amp;amp;rarr; «EncryptionKey AES-256-GCM #7a2f»&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A key has four readable members:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;MyKey-&amp;amp;gt;algorithm&amp;lt;/code&amp;gt; -- &amp;lt;code&amp;gt;&#039;AES-256-GCM&#039;&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;&#039;AES-192-GCM&#039;&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;&#039;AES-128-GCM&#039;&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;&#039;ChaCha20-Poly1305&#039;&amp;lt;/code&amp;gt;.&lt;br /&gt;
* &amp;lt;code&amp;gt;MyKey-&amp;amp;gt;keyBits&amp;lt;/code&amp;gt; -- 128, 192 or 256.&lt;br /&gt;
* &amp;lt;code&amp;gt;MyKey-&amp;amp;gt;fingerprint&amp;lt;/code&amp;gt; -- four hexadecimal digits that identify the key without revealing it. Two keys are the same key if and only if their fingerprints match (to a very high probability). Useful for answering &amp;quot;did this deployment get the key I think it did?&amp;quot; without ever displaying a key.&lt;br /&gt;
* &amp;lt;code&amp;gt;MyKey-&amp;amp;gt;fromSecret&amp;lt;/code&amp;gt; -- [[True]] when the key material came from a [[Secret]].&lt;br /&gt;
&lt;br /&gt;
=== Passwords and salts ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;Crypto::EncryptionKey( password: p, salt: s )&amp;lt;/code&amp;gt; stretches a password into a key using PBKDF2-HMAC-SHA-256 with «iterations» rounds, 600,000 by default. The iterations are the point: they make each guess at your password expensive for an attacker, which is the only defence a human-chosen password has.&lt;br /&gt;
&lt;br /&gt;
The «salt» is &#039;&#039;&#039;not&#039;&#039;&#039; secret. Keep it in an ordinary variable next to the key, and save it with your model -- you need the same salt to derive the same key again. Its job is to make precomputed attack tables useless, which it does even though it is public. Make one with &amp;lt;code&amp;gt;[[#RandomBytes|Crypto::RandomBytes]](16)&amp;lt;/code&amp;gt; and then leave it alone; changing the salt changes the key, and data encrypted under the old key can no longer be read.&lt;br /&gt;
&lt;br /&gt;
=== Keeping the key out of the model ===&lt;br /&gt;
&lt;br /&gt;
If the key is written in the model, anyone who has the model has the key, and the encryption protects nothing. The usual arrangement is the other way round: the &#039;&#039;sealed data&#039;&#039; travels in the &amp;lt;code&amp;gt;.ana&amp;lt;/code&amp;gt; file and the &#039;&#039;key&#039;&#039; does not.&lt;br /&gt;
&lt;br /&gt;
Give «key» or «password» a [[Secret]] to do that. The secret&#039;s value reaches the key derivation without ever entering the model as a value:&lt;br /&gt;
&lt;br /&gt;
: &amp;lt;code&amp;gt;Crypto::EncryptionKey( key: MyKeySecret )&amp;lt;/code&amp;gt;&lt;br /&gt;
: &amp;lt;code&amp;gt;Crypto::EncryptionKey( password: MyPasswordSecret, salt: Salt )&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The [[Secret]] needs two things set before Analytica will allow this:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Sinks&#039;&#039;&#039; must list &amp;lt;code&amp;gt;EncryptionKey&amp;lt;/code&amp;gt;.&lt;br /&gt;
* &#039;&#039;&#039;Destination&#039;&#039;&#039; must be the single word &amp;lt;code&amp;gt;local&amp;lt;/code&amp;gt;. Unlike a URL or a database connection string, key material never leaves the Analytica process, so there is no destination to pin -- but the setting is still required, so that allowing it is a deliberate act.&lt;br /&gt;
&lt;br /&gt;
Consider also setting the secret&#039;s &#039;&#039;&#039;Caller&#039;&#039;&#039; to the module that is allowed to build the key, which restricts the use of the secret far more tightly than any destination could.&lt;br /&gt;
&lt;br /&gt;
A key made from a [[Secret]] refuses &amp;lt;code&amp;gt;-&amp;amp;gt;Export()&amp;lt;/code&amp;gt;, and reads &amp;lt;code&amp;gt;fromSecret = True&amp;lt;/code&amp;gt;. That is deliberate: a secret&#039;s value must never come back into the model, and exporting the key it produced would be exactly that.&lt;br /&gt;
&lt;br /&gt;
=== Generating and saving a key ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;Crypto::EncryptionKey()&amp;lt;/code&amp;gt; with no arguments makes a fresh random key. To use the same key again later, export the material and store it somewhere outside the model -- a [[Secret]], a file, a password manager:&lt;br /&gt;
&lt;br /&gt;
: &amp;lt;code&amp;gt;Crypto::EncryptionKey()-&amp;amp;gt;Export() &amp;amp;rarr; a 32-byte binary value&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To see it as text you can copy, format it:&lt;br /&gt;
&lt;br /&gt;
: &amp;lt;code&amp;gt;f&amp;quot;{Crypto::EncryptionKey()-&amp;amp;gt;Export():b}&amp;quot; &amp;amp;rarr; base64&#039;8Xk2wQ...&#039;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Paste that &amp;lt;code&amp;gt;base64&#039;...&#039;&amp;lt;/code&amp;gt; literal back into an expression to rebuild the same key:&lt;br /&gt;
&lt;br /&gt;
: &amp;lt;code&amp;gt;Crypto::EncryptionKey( key: base64&#039;8Xk2wQ...&#039; )&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;A key cannot be stored in a variable&#039;&#039;&#039; the way other values can. Assigning one to a variable&#039;s definition is refused, because that would write the key material into the &amp;lt;code&amp;gt;.ana&amp;lt;/code&amp;gt; file. Build the key where it is used instead.&lt;br /&gt;
&lt;br /&gt;
== Sealed values ==&lt;br /&gt;
&lt;br /&gt;
[[#Encrypt|Crypto::Encrypt]] returns [[In-memory binary data terms|binary data]] by default. The &amp;lt;code&amp;gt;&#039;analytica&#039;&amp;lt;/code&amp;gt; container adds 36 bytes to whatever you encrypted: a marker that says this is an Analytica sealed value, which algorithm made it, whether the plaintext was text or binary, the nonce, and the authentication tag.&lt;br /&gt;
&lt;br /&gt;
Because the container records whether the plaintext was text or binary, [[#Decrypt|Crypto::Decrypt]] hands back the same kind of value you encrypted, with nothing extra to say.&lt;br /&gt;
&lt;br /&gt;
=== Every call produces a different result ===&lt;br /&gt;
&lt;br /&gt;
Each call draws a fresh random &#039;&#039;nonce&#039;&#039;, so encrypting the same text twice gives two different sealed values. That is required -- reusing a nonce with the same key breaks the encryption completely -- but it has a practical consequence in a model: a sealed value computed by a definition changes every time the definition is re-evaluated.&lt;br /&gt;
&lt;br /&gt;
So do not leave &amp;lt;code&amp;gt;Crypto::Encrypt(...)&amp;lt;/code&amp;gt; in the definition of a variable whose result you intend to keep. Compute the sealed value once, in a button script, and assign it:&lt;br /&gt;
&lt;br /&gt;
: &amp;lt;code&amp;gt;Sealed := Crypto::Encrypt( Plaintext, MyKey )&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The assignment writes the sealed value into &amp;lt;code&amp;gt;Sealed&amp;lt;/code&amp;gt;&#039;s definition as a &amp;lt;code&amp;gt;base64&#039;...&#039;&amp;lt;/code&amp;gt; literal, which is saved with the model and decrypts unchanged when it is reopened.&lt;br /&gt;
&lt;br /&gt;
=== Binding a sealed value to its context ===&lt;br /&gt;
&lt;br /&gt;
The optional «aad» parameter -- additional authenticated data -- is authenticated but not encrypted. Whoever decrypts must supply the same «aad» or the decryption fails.&lt;br /&gt;
&lt;br /&gt;
Use it to tie each sealed value to where it belongs. If you seal a column of salaries with no «aad», someone who cannot read them can still swap two rows and go undetected. Seal them with the employee as «aad» and the swap is caught:&lt;br /&gt;
&lt;br /&gt;
: &amp;lt;code&amp;gt;Crypto::Encrypt( Salary, MyKey, aad: Employee )&amp;lt;/code&amp;gt;&lt;br /&gt;
: &amp;lt;code&amp;gt;Crypto::Decrypt( SealedSalary, MyKey, aad: Employee )&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The «aad» is not secret and does not need to be hidden -- the reader has to know it anyway.&lt;br /&gt;
&lt;br /&gt;
== Encrypting a whole table ==&lt;br /&gt;
&lt;br /&gt;
«data» and «aad» are atomic parameters, so both functions [[Array Abstraction|array abstract]]. One expression seals an entire indexed table, each cell under its own fresh nonce, all under the one key:&lt;br /&gt;
&lt;br /&gt;
: &amp;lt;code&amp;gt;[[Variable]] SealedRows := Crypto::Encrypt( PlainRows, MyKey )&amp;lt;/code&amp;gt;&lt;br /&gt;
: &amp;lt;code&amp;gt;[[Variable]] PlainAgain := Crypto::Decrypt( SealedRows, MyKey )&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is where making the key its own variable pays off: the key is derived once and every cell reuses it.&lt;br /&gt;
&lt;br /&gt;
== Interoperating with other systems ==&lt;br /&gt;
&lt;br /&gt;
To exchange sealed data with something outside Analytica, you need to agree on two things: how the bytes are packaged, and how they are written as text.&lt;br /&gt;
&lt;br /&gt;
=== Layouts ===&lt;br /&gt;
&lt;br /&gt;
There is no single universal convention for packaging a nonce, a ciphertext and an authentication tag, so «layout» lets you pick the one your counterpart uses.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! «layout» !! The sealed value contains !! Used by&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;&#039;analytica&#039;&amp;lt;/code&amp;gt; (default) || A self-describing container: marker, algorithm, text-or-binary flag, nonce, tag, ciphertext || Analytica&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;&#039;nonce,ct,tag&#039;&amp;lt;/code&amp;gt; || nonce, then ciphertext, then tag || The common Go idiom, and most published examples&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;&#039;ct,tag&#039;&amp;lt;/code&amp;gt; || ciphertext, then tag; the nonce is separate || Python&#039;s &amp;lt;code&amp;gt;cryptography&amp;lt;/code&amp;gt; package, libsodium&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;&#039;nonce,tag,ct&#039;&amp;lt;/code&amp;gt; || nonce, then tag, then ciphertext ||&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;&#039;none&#039;&amp;lt;/code&amp;gt; || the bare ciphertext; nonce and tag are separate || Node.js &amp;lt;code&amp;gt;crypto&amp;lt;/code&amp;gt;, .NET &amp;lt;code&amp;gt;AesGcm&amp;lt;/code&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Whatever the layout, the nonce and the tag are &#039;&#039;&#039;also&#039;&#039;&#039; available as the second and third return values, so you can always get at them:&lt;br /&gt;
&lt;br /&gt;
: &amp;lt;code&amp;gt;[[Local]] (sealed, nonce, tag) := Crypto::Encrypt( x, MyKey );&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
and supply them again when decrypting a layout that does not carry them:&lt;br /&gt;
&lt;br /&gt;
: &amp;lt;code&amp;gt;Crypto::Decrypt( ct, MyKey, nonce: n, tag: t, layout: &#039;none&#039;, as: &#039;text&#039; )&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The raw layouts carry no record of whether the plaintext was text or binary, so «as» is required with them.&lt;br /&gt;
&lt;br /&gt;
=== Text encodings ===&lt;br /&gt;
&lt;br /&gt;
«format» writes the sealed value as text instead of binary data -- &amp;lt;code&amp;gt;&#039;base64&#039;&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;&#039;base64url&#039;&amp;lt;/code&amp;gt; (the URL-safe alphabet, no padding) or &amp;lt;code&amp;gt;&#039;hex&#039;&amp;lt;/code&amp;gt;. [[#Decrypt|Crypto::Decrypt]] reads text in the same encodings.&lt;br /&gt;
&lt;br /&gt;
: &amp;lt;code&amp;gt;Crypto::Encrypt( &#039;hello&#039;, MyKey, format: &#039;base64&#039; )&amp;lt;/code&amp;gt;&lt;br /&gt;
: &amp;lt;code&amp;gt;Crypto::Decrypt( sealedText, MyKey, format: &#039;base64&#039; )&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== A worked example ===&lt;br /&gt;
&lt;br /&gt;
This Python produces a value Analytica reads, and reads a value Analytica produced. It uses the &amp;lt;code&amp;gt;cryptography&amp;lt;/code&amp;gt; package.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
from cryptography.hazmat.primitives.ciphers.aead import AESGCM&lt;br /&gt;
import base64&lt;br /&gt;
&lt;br /&gt;
key   = bytes(range(32))        # the same 32 bytes as base64&#039;AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8=&#039;&lt;br /&gt;
nonce = bytes(range(12))&lt;br /&gt;
&lt;br /&gt;
# Seal something for Analytica&lt;br /&gt;
ct = AESGCM(key).encrypt(nonce, &#039;from python&#039;.encode(&#039;utf-8&#039;), b&#039;ctx7&#039;)&lt;br /&gt;
print(base64.b64encode(nonce + ct).decode())&lt;br /&gt;
&lt;br /&gt;
# Open something Analytica sealed with layout:&#039;ct,tag&#039;&lt;br /&gt;
plain = AESGCM(key).decrypt(nonce, bytes.fromhex(&#039;66d9d9b2da0e0c4679f3a82524f5e0499271e16f30&#039;), None)&lt;br /&gt;
print(plain.decode(&#039;utf-8&#039;))    # -&amp;gt; hello&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In Analytica:&lt;br /&gt;
&lt;br /&gt;
: &amp;lt;code&amp;gt;[[Variable]] K := Crypto::EncryptionKey( key: base64&#039;AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8=&#039; )&amp;lt;/code&amp;gt;&lt;br /&gt;
: &amp;lt;code&amp;gt;Crypto::Decrypt( base64&#039;AAECAwQFBgcICQoLIXC5duWVu2/lLvmrU3Xrc1+3jRqJx0hC/3QKjDlK2w==&#039;, K, layout: &#039;nonce,ct,tag&#039;, aad: &#039;ctx7&#039;, as: &#039;text&#039; ) &amp;amp;rarr; &#039;from python&#039;&amp;lt;/code&amp;gt;&lt;br /&gt;
: &amp;lt;code&amp;gt;Crypto::Encrypt( &#039;hello&#039;, K, nonce: base64&#039;AAAAAAAAAAAAAAAA&#039;, layout: &#039;ct,tag&#039;, format: &#039;hex&#039; ) &amp;amp;rarr; &#039;66d9d9b2da0e0c4679f3a82524f5e0499271e16f30&#039;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The «nonce» parameter is used here only to make the output reproducible so it can be checked against a published test vector. &#039;&#039;&#039;Do not supply a nonce in real use.&#039;&#039;&#039; Reusing a nonce with the same key destroys the security of the encryption completely; omitting it is always the right thing.&lt;br /&gt;
&lt;br /&gt;
=== The &#039;analytica&#039; container ===&lt;br /&gt;
&lt;br /&gt;
If you need to read Analytica&#039;s own container from another language, its bytes are:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Offset !! Length !! Field&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 4 || The four characters &amp;lt;code&amp;gt;ACRY&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| 4 || 1 || Format version, currently 1&lt;br /&gt;
|-&lt;br /&gt;
| 5 || 1 || Algorithm: 1 = AES-128-GCM, 2 = AES-192-GCM, 3 = AES-256-GCM, 4 = ChaCha20-Poly1305&lt;br /&gt;
|-&lt;br /&gt;
| 6 || 1 || Flags. Bit 0 set means the plaintext was text, encoded as UTF-8. Bit 1 set means an «aad» was supplied.&lt;br /&gt;
|-&lt;br /&gt;
| 7 || 1 || Nonce length, 12 for every algorithm above&lt;br /&gt;
|-&lt;br /&gt;
| 8 || 12 || Nonce&lt;br /&gt;
|-&lt;br /&gt;
| 20 || 16 || Authentication tag&lt;br /&gt;
|-&lt;br /&gt;
| 36 || rest || Ciphertext&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The salt and the iteration count are &#039;&#039;&#039;not&#039;&#039;&#039; in the container. They belong to the key, which you construct explicitly, so keep the salt in a variable of your own next to the key that uses it.&lt;br /&gt;
&lt;br /&gt;
= Function reference =&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;EncryptionKey&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Crypto::EncryptionKey( &#039;&#039;algorithm, key, keyFormat, password, salt, iterations, kdf&#039;&#039; ) ==&lt;br /&gt;
&#039;&#039;Requires [[Analytica Developer]] edition or better.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Creates a key for [[#Encrypt|Crypto::Encrypt]] and [[#Decrypt|Crypto::Decrypt]]. Give it either «key» or «password», or neither for a fresh random key.&lt;br /&gt;
&lt;br /&gt;
=== Parameters ===&lt;br /&gt;
&lt;br /&gt;
* «algorithm»: (optional, default &amp;lt;code&amp;gt;&#039;AES-256-GCM&#039;&amp;lt;/code&amp;gt;) One of &amp;lt;code&amp;gt;&#039;AES-256-GCM&#039;&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;&#039;AES-192-GCM&#039;&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;&#039;AES-128-GCM&#039;&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;&#039;ChaCha20-Poly1305&#039;&amp;lt;/code&amp;gt;. ChaCha20-Poly1305 requires Windows 10 version 1903 or later; on an older system it reports that it is unavailable rather than failing obscurely.&lt;br /&gt;
&lt;br /&gt;
* «key»: (optional) Raw key material, as [[In-memory binary data terms|binary data]] -- usually a &amp;lt;code&amp;gt;base64&#039;...&#039;&amp;lt;/code&amp;gt; literal. It must be exactly the algorithm&#039;s key length: 32 bytes for AES-256-GCM and ChaCha20-Poly1305, 24 for AES-192-GCM, 16 for AES-128-GCM. A key of the wrong length is an error; Analytica will not pad or hash it to fit, because that would quietly weaken it. You may also pass a [[Secret]] here, in which case the secret&#039;s text is decoded according to «keyFormat». Plain text that is not a [[Secret]] is refused -- use «password» for a passphrase.&lt;br /&gt;
&lt;br /&gt;
* «keyFormat»: (optional, default &amp;lt;code&amp;gt;&#039;base64&#039;&amp;lt;/code&amp;gt;) How to read the text a [[Secret]] holds: &amp;lt;code&amp;gt;&#039;base64&#039;&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;&#039;base64url&#039;&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;&#039;hex&#039;&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;&#039;utf8&#039;&amp;lt;/code&amp;gt;. Meaningful only when «key» is given a [[Secret]].&lt;br /&gt;
&lt;br /&gt;
* «password»: (optional) A passphrase to derive the key from. Requires «salt». May be given a [[Secret]].&lt;br /&gt;
&lt;br /&gt;
* «salt»: (optional) Text or [[In-memory binary data terms|binary data]], at least 8 bytes, required whenever «password» is given. Not secret; see [[#Passwords and salts|Passwords and salts]].&lt;br /&gt;
&lt;br /&gt;
* «iterations»: (optional, default 600000) PBKDF2 rounds. More is slower and safer. Use the same number every time, or you get a different key.&lt;br /&gt;
&lt;br /&gt;
* «kdf»: (optional, default &amp;lt;code&amp;gt;&#039;PBKDF2-HMAC-SHA-256&#039;&amp;lt;/code&amp;gt;) The key-derivation function. This release supports only that one; the parameter exists so that adding others later does not change how you write the call.&lt;br /&gt;
&lt;br /&gt;
A parameter that could not take effect is refused rather than quietly ignored -- «iterations» without «password», for example.&lt;br /&gt;
&lt;br /&gt;
=== Members ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;-&amp;amp;gt;algorithm&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;-&amp;amp;gt;keyBits&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;-&amp;amp;gt;fingerprint&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;-&amp;amp;gt;fromSecret&amp;lt;/code&amp;gt;, described under [[#Keys|Keys]]. There is no member that returns the key material.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;Export&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== key-&amp;gt;Export( ) ==&lt;br /&gt;
&lt;br /&gt;
Returns the raw key material of a key as [[In-memory binary data terms|binary data]]. This is the only way to get key material back out, and it is meant for saving a randomly generated key so the same key can be used again.&lt;br /&gt;
&lt;br /&gt;
: &amp;lt;code&amp;gt;MyKey-&amp;amp;gt;Export() &amp;amp;rarr; a 32-byte binary value&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A key derived from a [[Secret]] cannot be exported, and reports an error. A secret&#039;s value is never allowed back into the model.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;Encrypt&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Crypto::Encrypt( data, key&#039;&#039;, aad, nonce, format, layout&#039;&#039; ) ==&lt;br /&gt;
&#039;&#039;Requires [[Analytica Developer]] edition or better.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Encrypts «data» under «key» and returns a sealed value that only the same key can open. Returns [[Null]] when «data» is [[Null]].&lt;br /&gt;
&lt;br /&gt;
=== Parameters ===&lt;br /&gt;
&lt;br /&gt;
* «data»: Text or [[In-memory binary data terms|binary data]]. A number is refused rather than converted, since &amp;lt;code&amp;gt;42&amp;lt;/code&amp;gt; could reasonably mean either the text &amp;lt;code&amp;gt;&#039;42&#039;&amp;lt;/code&amp;gt; or eight bytes -- use &amp;lt;code&amp;gt;[[Text]](x)&amp;lt;/code&amp;gt; if you mean its textual form.&lt;br /&gt;
&lt;br /&gt;
* «key»: A key from [[#EncryptionKey|Crypto::EncryptionKey]]. The algorithm comes from the key.&lt;br /&gt;
&lt;br /&gt;
* «aad»: (optional) Additional authenticated data -- authenticated but not encrypted. [[#Decrypt|Crypto::Decrypt]] must be given the same value. See [[#Binding a sealed value to its context|Binding a sealed value to its context]].&lt;br /&gt;
&lt;br /&gt;
* «nonce»: (optional, named) Supplies the nonce instead of generating one. &#039;&#039;&#039;Reusing a nonce with the same key destroys the security of the encryption completely.&#039;&#039;&#039; Omit this unless you are reproducing a published test vector or another system&#039;s exact output.&lt;br /&gt;
&lt;br /&gt;
* «format»: (optional, named, default &amp;lt;code&amp;gt;&#039;binary&#039;&amp;lt;/code&amp;gt;) &amp;lt;code&amp;gt;&#039;binary&#039;&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;&#039;base64&#039;&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;&#039;base64url&#039;&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;&#039;hex&#039;&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
* «layout»: (optional, named, default &amp;lt;code&amp;gt;&#039;analytica&#039;&amp;lt;/code&amp;gt;) See [[#Layouts|Layouts]].&lt;br /&gt;
&lt;br /&gt;
=== Return value ===&lt;br /&gt;
&lt;br /&gt;
The sealed value, as [[In-memory binary data terms|binary data]] or as text according to «format». The nonce and the authentication tag are returned as the second and third return values:&lt;br /&gt;
&lt;br /&gt;
: &amp;lt;code&amp;gt;[[Local]] (sealed, nonce, tag) := Crypto::Encrypt( x, MyKey );&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
It [[Array Abstraction|array abstracts]] over «data» and «aad», sealing each cell under its own nonce.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;Decrypt&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Crypto::Decrypt( data, key&#039;&#039;, aad, nonce, tag, format, layout, as&#039;&#039; ) ==&lt;br /&gt;
&#039;&#039;Requires [[Analytica Developer]] edition or better.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Opens a value sealed by [[#Encrypt|Crypto::Encrypt]]. Returns [[Null]] when «data» is [[Null]].&lt;br /&gt;
&lt;br /&gt;
Reports an error if the value cannot be decrypted -- wrong key, wrong «aad», or data that has been altered. The message deliberately does not say which, because that distinction would help an attacker. Use [[Try]] if your model should carry on regardless.&lt;br /&gt;
&lt;br /&gt;
=== Parameters ===&lt;br /&gt;
&lt;br /&gt;
* «data»: The sealed value, as [[In-memory binary data terms|binary data]], or as text in the encoding named by «format».&lt;br /&gt;
&lt;br /&gt;
* «key»: The same key the value was sealed with. If it is a key for a different algorithm, that is reported specifically, since it is a mistake you can act on rather than a failed decryption.&lt;br /&gt;
&lt;br /&gt;
* «aad»: (optional) Must equal the «aad» the value was sealed with.&lt;br /&gt;
&lt;br /&gt;
* «nonce», «tag»: (optional, named) Required for a «layout» that does not carry them.&lt;br /&gt;
&lt;br /&gt;
* «format»: (optional, named, default &amp;lt;code&amp;gt;&#039;base64&#039;&amp;lt;/code&amp;gt;) How to read a textual «data»: &amp;lt;code&amp;gt;&#039;base64&#039;&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;&#039;base64url&#039;&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;&#039;hex&#039;&amp;lt;/code&amp;gt;. Ignored when «data» is binary.&lt;br /&gt;
&lt;br /&gt;
* «layout»: (optional, named, default &amp;lt;code&amp;gt;&#039;analytica&#039;&amp;lt;/code&amp;gt;) Must match the layout the value was sealed with. See [[#Layouts|Layouts]].&lt;br /&gt;
&lt;br /&gt;
* «as»: (optional, named) &amp;lt;code&amp;gt;&#039;text&#039;&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;&#039;binaryData&#039;&amp;lt;/code&amp;gt;. With the default &amp;lt;code&amp;gt;&#039;analytica&#039;&amp;lt;/code&amp;gt; layout it is optional, because the sealed value records which one was encrypted. For every other layout it is required. &amp;lt;code&amp;gt;&#039;text&#039;&amp;lt;/code&amp;gt; reports an error if the decrypted bytes are not valid UTF-8, which is usually a sign that you wanted &amp;lt;code&amp;gt;&#039;binaryData&#039;&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;RandomBytes&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Crypto::RandomBytes( n ) ==&lt;br /&gt;
&lt;br /&gt;
Returns «n» cryptographically random bytes as [[In-memory binary data terms|binary data]]. Use it for a «salt», or for raw key material.&lt;br /&gt;
&lt;br /&gt;
: &amp;lt;code&amp;gt;[[Variable]] Salt := Crypto::RandomBytes(16)&amp;lt;/code&amp;gt;&lt;br /&gt;
: &amp;lt;code&amp;gt;[[Variable]] NewKey := Crypto::EncryptionKey( key: Crypto::RandomBytes(32) )&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
«n» may not exceed 65536.&lt;br /&gt;
&lt;br /&gt;
Each evaluation returns different bytes. Like [[#Encrypt|Crypto::Encrypt]], this means a definition that calls it does not hold still across re-evaluations -- assign the result once if you need it to stay the same.&lt;br /&gt;
&lt;br /&gt;
== Errors ==&lt;br /&gt;
&lt;br /&gt;
Most mistakes are reported specifically, and can be caught with [[Try]]:&lt;br /&gt;
&lt;br /&gt;
* The «key» is the wrong length for the algorithm, or «key» was given plain text rather than key material or a [[Secret]].&lt;br /&gt;
* Both «key» and «password» were given, or «password» was given without a «salt», or a parameter such as «iterations» cannot take effect.&lt;br /&gt;
* The algorithm is not one of the four, or is not available on this computer.&lt;br /&gt;
* «data» is a number rather than text or binary data.&lt;br /&gt;
* The sealed value is not an Analytica sealed value, or was sealed with a different algorithm, or the text is not valid for the stated «format».&lt;br /&gt;
* A [[Secret]] was passed where a secret makes no sense, such as «aad» or «salt», neither of which is secret.&lt;br /&gt;
* &amp;lt;code&amp;gt;-&amp;amp;gt;Export()&amp;lt;/code&amp;gt; was called on a key derived from a [[Secret]].&lt;br /&gt;
&lt;br /&gt;
The one deliberately vague error is a failed decryption, described under [[#Decrypt|Crypto::Decrypt]].&lt;br /&gt;
&lt;br /&gt;
== See Also ==&lt;br /&gt;
&lt;br /&gt;
* [[Secret]] -- keeping a key out of the model&lt;br /&gt;
* [[TextCharacterEncode]] -- hashing text with SHA-1 or SHA-256, and other text encodings&lt;br /&gt;
* [[In-memory binary data terms]]&lt;br /&gt;
* [[NamespaceImports]]&lt;br /&gt;
* [[:category:Encrypting and hashing functions|Encrypting and hashing functions]]&lt;/div&gt;</summary>
		<author><name>Lchrisman</name></author>
	</entry>
	<entry>
		<id>https://docs.analytica.com/index.php?title=Encrypting_and_decrypting&amp;diff=64603</id>
		<title>Encrypting and decrypting</title>
		<link rel="alternate" type="text/html" href="https://docs.analytica.com/index.php?title=Encrypting_and_decrypting&amp;diff=64603"/>
		<updated>2026-09-22T17:58:17Z</updated>

		<summary type="html">&lt;p&gt;Lchrisman: /* Function reference */  italicized optional params&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[category:Encrypting and hashing functions]]&lt;br /&gt;
&lt;br /&gt;
{{ReleaseBar}}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;New to [[Analytica 7.2]].&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Analytica can encrypt and decrypt data using strong, standard, &#039;&#039;authenticated&#039;&#039; encryption. The four functions live in the &#039;&#039;&#039;&amp;lt;code&amp;gt;Crypto&amp;lt;/code&amp;gt;&#039;&#039;&#039; namespace:&lt;br /&gt;
&lt;br /&gt;
* [[#EncryptionKey|Crypto::EncryptionKey]] -- makes a key, which is a value of its own data type.&lt;br /&gt;
* [[#Encrypt|Crypto::Encrypt]] -- seals text or binary data under a key.&lt;br /&gt;
* [[#Decrypt|Crypto::Decrypt]] -- opens a sealed value, or reports an error if anything about it has changed.&lt;br /&gt;
* [[#RandomBytes|Crypto::RandomBytes]] -- cryptographically random bytes, for salts and keys.&lt;br /&gt;
&lt;br /&gt;
: &amp;lt;code&amp;gt;[[Variable]] Salt := Crypto::RandomBytes(16)&amp;lt;/code&amp;gt;&lt;br /&gt;
: &amp;lt;code&amp;gt;[[Variable]] MyKey := Crypto::EncryptionKey( password: &#039;correct horse battery staple&#039;, salt: Salt )&amp;lt;/code&amp;gt;&lt;br /&gt;
: &amp;lt;code&amp;gt;[[Variable]] Sealed := Crypto::Encrypt( &#039;launch codes&#039;, MyKey )&amp;lt;/code&amp;gt;&lt;br /&gt;
: &amp;lt;code&amp;gt;[[Variable]] Plain := Crypto::Decrypt( Sealed, MyKey ) &amp;amp;rarr; &#039;launch codes&#039;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== The Crypto namespace ==&lt;br /&gt;
&lt;br /&gt;
These are specialist functions, so they are not in your model&#039;s namespace to begin with. A model that never encrypts anything is not troubled by them, and they cannot collide with your own identifiers. You reach them in either of two ways.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Qualify each call&#039;&#039;&#039; with &amp;lt;code&amp;gt;Crypto::&amp;lt;/code&amp;gt;, as in the example above. Nothing is needed to make this work.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Or import the namespace once.&#039;&#039;&#039; Put &amp;lt;code&amp;gt;Crypto&amp;lt;/code&amp;gt; on a line of its own in your model&#039;s [[NamespaceImports]] attribute, and then write the names bare:&lt;br /&gt;
&lt;br /&gt;
: &amp;lt;code&amp;gt;Encrypt( &#039;launch codes&#039;, MyKey )&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The rest of this page writes &amp;lt;code&amp;gt;Crypto::&amp;lt;/code&amp;gt; explicitly so that each example works whether or not you have imported the namespace.&lt;br /&gt;
&lt;br /&gt;
== What authenticated encryption means ==&lt;br /&gt;
&lt;br /&gt;
All four algorithms here are &#039;&#039;AEAD&#039;&#039; ciphers -- Authenticated Encryption with Associated Data. Along with the encrypted data, a sealed value carries an &#039;&#039;authentication tag&#039;&#039; computed from the key. [[#Decrypt|Crypto::Decrypt]] checks the tag before it returns anything, so a sealed value that has been altered -- even by a single bit, even by someone who cannot read it -- produces an error instead of altered data.&lt;br /&gt;
&lt;br /&gt;
This is what you want, and it is not what simpler schemes give you. Encryption on its own hides data but does not stop an attacker from changing it in ways that change the decrypted result predictably. Nothing on this page lets you do unauthenticated encryption, deliberately.&lt;br /&gt;
&lt;br /&gt;
The error from a failed decryption is deliberately the same whether the key was wrong, the «aad» was wrong, or the data was altered. Telling those cases apart would help an attacker work out a key one guess at a time.&lt;br /&gt;
&lt;br /&gt;
== Keys ==&lt;br /&gt;
&lt;br /&gt;
A key is a value of its own type, made by [[#EncryptionKey|Crypto::EncryptionKey]]. It carries the algorithm along with the key material, so a key and an algorithm can never disagree, and [[#Encrypt|Crypto::Encrypt]] needs no «algorithm» parameter.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Make the key its own variable.&#039;&#039;&#039; Deriving a key from a password takes a noticeable fraction of a second by design (see [[#Passwords and salts|Passwords and salts]]). When the key is a variable, Analytica computes it once and reuses it, so encrypting a whole column of a table costs one derivation, not one per cell. Writing the derivation inline inside [[#Encrypt|Crypto::Encrypt]] would repeat it for every cell.&lt;br /&gt;
&lt;br /&gt;
The key material itself cannot be read from any expression. Printing a key shows only what it is:&lt;br /&gt;
&lt;br /&gt;
: &amp;lt;code&amp;gt;MyKey &amp;amp;rarr; «EncryptionKey AES-256-GCM #7a2f»&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A key has four readable members:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;MyKey-&amp;amp;gt;algorithm&amp;lt;/code&amp;gt; -- &amp;lt;code&amp;gt;&#039;AES-256-GCM&#039;&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;&#039;AES-192-GCM&#039;&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;&#039;AES-128-GCM&#039;&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;&#039;ChaCha20-Poly1305&#039;&amp;lt;/code&amp;gt;.&lt;br /&gt;
* &amp;lt;code&amp;gt;MyKey-&amp;amp;gt;keyBits&amp;lt;/code&amp;gt; -- 128, 192 or 256.&lt;br /&gt;
* &amp;lt;code&amp;gt;MyKey-&amp;amp;gt;fingerprint&amp;lt;/code&amp;gt; -- four hexadecimal digits that identify the key without revealing it. Two keys are the same key if and only if their fingerprints match (to a very high probability). Useful for answering &amp;quot;did this deployment get the key I think it did?&amp;quot; without ever displaying a key.&lt;br /&gt;
* &amp;lt;code&amp;gt;MyKey-&amp;amp;gt;fromSecret&amp;lt;/code&amp;gt; -- [[True]] when the key material came from a [[Secret]].&lt;br /&gt;
&lt;br /&gt;
=== Passwords and salts ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;Crypto::EncryptionKey( password: p, salt: s )&amp;lt;/code&amp;gt; stretches a password into a key using PBKDF2-HMAC-SHA-256 with «iterations» rounds, 600,000 by default. The iterations are the point: they make each guess at your password expensive for an attacker, which is the only defence a human-chosen password has.&lt;br /&gt;
&lt;br /&gt;
The «salt» is &#039;&#039;&#039;not&#039;&#039;&#039; secret. Keep it in an ordinary variable next to the key, and save it with your model -- you need the same salt to derive the same key again. Its job is to make precomputed attack tables useless, which it does even though it is public. Make one with &amp;lt;code&amp;gt;[[#RandomBytes|Crypto::RandomBytes]](16)&amp;lt;/code&amp;gt; and then leave it alone; changing the salt changes the key, and data encrypted under the old key can no longer be read.&lt;br /&gt;
&lt;br /&gt;
=== Keeping the key out of the model ===&lt;br /&gt;
&lt;br /&gt;
If the key is written in the model, anyone who has the model has the key, and the encryption protects nothing. The usual arrangement is the other way round: the &#039;&#039;sealed data&#039;&#039; travels in the &amp;lt;code&amp;gt;.ana&amp;lt;/code&amp;gt; file and the &#039;&#039;key&#039;&#039; does not.&lt;br /&gt;
&lt;br /&gt;
Give «key» or «password» a [[Secret]] to do that. The secret&#039;s value reaches the key derivation without ever entering the model as a value:&lt;br /&gt;
&lt;br /&gt;
: &amp;lt;code&amp;gt;Crypto::EncryptionKey( key: MyKeySecret )&amp;lt;/code&amp;gt;&lt;br /&gt;
: &amp;lt;code&amp;gt;Crypto::EncryptionKey( password: MyPasswordSecret, salt: Salt )&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The [[Secret]] needs two things set before Analytica will allow this:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Sinks&#039;&#039;&#039; must list &amp;lt;code&amp;gt;EncryptionKey&amp;lt;/code&amp;gt;.&lt;br /&gt;
* &#039;&#039;&#039;Destination&#039;&#039;&#039; must be the single word &amp;lt;code&amp;gt;local&amp;lt;/code&amp;gt;. Unlike a URL or a database connection string, key material never leaves the Analytica process, so there is no destination to pin -- but the setting is still required, so that allowing it is a deliberate act.&lt;br /&gt;
&lt;br /&gt;
Consider also setting the secret&#039;s &#039;&#039;&#039;Caller&#039;&#039;&#039; to the module that is allowed to build the key, which restricts the use of the secret far more tightly than any destination could.&lt;br /&gt;
&lt;br /&gt;
A key made from a [[Secret]] refuses &amp;lt;code&amp;gt;-&amp;amp;gt;Export()&amp;lt;/code&amp;gt;, and reads &amp;lt;code&amp;gt;fromSecret = True&amp;lt;/code&amp;gt;. That is deliberate: a secret&#039;s value must never come back into the model, and exporting the key it produced would be exactly that.&lt;br /&gt;
&lt;br /&gt;
=== Generating and saving a key ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;Crypto::EncryptionKey()&amp;lt;/code&amp;gt; with no arguments makes a fresh random key. To use the same key again later, export the material and store it somewhere outside the model -- a [[Secret]], a file, a password manager:&lt;br /&gt;
&lt;br /&gt;
: &amp;lt;code&amp;gt;Crypto::EncryptionKey()-&amp;amp;gt;Export() &amp;amp;rarr; a 32-byte binary value&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To see it as text you can copy, format it:&lt;br /&gt;
&lt;br /&gt;
: &amp;lt;code&amp;gt;f&amp;quot;{Crypto::EncryptionKey()-&amp;amp;gt;Export():b}&amp;quot; &amp;amp;rarr; base64&#039;8Xk2wQ...&#039;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Paste that &amp;lt;code&amp;gt;base64&#039;...&#039;&amp;lt;/code&amp;gt; literal back into an expression to rebuild the same key:&lt;br /&gt;
&lt;br /&gt;
: &amp;lt;code&amp;gt;Crypto::EncryptionKey( key: base64&#039;8Xk2wQ...&#039; )&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;A key cannot be stored in a variable&#039;&#039;&#039; the way other values can. Assigning one to a variable&#039;s definition is refused, because that would write the key material into the &amp;lt;code&amp;gt;.ana&amp;lt;/code&amp;gt; file. Build the key where it is used instead.&lt;br /&gt;
&lt;br /&gt;
== Sealed values ==&lt;br /&gt;
&lt;br /&gt;
[[#Encrypt|Crypto::Encrypt]] returns [[In-memory binary data terms|binary data]] by default. The &amp;lt;code&amp;gt;&#039;analytica&#039;&amp;lt;/code&amp;gt; container adds 36 bytes to whatever you encrypted: a marker that says this is an Analytica sealed value, which algorithm made it, whether the plaintext was text or binary, the nonce, and the authentication tag.&lt;br /&gt;
&lt;br /&gt;
Because the container records whether the plaintext was text or binary, [[#Decrypt|Crypto::Decrypt]] hands back the same kind of value you encrypted, with nothing extra to say.&lt;br /&gt;
&lt;br /&gt;
=== Every call produces a different result ===&lt;br /&gt;
&lt;br /&gt;
Each call draws a fresh random &#039;&#039;nonce&#039;&#039;, so encrypting the same text twice gives two different sealed values. That is required -- reusing a nonce with the same key breaks the encryption completely -- but it has a practical consequence in a model: a sealed value computed by a definition changes every time the definition is re-evaluated.&lt;br /&gt;
&lt;br /&gt;
So do not leave &amp;lt;code&amp;gt;Crypto::Encrypt(...)&amp;lt;/code&amp;gt; in the definition of a variable whose result you intend to keep. Compute the sealed value once, in a button script, and assign it:&lt;br /&gt;
&lt;br /&gt;
: &amp;lt;code&amp;gt;Sealed := Crypto::Encrypt( Plaintext, MyKey )&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The assignment writes the sealed value into &amp;lt;code&amp;gt;Sealed&amp;lt;/code&amp;gt;&#039;s definition as a &amp;lt;code&amp;gt;base64&#039;...&#039;&amp;lt;/code&amp;gt; literal, which is saved with the model and decrypts unchanged when it is reopened.&lt;br /&gt;
&lt;br /&gt;
=== Binding a sealed value to its context ===&lt;br /&gt;
&lt;br /&gt;
The optional «aad» parameter -- additional authenticated data -- is authenticated but not encrypted. Whoever decrypts must supply the same «aad» or the decryption fails.&lt;br /&gt;
&lt;br /&gt;
Use it to tie each sealed value to where it belongs. If you seal a column of salaries with no «aad», someone who cannot read them can still swap two rows and go undetected. Seal them with the employee as «aad» and the swap is caught:&lt;br /&gt;
&lt;br /&gt;
: &amp;lt;code&amp;gt;Crypto::Encrypt( Salary, MyKey, aad: Employee )&amp;lt;/code&amp;gt;&lt;br /&gt;
: &amp;lt;code&amp;gt;Crypto::Decrypt( SealedSalary, MyKey, aad: Employee )&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The «aad» is not secret and does not need to be hidden -- the reader has to know it anyway.&lt;br /&gt;
&lt;br /&gt;
== Encrypting a whole table ==&lt;br /&gt;
&lt;br /&gt;
«data» and «aad» are atomic parameters, so both functions [[Array Abstraction|array abstract]]. One expression seals an entire indexed table, each cell under its own fresh nonce, all under the one key:&lt;br /&gt;
&lt;br /&gt;
: &amp;lt;code&amp;gt;[[Variable]] SealedRows := Crypto::Encrypt( PlainRows, MyKey )&amp;lt;/code&amp;gt;&lt;br /&gt;
: &amp;lt;code&amp;gt;[[Variable]] PlainAgain := Crypto::Decrypt( SealedRows, MyKey )&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is where making the key its own variable pays off: the key is derived once and every cell reuses it.&lt;br /&gt;
&lt;br /&gt;
== Interoperating with other systems ==&lt;br /&gt;
&lt;br /&gt;
To exchange sealed data with something outside Analytica, you need to agree on two things: how the bytes are packaged, and how they are written as text.&lt;br /&gt;
&lt;br /&gt;
=== Layouts ===&lt;br /&gt;
&lt;br /&gt;
There is no single universal convention for packaging a nonce, a ciphertext and an authentication tag, so «layout» lets you pick the one your counterpart uses.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! «layout» !! The sealed value contains !! Used by&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;&#039;analytica&#039;&amp;lt;/code&amp;gt; (default) || A self-describing container: marker, algorithm, text-or-binary flag, nonce, tag, ciphertext || Analytica&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;&#039;nonce,ct,tag&#039;&amp;lt;/code&amp;gt; || nonce, then ciphertext, then tag || The common Go idiom, and most published examples&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;&#039;ct,tag&#039;&amp;lt;/code&amp;gt; || ciphertext, then tag; the nonce is separate || Python&#039;s &amp;lt;code&amp;gt;cryptography&amp;lt;/code&amp;gt; package, libsodium&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;&#039;nonce,tag,ct&#039;&amp;lt;/code&amp;gt; || nonce, then tag, then ciphertext ||&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;&#039;none&#039;&amp;lt;/code&amp;gt; || the bare ciphertext; nonce and tag are separate || Node.js &amp;lt;code&amp;gt;crypto&amp;lt;/code&amp;gt;, .NET &amp;lt;code&amp;gt;AesGcm&amp;lt;/code&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Whatever the layout, the nonce and the tag are &#039;&#039;&#039;also&#039;&#039;&#039; available as the second and third return values, so you can always get at them:&lt;br /&gt;
&lt;br /&gt;
: &amp;lt;code&amp;gt;[[Local]] (sealed, nonce, tag) := Crypto::Encrypt( x, MyKey );&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
and supply them again when decrypting a layout that does not carry them:&lt;br /&gt;
&lt;br /&gt;
: &amp;lt;code&amp;gt;Crypto::Decrypt( ct, MyKey, nonce: n, tag: t, layout: &#039;none&#039;, as: &#039;text&#039; )&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The raw layouts carry no record of whether the plaintext was text or binary, so «as» is required with them.&lt;br /&gt;
&lt;br /&gt;
=== Text encodings ===&lt;br /&gt;
&lt;br /&gt;
«format» writes the sealed value as text instead of binary data -- &amp;lt;code&amp;gt;&#039;base64&#039;&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;&#039;base64url&#039;&amp;lt;/code&amp;gt; (the URL-safe alphabet, no padding) or &amp;lt;code&amp;gt;&#039;hex&#039;&amp;lt;/code&amp;gt;. [[#Decrypt|Crypto::Decrypt]] reads text in the same encodings.&lt;br /&gt;
&lt;br /&gt;
: &amp;lt;code&amp;gt;Crypto::Encrypt( &#039;hello&#039;, MyKey, format: &#039;base64&#039; )&amp;lt;/code&amp;gt;&lt;br /&gt;
: &amp;lt;code&amp;gt;Crypto::Decrypt( sealedText, MyKey, format: &#039;base64&#039; )&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== A worked example ===&lt;br /&gt;
&lt;br /&gt;
This Python produces a value Analytica reads, and reads a value Analytica produced. It uses the &amp;lt;code&amp;gt;cryptography&amp;lt;/code&amp;gt; package.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
from cryptography.hazmat.primitives.ciphers.aead import AESGCM&lt;br /&gt;
import base64&lt;br /&gt;
&lt;br /&gt;
key   = bytes(range(32))        # the same 32 bytes as base64&#039;AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8=&#039;&lt;br /&gt;
nonce = bytes(range(12))&lt;br /&gt;
&lt;br /&gt;
# Seal something for Analytica&lt;br /&gt;
ct = AESGCM(key).encrypt(nonce, &#039;from python&#039;.encode(&#039;utf-8&#039;), b&#039;ctx7&#039;)&lt;br /&gt;
print(base64.b64encode(nonce + ct).decode())&lt;br /&gt;
&lt;br /&gt;
# Open something Analytica sealed with layout:&#039;ct,tag&#039;&lt;br /&gt;
plain = AESGCM(key).decrypt(nonce, bytes.fromhex(&#039;66d9d9b2da0e0c4679f3a82524f5e0499271e16f30&#039;), None)&lt;br /&gt;
print(plain.decode(&#039;utf-8&#039;))    # -&amp;gt; hello&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In Analytica:&lt;br /&gt;
&lt;br /&gt;
: &amp;lt;code&amp;gt;[[Variable]] K := Crypto::EncryptionKey( key: base64&#039;AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8=&#039; )&amp;lt;/code&amp;gt;&lt;br /&gt;
: &amp;lt;code&amp;gt;Crypto::Decrypt( base64&#039;AAECAwQFBgcICQoLIXC5duWVu2/lLvmrU3Xrc1+3jRqJx0hC/3QKjDlK2w==&#039;, K, layout: &#039;nonce,ct,tag&#039;, aad: &#039;ctx7&#039;, as: &#039;text&#039; ) &amp;amp;rarr; &#039;from python&#039;&amp;lt;/code&amp;gt;&lt;br /&gt;
: &amp;lt;code&amp;gt;Crypto::Encrypt( &#039;hello&#039;, K, nonce: base64&#039;AAAAAAAAAAAAAAAA&#039;, layout: &#039;ct,tag&#039;, format: &#039;hex&#039; ) &amp;amp;rarr; &#039;66d9d9b2da0e0c4679f3a82524f5e0499271e16f30&#039;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The «nonce» parameter is used here only to make the output reproducible so it can be checked against a published test vector. &#039;&#039;&#039;Do not supply a nonce in real use.&#039;&#039;&#039; Reusing a nonce with the same key destroys the security of the encryption completely; omitting it is always the right thing.&lt;br /&gt;
&lt;br /&gt;
=== The &#039;analytica&#039; container ===&lt;br /&gt;
&lt;br /&gt;
If you need to read Analytica&#039;s own container from another language, its bytes are:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Offset !! Length !! Field&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 4 || The four characters &amp;lt;code&amp;gt;ACRY&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| 4 || 1 || Format version, currently 1&lt;br /&gt;
|-&lt;br /&gt;
| 5 || 1 || Algorithm: 1 = AES-128-GCM, 2 = AES-192-GCM, 3 = AES-256-GCM, 4 = ChaCha20-Poly1305&lt;br /&gt;
|-&lt;br /&gt;
| 6 || 1 || Flags. Bit 0 set means the plaintext was text, encoded as UTF-8. Bit 1 set means an «aad» was supplied.&lt;br /&gt;
|-&lt;br /&gt;
| 7 || 1 || Nonce length, 12 for every algorithm above&lt;br /&gt;
|-&lt;br /&gt;
| 8 || 12 || Nonce&lt;br /&gt;
|-&lt;br /&gt;
| 20 || 16 || Authentication tag&lt;br /&gt;
|-&lt;br /&gt;
| 36 || rest || Ciphertext&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The salt and the iteration count are &#039;&#039;&#039;not&#039;&#039;&#039; in the container. They belong to the key, which you construct explicitly, so keep the salt in a variable of your own next to the key that uses it.&lt;br /&gt;
&lt;br /&gt;
= Function reference =&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;EncryptionKey&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Crypto::EncryptionKey( &#039;&#039;algorithm, key, keyFormat, password, salt, iterations, kdf&#039;&#039; ) ==&lt;br /&gt;
&lt;br /&gt;
Creates a key for [[#Encrypt|Crypto::Encrypt]] and [[#Decrypt|Crypto::Decrypt]]. Give it either «key» or «password», or neither for a fresh random key.&lt;br /&gt;
&lt;br /&gt;
=== Parameters ===&lt;br /&gt;
&lt;br /&gt;
* «algorithm»: (optional, default &amp;lt;code&amp;gt;&#039;AES-256-GCM&#039;&amp;lt;/code&amp;gt;) One of &amp;lt;code&amp;gt;&#039;AES-256-GCM&#039;&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;&#039;AES-192-GCM&#039;&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;&#039;AES-128-GCM&#039;&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;&#039;ChaCha20-Poly1305&#039;&amp;lt;/code&amp;gt;. ChaCha20-Poly1305 requires Windows 10 version 1903 or later; on an older system it reports that it is unavailable rather than failing obscurely.&lt;br /&gt;
&lt;br /&gt;
* «key»: (optional) Raw key material, as [[In-memory binary data terms|binary data]] -- usually a &amp;lt;code&amp;gt;base64&#039;...&#039;&amp;lt;/code&amp;gt; literal. It must be exactly the algorithm&#039;s key length: 32 bytes for AES-256-GCM and ChaCha20-Poly1305, 24 for AES-192-GCM, 16 for AES-128-GCM. A key of the wrong length is an error; Analytica will not pad or hash it to fit, because that would quietly weaken it. You may also pass a [[Secret]] here, in which case the secret&#039;s text is decoded according to «keyFormat». Plain text that is not a [[Secret]] is refused -- use «password» for a passphrase.&lt;br /&gt;
&lt;br /&gt;
* «keyFormat»: (optional, default &amp;lt;code&amp;gt;&#039;base64&#039;&amp;lt;/code&amp;gt;) How to read the text a [[Secret]] holds: &amp;lt;code&amp;gt;&#039;base64&#039;&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;&#039;base64url&#039;&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;&#039;hex&#039;&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;&#039;utf8&#039;&amp;lt;/code&amp;gt;. Meaningful only when «key» is given a [[Secret]].&lt;br /&gt;
&lt;br /&gt;
* «password»: (optional) A passphrase to derive the key from. Requires «salt». May be given a [[Secret]].&lt;br /&gt;
&lt;br /&gt;
* «salt»: (optional) Text or [[In-memory binary data terms|binary data]], at least 8 bytes, required whenever «password» is given. Not secret; see [[#Passwords and salts|Passwords and salts]].&lt;br /&gt;
&lt;br /&gt;
* «iterations»: (optional, default 600000) PBKDF2 rounds. More is slower and safer. Use the same number every time, or you get a different key.&lt;br /&gt;
&lt;br /&gt;
* «kdf»: (optional, default &amp;lt;code&amp;gt;&#039;PBKDF2-HMAC-SHA-256&#039;&amp;lt;/code&amp;gt;) The key-derivation function. This release supports only that one; the parameter exists so that adding others later does not change how you write the call.&lt;br /&gt;
&lt;br /&gt;
A parameter that could not take effect is refused rather than quietly ignored -- «iterations» without «password», for example.&lt;br /&gt;
&lt;br /&gt;
=== Members ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;-&amp;amp;gt;algorithm&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;-&amp;amp;gt;keyBits&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;-&amp;amp;gt;fingerprint&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;-&amp;amp;gt;fromSecret&amp;lt;/code&amp;gt;, described under [[#Keys|Keys]]. There is no member that returns the key material.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;Export&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== key-&amp;gt;Export( ) ==&lt;br /&gt;
&lt;br /&gt;
Returns the raw key material of a key as [[In-memory binary data terms|binary data]]. This is the only way to get key material back out, and it is meant for saving a randomly generated key so the same key can be used again.&lt;br /&gt;
&lt;br /&gt;
: &amp;lt;code&amp;gt;MyKey-&amp;amp;gt;Export() &amp;amp;rarr; a 32-byte binary value&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A key derived from a [[Secret]] cannot be exported, and reports an error. A secret&#039;s value is never allowed back into the model.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;Encrypt&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Crypto::Encrypt( data, key&#039;&#039;, aad, nonce, format, layout&#039;&#039; ) ==&lt;br /&gt;
&lt;br /&gt;
Encrypts «data» under «key» and returns a sealed value that only the same key can open. Returns [[Null]] when «data» is [[Null]].&lt;br /&gt;
&lt;br /&gt;
=== Parameters ===&lt;br /&gt;
&lt;br /&gt;
* «data»: Text or [[In-memory binary data terms|binary data]]. A number is refused rather than converted, since &amp;lt;code&amp;gt;42&amp;lt;/code&amp;gt; could reasonably mean either the text &amp;lt;code&amp;gt;&#039;42&#039;&amp;lt;/code&amp;gt; or eight bytes -- use &amp;lt;code&amp;gt;[[Text]](x)&amp;lt;/code&amp;gt; if you mean its textual form.&lt;br /&gt;
&lt;br /&gt;
* «key»: A key from [[#EncryptionKey|Crypto::EncryptionKey]]. The algorithm comes from the key.&lt;br /&gt;
&lt;br /&gt;
* «aad»: (optional) Additional authenticated data -- authenticated but not encrypted. [[#Decrypt|Crypto::Decrypt]] must be given the same value. See [[#Binding a sealed value to its context|Binding a sealed value to its context]].&lt;br /&gt;
&lt;br /&gt;
* «nonce»: (optional, named) Supplies the nonce instead of generating one. &#039;&#039;&#039;Reusing a nonce with the same key destroys the security of the encryption completely.&#039;&#039;&#039; Omit this unless you are reproducing a published test vector or another system&#039;s exact output.&lt;br /&gt;
&lt;br /&gt;
* «format»: (optional, named, default &amp;lt;code&amp;gt;&#039;binary&#039;&amp;lt;/code&amp;gt;) &amp;lt;code&amp;gt;&#039;binary&#039;&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;&#039;base64&#039;&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;&#039;base64url&#039;&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;&#039;hex&#039;&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
* «layout»: (optional, named, default &amp;lt;code&amp;gt;&#039;analytica&#039;&amp;lt;/code&amp;gt;) See [[#Layouts|Layouts]].&lt;br /&gt;
&lt;br /&gt;
=== Return value ===&lt;br /&gt;
&lt;br /&gt;
The sealed value, as [[In-memory binary data terms|binary data]] or as text according to «format». The nonce and the authentication tag are returned as the second and third return values:&lt;br /&gt;
&lt;br /&gt;
: &amp;lt;code&amp;gt;[[Local]] (sealed, nonce, tag) := Crypto::Encrypt( x, MyKey );&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
It [[Array Abstraction|array abstracts]] over «data» and «aad», sealing each cell under its own nonce.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;Decrypt&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Crypto::Decrypt( data, key&#039;&#039;, aad, nonce, tag, format, layout, as&#039;&#039; ) ==&lt;br /&gt;
&lt;br /&gt;
Opens a value sealed by [[#Encrypt|Crypto::Encrypt]]. Returns [[Null]] when «data» is [[Null]].&lt;br /&gt;
&lt;br /&gt;
Reports an error if the value cannot be decrypted -- wrong key, wrong «aad», or data that has been altered. The message deliberately does not say which, because that distinction would help an attacker. Use [[Try]] if your model should carry on regardless.&lt;br /&gt;
&lt;br /&gt;
=== Parameters ===&lt;br /&gt;
&lt;br /&gt;
* «data»: The sealed value, as [[In-memory binary data terms|binary data]], or as text in the encoding named by «format».&lt;br /&gt;
&lt;br /&gt;
* «key»: The same key the value was sealed with. If it is a key for a different algorithm, that is reported specifically, since it is a mistake you can act on rather than a failed decryption.&lt;br /&gt;
&lt;br /&gt;
* «aad»: (optional) Must equal the «aad» the value was sealed with.&lt;br /&gt;
&lt;br /&gt;
* «nonce», «tag»: (optional, named) Required for a «layout» that does not carry them.&lt;br /&gt;
&lt;br /&gt;
* «format»: (optional, named, default &amp;lt;code&amp;gt;&#039;base64&#039;&amp;lt;/code&amp;gt;) How to read a textual «data»: &amp;lt;code&amp;gt;&#039;base64&#039;&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;&#039;base64url&#039;&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;&#039;hex&#039;&amp;lt;/code&amp;gt;. Ignored when «data» is binary.&lt;br /&gt;
&lt;br /&gt;
* «layout»: (optional, named, default &amp;lt;code&amp;gt;&#039;analytica&#039;&amp;lt;/code&amp;gt;) Must match the layout the value was sealed with. See [[#Layouts|Layouts]].&lt;br /&gt;
&lt;br /&gt;
* «as»: (optional, named) &amp;lt;code&amp;gt;&#039;text&#039;&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;&#039;binaryData&#039;&amp;lt;/code&amp;gt;. With the default &amp;lt;code&amp;gt;&#039;analytica&#039;&amp;lt;/code&amp;gt; layout it is optional, because the sealed value records which one was encrypted. For every other layout it is required. &amp;lt;code&amp;gt;&#039;text&#039;&amp;lt;/code&amp;gt; reports an error if the decrypted bytes are not valid UTF-8, which is usually a sign that you wanted &amp;lt;code&amp;gt;&#039;binaryData&#039;&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;RandomBytes&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Crypto::RandomBytes( n ) ==&lt;br /&gt;
&lt;br /&gt;
Returns «n» cryptographically random bytes as [[In-memory binary data terms|binary data]]. Use it for a «salt», or for raw key material.&lt;br /&gt;
&lt;br /&gt;
: &amp;lt;code&amp;gt;[[Variable]] Salt := Crypto::RandomBytes(16)&amp;lt;/code&amp;gt;&lt;br /&gt;
: &amp;lt;code&amp;gt;[[Variable]] NewKey := Crypto::EncryptionKey( key: Crypto::RandomBytes(32) )&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
«n» may not exceed 65536.&lt;br /&gt;
&lt;br /&gt;
Each evaluation returns different bytes. Like [[#Encrypt|Crypto::Encrypt]], this means a definition that calls it does not hold still across re-evaluations -- assign the result once if you need it to stay the same.&lt;br /&gt;
&lt;br /&gt;
== Errors ==&lt;br /&gt;
&lt;br /&gt;
Most mistakes are reported specifically, and can be caught with [[Try]]:&lt;br /&gt;
&lt;br /&gt;
* The «key» is the wrong length for the algorithm, or «key» was given plain text rather than key material or a [[Secret]].&lt;br /&gt;
* Both «key» and «password» were given, or «password» was given without a «salt», or a parameter such as «iterations» cannot take effect.&lt;br /&gt;
* The algorithm is not one of the four, or is not available on this computer.&lt;br /&gt;
* «data» is a number rather than text or binary data.&lt;br /&gt;
* The sealed value is not an Analytica sealed value, or was sealed with a different algorithm, or the text is not valid for the stated «format».&lt;br /&gt;
* A [[Secret]] was passed where a secret makes no sense, such as «aad» or «salt», neither of which is secret.&lt;br /&gt;
* &amp;lt;code&amp;gt;-&amp;amp;gt;Export()&amp;lt;/code&amp;gt; was called on a key derived from a [[Secret]].&lt;br /&gt;
&lt;br /&gt;
The one deliberately vague error is a failed decryption, described under [[#Decrypt|Crypto::Decrypt]].&lt;br /&gt;
&lt;br /&gt;
== See Also ==&lt;br /&gt;
&lt;br /&gt;
* [[Secret]] -- keeping a key out of the model&lt;br /&gt;
* [[TextCharacterEncode]] -- hashing text with SHA-1 or SHA-256, and other text encodings&lt;br /&gt;
* [[In-memory binary data terms]]&lt;br /&gt;
* [[NamespaceImports]]&lt;br /&gt;
* [[:category:Encrypting and hashing functions|Encrypting and hashing functions]]&lt;/div&gt;</summary>
		<author><name>Lchrisman</name></author>
	</entry>
	<entry>
		<id>https://docs.analytica.com/index.php?title=Alphabetical_Function_List&amp;diff=64602</id>
		<title>Alphabetical Function List</title>
		<link rel="alternate" type="text/html" href="https://docs.analytica.com/index.php?title=Alphabetical_Function_List&amp;diff=64602"/>
		<updated>2026-09-22T17:42:18Z</updated>

		<summary type="html">&lt;p&gt;Lchrisman: ER 22440: add Decrypt, Encrypt, EncryptionKey, RandomBytes&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:Functions]]&lt;br /&gt;
(Back to [[Analytica Reference]])&lt;br /&gt;
&lt;br /&gt;
See also [[:Category: Functions]] and [[Functions by category]].&lt;br /&gt;
&lt;br /&gt;
* [[Comparison Operators|=,&amp;lt;,&amp;gt;,&amp;lt;&amp;gt;,&amp;lt;=,&amp;gt;=]] : Comparison operators&lt;br /&gt;
* [[Subscript/Slice Operator| [I=x] ]] : Subscript operator &lt;br /&gt;
* [[Subscript/Slice Operator| [@I=x] ]] : Slice operator &lt;br /&gt;
* [[Using References|\]] : Reference operator&lt;br /&gt;
* [[Dereference Operator|#]] : Dereference operator&lt;br /&gt;
* [[Index Position Operator::@|@]] : Index Position operator&lt;br /&gt;
* [[Assignment Operator:: ::=|:=]] : Assignment operator&lt;br /&gt;
* [[Text Concatenation Operator: &amp;amp;|&amp;amp;]] : Text concatenation operator&lt;br /&gt;
* [[Dot operator::A.I|A.I]] : Dot operator, to access a local index of an array&lt;br /&gt;
* [[Repeated Parameter Forwarding|...]] : Repeated parameter forwarding&lt;br /&gt;
* [[Exponentiation of negative numbers|x^y]] : Exponentiation&lt;br /&gt;
* [[AbortCalculation]]&lt;br /&gt;
* [[Abs]]&lt;br /&gt;
* [[AddIndex]]&lt;br /&gt;
* [[Aggregate]]&lt;br /&gt;
* [[Airy_Ai]]&lt;br /&gt;
* [[Airy_Ai_deriv]]&lt;br /&gt;
* [[Airy_Ai_zero]]&lt;br /&gt;
* [[Airy_Bi]]&lt;br /&gt;
* [[Airy_Bi_deriv]]&lt;br /&gt;
* [[Airy_Bi_zero]]&lt;br /&gt;
* [[AnalyticaLicenseInfo]]&lt;br /&gt;
* [[And]]&lt;br /&gt;
* [[Apply_slicers_to_val]]                ([[:Category:OnGraphDraw annotations library functions|OnGraphDraw annotations library]])&lt;br /&gt;
* [[ArcCos]]&lt;br /&gt;
* [[ArcCosH]]&lt;br /&gt;
* [[ArcSin]]&lt;br /&gt;
* [[ArcSinH]]&lt;br /&gt;
* [[ArcTan]]&lt;br /&gt;
* [[ArcTan2]]&lt;br /&gt;
* [[ArcTanH]]&lt;br /&gt;
* [[Area]]&lt;br /&gt;
* [[ArgMin_and_ArgMax| Argmax]]&lt;br /&gt;
* [[ArgMin_and_ArgMax| Argmin]]&lt;br /&gt;
* [[Array]]&lt;br /&gt;
* [[Asc]]&lt;br /&gt;
* [[AskMsgChoice]]&lt;br /&gt;
* [[AskMsgNumber]]&lt;br /&gt;
* [[AskMsgText]]&lt;br /&gt;
* [[Assignment Operator:: ::=]] : Assignment operator&lt;br /&gt;
* [[AttribGet]]&lt;br /&gt;
* [[Attrib of Obj]]&lt;br /&gt;
* [[_AttTrackerQuery]]&lt;br /&gt;
* [[_AttTrackerReset]]&lt;br /&gt;
* [[Average]]&lt;br /&gt;
* [[Bernoulli]]&lt;br /&gt;
* [[BesselJ]], [[BesselY]], [[BesselI]], [[BesselK]]&lt;br /&gt;
* [[BesselJzero]], [[BesselYzero]]&lt;br /&gt;
* [[Beta]]&lt;br /&gt;
* [[Beta_m_sd|Beta_m_sd]]    (Distribution variations.ana)&lt;br /&gt;
* [[Betafn]]&lt;br /&gt;
* [[BetaI]]&lt;br /&gt;
* [[BetaIaInv]]&lt;br /&gt;
* [[BetaIInv]]&lt;br /&gt;
* [[Binomial]]&lt;br /&gt;
* [[BiNormal]]     (Multivariate distributions.ana)&lt;br /&gt;
* [[BitAnd]]&lt;br /&gt;
* [[BitCount]]&lt;br /&gt;
* [[BitNot]]&lt;br /&gt;
* [[BitOr]]&lt;br /&gt;
* [[BitShift]]&lt;br /&gt;
* [[BitXOr]]&lt;br /&gt;
* [[Boolean]]&lt;br /&gt;
* [[CAbs]]         (Complex Library.ana)&lt;br /&gt;
* [[CAdjoint]]     (Complex Library.ana)&lt;br /&gt;
* [[Calloption]]   (Financial Library.ana)&lt;br /&gt;
* [[Canvas]]&lt;br /&gt;
* [[CanvasContext]]&lt;br /&gt;
* [[CanvasDrawEllipse]]&lt;br /&gt;
* [[CanvasDrawImage]]&lt;br /&gt;
* [[CanvasDrawLine]]&lt;br /&gt;
* [[CanvasDrawPixel]]&lt;br /&gt;
* [[CanvasDrawPolygon]]&lt;br /&gt;
* [[CanvasDrawRectangle]]&lt;br /&gt;
* [[CanvasDrawText]]&lt;br /&gt;
* [[CanvasImage]]&lt;br /&gt;
* [[Capm]]         (Financial Library.ana)&lt;br /&gt;
* [[CArcCos]]      (Complex Library.ana)&lt;br /&gt;
* [[CArcSin]]      (Complex Library.ana)&lt;br /&gt;
* [[CArcTan]]      (Complex Library.ana)&lt;br /&gt;
* [[CCos]]         (Complex Library.ana)&lt;br /&gt;
* [[CDeterminant]] (Complex Library.ana)&lt;br /&gt;
* [[Cdf]]&lt;br /&gt;
* [[CDiv]]         (Complex Library.ana)&lt;br /&gt;
* [[Ceil]]&lt;br /&gt;
* [[CellAlignment]]&lt;br /&gt;
* [[CellBar]]&lt;br /&gt;
* [[CellBorder]]&lt;br /&gt;
* [[CellDefaults]]&lt;br /&gt;
* [[CellEntry]]&lt;br /&gt;
* [[CellFill]]&lt;br /&gt;
* [[CellFont]]&lt;br /&gt;
* [[CellFormats]]&lt;br /&gt;
* [[CellIcon]]&lt;br /&gt;
* [[CellNumberFormat]]&lt;br /&gt;
* [[CellOnClick]]&lt;br /&gt;
* [[CellSpan]]&lt;br /&gt;
* [[Certain]]&lt;br /&gt;
* [[CExp]]         (Complex Library.ana)&lt;br /&gt;
* [[ChanceDist]]&lt;br /&gt;
* [[ChangeArraySparsity]]&lt;br /&gt;
* [[Change_index]]        (Expand Index.ana)&lt;br /&gt;
* [[ChangeNodeVisibility]]&lt;br /&gt;
* [[ChiSquared]]&lt;br /&gt;
* [[Choice| Choice]]&lt;br /&gt;
* [[CInverse]]     (Complex Library.ana)&lt;br /&gt;
* [[Chr]]&lt;br /&gt;
* [[Clip_to_PlotArea]]                ([[:Category:OnGraphDraw annotations library functions|OnGraphDraw annotations library]])&lt;br /&gt;
* [[CLn]]          (Complex Library.ana)&lt;br /&gt;
* [[CloneObjects]]&lt;br /&gt;
* [[CloseWindow]]&lt;br /&gt;
* [[CMatMult]]     (Complex Library.ana)&lt;br /&gt;
* [[CMult]]        (Complex Library.ana)&lt;br /&gt;
* [[Coerce_to_Numeric]]     (Flat file library.ana)&lt;br /&gt;
* [[COMArray]]&lt;br /&gt;
* [[Combinations]]&lt;br /&gt;
* [[COMCallMethod]]&lt;br /&gt;
* [[COMCreateObject]]&lt;br /&gt;
* [[COMEnumerate]]&lt;br /&gt;
* [[COMGetProperty]]&lt;br /&gt;
* [[Comparison Operators]]: =, &amp;lt;, &amp;gt;, &amp;lt;&amp;gt;, &amp;lt;=, &amp;gt;=&lt;br /&gt;
* [[ComparisonPart]]&lt;br /&gt;
* [[Complex]]      (Complex Library.ana)&lt;br /&gt;
* [[ComplexDegrees]]&lt;br /&gt;
* [[ComplexRadians]]&lt;br /&gt;
* [[CompressMemoryUsedBy]]&lt;br /&gt;
* [[ComputedBy]]&lt;br /&gt;
* [[COMPutProperty]]&lt;br /&gt;
* [[Concat]]&lt;br /&gt;
* [[ConcatN|Concat&amp;lt;N&amp;gt;]]    (Concatenation UDFs.ana&lt;br /&gt;
* [[ConcatRows]]            (Concatenation UDFs.ana)&lt;br /&gt;
* [[ConsolePrint]]&lt;br /&gt;
* [[Functions_Min_and_Max| Condmax]]&lt;br /&gt;
* [[Functions_Min_and_Max| Condmin]]&lt;br /&gt;
* [[ConsolePrint]]&lt;br /&gt;
* [[Continuous]]&lt;br /&gt;
* [[CopyIndex]]&lt;br /&gt;
* [[CopyToClipboard]]&lt;br /&gt;
* [[Correlate_Dists]]     (Multivariate Distributions.ana)&lt;br /&gt;
* [[Correlate_With]]      (Multivariate Distributions.ana)&lt;br /&gt;
* [[Correlation]]&lt;br /&gt;
* [[Trig Functions| Cos]]&lt;br /&gt;
* [[Trig Functions| Cosh]]&lt;br /&gt;
* [[CostCapme]]     (Financial Library.ana)&lt;br /&gt;
* [[CostCapmm]]     (Financial library.ana)&lt;br /&gt;
* [[CreateNewObject]]&lt;br /&gt;
* [[CRoots]]              (Complex Library.ana)&lt;br /&gt;
* [[CSin]]                (Complex Library.ana)&lt;br /&gt;
* [[CSqrt]]               (Complex Library.ana)&lt;br /&gt;
* [[CTan]]                (Complex Library.ana)&lt;br /&gt;
* [[CTheta]]              (Complex Library.ana)&lt;br /&gt;
* [[CToPolar]]            (Complex Library.ana)&lt;br /&gt;
* [[CubicInterp]]&lt;br /&gt;
* [[CumBinomial]]         ([[Distribution Densities Library]])&lt;br /&gt;
* [[CumBinomialInv]]      ([[Distribution Densities Library]])&lt;br /&gt;
* [[CumChiSquared]]       ([[Distribution Densities Library]])&lt;br /&gt;
* [[CumChiSquaredInv]]    ([[Distribution Densities Library]])&lt;br /&gt;
* [[CumDist]]&lt;br /&gt;
* [[CumExponential]]      ([[Distribution Densities Library]])&lt;br /&gt;
* [[CumExponentialInv]]   ([[Distribution Densities Library]])&lt;br /&gt;
* [[CumFDist]]            ([[Distribution Densities Library]])&lt;br /&gt;
* [[CumFDistInv]]         ([[Distribution Densities Library]])&lt;br /&gt;
* [[CumGeometric]]        ([[Distribution Densities Library]])&lt;br /&gt;
* [[CumGeometricInv]]     ([[Distribution Densities Library]])&lt;br /&gt;
* [[CumIPmt]]&lt;br /&gt;
* [[CumKeelin]]&lt;br /&gt;
* [[CumKeelinInv]]&lt;br /&gt;
* [[CumLogistic]]         ([[Distribution Densities Library]])&lt;br /&gt;
* [[CumLogisticInv]]      ([[Distribution Densities Library]])&lt;br /&gt;
* [[CumLogNormal]]        ([[Distribution Densities Library]])&lt;br /&gt;
* [[CumLogNormalInv]]     ([[Distribution Densities Library]])&lt;br /&gt;
* [[CumMax]]&lt;br /&gt;
* [[CumMin]]&lt;br /&gt;
* [[CumNegativeBinomial]] ([[Distribution Densities Library]])&lt;br /&gt;
* [[CumNegativeBinomInv]] ([[Distribution Densities Library]])&lt;br /&gt;
* [[CumNormal]]&lt;br /&gt;
* [[CumNormalInv]]&lt;br /&gt;
* [[CumPoisson]]          ([[Distribution Densities Library]])&lt;br /&gt;
* [[CumPoissonInv]]&lt;br /&gt;
* [[CumPrinc]]&lt;br /&gt;
* [[CumProduct]]&lt;br /&gt;
* [[CumStudentT]]         ([[Distribution Densities Library]])&lt;br /&gt;
* [[CumStudentTInv]]      ([[Distribution Densities Library]])&lt;br /&gt;
* [[CumTriangular]]       ([[Distribution Densities Library]])&lt;br /&gt;
* [[CumTriangularInv]]    ([[Distribution Densities Library]])&lt;br /&gt;
* [[CumUncertainLMH]]&lt;br /&gt;
* [[CumUncertainLMHInv]]&lt;br /&gt;
* [[CumWilcoxon]]&lt;br /&gt;
* [[CumWilcoxonInv]]&lt;br /&gt;
* [[Cumulate]]&lt;br /&gt;
* [[CumUniform]]          ([[Distribution Densities Library]])&lt;br /&gt;
* [[CumUniformInv]]       ([[Distribution Densities Library]])&lt;br /&gt;
* [[CumWeibull]]          ([[Distribution Densities Library]])&lt;br /&gt;
* [[CumWeibullInv]]       ([[Distribution Densities Library]])&lt;br /&gt;
* [[CurrentDataDirectory]]        (&#039;&#039;Replaced with [[CurrentDataFolder]] as of [[Analytica 4.6]]&#039;&#039;)&lt;br /&gt;
* [[CurrentDataFolder]]&lt;br /&gt;
* [[CurrentModelDirector]]        (&#039;&#039;Replaced with [[CurrentDataFolder]] as of [[Analytica 4.6]]&#039;&#039;)&lt;br /&gt;
* [[CurrentModelFolder]]&lt;br /&gt;
* [[Expected value of information -- EVI, EVPI, and ESVI#Function CVI_x(v, d, xVars, pc)|CVI_x]]   ([[Expected value of information -- EVI, EVPI, and ESVI|Value of Information library]])&lt;br /&gt;
* [[DateAdd]]&lt;br /&gt;
* [[DatePart]]&lt;br /&gt;
* [[Dawson]]&lt;br /&gt;
* [[DbLabels]]&lt;br /&gt;
* [[DbQuery]]&lt;br /&gt;
* [[DbTable]]&lt;br /&gt;
* [[DbTablenames]]&lt;br /&gt;
* [[DbWrite]]&lt;br /&gt;
* [[Decompose]]&lt;br /&gt;
* [[Decrypt]]&lt;br /&gt;
* [[DeferWindowsResynch]]&lt;br /&gt;
* [[Degrees]]&lt;br /&gt;
* [[DensBeta]]&lt;br /&gt;
* [[Dens_Beta]]         ([[Distribution Densities Library]])&lt;br /&gt;
* [[DensChiSquared]]&lt;br /&gt;
* [[Dens_ChiSquared]]         ([[Distribution Densities Library]])&lt;br /&gt;
* [[DensCumDist]]&lt;br /&gt;
* [[Dens_CumDist]]         ([[Distribution Densities Library]])&lt;br /&gt;
* [[DensExponential]]&lt;br /&gt;
* [[Dens_Exponential]]         ([[Distribution Densities Library]])&lt;br /&gt;
* [[Dens_FDist]]         ([[Distribution Densities Library]])&lt;br /&gt;
* [[DensGamma]&lt;br /&gt;
* [[Dens_Gamma]]         ([[Distribution Densities Library]])&lt;br /&gt;
* [[DensGaussian]]&lt;br /&gt;
* [[Dens_Gaussian]]         ([[Distribution Densities Library]])&lt;br /&gt;
* [[DensKeelin]]&lt;br /&gt;
* [[DensLogistic]]&lt;br /&gt;
* [[Dens_Logistic]]         ([[Distribution Densities Library]])&lt;br /&gt;
* [[DensLogNormal]]&lt;br /&gt;
* [[Dens_LogNormal]]         ([[Distribution Densities Library]])&lt;br /&gt;
* [[DensNormal]]&lt;br /&gt;
* [[Dens_Normal]]         ([[Distribution Densities Library]])&lt;br /&gt;
* [[DensProbDist]]&lt;br /&gt;
* [[Dens_ProbDist]]         ([[Distribution Densities Library]])&lt;br /&gt;
* [[DensStudentT]]&lt;br /&gt;
* [[Dens_StudentT]]         ([[Distribution Densities Library]])&lt;br /&gt;
* [[DensTriangular]]&lt;br /&gt;
* [[Dens_Triangular]]         ([[Distribution Densities Library]])&lt;br /&gt;
* [[DensUncertainLMH]]&lt;br /&gt;
* [[DensUniform]]&lt;br /&gt;
* [[Dens_Uniform]]         ([[Distribution Densities Library]])&lt;br /&gt;
* [[DensWeibull]]&lt;br /&gt;
* [[Dens_Weibull]]         ([[Distribution Densities Library]])&lt;br /&gt;
* [[Dereference Operator::#|Dereference Operator: #R]] : Return the value pointed to by a reference R.&lt;br /&gt;
* [[Determinant]]&lt;br /&gt;
* [[DetermTable]]&lt;br /&gt;
* [[Dirichlet]]             (Multivariate distributions.ana)&lt;br /&gt;
* [[Discrete]]&lt;br /&gt;
* [[Dispatch]]&lt;br /&gt;
* [[Dist_additive_growth]]  (Multivariate distributions.ana)&lt;br /&gt;
* [[Dist_compound_growth]]  (Multivariate distributions.ana)&lt;br /&gt;
* [[Dist_reshape]]          (Multivariate distributions.ana)&lt;br /&gt;
* [[Dist_serial_correl]]     (Multivariate distributions.ana)&lt;br /&gt;
* [[DomainAllowed]]&lt;br /&gt;
* [[DomainIntegerGroup]]&lt;br /&gt;
* [[DomainLowerBound]]&lt;br /&gt;
* [[DomainNullOk]]&lt;br /&gt;
* [[DomainType]]&lt;br /&gt;
* [[DomainUpperBound]]&lt;br /&gt;
* [[DotProduct]]&lt;br /&gt;
* [[DownloadFileToClient]]&lt;br /&gt;
* [[Dydx]]&lt;br /&gt;
* [[Dynamic]]&lt;br /&gt;
* [[EigenDecomp]]&lt;br /&gt;
* [[Elasticity]]&lt;br /&gt;
* [[Encrypt]]&lt;br /&gt;
* [[EncryptionKey]]&lt;br /&gt;
* [[Erf]]&lt;br /&gt;
* [[ErfInv]]&lt;br /&gt;
* [[Erlang]]          (Distribution variations.ana)&lt;br /&gt;
* [[Error]]&lt;br /&gt;
* [[Evaluate]]&lt;br /&gt;
* [[EvaluateScript]]&lt;br /&gt;
* [[Expected value of information -- EVI, EVPI, and ESVI#Function EVI_x(v, d, xVars)|EVI_x]]   ([[Expected value of information -- EVI, EVPI, and ESVI|Value of Information library]])&lt;br /&gt;
* [[Expected value of information -- EVI, EVPI, and ESVI#Function EVIU(v, d)|EVIU]]   ([[Expected value of information -- EVI, EVPI, and ESVI|Value of Information library]])&lt;br /&gt;
* [[Expected value of information -- EVI, EVPI, and ESVI#Function EVIU_by_x(v, d, xvars)|EVIU_by_x]]   ([[Expected value of information -- EVI, EVPI, and ESVI|Value of Information library]])&lt;br /&gt;
* [[Expected value of information -- EVI, EVPI, and ESVI#Function EVPI(v, d)|EVPI]]   ([[Expected value of information -- EVI, EVPI, and ESVI|Value of Information library]])&lt;br /&gt;
* [[Exp]]&lt;br /&gt;
* [[_Expm1]]&lt;br /&gt;
* [[Exponential]]&lt;br /&gt;
* [[expr1 ; expr2]] : Expression Sequence Operator&lt;br /&gt;
* [[Factorial]]&lt;br /&gt;
* [[Faddeeva]]&lt;br /&gt;
* [[FFT]]&lt;br /&gt;
* [[FFTInv]]&lt;br /&gt;
* [[FileFullPath]]&lt;br /&gt;
* [[FilePathPart]]&lt;br /&gt;
* [[FileSystemCopy]]&lt;br /&gt;
* [[FileSystemDelete]]&lt;br /&gt;
* [[FileSystemListing]]&lt;br /&gt;
* [[FileSystemMove]]&lt;br /&gt;
* [[FileSystemNewFolder]]&lt;br /&gt;
* [[FindInText]]&lt;br /&gt;
* [[FindObjects]]&lt;br /&gt;
* [[FindPolynomialZeroes]]&lt;br /&gt;
* [[Flatten]]&lt;br /&gt;
* [[Floor]]&lt;br /&gt;
* [[For..Do]]&lt;br /&gt;
* [[Fractiles]]&lt;br /&gt;
* [[Frequency]]&lt;br /&gt;
* [[FunctionOf]]&lt;br /&gt;
* [[Fv]]&lt;br /&gt;
* [[Gamma]]&lt;br /&gt;
* [[Gamma_m_sd|Gamma_m_sd]]                (Distribution Variations.ana)&lt;br /&gt;
* [[GammaFn]]&lt;br /&gt;
* [[GammaI]]&lt;br /&gt;
* [[GammaIInv]]&lt;br /&gt;
* [[Gauss_Quadrature_Pts]]   ([[Legendre Library]])&lt;br /&gt;
* [[Gaussian]]&lt;br /&gt;
* [[GCD]]                   ([[media:GCD function library.ana|GCD function library.ana]])&lt;br /&gt;
* [[Geometric]]&lt;br /&gt;
* [[GetArrowsOnDiagram]]&lt;br /&gt;
* [[GetEvaluationContext]]&lt;br /&gt;
* [[GetFract]]&lt;br /&gt;
* [[GetProcessInfo]]&lt;br /&gt;
* [[GetRegistryValue]]&lt;br /&gt;
* [[VarTerm Functions#Function_GetVariableByName| GetVariableByName]]&lt;br /&gt;
* [[GoalSeek]]              (Optimization Functions.ana)&lt;br /&gt;
* [[Gradient]]              (Optimization Functions.ana)&lt;br /&gt;
* [[GroupedInteger]]&lt;br /&gt;
* [[The Sensitivity Analysis Library#Importance Details|GRSensDetails]]   ([[The Sensitivity Analysis Library]])&lt;br /&gt;
* [[HasImplicitDimension]]&lt;br /&gt;
* [[HasIndex]]&lt;br /&gt;
* [[HyperGeometric]]&lt;br /&gt;
* [[IdentPred]]&lt;br /&gt;
* [[If-Then-Else]]&lt;br /&gt;
* [[Ifall-Then-Else]]&lt;br /&gt;
* [[Ifonly-Then-Else]]&lt;br /&gt;
* [[If0]]&lt;br /&gt;
* [[Ifpos]]&lt;br /&gt;
* [[IgnoreWarnings]]&lt;br /&gt;
* [[Im]]                     (Complex Library.ana)&lt;br /&gt;
* [[ImageFromHex]]&lt;br /&gt;
* [[ImageInfo]]&lt;br /&gt;
* [[ImPart]]&lt;br /&gt;
* [[Implied_volatility_c]]   (Financial library.ana)&lt;br /&gt;
* [[Implied_volatility_p]]   (Financial library.ana)&lt;br /&gt;
* [[The Sensitivity Analysis Library#Importance_graph|ImportanceGraph]]   ([[The Sensitivity Analysis Library]])&lt;br /&gt;
* [[Index Position Operator::@|Index Position Operator: @I, @[I=x] ]] : Get the numeric position of index elements&lt;br /&gt;
* [[VarTerm Functions#Function_IndexesOf| IndexesOf]]&lt;br /&gt;
* [[Index..Do]]&lt;br /&gt;
* [[IndexLength]]&lt;br /&gt;
* [[IndexNames]]&lt;br /&gt;
* [[IndexValue]]&lt;br /&gt;
* [[The Sensitivity Analysis Library#Input Descriptor|InputDescriptors]]   ([[The Sensitivity Analysis Library]])&lt;br /&gt;
* [[InsertRecSql]]       (ODBC Library.ana)&lt;br /&gt;
* [[Integer]]&lt;br /&gt;
* [[Integrate]]&lt;br /&gt;
* [[InvalidateResult]]&lt;br /&gt;
* [[InverseGaussian]]   (Distribution Variations.ana)&lt;br /&gt;
* [[Invert]]&lt;br /&gt;
* [[InvertedWishart]]   (Distribution Variations.ana)&lt;br /&gt;
* [[InvLogit]]           (Generalized Regression.ana)&lt;br /&gt;
* [[IPmt]]&lt;br /&gt;
* [[Irr]]&lt;br /&gt;
* [[IsDateTime]]&lt;br /&gt;
* [[IsHandle]]&lt;br /&gt;
* [[Data_Type_Functions#Function_IsNaN| IsNaN]]&lt;br /&gt;
* [[Data_Type_Functions#Function_IsNotSpecified| IsNotSpecified]]&lt;br /&gt;
* [[IsNull]]&lt;br /&gt;
* [[Data_Type_Functions#Function_IsNumber| IsNumber]]&lt;br /&gt;
* [[IsRealNumber]]&lt;br /&gt;
* [[Data_Type_Functions#Function_IsReference| IsReference]]&lt;br /&gt;
* [[IsResultComputed]]&lt;br /&gt;
* [[Data_Type_Functions#Function_IsText| IsText]]&lt;br /&gt;
* [[Data_Type_Functions#Function_IsUndef| IsUndef]]&lt;br /&gt;
* [[Iterate]]&lt;br /&gt;
* [[Join]]&lt;br /&gt;
* [[JoinText]]&lt;br /&gt;
* [[Keelin]]&lt;br /&gt;
* [[KeelinCoefficients]]&lt;br /&gt;
* [[Kurtosis]]&lt;br /&gt;
* [[LDens_InvertedWishart]] (Distribution Densities.ana)&lt;br /&gt;
* [[LDens_Wishart]]       (Distribution Densities.ana)&lt;br /&gt;
* [[Legendre_kth_root]]     ([[Legendre Library]])&lt;br /&gt;
* [[LegendreP]]     ([[Legendre Library]])&lt;br /&gt;
* [[LegendreP_coefs]]     ([[Legendre Library]])&lt;br /&gt;
* [[LegendreP_deriv]]     ([[Legendre Library]])&lt;br /&gt;
* [[LGamma]]&lt;br /&gt;
* [[LinearInterp]]&lt;br /&gt;
* [[ListOfHandles]]&lt;br /&gt;
* [[LL_First]]            (Linked List Library.ana)&lt;br /&gt;
* [[LL_Length]]           (Linked List Library.ana)&lt;br /&gt;
* [[LL_Nth]]              (Linked List Library.ana)&lt;br /&gt;
* [[LL_Push]]             (Linked List Library.ana)&lt;br /&gt;
* [[LL_Remove_First]]     (Linked List Library.ana)&lt;br /&gt;
* [[LL_To_Array]]         (Linked List Library.ana)&lt;br /&gt;
* [[LL_to_RArray]]        (Linked List Library.ana)&lt;br /&gt;
* [[Ln]]&lt;br /&gt;
* [[Local Index Operator::A.I]] : Get an index (usually a local index) of an array&lt;br /&gt;
* [[Log]]&lt;br /&gt;
* [[_Log1p]]&lt;br /&gt;
* [[Logistic]]&lt;br /&gt;
* [[Logistic_Regression]]  (Generalized Regression.ana)&lt;br /&gt;
* [[Logit]]                (Generalized Regression.ana)&lt;br /&gt;
* [[LogNormal]]&lt;br /&gt;
* [[Lognormal_m_sd]]       (Distribution variations.ana) - obsolete, use [[LogNormal]] (mean:m,stddev:sd)&lt;br /&gt;
* [[LogTen]]&lt;br /&gt;
* [[Lorenzian]]            (Distribution variations.ana)&lt;br /&gt;
* [[LpDefine]]&lt;br /&gt;
* [[LpFindIIS]]&lt;br /&gt;
* [[LpObjSa]]&lt;br /&gt;
* [[LpOpt]]&lt;br /&gt;
* [[LpRead]]&lt;br /&gt;
* [[LpReducedCost]]&lt;br /&gt;
* [[LpRhsSa]]&lt;br /&gt;
* [[LpShadow]]&lt;br /&gt;
* [[LpSlack]]&lt;br /&gt;
* [[LpSolution]]&lt;br /&gt;
* [[LpStatusNum]]&lt;br /&gt;
* [[LpStatusText]]&lt;br /&gt;
* [[LpWrite]]&lt;br /&gt;
* [[LpWriteIIS]]&lt;br /&gt;
* [[MakeCSV]]&lt;br /&gt;
* [[Date_Functions#MakeDate.28year.2C_month.2C_day.29| MakeDate]]&lt;br /&gt;
* [[MakeJSON]]&lt;br /&gt;
* [[MantissaAndExponent]]&lt;br /&gt;
* [[MatrixMultiply]]&lt;br /&gt;
* [[Functions_Min_and_Max| Max]]&lt;br /&gt;
* [[MdArrayToTable]]&lt;br /&gt;
* [[MdTable]]&lt;br /&gt;
* [[MdxQuery]]&lt;br /&gt;
* [[Mean]]&lt;br /&gt;
* [[Median]] &lt;br /&gt;
* [[MemoryInUseBy]]&lt;br /&gt;
* [[Mid]]&lt;br /&gt;
* [[Functions_Min_and_Max| Min]]&lt;br /&gt;
* [[Mod]]&lt;br /&gt;
* [[MonoCubicInterp]]&lt;br /&gt;
* [[Move]]&lt;br /&gt;
* [[MsgBox]]&lt;br /&gt;
* [[MultiChoice]]&lt;br /&gt;
* [[Multinomial]]              (Multivariate Distributions.ana)&lt;br /&gt;
* [[MultiNormal]]              (Multivariate Distributions.ana)&lt;br /&gt;
* [[MultiTable]]&lt;br /&gt;
* [[MultiUniform]]             (Multivariate Distributions.ana)&lt;br /&gt;
* [[MutableGet]]                ([[Mutables library]])&lt;br /&gt;
* [[MutableNew]]               ([[Mutables library]])&lt;br /&gt;
* [[MutableSet]]                ([[Mutables library]])&lt;br /&gt;
* [[NegBinomial]]              (Distribution Variations.ana)&lt;br /&gt;
* [[NegativeBinomial]]&lt;br /&gt;
* [[NextFloatToward]]&lt;br /&gt;
* [[NlpDefine]]&lt;br /&gt;
* [[Normal]]&lt;br /&gt;
* [[Normal_additive_gro]]      (Multivariate Distributions.ana)&lt;br /&gt;
* [[Normal_compound_gro]]      (Multivariate Distributions.ana)&lt;br /&gt;
* [[Normal_correl]]            (Multivariate Distributions.ana)&lt;br /&gt;
* [[Normal_p1_p2]]                ([[:category:Distribution Variations library functions|Distribution Variations.ana]])&lt;br /&gt;
* [[Normal_serial_correl]]     (Multivariate Distributions.ana)&lt;br /&gt;
* [[Normalize]]&lt;br /&gt;
* [[Not]]&lt;br /&gt;
* [[NPer]]&lt;br /&gt;
* [[Npv]]&lt;br /&gt;
* [[NumberToText]]&lt;br /&gt;
* [[OnDraw_Google_map]]         ( [[:Category:Google Maps from OnGraphDraw library functions|Google Maps from OnGraphDraw library]] )&lt;br /&gt;
* [[OpenExcelFile]]  -- deprecated: use [[SpreadsheetOpen]]&lt;br /&gt;
* [[OpenModelFile]]&lt;br /&gt;
* [[OpenURL]]&lt;br /&gt;
* [[OptEngineInfo]]&lt;br /&gt;
* [[OptFindIIS]]&lt;br /&gt;
* [[OptGuess]]&lt;br /&gt;
* [[OptInfo]]&lt;br /&gt;
* [[OptObjective]]&lt;br /&gt;
* [[OptObjectiveSa]]&lt;br /&gt;
* [[OptRead]]&lt;br /&gt;
* [[OptReducedCost]]&lt;br /&gt;
* [[OptRhsSa]]&lt;br /&gt;
* [[OptScalarToConstraint]]&lt;br /&gt;
* [[OptScalarToDecision]]&lt;br /&gt;
* [[OptShadow]]&lt;br /&gt;
* [[OptSlack]]&lt;br /&gt;
* [[OptSolution]]&lt;br /&gt;
* [[OptStatusNum]]&lt;br /&gt;
* [[OptStatusText]]&lt;br /&gt;
* [[OptWrite]]&lt;br /&gt;
* [[OptWriteIIS]]&lt;br /&gt;
* [[Or]]&lt;br /&gt;
* [[Pareto]]                    (Distribution Variations.ana)&lt;br /&gt;
* [[ParseCSV]]&lt;br /&gt;
* [[ParseDate]]&lt;br /&gt;
* [[ParseExpression]]&lt;br /&gt;
* [[ParsedExprFunction]]&lt;br /&gt;
* [[ParsedExprParameters]]&lt;br /&gt;
* [[ParseJSON]]&lt;br /&gt;
* [[ParseNum]]                  (Flat file library.ana - for Analytica 4.1)&lt;br /&gt;
* [[ParseNumber]]&lt;br /&gt;
* [[Partitions]]&lt;br /&gt;
* [[Pdf]]&lt;br /&gt;
* [[Permutations]]&lt;br /&gt;
* [[Pert]]                      (Distribution Variations.ana)&lt;br /&gt;
* [[Plot_error_bars]]                      ([[:Category:OnGraphDraw annotations library functions|OnGraphDraw annotations library]])&lt;br /&gt;
* [[Plot_point_labels]]                   ([[:Category:OnGraphDraw annotations library functions|OnGraphDraw annotations library]])&lt;br /&gt;
* [[Plot_solid_band]]                     ([[:Category:OnGraphDraw annotations library functions|OnGraphDraw annotations library]])&lt;br /&gt;
* [[Plot_solid_prob_bands]]         ([[:Category:OnGraphDraw annotations library functions|OnGraphDraw annotations library]])&lt;br /&gt;
* [[Plot_Tukey_bars]]                     ([[:Category:OnGraphDraw annotations library functions|OnGraphDraw annotations library]])&lt;br /&gt;
* [[Pmt]]&lt;br /&gt;
* [[Poisson]]&lt;br /&gt;
* [[PolarToC]]                  (Complex Library.ana)&lt;br /&gt;
* [[PolyGamma]]&lt;br /&gt;
* [[Index Position Operator::@|Position Operator: @I, @[I=x] ]] : Get the numeric position of index elements&lt;br /&gt;
* [[PositionInIndex]]&lt;br /&gt;
* [[PowerMod]]&lt;br /&gt;
* [[PPmt]]&lt;br /&gt;
* [[Probability]]&lt;br /&gt;
* [[ProbBands]]&lt;br /&gt;
* [[ProbDist]]&lt;br /&gt;
* [[Probit_Regression]] (Generalized Regression.ana)&lt;br /&gt;
* [[ProbTable]]&lt;br /&gt;
* [[Prob_Bernoulli]]        ([[Distribution Densities Library]])&lt;br /&gt;
* [[Prob_Binomial]]         ([[Distribution Densities Library]])&lt;br /&gt;
* [[Prob_ChanceDist]]       ([[Distribution Densities Library]])&lt;br /&gt;
* [[Prob_Geometric]]        ([[Distribution Densities Library]])&lt;br /&gt;
* [[Prob_HyperGeometric]]   ([[Distribution Densities Library]])&lt;br /&gt;
* [[Prob_NegativeBinomia]]  ([[Distribution Densities Library]])&lt;br /&gt;
* [[Prob_Poisson]]          ([[Distribution Densities Library]])&lt;br /&gt;
* [[ProbWilcoxon]]&lt;br /&gt;
* [[Product]]&lt;br /&gt;
* [[ProductLog]]&lt;br /&gt;
* [[Putoption]]        (Financial Library.ana)&lt;br /&gt;
* [[Pv]]&lt;br /&gt;
* [[Pvgperp]]          (Financial Library.ana)&lt;br /&gt;
* [[Pvperp]]           (Financial Library.ana)&lt;br /&gt;
* [[QpDefine]]&lt;br /&gt;
* [[Q_infromrec]]&lt;br /&gt;
* [[Q_makerect]]&lt;br /&gt;
* [[Q_squareinterp]]&lt;br /&gt;
* [[Radians]]&lt;br /&gt;
* [[Random]]&lt;br /&gt;
* [[RandomBytes]]&lt;br /&gt;
* [[Rank]]&lt;br /&gt;
* [[RankCorrel]]&lt;br /&gt;
* [[The Sensitivity Analysis Library#Importance Details|RankCorrelDetails]]   ([[The Sensitivity Analysis Library]])&lt;br /&gt;
* [[Rate]]&lt;br /&gt;
* [[Rayleigh]]         (Distribution variations.ana)&lt;br /&gt;
* [[Re]]               (Complex Library.ana)&lt;br /&gt;
* [[ReadBinaryFile]]&lt;br /&gt;
* [[ReadCsvFile]]&lt;br /&gt;
* [[ReadExportFile]]&lt;br /&gt;
* [[ReadFromUrl]]&lt;br /&gt;
* [[ReadImageFile]]&lt;br /&gt;
* [[ReadTextFile]]&lt;br /&gt;
* [[RealPart]]&lt;br /&gt;
* [[Reference Operator::\|Reference Operator: \X]] : Return a reference to X&lt;br /&gt;
* [[Reform]]&lt;br /&gt;
* [[Regression]]&lt;br /&gt;
* [[RegressionDist]]       (Multivariate Distributions.ana)&lt;br /&gt;
* [[RegressionFitProb]]    (Multivariate Distributions.ana)&lt;br /&gt;
* [[RegressionNoise]]      (Multivariate Distributions.ana)&lt;br /&gt;
* [[Relu]]&lt;br /&gt;
* [[ReThrow]]&lt;br /&gt;
* [[Reverse]]&lt;br /&gt;
* [[Round]]&lt;br /&gt;
* [[RunConsoleProcess]]&lt;br /&gt;
* [[Sample]]&lt;br /&gt;
* [[SampleCorrelation]]    (Multivariate Distributions.ana)&lt;br /&gt;
* [[SampleCovariance]]     (Multivariate Distributions.ana)&lt;br /&gt;
* [[SaveExcelWorkbook]]  -- deprecated: use [[SpreadsheetSave]]&lt;br /&gt;
* [[ScanAttFromModelFile]]&lt;br /&gt;
* [[SchedulePublish]]&lt;br /&gt;
* [[SDeviation]]&lt;br /&gt;
* [[SelectText]]&lt;br /&gt;
* [[The Sensitivity Analysis Library#Input Number|SensitivityIndex]]   ([[The Sensitivity Analysis Library]])&lt;br /&gt;
* [[Sequence]]&lt;br /&gt;
* [[Sequence Operator|Sequence Operator: first..last]]&lt;br /&gt;
* [[SetContains]]&lt;br /&gt;
* [[SetDifference]]&lt;br /&gt;
* [[SetEvaluationFlag]]&lt;br /&gt;
* [[SetIntersection]]&lt;br /&gt;
* [[SetUnion]]&lt;br /&gt;
* [[ShowAskAttribute]]&lt;br /&gt;
* [[ShowPdfFile]]&lt;br /&gt;
* [[ShowProgressBar]]&lt;br /&gt;
* [[ShowWindow]]&lt;br /&gt;
* [[Shuffle]]&lt;br /&gt;
* [[Sign]]&lt;br /&gt;
* [[Trig Functions| Sin]]&lt;br /&gt;
* [[SingularValueDecomp]]&lt;br /&gt;
* [[Trig Functions| Sinh]]&lt;br /&gt;
* [[SipDecode]]&lt;br /&gt;
* [[SipEncode]]&lt;br /&gt;
* [[Size]]&lt;br /&gt;
* [[Skewness]]&lt;br /&gt;
* [[Sleep]]&lt;br /&gt;
* [[Slice]]&lt;br /&gt;
* [[Slider]]&lt;br /&gt;
* [[Subscript/Slice Operator| Slice Operator: [@I=n] ]]&lt;br /&gt;
* [[Smooth_Fractile]]     (Distribution variations.ana)&lt;br /&gt;
* [[SobolSequence]]&lt;br /&gt;
* [[Solve]]               (Optimization Functions.ana)&lt;br /&gt;
* [[SolverInfo]]&lt;br /&gt;
* [[Sort]]&lt;br /&gt;
* [[SortIndex]]&lt;br /&gt;
* [[Split]]&lt;br /&gt;
* [[SplitText]]&lt;br /&gt;
* [[SpreadsheetCell]]&lt;br /&gt;
* [[SpreadsheetInfo]]&lt;br /&gt;
* [[SpreadsheetOpen]]&lt;br /&gt;
* [[SpreadsheetRange]]&lt;br /&gt;
* [[SpreadsheetSave]]&lt;br /&gt;
* [[SpreadsheetSetCell]]&lt;br /&gt;
* [[SpreadsheetSetInfo]]&lt;br /&gt;
* [[SpreadsheetSetRange]]&lt;br /&gt;
* [[SqlDriverInfo]]&lt;br /&gt;
* [[Sqr]]&lt;br /&gt;
* [[Sqrt]]&lt;br /&gt;
* [[Statistics]]&lt;br /&gt;
* [[StepInterp]]&lt;br /&gt;
* [[StringLength]]&lt;br /&gt;
* [[StringLowercase]]&lt;br /&gt;
* [[StringMixedCase]]&lt;br /&gt;
* [[StringReplace]]&lt;br /&gt;
* [[StringUpperCase]]&lt;br /&gt;
* [[StudentT]]&lt;br /&gt;
* [[SubFindString]]&lt;br /&gt;
* [[SubIndex]]&lt;br /&gt;
* [[Subscript]]&lt;br /&gt;
* [[Subscript/Slice Operator| Subscript Operator: [I=x] ]] &lt;br /&gt;
* [[Subset]]&lt;br /&gt;
* [[SubString]]&lt;br /&gt;
* [[SubTable]]&lt;br /&gt;
* [[Sum]]&lt;br /&gt;
* [[The Sensitivity Analysis Library#Importance Details|SWRSensDetails]]   ([[The Sensitivity Analysis Library]])&lt;br /&gt;
* [[Sys_coordindex]]&lt;br /&gt;
* [[Sys_localindex]]&lt;br /&gt;
* [[SysMenu_AddItem]]&lt;br /&gt;
* [[Table]]&lt;br /&gt;
* [[TableJoin]]&lt;br /&gt;
* [[Trig Functions| Tan]]&lt;br /&gt;
* [[Trig Functions| Tanh]]&lt;br /&gt;
* [[Test_map_pivot_slice]]         ( [[:Category:Google Maps from OnGraphDraw library functions|Google Maps from OnGraphDraw library]] )&lt;br /&gt;
* [[Test_map_pivot_vars]]         ( [[:Category:Google Maps from OnGraphDraw library functions|Google Maps from OnGraphDraw library]] )&lt;br /&gt;
* [[TestHeapConsistency]]&lt;br /&gt;
* [[TextCharacterEncode]]&lt;br /&gt;
* [[Text Concatenation Operator: &amp;amp;|Text Concatenation Operator: A&amp;amp;B]] : Concatenate two text strings&lt;br /&gt;
* [[TextDistance]]&lt;br /&gt;
* [[TextLength]]&lt;br /&gt;
* [[TextLowerCase]]&lt;br /&gt;
* [[TextReplace]]&lt;br /&gt;
* [[TextSentenceCase]]&lt;br /&gt;
* [[TextTrim]]&lt;br /&gt;
* [[TextUpperCase]]&lt;br /&gt;
* [[Today]]&lt;br /&gt;
* [[_TrackAttChanges]]&lt;br /&gt;
* [[Transpose]]&lt;br /&gt;
* [[Triangular]]&lt;br /&gt;
* [[Triangular_10_50_90]]  (Distribution Variations.ana)&lt;br /&gt;
* [[Triangular_10_mode_90]] (Distribution Variations.ana)&lt;br /&gt;
* [[Truncate]]&lt;br /&gt;
* [[Try]]&lt;br /&gt;
* [[Data_Type_Functions#Function_TypeOf| TypeOf]]&lt;br /&gt;
* [[UncertainLMH]]&lt;br /&gt;
* [[Uncumulate]]&lt;br /&gt;
* [[Unflatten]]&lt;br /&gt;
* [[Uniform]]&lt;br /&gt;
* [[UniformSpherical]]    (Multivariate distributions.ana)&lt;br /&gt;
* [[Unique]]&lt;br /&gt;
* [[Using..Do]]&lt;br /&gt;
* [[Var..Do]]&lt;br /&gt;
* [[Variance]]&lt;br /&gt;
* [[VarTerm]]&lt;br /&gt;
* [[VectorCrossProduct]]&lt;br /&gt;
* [[Expected value of information -- EVI, EVPI, and ESVI#Function VPI(v, d)|VPI]]   ([[Expected value of information -- EVI, EVPI, and ESVI|Value of Information library]])&lt;br /&gt;
* [[Wacc]]               (Financial Library.ana)&lt;br /&gt;
* [[Wald]]               (Distribution Variations.ana)&lt;br /&gt;
* [[Warp_Dist]]          (Distribution variations.ana)&lt;br /&gt;
* [[_WebConnectionClose]]&lt;br /&gt;
* [[_WebConnectionRead]]&lt;br /&gt;
* [[_WebConnectionSend]]&lt;br /&gt;
* [[_WebConnectionStatus]]&lt;br /&gt;
* [[Weibull]]&lt;br /&gt;
* [[WhatIf]]&lt;br /&gt;
* [[WhatIfAll]]&lt;br /&gt;
* [[While..Do]]&lt;br /&gt;
* [[Wilcoxon]]&lt;br /&gt;
* [[Wishart]]            (Distribution variations.ana)&lt;br /&gt;
* [[WorksheetCell]]  -- deprecated: use [[SpreadsheetCell]]&lt;br /&gt;
* [[WorksheetRange]] -- deprecated: use [[SpreadsheetRange]]&lt;br /&gt;
* [[WriteBinaryFile]]&lt;br /&gt;
* [[WriteCsvFile]]&lt;br /&gt;
* [[WriteImageFile]]&lt;br /&gt;
* [[WriteTableSql]]      (ODBC Library.ana)&lt;br /&gt;
* [[WriteTextFile]]&lt;br /&gt;
* [[WriteWorksheetCell]] -- deprecated: use [[SpreadsheetSetCell]]&lt;br /&gt;
* [[WriteWorksheetRange]] -- deprecated: use [[SpreadsheetSetRange]]&lt;br /&gt;
* [[XIrr]]&lt;br /&gt;
* [[XNpv]]&lt;br /&gt;
* [[YearFrac]]&lt;/div&gt;</summary>
		<author><name>Lchrisman</name></author>
	</entry>
	<entry>
		<id>https://docs.analytica.com/index.php?title=RandomBytes&amp;diff=64601</id>
		<title>RandomBytes</title>
		<link rel="alternate" type="text/html" href="https://docs.analytica.com/index.php?title=RandomBytes&amp;diff=64601"/>
		<updated>2026-09-22T17:41:51Z</updated>

		<summary type="html">&lt;p&gt;Lchrisman: ER 22440: point the redirect at the function-reference anchor&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;#REDIRECT [[Encrypting and decrypting#RandomBytes]]&lt;br /&gt;
[[category:Encrypting and hashing functions]]&lt;/div&gt;</summary>
		<author><name>Lchrisman</name></author>
	</entry>
	<entry>
		<id>https://docs.analytica.com/index.php?title=Decrypt&amp;diff=64600</id>
		<title>Decrypt</title>
		<link rel="alternate" type="text/html" href="https://docs.analytica.com/index.php?title=Decrypt&amp;diff=64600"/>
		<updated>2026-09-22T17:41:51Z</updated>

		<summary type="html">&lt;p&gt;Lchrisman: ER 22440: point the redirect at the function-reference anchor&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;#REDIRECT [[Encrypting and decrypting#Decrypt]]&lt;br /&gt;
[[category:Encrypting and hashing functions]]&lt;/div&gt;</summary>
		<author><name>Lchrisman</name></author>
	</entry>
	<entry>
		<id>https://docs.analytica.com/index.php?title=Encrypt&amp;diff=64599</id>
		<title>Encrypt</title>
		<link rel="alternate" type="text/html" href="https://docs.analytica.com/index.php?title=Encrypt&amp;diff=64599"/>
		<updated>2026-09-22T17:41:51Z</updated>

		<summary type="html">&lt;p&gt;Lchrisman: ER 22440: point the redirect at the function-reference anchor&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;#REDIRECT [[Encrypting and decrypting#Encrypt]]&lt;br /&gt;
[[category:Encrypting and hashing functions]]&lt;/div&gt;</summary>
		<author><name>Lchrisman</name></author>
	</entry>
	<entry>
		<id>https://docs.analytica.com/index.php?title=EncryptionKey&amp;diff=64598</id>
		<title>EncryptionKey</title>
		<link rel="alternate" type="text/html" href="https://docs.analytica.com/index.php?title=EncryptionKey&amp;diff=64598"/>
		<updated>2026-09-22T17:41:51Z</updated>

		<summary type="html">&lt;p&gt;Lchrisman: ER 22440: point the redirect at the function-reference anchor&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;#REDIRECT [[Encrypting and decrypting#EncryptionKey]]&lt;br /&gt;
[[category:Encrypting and hashing functions]]&lt;/div&gt;</summary>
		<author><name>Lchrisman</name></author>
	</entry>
	<entry>
		<id>https://docs.analytica.com/index.php?title=Encrypting_and_decrypting&amp;diff=64597</id>
		<title>Encrypting and decrypting</title>
		<link rel="alternate" type="text/html" href="https://docs.analytica.com/index.php?title=Encrypting_and_decrypting&amp;diff=64597"/>
		<updated>2026-09-22T17:41:28Z</updated>

		<summary type="html">&lt;p&gt;Lchrisman: ER 22440: document the Crypto namespace -- EncryptionKey, Encrypt, Decrypt, RandomBytes, key-&amp;gt;Export()&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[category:Encrypting and hashing functions]]&lt;br /&gt;
&lt;br /&gt;
{{ReleaseBar}}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;New to [[Analytica 7.2]].&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Analytica can encrypt and decrypt data using strong, standard, &#039;&#039;authenticated&#039;&#039; encryption. The four functions live in the &#039;&#039;&#039;&amp;lt;code&amp;gt;Crypto&amp;lt;/code&amp;gt;&#039;&#039;&#039; namespace:&lt;br /&gt;
&lt;br /&gt;
* [[#EncryptionKey|Crypto::EncryptionKey]] -- makes a key, which is a value of its own data type.&lt;br /&gt;
* [[#Encrypt|Crypto::Encrypt]] -- seals text or binary data under a key.&lt;br /&gt;
* [[#Decrypt|Crypto::Decrypt]] -- opens a sealed value, or reports an error if anything about it has changed.&lt;br /&gt;
* [[#RandomBytes|Crypto::RandomBytes]] -- cryptographically random bytes, for salts and keys.&lt;br /&gt;
&lt;br /&gt;
: &amp;lt;code&amp;gt;[[Variable]] Salt := Crypto::RandomBytes(16)&amp;lt;/code&amp;gt;&lt;br /&gt;
: &amp;lt;code&amp;gt;[[Variable]] MyKey := Crypto::EncryptionKey( password: &#039;correct horse battery staple&#039;, salt: Salt )&amp;lt;/code&amp;gt;&lt;br /&gt;
: &amp;lt;code&amp;gt;[[Variable]] Sealed := Crypto::Encrypt( &#039;launch codes&#039;, MyKey )&amp;lt;/code&amp;gt;&lt;br /&gt;
: &amp;lt;code&amp;gt;[[Variable]] Plain := Crypto::Decrypt( Sealed, MyKey ) &amp;amp;rarr; &#039;launch codes&#039;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== The Crypto namespace ==&lt;br /&gt;
&lt;br /&gt;
These are specialist functions, so they are not in your model&#039;s namespace to begin with. A model that never encrypts anything is not troubled by them, and they cannot collide with your own identifiers. You reach them in either of two ways.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Qualify each call&#039;&#039;&#039; with &amp;lt;code&amp;gt;Crypto::&amp;lt;/code&amp;gt;, as in the example above. Nothing is needed to make this work.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Or import the namespace once.&#039;&#039;&#039; Put &amp;lt;code&amp;gt;Crypto&amp;lt;/code&amp;gt; on a line of its own in your model&#039;s [[NamespaceImports]] attribute, and then write the names bare:&lt;br /&gt;
&lt;br /&gt;
: &amp;lt;code&amp;gt;Encrypt( &#039;launch codes&#039;, MyKey )&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The rest of this page writes &amp;lt;code&amp;gt;Crypto::&amp;lt;/code&amp;gt; explicitly so that each example works whether or not you have imported the namespace.&lt;br /&gt;
&lt;br /&gt;
== What authenticated encryption means ==&lt;br /&gt;
&lt;br /&gt;
All four algorithms here are &#039;&#039;AEAD&#039;&#039; ciphers -- Authenticated Encryption with Associated Data. Along with the encrypted data, a sealed value carries an &#039;&#039;authentication tag&#039;&#039; computed from the key. [[#Decrypt|Crypto::Decrypt]] checks the tag before it returns anything, so a sealed value that has been altered -- even by a single bit, even by someone who cannot read it -- produces an error instead of altered data.&lt;br /&gt;
&lt;br /&gt;
This is what you want, and it is not what simpler schemes give you. Encryption on its own hides data but does not stop an attacker from changing it in ways that change the decrypted result predictably. Nothing on this page lets you do unauthenticated encryption, deliberately.&lt;br /&gt;
&lt;br /&gt;
The error from a failed decryption is deliberately the same whether the key was wrong, the «aad» was wrong, or the data was altered. Telling those cases apart would help an attacker work out a key one guess at a time.&lt;br /&gt;
&lt;br /&gt;
== Keys ==&lt;br /&gt;
&lt;br /&gt;
A key is a value of its own type, made by [[#EncryptionKey|Crypto::EncryptionKey]]. It carries the algorithm along with the key material, so a key and an algorithm can never disagree, and [[#Encrypt|Crypto::Encrypt]] needs no «algorithm» parameter.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Make the key its own variable.&#039;&#039;&#039; Deriving a key from a password takes a noticeable fraction of a second by design (see [[#Passwords and salts|Passwords and salts]]). When the key is a variable, Analytica computes it once and reuses it, so encrypting a whole column of a table costs one derivation, not one per cell. Writing the derivation inline inside [[#Encrypt|Crypto::Encrypt]] would repeat it for every cell.&lt;br /&gt;
&lt;br /&gt;
The key material itself cannot be read from any expression. Printing a key shows only what it is:&lt;br /&gt;
&lt;br /&gt;
: &amp;lt;code&amp;gt;MyKey &amp;amp;rarr; «EncryptionKey AES-256-GCM #7a2f»&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A key has four readable members:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;MyKey-&amp;amp;gt;algorithm&amp;lt;/code&amp;gt; -- &amp;lt;code&amp;gt;&#039;AES-256-GCM&#039;&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;&#039;AES-192-GCM&#039;&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;&#039;AES-128-GCM&#039;&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;&#039;ChaCha20-Poly1305&#039;&amp;lt;/code&amp;gt;.&lt;br /&gt;
* &amp;lt;code&amp;gt;MyKey-&amp;amp;gt;keyBits&amp;lt;/code&amp;gt; -- 128, 192 or 256.&lt;br /&gt;
* &amp;lt;code&amp;gt;MyKey-&amp;amp;gt;fingerprint&amp;lt;/code&amp;gt; -- four hexadecimal digits that identify the key without revealing it. Two keys are the same key if and only if their fingerprints match (to a very high probability). Useful for answering &amp;quot;did this deployment get the key I think it did?&amp;quot; without ever displaying a key.&lt;br /&gt;
* &amp;lt;code&amp;gt;MyKey-&amp;amp;gt;fromSecret&amp;lt;/code&amp;gt; -- [[True]] when the key material came from a [[Secret]].&lt;br /&gt;
&lt;br /&gt;
=== Passwords and salts ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;Crypto::EncryptionKey( password: p, salt: s )&amp;lt;/code&amp;gt; stretches a password into a key using PBKDF2-HMAC-SHA-256 with «iterations» rounds, 600,000 by default. The iterations are the point: they make each guess at your password expensive for an attacker, which is the only defence a human-chosen password has.&lt;br /&gt;
&lt;br /&gt;
The «salt» is &#039;&#039;&#039;not&#039;&#039;&#039; secret. Keep it in an ordinary variable next to the key, and save it with your model -- you need the same salt to derive the same key again. Its job is to make precomputed attack tables useless, which it does even though it is public. Make one with &amp;lt;code&amp;gt;[[#RandomBytes|Crypto::RandomBytes]](16)&amp;lt;/code&amp;gt; and then leave it alone; changing the salt changes the key, and data encrypted under the old key can no longer be read.&lt;br /&gt;
&lt;br /&gt;
=== Keeping the key out of the model ===&lt;br /&gt;
&lt;br /&gt;
If the key is written in the model, anyone who has the model has the key, and the encryption protects nothing. The usual arrangement is the other way round: the &#039;&#039;sealed data&#039;&#039; travels in the &amp;lt;code&amp;gt;.ana&amp;lt;/code&amp;gt; file and the &#039;&#039;key&#039;&#039; does not.&lt;br /&gt;
&lt;br /&gt;
Give «key» or «password» a [[Secret]] to do that. The secret&#039;s value reaches the key derivation without ever entering the model as a value:&lt;br /&gt;
&lt;br /&gt;
: &amp;lt;code&amp;gt;Crypto::EncryptionKey( key: MyKeySecret )&amp;lt;/code&amp;gt;&lt;br /&gt;
: &amp;lt;code&amp;gt;Crypto::EncryptionKey( password: MyPasswordSecret, salt: Salt )&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The [[Secret]] needs two things set before Analytica will allow this:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Sinks&#039;&#039;&#039; must list &amp;lt;code&amp;gt;EncryptionKey&amp;lt;/code&amp;gt;.&lt;br /&gt;
* &#039;&#039;&#039;Destination&#039;&#039;&#039; must be the single word &amp;lt;code&amp;gt;local&amp;lt;/code&amp;gt;. Unlike a URL or a database connection string, key material never leaves the Analytica process, so there is no destination to pin -- but the setting is still required, so that allowing it is a deliberate act.&lt;br /&gt;
&lt;br /&gt;
Consider also setting the secret&#039;s &#039;&#039;&#039;Caller&#039;&#039;&#039; to the module that is allowed to build the key, which restricts the use of the secret far more tightly than any destination could.&lt;br /&gt;
&lt;br /&gt;
A key made from a [[Secret]] refuses &amp;lt;code&amp;gt;-&amp;amp;gt;Export()&amp;lt;/code&amp;gt;, and reads &amp;lt;code&amp;gt;fromSecret = True&amp;lt;/code&amp;gt;. That is deliberate: a secret&#039;s value must never come back into the model, and exporting the key it produced would be exactly that.&lt;br /&gt;
&lt;br /&gt;
=== Generating and saving a key ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;Crypto::EncryptionKey()&amp;lt;/code&amp;gt; with no arguments makes a fresh random key. To use the same key again later, export the material and store it somewhere outside the model -- a [[Secret]], a file, a password manager:&lt;br /&gt;
&lt;br /&gt;
: &amp;lt;code&amp;gt;Crypto::EncryptionKey()-&amp;amp;gt;Export() &amp;amp;rarr; a 32-byte binary value&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To see it as text you can copy, format it:&lt;br /&gt;
&lt;br /&gt;
: &amp;lt;code&amp;gt;f&amp;quot;{Crypto::EncryptionKey()-&amp;amp;gt;Export():b}&amp;quot; &amp;amp;rarr; base64&#039;8Xk2wQ...&#039;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Paste that &amp;lt;code&amp;gt;base64&#039;...&#039;&amp;lt;/code&amp;gt; literal back into an expression to rebuild the same key:&lt;br /&gt;
&lt;br /&gt;
: &amp;lt;code&amp;gt;Crypto::EncryptionKey( key: base64&#039;8Xk2wQ...&#039; )&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;A key cannot be stored in a variable&#039;&#039;&#039; the way other values can. Assigning one to a variable&#039;s definition is refused, because that would write the key material into the &amp;lt;code&amp;gt;.ana&amp;lt;/code&amp;gt; file. Build the key where it is used instead.&lt;br /&gt;
&lt;br /&gt;
== Sealed values ==&lt;br /&gt;
&lt;br /&gt;
[[#Encrypt|Crypto::Encrypt]] returns [[In-memory binary data terms|binary data]] by default. The &amp;lt;code&amp;gt;&#039;analytica&#039;&amp;lt;/code&amp;gt; container adds 36 bytes to whatever you encrypted: a marker that says this is an Analytica sealed value, which algorithm made it, whether the plaintext was text or binary, the nonce, and the authentication tag.&lt;br /&gt;
&lt;br /&gt;
Because the container records whether the plaintext was text or binary, [[#Decrypt|Crypto::Decrypt]] hands back the same kind of value you encrypted, with nothing extra to say.&lt;br /&gt;
&lt;br /&gt;
=== Every call produces a different result ===&lt;br /&gt;
&lt;br /&gt;
Each call draws a fresh random &#039;&#039;nonce&#039;&#039;, so encrypting the same text twice gives two different sealed values. That is required -- reusing a nonce with the same key breaks the encryption completely -- but it has a practical consequence in a model: a sealed value computed by a definition changes every time the definition is re-evaluated.&lt;br /&gt;
&lt;br /&gt;
So do not leave &amp;lt;code&amp;gt;Crypto::Encrypt(...)&amp;lt;/code&amp;gt; in the definition of a variable whose result you intend to keep. Compute the sealed value once, in a button script, and assign it:&lt;br /&gt;
&lt;br /&gt;
: &amp;lt;code&amp;gt;Sealed := Crypto::Encrypt( Plaintext, MyKey )&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The assignment writes the sealed value into &amp;lt;code&amp;gt;Sealed&amp;lt;/code&amp;gt;&#039;s definition as a &amp;lt;code&amp;gt;base64&#039;...&#039;&amp;lt;/code&amp;gt; literal, which is saved with the model and decrypts unchanged when it is reopened.&lt;br /&gt;
&lt;br /&gt;
=== Binding a sealed value to its context ===&lt;br /&gt;
&lt;br /&gt;
The optional «aad» parameter -- additional authenticated data -- is authenticated but not encrypted. Whoever decrypts must supply the same «aad» or the decryption fails.&lt;br /&gt;
&lt;br /&gt;
Use it to tie each sealed value to where it belongs. If you seal a column of salaries with no «aad», someone who cannot read them can still swap two rows and go undetected. Seal them with the employee as «aad» and the swap is caught:&lt;br /&gt;
&lt;br /&gt;
: &amp;lt;code&amp;gt;Crypto::Encrypt( Salary, MyKey, aad: Employee )&amp;lt;/code&amp;gt;&lt;br /&gt;
: &amp;lt;code&amp;gt;Crypto::Decrypt( SealedSalary, MyKey, aad: Employee )&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The «aad» is not secret and does not need to be hidden -- the reader has to know it anyway.&lt;br /&gt;
&lt;br /&gt;
== Encrypting a whole table ==&lt;br /&gt;
&lt;br /&gt;
«data» and «aad» are atomic parameters, so both functions [[Array Abstraction|array abstract]]. One expression seals an entire indexed table, each cell under its own fresh nonce, all under the one key:&lt;br /&gt;
&lt;br /&gt;
: &amp;lt;code&amp;gt;[[Variable]] SealedRows := Crypto::Encrypt( PlainRows, MyKey )&amp;lt;/code&amp;gt;&lt;br /&gt;
: &amp;lt;code&amp;gt;[[Variable]] PlainAgain := Crypto::Decrypt( SealedRows, MyKey )&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is where making the key its own variable pays off: the key is derived once and every cell reuses it.&lt;br /&gt;
&lt;br /&gt;
== Interoperating with other systems ==&lt;br /&gt;
&lt;br /&gt;
To exchange sealed data with something outside Analytica, you need to agree on two things: how the bytes are packaged, and how they are written as text.&lt;br /&gt;
&lt;br /&gt;
=== Layouts ===&lt;br /&gt;
&lt;br /&gt;
There is no single universal convention for packaging a nonce, a ciphertext and an authentication tag, so «layout» lets you pick the one your counterpart uses.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! «layout» !! The sealed value contains !! Used by&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;&#039;analytica&#039;&amp;lt;/code&amp;gt; (default) || A self-describing container: marker, algorithm, text-or-binary flag, nonce, tag, ciphertext || Analytica&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;&#039;nonce,ct,tag&#039;&amp;lt;/code&amp;gt; || nonce, then ciphertext, then tag || The common Go idiom, and most published examples&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;&#039;ct,tag&#039;&amp;lt;/code&amp;gt; || ciphertext, then tag; the nonce is separate || Python&#039;s &amp;lt;code&amp;gt;cryptography&amp;lt;/code&amp;gt; package, libsodium&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;&#039;nonce,tag,ct&#039;&amp;lt;/code&amp;gt; || nonce, then tag, then ciphertext ||&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;&#039;none&#039;&amp;lt;/code&amp;gt; || the bare ciphertext; nonce and tag are separate || Node.js &amp;lt;code&amp;gt;crypto&amp;lt;/code&amp;gt;, .NET &amp;lt;code&amp;gt;AesGcm&amp;lt;/code&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Whatever the layout, the nonce and the tag are &#039;&#039;&#039;also&#039;&#039;&#039; available as the second and third return values, so you can always get at them:&lt;br /&gt;
&lt;br /&gt;
: &amp;lt;code&amp;gt;[[Local]] (sealed, nonce, tag) := Crypto::Encrypt( x, MyKey );&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
and supply them again when decrypting a layout that does not carry them:&lt;br /&gt;
&lt;br /&gt;
: &amp;lt;code&amp;gt;Crypto::Decrypt( ct, MyKey, nonce: n, tag: t, layout: &#039;none&#039;, as: &#039;text&#039; )&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The raw layouts carry no record of whether the plaintext was text or binary, so «as» is required with them.&lt;br /&gt;
&lt;br /&gt;
=== Text encodings ===&lt;br /&gt;
&lt;br /&gt;
«format» writes the sealed value as text instead of binary data -- &amp;lt;code&amp;gt;&#039;base64&#039;&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;&#039;base64url&#039;&amp;lt;/code&amp;gt; (the URL-safe alphabet, no padding) or &amp;lt;code&amp;gt;&#039;hex&#039;&amp;lt;/code&amp;gt;. [[#Decrypt|Crypto::Decrypt]] reads text in the same encodings.&lt;br /&gt;
&lt;br /&gt;
: &amp;lt;code&amp;gt;Crypto::Encrypt( &#039;hello&#039;, MyKey, format: &#039;base64&#039; )&amp;lt;/code&amp;gt;&lt;br /&gt;
: &amp;lt;code&amp;gt;Crypto::Decrypt( sealedText, MyKey, format: &#039;base64&#039; )&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== A worked example ===&lt;br /&gt;
&lt;br /&gt;
This Python produces a value Analytica reads, and reads a value Analytica produced. It uses the &amp;lt;code&amp;gt;cryptography&amp;lt;/code&amp;gt; package.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
from cryptography.hazmat.primitives.ciphers.aead import AESGCM&lt;br /&gt;
import base64&lt;br /&gt;
&lt;br /&gt;
key   = bytes(range(32))        # the same 32 bytes as base64&#039;AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8=&#039;&lt;br /&gt;
nonce = bytes(range(12))&lt;br /&gt;
&lt;br /&gt;
# Seal something for Analytica&lt;br /&gt;
ct = AESGCM(key).encrypt(nonce, &#039;from python&#039;.encode(&#039;utf-8&#039;), b&#039;ctx7&#039;)&lt;br /&gt;
print(base64.b64encode(nonce + ct).decode())&lt;br /&gt;
&lt;br /&gt;
# Open something Analytica sealed with layout:&#039;ct,tag&#039;&lt;br /&gt;
plain = AESGCM(key).decrypt(nonce, bytes.fromhex(&#039;66d9d9b2da0e0c4679f3a82524f5e0499271e16f30&#039;), None)&lt;br /&gt;
print(plain.decode(&#039;utf-8&#039;))    # -&amp;gt; hello&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In Analytica:&lt;br /&gt;
&lt;br /&gt;
: &amp;lt;code&amp;gt;[[Variable]] K := Crypto::EncryptionKey( key: base64&#039;AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8=&#039; )&amp;lt;/code&amp;gt;&lt;br /&gt;
: &amp;lt;code&amp;gt;Crypto::Decrypt( base64&#039;AAECAwQFBgcICQoLIXC5duWVu2/lLvmrU3Xrc1+3jRqJx0hC/3QKjDlK2w==&#039;, K, layout: &#039;nonce,ct,tag&#039;, aad: &#039;ctx7&#039;, as: &#039;text&#039; ) &amp;amp;rarr; &#039;from python&#039;&amp;lt;/code&amp;gt;&lt;br /&gt;
: &amp;lt;code&amp;gt;Crypto::Encrypt( &#039;hello&#039;, K, nonce: base64&#039;AAAAAAAAAAAAAAAA&#039;, layout: &#039;ct,tag&#039;, format: &#039;hex&#039; ) &amp;amp;rarr; &#039;66d9d9b2da0e0c4679f3a82524f5e0499271e16f30&#039;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The «nonce» parameter is used here only to make the output reproducible so it can be checked against a published test vector. &#039;&#039;&#039;Do not supply a nonce in real use.&#039;&#039;&#039; Reusing a nonce with the same key destroys the security of the encryption completely; omitting it is always the right thing.&lt;br /&gt;
&lt;br /&gt;
=== The &#039;analytica&#039; container ===&lt;br /&gt;
&lt;br /&gt;
If you need to read Analytica&#039;s own container from another language, its bytes are:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Offset !! Length !! Field&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 4 || The four characters &amp;lt;code&amp;gt;ACRY&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| 4 || 1 || Format version, currently 1&lt;br /&gt;
|-&lt;br /&gt;
| 5 || 1 || Algorithm: 1 = AES-128-GCM, 2 = AES-192-GCM, 3 = AES-256-GCM, 4 = ChaCha20-Poly1305&lt;br /&gt;
|-&lt;br /&gt;
| 6 || 1 || Flags. Bit 0 set means the plaintext was text, encoded as UTF-8. Bit 1 set means an «aad» was supplied.&lt;br /&gt;
|-&lt;br /&gt;
| 7 || 1 || Nonce length, 12 for every algorithm above&lt;br /&gt;
|-&lt;br /&gt;
| 8 || 12 || Nonce&lt;br /&gt;
|-&lt;br /&gt;
| 20 || 16 || Authentication tag&lt;br /&gt;
|-&lt;br /&gt;
| 36 || rest || Ciphertext&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The salt and the iteration count are &#039;&#039;&#039;not&#039;&#039;&#039; in the container. They belong to the key, which you construct explicitly, so keep the salt in a variable of your own next to the key that uses it.&lt;br /&gt;
&lt;br /&gt;
= Function reference =&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;EncryptionKey&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Crypto::EncryptionKey( algorithm, key, keyFormat, password, salt, iterations, kdf ) ==&lt;br /&gt;
&lt;br /&gt;
Creates a key for [[#Encrypt|Crypto::Encrypt]] and [[#Decrypt|Crypto::Decrypt]]. Give it either «key» or «password», or neither for a fresh random key.&lt;br /&gt;
&lt;br /&gt;
=== Parameters ===&lt;br /&gt;
&lt;br /&gt;
* «algorithm»: (optional, default &amp;lt;code&amp;gt;&#039;AES-256-GCM&#039;&amp;lt;/code&amp;gt;) One of &amp;lt;code&amp;gt;&#039;AES-256-GCM&#039;&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;&#039;AES-192-GCM&#039;&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;&#039;AES-128-GCM&#039;&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;&#039;ChaCha20-Poly1305&#039;&amp;lt;/code&amp;gt;. ChaCha20-Poly1305 requires Windows 10 version 1903 or later; on an older system it reports that it is unavailable rather than failing obscurely.&lt;br /&gt;
&lt;br /&gt;
* «key»: (optional) Raw key material, as [[In-memory binary data terms|binary data]] -- usually a &amp;lt;code&amp;gt;base64&#039;...&#039;&amp;lt;/code&amp;gt; literal. It must be exactly the algorithm&#039;s key length: 32 bytes for AES-256-GCM and ChaCha20-Poly1305, 24 for AES-192-GCM, 16 for AES-128-GCM. A key of the wrong length is an error; Analytica will not pad or hash it to fit, because that would quietly weaken it. You may also pass a [[Secret]] here, in which case the secret&#039;s text is decoded according to «keyFormat». Plain text that is not a [[Secret]] is refused -- use «password» for a passphrase.&lt;br /&gt;
&lt;br /&gt;
* «keyFormat»: (optional, default &amp;lt;code&amp;gt;&#039;base64&#039;&amp;lt;/code&amp;gt;) How to read the text a [[Secret]] holds: &amp;lt;code&amp;gt;&#039;base64&#039;&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;&#039;base64url&#039;&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;&#039;hex&#039;&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;&#039;utf8&#039;&amp;lt;/code&amp;gt;. Meaningful only when «key» is given a [[Secret]].&lt;br /&gt;
&lt;br /&gt;
* «password»: (optional) A passphrase to derive the key from. Requires «salt». May be given a [[Secret]].&lt;br /&gt;
&lt;br /&gt;
* «salt»: (optional) Text or [[In-memory binary data terms|binary data]], at least 8 bytes, required whenever «password» is given. Not secret; see [[#Passwords and salts|Passwords and salts]].&lt;br /&gt;
&lt;br /&gt;
* «iterations»: (optional, default 600000) PBKDF2 rounds. More is slower and safer. Use the same number every time, or you get a different key.&lt;br /&gt;
&lt;br /&gt;
* «kdf»: (optional, default &amp;lt;code&amp;gt;&#039;PBKDF2-HMAC-SHA-256&#039;&amp;lt;/code&amp;gt;) The key-derivation function. This release supports only that one; the parameter exists so that adding others later does not change how you write the call.&lt;br /&gt;
&lt;br /&gt;
A parameter that could not take effect is refused rather than quietly ignored -- «iterations» without «password», for example.&lt;br /&gt;
&lt;br /&gt;
=== Members ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;-&amp;amp;gt;algorithm&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;-&amp;amp;gt;keyBits&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;-&amp;amp;gt;fingerprint&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;-&amp;amp;gt;fromSecret&amp;lt;/code&amp;gt;, described under [[#Keys|Keys]]. There is no member that returns the key material.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;Export&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== key-&amp;gt;Export( ) ==&lt;br /&gt;
&lt;br /&gt;
Returns the raw key material of a key as [[In-memory binary data terms|binary data]]. This is the only way to get key material back out, and it is meant for saving a randomly generated key so the same key can be used again.&lt;br /&gt;
&lt;br /&gt;
: &amp;lt;code&amp;gt;MyKey-&amp;amp;gt;Export() &amp;amp;rarr; a 32-byte binary value&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A key derived from a [[Secret]] cannot be exported, and reports an error. A secret&#039;s value is never allowed back into the model.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;Encrypt&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Crypto::Encrypt( data, key, aad, nonce, format, layout ) ==&lt;br /&gt;
&lt;br /&gt;
Encrypts «data» under «key» and returns a sealed value that only the same key can open. Returns [[Null]] when «data» is [[Null]].&lt;br /&gt;
&lt;br /&gt;
=== Parameters ===&lt;br /&gt;
&lt;br /&gt;
* «data»: Text or [[In-memory binary data terms|binary data]]. A number is refused rather than converted, since &amp;lt;code&amp;gt;42&amp;lt;/code&amp;gt; could reasonably mean either the text &amp;lt;code&amp;gt;&#039;42&#039;&amp;lt;/code&amp;gt; or eight bytes -- use &amp;lt;code&amp;gt;[[Text]](x)&amp;lt;/code&amp;gt; if you mean its textual form.&lt;br /&gt;
&lt;br /&gt;
* «key»: A key from [[#EncryptionKey|Crypto::EncryptionKey]]. The algorithm comes from the key.&lt;br /&gt;
&lt;br /&gt;
* «aad»: (optional) Additional authenticated data -- authenticated but not encrypted. [[#Decrypt|Crypto::Decrypt]] must be given the same value. See [[#Binding a sealed value to its context|Binding a sealed value to its context]].&lt;br /&gt;
&lt;br /&gt;
* «nonce»: (optional, named) Supplies the nonce instead of generating one. &#039;&#039;&#039;Reusing a nonce with the same key destroys the security of the encryption completely.&#039;&#039;&#039; Omit this unless you are reproducing a published test vector or another system&#039;s exact output.&lt;br /&gt;
&lt;br /&gt;
* «format»: (optional, named, default &amp;lt;code&amp;gt;&#039;binary&#039;&amp;lt;/code&amp;gt;) &amp;lt;code&amp;gt;&#039;binary&#039;&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;&#039;base64&#039;&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;&#039;base64url&#039;&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;&#039;hex&#039;&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
* «layout»: (optional, named, default &amp;lt;code&amp;gt;&#039;analytica&#039;&amp;lt;/code&amp;gt;) See [[#Layouts|Layouts]].&lt;br /&gt;
&lt;br /&gt;
=== Return value ===&lt;br /&gt;
&lt;br /&gt;
The sealed value, as [[In-memory binary data terms|binary data]] or as text according to «format». The nonce and the authentication tag are returned as the second and third return values:&lt;br /&gt;
&lt;br /&gt;
: &amp;lt;code&amp;gt;[[Local]] (sealed, nonce, tag) := Crypto::Encrypt( x, MyKey );&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
It [[Array Abstraction|array abstracts]] over «data» and «aad», sealing each cell under its own nonce.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;Decrypt&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Crypto::Decrypt( data, key, aad, nonce, tag, format, layout, as ) ==&lt;br /&gt;
&lt;br /&gt;
Opens a value sealed by [[#Encrypt|Crypto::Encrypt]]. Returns [[Null]] when «data» is [[Null]].&lt;br /&gt;
&lt;br /&gt;
Reports an error if the value cannot be decrypted -- wrong key, wrong «aad», or data that has been altered. The message deliberately does not say which, because that distinction would help an attacker. Use [[Try]] if your model should carry on regardless.&lt;br /&gt;
&lt;br /&gt;
=== Parameters ===&lt;br /&gt;
&lt;br /&gt;
* «data»: The sealed value, as [[In-memory binary data terms|binary data]], or as text in the encoding named by «format».&lt;br /&gt;
&lt;br /&gt;
* «key»: The same key the value was sealed with. If it is a key for a different algorithm, that is reported specifically, since it is a mistake you can act on rather than a failed decryption.&lt;br /&gt;
&lt;br /&gt;
* «aad»: (optional) Must equal the «aad» the value was sealed with.&lt;br /&gt;
&lt;br /&gt;
* «nonce», «tag»: (optional, named) Required for a «layout» that does not carry them.&lt;br /&gt;
&lt;br /&gt;
* «format»: (optional, named, default &amp;lt;code&amp;gt;&#039;base64&#039;&amp;lt;/code&amp;gt;) How to read a textual «data»: &amp;lt;code&amp;gt;&#039;base64&#039;&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;&#039;base64url&#039;&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;&#039;hex&#039;&amp;lt;/code&amp;gt;. Ignored when «data» is binary.&lt;br /&gt;
&lt;br /&gt;
* «layout»: (optional, named, default &amp;lt;code&amp;gt;&#039;analytica&#039;&amp;lt;/code&amp;gt;) Must match the layout the value was sealed with. See [[#Layouts|Layouts]].&lt;br /&gt;
&lt;br /&gt;
* «as»: (optional, named) &amp;lt;code&amp;gt;&#039;text&#039;&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;&#039;binaryData&#039;&amp;lt;/code&amp;gt;. With the default &amp;lt;code&amp;gt;&#039;analytica&#039;&amp;lt;/code&amp;gt; layout it is optional, because the sealed value records which one was encrypted. For every other layout it is required. &amp;lt;code&amp;gt;&#039;text&#039;&amp;lt;/code&amp;gt; reports an error if the decrypted bytes are not valid UTF-8, which is usually a sign that you wanted &amp;lt;code&amp;gt;&#039;binaryData&#039;&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;RandomBytes&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Crypto::RandomBytes( n ) ==&lt;br /&gt;
&lt;br /&gt;
Returns «n» cryptographically random bytes as [[In-memory binary data terms|binary data]]. Use it for a «salt», or for raw key material.&lt;br /&gt;
&lt;br /&gt;
: &amp;lt;code&amp;gt;[[Variable]] Salt := Crypto::RandomBytes(16)&amp;lt;/code&amp;gt;&lt;br /&gt;
: &amp;lt;code&amp;gt;[[Variable]] NewKey := Crypto::EncryptionKey( key: Crypto::RandomBytes(32) )&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
«n» may not exceed 65536.&lt;br /&gt;
&lt;br /&gt;
Each evaluation returns different bytes. Like [[#Encrypt|Crypto::Encrypt]], this means a definition that calls it does not hold still across re-evaluations -- assign the result once if you need it to stay the same.&lt;br /&gt;
&lt;br /&gt;
== Errors ==&lt;br /&gt;
&lt;br /&gt;
Most mistakes are reported specifically, and can be caught with [[Try]]:&lt;br /&gt;
&lt;br /&gt;
* The «key» is the wrong length for the algorithm, or «key» was given plain text rather than key material or a [[Secret]].&lt;br /&gt;
* Both «key» and «password» were given, or «password» was given without a «salt», or a parameter such as «iterations» cannot take effect.&lt;br /&gt;
* The algorithm is not one of the four, or is not available on this computer.&lt;br /&gt;
* «data» is a number rather than text or binary data.&lt;br /&gt;
* The sealed value is not an Analytica sealed value, or was sealed with a different algorithm, or the text is not valid for the stated «format».&lt;br /&gt;
* A [[Secret]] was passed where a secret makes no sense, such as «aad» or «salt», neither of which is secret.&lt;br /&gt;
* &amp;lt;code&amp;gt;-&amp;amp;gt;Export()&amp;lt;/code&amp;gt; was called on a key derived from a [[Secret]].&lt;br /&gt;
&lt;br /&gt;
The one deliberately vague error is a failed decryption, described under [[#Decrypt|Crypto::Decrypt]].&lt;br /&gt;
&lt;br /&gt;
== See Also ==&lt;br /&gt;
&lt;br /&gt;
* [[Secret]] -- keeping a key out of the model&lt;br /&gt;
* [[TextCharacterEncode]] -- hashing text with SHA-1 or SHA-256, and other text encodings&lt;br /&gt;
* [[In-memory binary data terms]]&lt;br /&gt;
* [[NamespaceImports]]&lt;br /&gt;
* [[:category:Encrypting and hashing functions|Encrypting and hashing functions]]&lt;/div&gt;</summary>
		<author><name>Lchrisman</name></author>
	</entry>
	<entry>
		<id>https://docs.analytica.com/index.php?title=Category:Encrypting_and_hashing_functions&amp;diff=64596</id>
		<title>Category:Encrypting and hashing functions</title>
		<link rel="alternate" type="text/html" href="https://docs.analytica.com/index.php?title=Category:Encrypting_and_hashing_functions&amp;diff=64596"/>
		<updated>2026-09-22T17:05:04Z</updated>

		<summary type="html">&lt;p&gt;Lchrisman: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[category:Functions]]&lt;br /&gt;
Functions used for encryption, decryption, hashing or other cryptographic purposes.&lt;/div&gt;</summary>
		<author><name>Lchrisman</name></author>
	</entry>
	<entry>
		<id>https://docs.analytica.com/index.php?title=TextCharacterEncode&amp;diff=64595</id>
		<title>TextCharacterEncode</title>
		<link rel="alternate" type="text/html" href="https://docs.analytica.com/index.php?title=TextCharacterEncode&amp;diff=64595"/>
		<updated>2026-09-22T17:04:21Z</updated>

		<summary type="html">&lt;p&gt;Lchrisman: category hashing&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[category:Text Functions]]&lt;br /&gt;
[[category:Analytica 5.0]]&lt;br /&gt;
[[category:Encrypting and hashing functions]]&lt;br /&gt;
&lt;br /&gt;
{{ReleaseBar}}&lt;br /&gt;
(&#039;&#039;new to [[Analytica 5.0]]&#039;&#039;)&lt;br /&gt;
&lt;br /&gt;
Converts text to or from many common codings, including URLs, XML, UTF-i, and NFC (unicode).&lt;br /&gt;
&lt;br /&gt;
== TextCharacterEncode( type, text ) ==&lt;br /&gt;
&lt;br /&gt;
Converts «text» into a special encoded or unencoded form according to «type». Possible values for «type» include:&lt;br /&gt;
&lt;br /&gt;
* For encoding or decoding URLs: &amp;lt;code&amp;gt;&#039;URL&#039;&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;&#039;IRI&#039;&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;&#039;URL%&#039;&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;&#039;-URL&#039;&amp;lt;/code&amp;gt;&lt;br /&gt;
* For encoding XML or HTML: &amp;lt;code&amp;gt;&#039;XML&#039;&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;&#039;-XML&#039;&amp;lt;/code&amp;gt;&lt;br /&gt;
* For UTF-8 encodings: &amp;lt;code&amp;gt;&#039;UTF-8&#039;&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;&#039;UTF-8+&#039;&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;&#039;-UTF-8&#039;&amp;lt;/code&amp;gt;&lt;br /&gt;
* For Unicode normalized forms: &amp;lt;code&amp;gt;&#039;NFC&#039;&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;&#039;NFD&#039;&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;&#039;NFKC&#039;&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;&#039;NFKD&#039;&amp;lt;/code&amp;gt;.{{Release|6.5||&lt;br /&gt;
* Hash code: &amp;lt;code&amp;gt;&#039;SHA-1&#039;&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;&#039;SHA-256&#039;&amp;lt;/code&amp;gt;}}&lt;br /&gt;
* Return «text» with no change: &amp;lt;code&amp;gt;&#039;None&#039;&amp;lt;/code&amp;gt;{{Release|6.3||&lt;br /&gt;
* [[Entering extended characters|Unicode character names]]: &amp;lt;code&amp;gt;&#039;characterName&#039;&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;&#039;-characterName&#039;&amp;lt;/code&amp;gt;}}&lt;br /&gt;
&lt;br /&gt;
Start «type» with a minus, code&amp;gt;&#039;-&#039;&amp;lt;/code&amp;gt;, to invert the encoding -- i.e. to decode the text.&lt;br /&gt;
&lt;br /&gt;
== Encoding text for inclusion in a URL ==&lt;br /&gt;
&lt;br /&gt;
«Type» options &amp;lt;code&amp;gt;&#039;URL&#039;&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;&#039;IRI&#039;&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;&#039;URL%&#039;&amp;lt;/code&amp;gt; encode data for inclusion on a URL. The option &amp;lt;code&amp;gt;&#039;-URL&#039;&amp;lt;/code&amp;gt; decodes URL data.&lt;br /&gt;
&lt;br /&gt;
Data is often passed in the query string portion of a URL, such as &amp;quot;John Doe&amp;quot; in the following URL:&lt;br /&gt;
::&amp;lt;code&amp;gt;http://acme.com/somePage?name=John+Doe&amp;lt;/code&amp;gt;&lt;br /&gt;
Notice that it converts the space to a &amp;lt;code&amp;gt;&#039;+&#039;&amp;lt;/code&amp;gt; before inserting it in the URL. The special characters &amp;quot;&amp;lt;code&amp;gt;&amp;quot;!*&#039;();:@&amp;amp;=+$,/?#[]%&amp;lt;/code&amp;gt; each have a special meaning in a URL and so must be converted into text that does not involve those characters. The «type» value &amp;lt;code&amp;gt;&#039;URL&#039;&amp;lt;/code&amp;gt; encodes data according to the RFC-3986 standard.&lt;br /&gt;
&lt;br /&gt;
If you ever need to pass a URL as a data item in another URL, you must encode all its special characters so they aren&#039;t interpreted as part of the outer URL.&lt;br /&gt;
&lt;br /&gt;
This same encoding appears in other standards as well, including submitting form data in HTTP, and in for JSON.&lt;br /&gt;
&lt;br /&gt;
When using &amp;lt;code&amp;gt;[[TextCharacterEncode]](&#039;URL&#039;, text)&amp;lt;/code&amp;gt;, your text should only encode the value that will be placed after an equal sign in the query, but nothing more. For example, you should write:&lt;br /&gt;
::&amp;lt;code&amp;gt;&#039;http://acme.com/somePage?name=&#039; &amp;amp; [[TextCharacterEncode]]( &#039;URL&#039;, &#039;John Doe&#039; )&amp;lt;/code&amp;gt;&lt;br /&gt;
not&lt;br /&gt;
::&amp;lt;code&amp;gt;[[TextCharacterEncode]]( &#039;URL&#039;, &#039;http://acme.com/somePage?name=John Doe&#039; )&amp;lt;/code&amp;gt;&lt;br /&gt;
since in the latter case the characters &amp;lt;code&amp;gt;? =:/&amp;lt;/code&amp;gt;, etc. will be encoded, which you don&#039;t want.&lt;br /&gt;
&lt;br /&gt;
A problem with the &amp;lt;code&amp;gt;&#039;URL&#039;&amp;lt;/code&amp;gt; encoding is that all characters except the letters, digits, and &amp;lt;code&amp;gt;-._~&amp;lt;/code&amp;gt; are percent encoded, making URLs very difficult to read, especially for non-English sites. The &amp;lt;code&amp;gt;&#039;IRI&#039;&amp;lt;/code&amp;gt; option (International Resource Identifier) preserves all but the reserved characters (&amp;quot;&amp;lt;code&amp;gt;!*&#039;();:@&amp;amp;=+$,/?#[]%&amp;lt;/code&amp;gt;&amp;quot;), which generally still works correctly for URLs.&lt;br /&gt;
&lt;br /&gt;
The standard URL encoding changes space to a plus character. The &amp;lt;code&amp;gt;&#039;URL%&#039;&amp;lt;/code&amp;gt; option uses percent encoding for space (&amp;lt;code&amp;gt;%20&amp;lt;/code&amp;gt;) instead.&lt;br /&gt;
&lt;br /&gt;
The «type» option &amp;lt;code&amp;gt;&#039;-URL&#039;&amp;lt;/code&amp;gt; converts the URL-encoded text back into the original text. It works for any of the encodings &amp;lt;code&amp;gt;&#039;URL&#039;&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;&#039;IRI&#039;&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;&#039;URL%&#039;&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Examples ===&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;code&amp;gt;[[TextCharacterEncode]](&#039;URL&#039;, &#039;(1+2) = 3&#039;) &amp;amp;rarr; &amp;quot;%281%2B2%29+%3D+3&amp;quot;&amp;lt;/code&amp;gt;&lt;br /&gt;
:&amp;lt;code&amp;gt;[[TextCharacterEncode]](&#039;URL%&#039;, &#039;(1+2) = 3&#039;) &amp;amp;rarr; &amp;quot;%281%2B2%29%20%3D%203&amp;quot;&amp;lt;/code&amp;gt;&lt;br /&gt;
:&amp;lt;code&amp;gt;[[TextCharacterEncode]](&#039;-URL&#039;,&#039;%281%2B2%29+%3D+3&#039;) &amp;amp;rarr; &amp;quot;(1+2) = 3&amp;quot;&amp;lt;/code&amp;gt;&lt;br /&gt;
:&amp;lt;code&amp;gt;[[TextCharacterEncode]](&#039;-URL&#039;,&#039;%281%2B2%29%20%3D%203&#039;) &amp;amp;rarr; &amp;quot;(1+2) = 3&amp;quot;&amp;lt;/code&amp;gt;&lt;br /&gt;
:&amp;lt;code&amp;gt;[[TextCharacterEncode]](&#039;URL&#039;, &#039;test@中文.com&#039;) &amp;amp;rarr; &amp;quot;test%40%E4%B8%AD%E6%96%87.com&amp;quot;&amp;lt;/code&amp;gt;&lt;br /&gt;
:&amp;lt;code&amp;gt;[[TextCharacterEncode]](&#039;IRI&#039;, &#039;test@中文.com&#039;) &amp;amp;rarr; &amp;quot;test%40中文.com&amp;quot;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
:Variable email := &amp;lt;code&amp;gt;&amp;quot;John_Doe@yahoo.com&amp;quot;&amp;lt;/code&amp;gt;&lt;br /&gt;
:Variable website := &amp;lt;code&amp;gt;&amp;quot;http://acme.com?name=johnDoe&amp;amp;type=student&amp;quot;&amp;lt;/code&amp;gt;&lt;br /&gt;
:Variable cityToFind = &amp;lt;code&amp;gt;&amp;quot;San Francisco, CA&amp;quot;&amp;lt;/code&amp;gt;&lt;br /&gt;
:Variable UrlToRead := &amp;lt;code&amp;gt;&amp;quot;http://dataSource.com/query?email=&amp;quot; &amp;amp; [[TextCharacterEncode]]( &#039;URL&#039;, email ) &amp;amp; &amp;quot;&amp;amp;site=&amp;quot; &amp;amp; [[TextCharacterEncode]](&#039;URL&#039;, website) &amp;amp; &amp;quot;&amp;amp;city=&amp;quot; &amp;amp; [[TextCharacterEncode]](&#039;URL&#039;, cityToFind)&amp;lt;/code&amp;gt;&lt;br /&gt;
::: &amp;lt;code&amp;gt;UrlToRead&amp;lt;/code&amp;gt; &amp;amp;rarr; &amp;lt;code&amp;gt;&amp;quot;http://dataSource.com/query?email=John_Doe%40yahoo.com&amp;amp;site=http%3A%2F%2Facme.com%3Fname%3DjohnDoe%26type%3Dstudent&amp;amp;city=San+Francisco%2C+CA&amp;quot;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Encoding text in XML or HTML ==&lt;br /&gt;
&lt;br /&gt;
The option &amp;lt;code&amp;gt;&#039;XML&#039;&amp;lt;/code&amp;gt; for «type» encodes data for insertion in XML or HTML. Without this encoding, an XML or HTML parser will attempt to interpret special characters such as &#039;&amp;lt;&#039;, &#039;&amp;gt;&#039;, &#039;&amp;amp;&#039;, quotes. Also, a few characters falling in control ranges (below ascii 32 or between ascii 128 and 159) will be automatically converted to entities as required be the standards.&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;&#039;-XML&#039;&amp;lt;/code&amp;gt; does the inverse decoding.&lt;br /&gt;
&lt;br /&gt;
{{Release|6.3||&lt;br /&gt;
== Character names ==&lt;br /&gt;
&#039;&#039;New to [[Analytica 6.3]]&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
You can map an extended character to its name, or a character name to the character, for example:&lt;br /&gt;
:&amp;lt;code&amp;gt;[[TextCharacterEncode]]( &#039;&amp;amp;Psi;&#039;, &#039;characterName&#039;)&amp;lt;/code&amp;gt; &amp;amp;rarr; &amp;lt;code&amp;gt;&#039;Psi&#039;&amp;lt;/code&amp;gt;&lt;br /&gt;
:&amp;lt;code&amp;gt;[[TextCharacterEncode]]( &#039;Psi&#039;, &#039;-characterName&#039;&amp;lt;/code&amp;gt; &amp;amp;rarr; &amp;lt;code&amp;gt;&#039;&amp;amp;Psi;&#039;&amp;lt;/code&amp;gt;&lt;br /&gt;
You can use these character names when [[Entering extended characters|typing extended characters]] by typing a backslash, &amp;lt;code&amp;gt;\&amp;lt;/code&amp;gt;, the character name, then the TAB key.&lt;br /&gt;
&lt;br /&gt;
When a character has no special name assigned, &amp;lt;code&amp;gt;[[TextCharacterEncode]]( &#039;&amp;amp;Psi;&#039;, &#039;characterName&#039;)&amp;lt;/code&amp;gt; returns [[Null]]. Analytica reads the character names from the file &amp;lt;code&amp;gt;&amp;quot;characterNames.ini&amp;quot;&amp;lt;/code&amp;gt; found in the Analytica installation folder.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== Examples ===&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;code&amp;gt;[[TextCharacterEncode]]( &#039;XML&#039;, &#039;One &amp;lt; Two, &amp;lt;b&amp;gt;Three &amp;amp; Four&amp;lt;/b&amp;gt; are &amp;quot;Bigger&amp;quot;&#039; ) &amp;lt;/code&amp;gt;&lt;br /&gt;
:::&amp;amp;rarr; &amp;lt;code&amp;gt;&amp;quot;One &amp;amp;amp;lt; Two, &amp;amp;amp;lt;b&amp;amp;amp;gt;Three &amp;amp;amp;amp; Four&amp;amp;amp;lt;/b&amp;amp;amp;gt; are &amp;amp;amp;quot;Bigger&amp;amp;amp;quot;&amp;quot;&amp;lt;/code&amp;gt;&lt;br /&gt;
:&amp;lt;code&amp;gt;[[TextCharacterEncode]](&#039;-XML&#039;, One &amp;amp;amp;lt; Two, &amp;amp;amp;lt;b&amp;amp;amp;gt;Three &amp;amp;amp;amp; Four&amp;amp;amp;lt;/b&amp;amp;amp;gt; are &amp;amp;amp;quot;Bigger&amp;amp;amp;quot;&#039; )&amp;lt;/code&amp;gt;&lt;br /&gt;
::: &amp;amp;rarr; &amp;lt;code&amp;gt;&#039;One &amp;lt; Two, &amp;lt;b&amp;gt;Three &amp;amp; Four&amp;lt;/b&amp;gt; are &amp;quot;Bigger&amp;quot;&#039;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== UTF-8 encoding ==&lt;br /&gt;
&lt;br /&gt;
Set «type» to &amp;lt;code&amp;gt;&#039;UTF-8&#039;&amp;lt;/code&amp;gt; to obtain the UTF-8 encoding, or to &amp;lt;code&amp;gt;&#039;-UTF-8&#039;&amp;lt;/code&amp;gt; to decode a UTF-8 encoding into the Unicode characters. &lt;br /&gt;
&lt;br /&gt;
The option &amp;lt;code&amp;gt;&#039;UTF-8+&#039;&amp;lt;/code&amp;gt; prepends the UTF-8 &#039;&#039;Byte Order Mark&#039;&#039; (BOM). The &amp;lt;code&amp;gt;&#039;-UTF-8&#039;&amp;lt;/code&amp;gt; decoding option always removes the BOM if it is present.&lt;br /&gt;
&lt;br /&gt;
=== Examples ===&lt;br /&gt;
:&amp;lt;code&amp;gt;[[TextCharacterEncode]](&#039;UTF-8&#039;, &#039;확률 분포&#039;) &amp;amp;rarr; &amp;quot;íë¥  ë¶í¬&amp;quot;&amp;lt;/code&amp;gt;&lt;br /&gt;
:&amp;lt;code&amp;gt;[[Asc]]([[SplitText]]([[TextCharacterEncode]](&#039;UTF-8&#039;, &#039;확률 분포&#039;)))&amp;lt;/code&amp;gt;&lt;br /&gt;
:::&amp;amp;rarr; &amp;lt;code&amp;gt;[0xed, 0x99, 0x95, 0xeb, 0xa5, 0xa0, 0x20, 0xeb, 0xb6, 0x84, 0xed, 0x8f, 0xac]&amp;lt;/code&amp;gt;&lt;br /&gt;
:&amp;lt;code&amp;gt;[[TextCharacterEncode]](&#039;-UTF-8&#039;, &#039;íë¥  ë¶í¬&#039;) &amp;amp;rarr; &amp;quot;확률 분포&amp;quot;&amp;lt;/code&amp;gt;&lt;br /&gt;
:&amp;lt;code&amp;gt;[[TextCharacterEncode]](&#039;UTF-8+&#039;, &#039;확률 분포&#039;) &amp;amp;rarr; &amp;quot;ï»¿íë¥  ë¶í¬&amp;quot;&amp;lt;/code&amp;gt;   { &amp;lt;code&amp;gt;&#039;ï»¿&#039;&amp;lt;/code&amp;gt; is the BOM }&lt;br /&gt;
&lt;br /&gt;
== Unicode normalization ==&lt;br /&gt;
&lt;br /&gt;
The «type» options &amp;lt;code&amp;gt;&#039;NFC&#039;&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;&#039;NFD&#039;&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;&#039;NFKC&#039;&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;&#039;NFKD&#039;&amp;lt;/code&amp;gt; convert text into canonical Unicode normalized forms.&lt;br /&gt;
&lt;br /&gt;
The [http://www.unicode.org/standard/principles.html Unicode standard] includes special &#039;&#039;combining&#039;&#039; characters that allow &#039; &#039;&#039;composite character&#039;&#039; to be constructed from one or more combining characters applied to a main character. As a result, there are often multiple ways to encode the same visible glyph, which is often the case with accented characters. For example, the accented &#039;a&#039;  character &amp;lt;code&amp;gt;&#039;á&#039;&amp;lt;/code&amp;gt; can be obtained with either of the following:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;code&amp;gt;[[Chr]](225) &amp;amp;rarr; &#039;á&#039;&amp;lt;/code&amp;gt;&lt;br /&gt;
:&amp;lt;code&amp;gt;&#039;a&#039; &amp;amp; [[Chr]](0x301) &amp;amp;rarr; &#039;á&#039; &amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Although these display the same, the first has a [[TextLength|text length]] of 1, the second a text length of 2. The character &amp;lt;code&amp;gt;[[Chr]](0x301)&amp;lt;/code&amp;gt; is the acute accent combining character, and can be applied to any character. In fact, it is even possible to apply multiple combining characters to the same glyph. &lt;br /&gt;
&lt;br /&gt;
When you want to ensure that combined characters are used (such as the one character &amp;lt;code&amp;gt;&#039;á&#039;&amp;lt;/code&amp;gt;), set «type» to &amp;lt;code&amp;gt;&#039;NFC&#039;&amp;lt;/code&amp;gt;, which stands for &#039;&#039;Normalized Form Combined&#039;&#039;. If the two character sequence &amp;lt;code&amp;gt;&#039;a&#039; &amp;amp; [[Chr]](0x301)&amp;lt;/code&amp;gt; appears in «text», it will be replaced with the single pre-composed character &amp;lt;code&amp;gt;[[Chr]](225)&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
When you want to ensure that composite characters are split into individual combining character constituents, set «type» to &amp;lt;code&amp;gt;&#039;NFD&#039;&amp;lt;/code&amp;gt;, which stands for &#039;&#039;Normal Form Decomposed&#039;&#039;. Hence, for example, the single character &amp;lt;code&amp;gt;&#039;á&#039;&amp;lt;/code&amp;gt; will be replaced with the two character sequence :&amp;lt;code&amp;gt;&#039;a&#039; &amp;amp; [[Chr]](0x301)&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Unicode also includes digraph [https://en.wikipedia.org/wiki/List_of_precomposed_Latin_characters_in_Unicode ligature characters]], such as the ligature &amp;lt;code&amp;gt;&#039;ﬁ&#039;&amp;lt;/code&amp;gt;, which is a single character glyph that contains both &amp;lt;code&amp;gt;&#039;f&#039;&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;&#039;i&#039;&amp;lt;/code&amp;gt;. The «type» options &amp;lt;code&amp;gt;&#039;NFKC&#039;&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;&#039;NFKD&#039;&amp;lt;/code&amp;gt; expand ligatures and digraphs into their individual characters. None of the four canonical Unicode encodings re-combine character sequences into pre-composed ligatures.&lt;br /&gt;
&lt;br /&gt;
Note: The four Unicode normalization encodings required Windows Vista or later. When running on XP, «text» is returned unchanged.&lt;br /&gt;
&lt;br /&gt;
{{Release|6.5||&lt;br /&gt;
== Hash codes ==&lt;br /&gt;
&#039;&#039;New to [[Analytica 6.5]]&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;&#039;SHA-1&#039;&amp;lt;/code&amp;gt; hash returns a 40 hexadecimal character string and &amp;lt;code&amp;gt;&#039;SHA-256&#039;&amp;lt;/code&amp;gt; hash returns a 64 hexadecimal character string. These can be used to verify that text from two sources is the same. It is a standard used in various cryptographic contexts. &amp;lt;code&amp;gt;&#039;SHA-1&#039;&amp;lt;/code&amp;gt; is considered as not very secure since being cryptographically broken. However, it is still widely used, for example by the 2-factor authentication standard that uses authenticator apps for the second form of authentication.&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;code&amp;gt;[[TextCharacterEncode]]( &#039;SHA-1&#039;, &#039;Hello world&#039;) &amp;amp;rarr; &#039;7b502c3a1f48c8609ae212cdfb639dee39673f5e&#039;&amp;lt;/code&amp;gt;&lt;br /&gt;
:&amp;lt;code&amp;gt;[[TextCharacterEncode]]( &#039;SHA-256&#039;, &#039;Hello world&#039;) &amp;amp;rarr; &#039;64ec88ca00b268e5ba1a35678a1b5316d212f4f366b2477232534a8aeca37f3c&#039;&amp;lt;/code&amp;gt;}}&lt;br /&gt;
&lt;br /&gt;
=== Examples ===&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;code&amp;gt;[[TextLength]]([[TextCharacterEncode]]( [&#039;NFC&#039;, &#039;NFD&#039;], &#039;á&#039; )) &amp;amp;rarr; [1,2]&amp;lt;/code&amp;gt;&lt;br /&gt;
:&amp;lt;code&amp;gt;[[TextLength]]([[TextCharacterEncode]]( [&#039;NFC&#039;, &#039;NFD&#039;], &#039;může&#039; )) &amp;amp; rarr; [4,6]&amp;lt;/code&amp;gt;&lt;br /&gt;
:&amp;lt;code&amp;gt;[[Chr]](0xfb01) &amp;amp;rarr; &#039;ﬁ&#039;&amp;lt;/code&amp;gt;&lt;br /&gt;
:&amp;lt;code&amp;gt;[[TextLength]]([[TextCharacterEncode]]( [&#039;None&#039;, &#039;NFC&#039;,&#039;NFD&#039;,&#039;NFKC&#039;,&#039;NFKD&#039;], &#039;ﬁx your résumé&#039; )) &amp;amp;rarr; [14,14,16,15,17]&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In the last example, note that the first character in «text» is the pre-composed ligature &amp;lt;code&amp;gt;&#039;ﬁ&#039;&amp;lt;/code&amp;gt;. The &amp;lt;code&amp;gt;&#039;NFKC&#039;&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;&#039;NFKD&#039;&amp;lt;/code&amp;gt; cases expanded this ligature into two characters.&lt;br /&gt;
&lt;br /&gt;
== See Also ==&lt;br /&gt;
* [[ReadTextFile]], [[WriteTextFile]]&lt;br /&gt;
* [[TextLocale]]&lt;/div&gt;</summary>
		<author><name>Lchrisman</name></author>
	</entry>
	<entry>
		<id>https://docs.analytica.com/index.php?title=Encrypting_and_decrypting&amp;diff=64594</id>
		<title>Encrypting and decrypting</title>
		<link rel="alternate" type="text/html" href="https://docs.analytica.com/index.php?title=Encrypting_and_decrypting&amp;diff=64594"/>
		<updated>2026-09-22T17:03:53Z</updated>

		<summary type="html">&lt;p&gt;Lchrisman: Created page with &amp;quot;category:Encrypting and hashing functions&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[category:Encrypting and hashing functions]]&lt;/div&gt;</summary>
		<author><name>Lchrisman</name></author>
	</entry>
	<entry>
		<id>https://docs.analytica.com/index.php?title=EncryptionKey&amp;diff=64593</id>
		<title>EncryptionKey</title>
		<link rel="alternate" type="text/html" href="https://docs.analytica.com/index.php?title=EncryptionKey&amp;diff=64593"/>
		<updated>2026-09-22T17:03:42Z</updated>

		<summary type="html">&lt;p&gt;Lchrisman: ER 22440&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;#REDIRECT [[Encrypting and decrypting]]&lt;br /&gt;
[[category:Encrypting and hashing functions]]&lt;/div&gt;</summary>
		<author><name>Lchrisman</name></author>
	</entry>
	<entry>
		<id>https://docs.analytica.com/index.php?title=RandomBytes&amp;diff=64592</id>
		<title>RandomBytes</title>
		<link rel="alternate" type="text/html" href="https://docs.analytica.com/index.php?title=RandomBytes&amp;diff=64592"/>
		<updated>2026-09-22T17:01:04Z</updated>

		<summary type="html">&lt;p&gt;Lchrisman: ER 22440&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;#REDIRECT [[Encrypting and decrypting]]&lt;br /&gt;
[[category:Encrypting and hashing functions]]&lt;/div&gt;</summary>
		<author><name>Lchrisman</name></author>
	</entry>
	<entry>
		<id>https://docs.analytica.com/index.php?title=Encrypt&amp;diff=64591</id>
		<title>Encrypt</title>
		<link rel="alternate" type="text/html" href="https://docs.analytica.com/index.php?title=Encrypt&amp;diff=64591"/>
		<updated>2026-09-22T17:00:20Z</updated>

		<summary type="html">&lt;p&gt;Lchrisman: ER 22440&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;#REDIRECT [[Encrypting and decrypting]]&lt;br /&gt;
[[category:Encrypting and hashing functions]]&lt;/div&gt;</summary>
		<author><name>Lchrisman</name></author>
	</entry>
	<entry>
		<id>https://docs.analytica.com/index.php?title=Category:Encrypting_and_hashing_functions&amp;diff=64590</id>
		<title>Category:Encrypting and hashing functions</title>
		<link rel="alternate" type="text/html" href="https://docs.analytica.com/index.php?title=Category:Encrypting_and_hashing_functions&amp;diff=64590"/>
		<updated>2026-09-22T16:59:47Z</updated>

		<summary type="html">&lt;p&gt;Lchrisman: Created page with &amp;quot;Functions used for encryption, decryption, hashing or other cryptographic purposes.&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Functions used for encryption, decryption, hashing or other cryptographic purposes.&lt;/div&gt;</summary>
		<author><name>Lchrisman</name></author>
	</entry>
	<entry>
		<id>https://docs.analytica.com/index.php?title=Decrypt&amp;diff=64589</id>
		<title>Decrypt</title>
		<link rel="alternate" type="text/html" href="https://docs.analytica.com/index.php?title=Decrypt&amp;diff=64589"/>
		<updated>2026-09-22T16:59:04Z</updated>

		<summary type="html">&lt;p&gt;Lchrisman: ER 22440&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;#REDIRECT [[Encrypting and decrypting]]&lt;br /&gt;
[[category:Encrypting and hashing functions]]&lt;/div&gt;</summary>
		<author><name>Lchrisman</name></author>
	</entry>
	<entry>
		<id>https://docs.analytica.com/index.php?title=In-memory_binary_data_terms&amp;diff=64588</id>
		<title>In-memory binary data terms</title>
		<link rel="alternate" type="text/html" href="https://docs.analytica.com/index.php?title=In-memory_binary_data_terms&amp;diff=64588"/>
		<updated>2026-09-21T18:01:16Z</updated>

		<summary type="html">&lt;p&gt;Lchrisman: table format fix, | should have been ||&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[category:Expressions]]&lt;br /&gt;
[[category:Data Type Functions]]&lt;br /&gt;
{{ReleaseBar}}&lt;br /&gt;
&lt;br /&gt;
== Binary data terms (or &amp;quot;blobs&amp;quot;) ==&lt;br /&gt;
&lt;br /&gt;
Sometimes it&#039;s useful to hold some arbitrary raw, binary data in memory because of its compactness and speed of reading or writing. &lt;br /&gt;
The downside is that you can&#039;t easily interpret binary data. Such an in-memory block is sometimes called a &amp;quot;blob&amp;quot;. The more formal name is a &#039;&#039;binary data term&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
A binary data term is just a block of memory with arbitrary data. Often it&#039;s an in-memory copy of a binary file. This can be useful when your code makes many individual reads from different locations in the file, since it avoids repeated file-open and file-close operations, and accesses everything in memory. Sometimes the in-memory image is a portion of a binary file.&lt;br /&gt;
&lt;br /&gt;
== Creating a binary data term in memory ==&lt;br /&gt;
&lt;br /&gt;
The following are ways in which a binary data term can be created.&lt;br /&gt;
* A base64 literal, e.g., &amp;lt;code&amp;gt;base64&#039;uC8AMcjLQA==&#039;&amp;lt;/code&amp;gt;{{Release|6.4||&lt;br /&gt;
* A base16 literal, e.g., &amp;lt;code&amp;gt;base16&#039;b82f0031c8cb40&#039;&amp;lt;/code&amp;gt;}}&lt;br /&gt;
* &amp;lt;code&amp;gt;[[WriteBinaryFile]](&amp;quot;&amp;lt;&amp;gt;&amp;quot;, ...)&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;[[ReadBinaryFile]]( filename, typeFlags:7 )&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;[[GetFromBinaryData]](..., typeFlags:7)&amp;lt;/code&amp;gt;{{Release|6.4||&lt;br /&gt;
* &amp;lt;code&amp;gt;[[WriteImageFile]]( &amp;quot;&amp;lt;&amp;gt;&amp;quot;, ...)&amp;lt;/code&amp;gt;}}&lt;br /&gt;
* &amp;lt;code&amp;gt;[[ReadProtoBufFile]]&amp;lt;/code&amp;gt; -- &#039;&#039;experimental non-supported function&#039;&#039;&lt;br /&gt;
Note that functions that read or write to external sources (files) require {{Analytica Developer}} or better.&lt;br /&gt;
&lt;br /&gt;
== Reading the contents of an in-memory binary data term ==&lt;br /&gt;
* &amp;lt;code&amp;gt;[[BinaryDataSize]](data)&amp;lt;/code&amp;gt; -- returns the number of bytes&lt;br /&gt;
* &amp;lt;code&amp;gt;[[GetFromBinaryData]](...)&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;[[ParseProtoBuf]]&amp;lt;/code&amp;gt; -- experimental, non-supported function&lt;br /&gt;
&lt;br /&gt;
Generally when you are reading a piece of information from the blob, you&#039;ll want to interpret the contents in some way. The parameters of [[GetFromBinaryData]] enable you to control this interpretation so that you can read as different non-opaque data types.&lt;br /&gt;
&lt;br /&gt;
{{Release|1=6.6|2=|3=(&#039;&#039;New to [[Analytica 6.6]]&#039;&#039;) You can also access a single byte (as an integer between 0 and 255) at the 1-based position &amp;lt;code&amp;gt;n&amp;lt;/code&amp;gt; using the [[Arrow operator]] syntax:&lt;br /&gt;
:&amp;lt;code&amp;gt;x-&amp;gt;[n]&amp;lt;/code&amp;gt;&lt;br /&gt;
or you can extract a subsequence as a new, smaller, [[In-memory binary data terms|binary data term]] using &lt;br /&gt;
:&amp;lt;code&amp;gt;x-&amp;gt;[m..n]&amp;lt;/code&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
== Conversions ==&lt;br /&gt;
&lt;br /&gt;
This table summarizes various data transformations to and from binary data formats that you might need.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
! From !! To !! Method !! Required release&lt;br /&gt;
|-&lt;br /&gt;
| binary file (or part of) || binary data term || [[ReadBinaryFile]] || [[Analytica 6.2]]&lt;br /&gt;
|-&lt;br /&gt;
| binary data term (or part of) || Binary file || [[WriteBinaryFile]] || [[Analytica 6.2]]&lt;br /&gt;
|-&lt;br /&gt;
| part of binary data term || another binary data term || &amp;lt;code&amp;gt;[[GetFromBinaryData]](...typeFlags:7)&amp;lt;/code&amp;gt; || [[Analytica 6.2]]&lt;br /&gt;
|-&lt;br /&gt;
| image file || binary data term || &amp;lt;code&amp;gt;[[ReadBinaryFile]](filename,typeFlags:7)&amp;lt;/code&amp;gt; || [[Analytica 6.4]]&lt;br /&gt;
|-&lt;br /&gt;
| in-memory image || binary data term || &amp;lt;code&amp;gt;[[WriteImageFile]](&amp;quot;&amp;lt;&amp;gt;&amp;quot;, image)&amp;lt;/code&amp;gt; || [[Analytica 6.4]]&lt;br /&gt;
|-&lt;br /&gt;
| binary data term || image file || &amp;lt;code&amp;gt;[[WriteBinaryFile]](...,typeflags:7) &amp;lt;/code&amp;gt;&amp;lt;br/&amp;gt;&#039;&#039;The binary data must contain the bytes for a valid image.&#039;&#039; || [[Analytica 6.2]]&lt;br /&gt;
|-&lt;br /&gt;
| binary data term || in-memory image || &amp;lt;code&amp;gt;[[GetFromBinaryData]]( ..., typeFlags:8 )&amp;lt;/code&amp;gt; || [[Analytica 6.4]]&lt;br /&gt;
|-&lt;br /&gt;
| base64 literal || binary data term || [[Base64-encoded_binary_term_literals|&amp;lt;code&amp;gt;base64&#039;....&#039;&#039;&amp;lt;/code&amp;gt;]]  || [[Analytica 6.2]]&lt;br /&gt;
|-&lt;br /&gt;
| base64 text || binary data term || &amp;lt;code&amp;gt;[[Evaluate]]( f&amp;quot;base64&#039;{txt}&#039;&amp;quot;)&amp;lt;/code&amp;gt; || [[Analytica 6.2]]&lt;br /&gt;
|-&lt;br /&gt;
| base64 literal || image || &amp;lt;code&amp;gt;[[GetFromBinaryData]]( base64&#039;...&#039;, typeFlags:8 )&amp;lt;/code&amp;gt; || [[Analytica 6.4]]&lt;br /&gt;
|-&lt;br /&gt;
| base64 text || image || &amp;lt;code&amp;gt;[[GetFromBinaryData]]( f&amp;quot;base64&#039;{txt}&#039;&amp;quot;, typeFlags:8 )&amp;lt;/code&amp;gt; || [[Analytica 6.4]]&lt;br /&gt;
|-&lt;br /&gt;
| binary data term || base64 literal text || &amp;lt;code&amp;gt;f&amp;quot;{data:b}&amp;quot;&amp;lt;/code&amp;gt; || [[Analytica 6.2]]&lt;br /&gt;
|-&lt;br /&gt;
| hexadecimal literal || binary data term || &amp;lt;code&amp;gt;base16&#039;....&#039;&#039;&amp;lt;/code&amp;gt;  || [[Analytica 6.4]]&lt;br /&gt;
|-&lt;br /&gt;
| hexadecimal text || binary data term || &amp;lt;code&amp;gt;[[Evaluate]]( f&amp;quot;base16&#039;{txt}&#039;&amp;quot;)&amp;lt;/code&amp;gt; || [[Analytica 6.4]]&lt;br /&gt;
|-&lt;br /&gt;
| hexadecimal literal || image || &amp;lt;code&amp;gt;[[GetFromBinaryData]]( base16&#039;...&#039;, typeFlags:8 )&amp;lt;/code&amp;gt; || [[Analytica 6.4]]&lt;br /&gt;
|-&lt;br /&gt;
| hexadecimal text || image || &amp;lt;code&amp;gt;[[ImageFromHex]]( text )&amp;lt;/code&amp;gt; || [[Analytica 5.0]]&lt;br /&gt;
|-&lt;br /&gt;
|binary data term || hexadecimal literal text || &amp;lt;code&amp;gt;f&amp;quot;{data:X}&amp;quot;&amp;lt;/code&amp;gt; || [[Analytica 6.4]]&lt;br /&gt;
|-&lt;br /&gt;
| binary data term || hexadecimal text (digits only)|| &amp;lt;code&amp;gt;f&amp;quot;{data:H}&amp;quot;&amp;lt;/code&amp;gt; || [[Analytica 6.4]]&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== See Also ==&lt;br /&gt;
* [[ReadBinaryFile]] &lt;br /&gt;
* [[GetFromBinaryData]]&lt;br /&gt;
* [[WriteBinaryFile]]&lt;br /&gt;
* [[BinaryDataSize]]&lt;br /&gt;
* [[Base64-encoded binary term literals]]&lt;br /&gt;
* [[ReadImageFile]], [[WriteImageFile]]&lt;br /&gt;
* [[Formatted Text Literals]]&lt;/div&gt;</summary>
		<author><name>Lchrisman</name></author>
	</entry>
	<entry>
		<id>https://docs.analytica.com/index.php?title=Help_menu&amp;diff=64587</id>
		<title>Help menu</title>
		<link rel="alternate" type="text/html" href="https://docs.analytica.com/index.php?title=Help_menu&amp;diff=64587"/>
		<updated>2026-09-21T15:25:13Z</updated>

		<summary type="html">&lt;p&gt;Lchrisman: EW 22472 - new screenshot for Help menu in 7.2&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:Analytica User Guide]] &lt;br /&gt;
[[Category: Menus]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;breadcrumbs&amp;gt;Analytica User Guide &amp;gt; Menus &amp;gt; {{PAGENAME}}&amp;lt;/breadcrumbs&amp;gt;&lt;br /&gt;
{{ReleaseBar}}&lt;br /&gt;
&lt;br /&gt;
The [[Help menu]] displays links to Analytica user manuals and contact information for Analytica tech support.&lt;br /&gt;
&lt;br /&gt;
The exact contents of the [[Help menu]] are described in the table below.&lt;br /&gt;
&lt;br /&gt;
:{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
| rowspan=&amp;quot;42&amp;quot; | {{Release?|7.2|[[Image:Help menu 7.2.png]]|[[Image:Help menu 6.0.png]]}}&lt;br /&gt;
! style=&amp;quot;width: 140px;&amp;quot; | Menu item&lt;br /&gt;
! Description&lt;br /&gt;
|-&lt;br /&gt;
|User guide&lt;br /&gt;
|Opens the [[Analytica User Guide]] {{Release|5.0||in your web browser}}.&lt;br /&gt;
|-&lt;br /&gt;
|Tutorial&lt;br /&gt;
|Opens the [[Analytica Tutorial]] {{Release|5.0||in your web browser}}.&lt;br /&gt;
|-&lt;br /&gt;
|Optimizer guide&lt;br /&gt;
|Opens to the [[Analytica Optimizer Guide]] (only appears in Optimizer-enabled version of Analytica).&lt;br /&gt;
|-&lt;br /&gt;
|{{Release||5.9|Analytica Wiki}}{{Release|6.0||Analytica online docs}}&lt;br /&gt;
|Jumps to the [[Analytica Docs]] home page in your web browser, and logs you in automatically if your login credentials are stored. The Docs are the main source for extended Analytica reference materials.&lt;br /&gt;
{{Release|5.0||&lt;br /&gt;
{{!}}-&lt;br /&gt;
{{!}} Functions by category&lt;br /&gt;
{{!}} Jumps to an [[:Category:Functions|index of Analytica functions]] on the Docs.&lt;br /&gt;
{{!}}-&lt;br /&gt;
{{!}}What&#039;s new in {{#svarget:anarelease|5.0}}?&lt;br /&gt;
{{!}}Jumps to the [[What&#039;s new in Analytica {{#svarget:anarelease|5.0}}?]] page.&lt;br /&gt;
}}&lt;br /&gt;
{{Release|1=|2=7.1|3=&lt;br /&gt;
{{!}}-&lt;br /&gt;
{{!}}Docs login info...&lt;br /&gt;
{{!}}&#039;&#039;&#039;&#039;&#039;Deprecated:&#039;&#039;&#039;&#039;&#039; This is no longer used by the docs server. You can log in from on the docs.}}&lt;br /&gt;
|-&lt;br /&gt;
|Tech support instructions&lt;br /&gt;
|Opens your default web browser to the [https://analytica.com/support/technical-support/ Analytica Tech Support page] at https://analytica.com.&lt;br /&gt;
|-&lt;br /&gt;
|Analytica Q&amp;amp;A forum&lt;br /&gt;
|The place to ask Analytica or modeling questions, browse what others have asked or shared, answer questions and share examples.&lt;br /&gt;
|-&lt;br /&gt;
|Email tech support&lt;br /&gt;
|Opens your email system to [mailto:support@lumina.com send an email to Analytica Tech Support].&lt;br /&gt;
|-&lt;br /&gt;
|Buy/Upgrade Analytica&lt;br /&gt;
|Opens to the [https://analytica.com/shoppingcart/productorder Buy Analytica] page on the [https://Analytica.com Analytica.com] web site.  You can use this page to find pricing information or to make a purchase or upgrade.&lt;br /&gt;
|-&lt;br /&gt;
|Contact Lumina...&lt;br /&gt;
|Provides contact information for Lumina.&lt;br /&gt;
|-&lt;br /&gt;
|Update license...&lt;br /&gt;
|Displays your current Analytica license information and allows you to update the license code.&lt;br /&gt;
|-&lt;br /&gt;
|About Analytica...&lt;br /&gt;
|Displays useful information such as the application’s edition, release number, your license code, and contact information.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tip title=&amp;quot;Tip&amp;quot;&amp;gt;The options that appear on the help menu vary depending on your computer setup and the version&lt;br /&gt;
of Analytica you have.&amp;lt;/tip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==See Also==&lt;br /&gt;
* [[Menus]]&lt;br /&gt;
* [[Help menu and documentation]]&lt;br /&gt;
* [[Error message types]]&lt;br /&gt;
* [[:Category: Error messages]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;footer&amp;gt;Window menu / {{PAGENAME}} / Windows and dialogs&amp;lt;/footer&amp;gt;&lt;/div&gt;</summary>
		<author><name>Lchrisman</name></author>
	</entry>
	<entry>
		<id>https://docs.analytica.com/index.php?title=File:Help_menu_7.2.png&amp;diff=64586</id>
		<title>File:Help menu 7.2.png</title>
		<link rel="alternate" type="text/html" href="https://docs.analytica.com/index.php?title=File:Help_menu_7.2.png&amp;diff=64586"/>
		<updated>2026-09-21T15:24:37Z</updated>

		<summary type="html">&lt;p&gt;Lchrisman: updated 7.2 screenshot. Docs login info... has been removed. (EW 22472)&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Summary ==&lt;br /&gt;
updated 7.2 screenshot. Docs login info... has been removed. (EW 22472)&lt;/div&gt;</summary>
		<author><name>Lchrisman</name></author>
	</entry>
	<entry>
		<id>https://docs.analytica.com/index.php?title=Help_menu&amp;diff=64585</id>
		<title>Help menu</title>
		<link rel="alternate" type="text/html" href="https://docs.analytica.com/index.php?title=Help_menu&amp;diff=64585"/>
		<updated>2026-09-21T15:21:44Z</updated>

		<summary type="html">&lt;p&gt;Lchrisman: EW 22472 - Removal of Docs login info... (Not used server-side any more)&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:Analytica User Guide]] &lt;br /&gt;
[[Category: Menus]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;breadcrumbs&amp;gt;Analytica User Guide &amp;gt; Menus &amp;gt; {{PAGENAME}}&amp;lt;/breadcrumbs&amp;gt;&lt;br /&gt;
{{ReleaseBar}}&lt;br /&gt;
&lt;br /&gt;
The [[Help menu]] displays links to Analytica user manuals and contact information for Analytica tech support.&lt;br /&gt;
&lt;br /&gt;
The exact contents of the [[Help menu]] are described in the table below.&lt;br /&gt;
&lt;br /&gt;
:{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
| rowspan=&amp;quot;42&amp;quot; | {{Release||4.6|[[Image:Menus_13.png]]}}{{Release|5.0|5.9|[[Image: Help menu 5.0.png]]}}{{Release|6.0||[[Image:Help menu 6.0.png]]}}&lt;br /&gt;
! style=&amp;quot;width: 140px;&amp;quot; | Menu item&lt;br /&gt;
! Description&lt;br /&gt;
|-&lt;br /&gt;
|User guide&lt;br /&gt;
|Opens the [[Analytica User Guide]] {{Release|5.0||in your web browser}}.&lt;br /&gt;
|-&lt;br /&gt;
|Tutorial&lt;br /&gt;
|Opens the [[Analytica Tutorial]] {{Release|5.0||in your web browser}}.&lt;br /&gt;
|-&lt;br /&gt;
|Optimizer guide&lt;br /&gt;
|Opens to the [[Analytica Optimizer Guide]] (only appears in Optimizer-enabled version of Analytica).&lt;br /&gt;
|-&lt;br /&gt;
|{{Release||5.9|Analytica Wiki}}{{Release|6.0||Analytica online docs}}&lt;br /&gt;
|Jumps to the [[Analytica Docs]] home page in your web browser, and logs you in automatically if your login credentials are stored. The Docs are the main source for extended Analytica reference materials.&lt;br /&gt;
{{Release|5.0||&lt;br /&gt;
{{!}}-&lt;br /&gt;
{{!}} Functions by category&lt;br /&gt;
{{!}} Jumps to an [[:Category:Functions|index of Analytica functions]] on the Docs.&lt;br /&gt;
{{!}}-&lt;br /&gt;
{{!}}What&#039;s new in {{#svarget:anarelease|5.0}}?&lt;br /&gt;
{{!}}Jumps to the [[What&#039;s new in Analytica {{#svarget:anarelease|5.0}}?]] page.&lt;br /&gt;
}}&lt;br /&gt;
{{Release|1=|2=7.1|3=&lt;br /&gt;
{{!}}-&lt;br /&gt;
{{!}}Docs login info...&lt;br /&gt;
{{!}}&#039;&#039;&#039;&#039;&#039;Deprecated:&#039;&#039;&#039;&#039;&#039; This is no longer used by the docs server. You can log in from on the docs.}}&lt;br /&gt;
|-&lt;br /&gt;
|Tech support instructions&lt;br /&gt;
|Opens your default web browser to the [https://analytica.com/support/technical-support/ Analytica Tech Support page] at https://analytica.com.&lt;br /&gt;
|-&lt;br /&gt;
|Analytica Q&amp;amp;A forum&lt;br /&gt;
|The place to ask Analytica or modeling questions, browse what others have asked or shared, answer questions and share examples.&lt;br /&gt;
|-&lt;br /&gt;
|Email tech support&lt;br /&gt;
|Opens your email system to [mailto:support@lumina.com send an email to Analytica Tech Support].&lt;br /&gt;
|-&lt;br /&gt;
|Buy/Upgrade Analytica&lt;br /&gt;
|Opens to the [https://analytica.com/shoppingcart/productorder Buy Analytica] page on the [https://Analytica.com Analytica.com] web site.  You can use this page to find pricing information or to make a purchase or upgrade.&lt;br /&gt;
|-&lt;br /&gt;
|Contact Lumina...&lt;br /&gt;
|Provides contact information for Lumina.&lt;br /&gt;
|-&lt;br /&gt;
|Update license...&lt;br /&gt;
|Displays your current Analytica license information and allows you to update the license code.&lt;br /&gt;
|-&lt;br /&gt;
|About Analytica...&lt;br /&gt;
|Displays useful information such as the application’s edition, release number, your license code, and contact information.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tip title=&amp;quot;Tip&amp;quot;&amp;gt;The options that appear on the help menu vary depending on your computer setup and the version&lt;br /&gt;
of Analytica you have.&amp;lt;/tip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==See Also==&lt;br /&gt;
* [[Menus]]&lt;br /&gt;
* [[Help menu and documentation]]&lt;br /&gt;
* [[Error message types]]&lt;br /&gt;
* [[:Category: Error messages]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;footer&amp;gt;Window menu / {{PAGENAME}} / Windows and dialogs&amp;lt;/footer&amp;gt;&lt;/div&gt;</summary>
		<author><name>Lchrisman</name></author>
	</entry>
	<entry>
		<id>https://docs.analytica.com/index.php?title=GoogleAccountEmail&amp;diff=64579</id>
		<title>GoogleAccountEmail</title>
		<link rel="alternate" type="text/html" href="https://docs.analytica.com/index.php?title=GoogleAccountEmail&amp;diff=64579"/>
		<updated>2026-09-18T19:31:31Z</updated>

		<summary type="html">&lt;p&gt;Lchrisman: ER 21588: redirect to the GoogleSheets backend documentation&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;#REDIRECT [[Functions To Read Excel Worksheets#SpreadsheetOpen]]&lt;br /&gt;
&lt;br /&gt;
[[Category:Integration Functions]]&lt;/div&gt;</summary>
		<author><name>Lchrisman</name></author>
	</entry>
	<entry>
		<id>https://docs.analytica.com/index.php?title=DisconnectGoogleAccount&amp;diff=64578</id>
		<title>DisconnectGoogleAccount</title>
		<link rel="alternate" type="text/html" href="https://docs.analytica.com/index.php?title=DisconnectGoogleAccount&amp;diff=64578"/>
		<updated>2026-09-18T19:31:31Z</updated>

		<summary type="html">&lt;p&gt;Lchrisman: ER 21588: redirect to the GoogleSheets backend documentation&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;#REDIRECT [[Functions To Read Excel Worksheets#SpreadsheetOpen]]&lt;br /&gt;
&lt;br /&gt;
[[Category:Integration Functions]]&lt;/div&gt;</summary>
		<author><name>Lchrisman</name></author>
	</entry>
	<entry>
		<id>https://docs.analytica.com/index.php?title=Functions_To_Read_Excel_Worksheets&amp;diff=64577</id>
		<title>Functions To Read Excel Worksheets</title>
		<link rel="alternate" type="text/html" href="https://docs.analytica.com/index.php?title=Functions_To_Read_Excel_Worksheets&amp;diff=64577"/>
		<updated>2026-09-18T19:31:30Z</updated>

		<summary type="html">&lt;p&gt;Lchrisman: ER 21588: document GoogleAccountEmail / DisconnectGoogleAccount where readers actually land&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:Excel to Analytica mappings]]&lt;br /&gt;
[[Category:Integration Functions]]&lt;br /&gt;
&lt;br /&gt;
__TOC__&lt;br /&gt;
{{ReleaseBar}}&lt;br /&gt;
&lt;br /&gt;
These functions let you open an Excel spreadsheet file, and read cells and ranges from it. For writing to a spreadsheet, see [[Functions to Write Data to Excel Worksheets]].&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;SpreadsheetOpen&amp;quot; &amp;gt;&lt;br /&gt;
== SpreadsheetOpen(filename&#039;&#039;, showDialog, title{{Release|7.0||, backend}}{{Release|7.2||, account}}&#039;&#039;) ==&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Opens a spreadsheet file and returns a workbook object for use by other functions (such as [[SpreadsheetCell]] or [[SpreadsheetRange]]) to read from or write to the file.&lt;br /&gt;
&lt;br /&gt;
The returned object displays in a result table as &amp;lt;code&amp;gt;«ExcelWorkbook»&amp;lt;/code&amp;gt;{{Release|7.2||, &amp;lt;code&amp;gt;«GoogleSheetsWorkbook»&amp;lt;/code&amp;gt; }}{{Release|7.0|| or &amp;lt;code&amp;gt;«LibXlWorkbook»&amp;lt;/code&amp;gt;, depending on the «backend» used.}} {{Release|1=7.2|2=|3=A workbook opened from a native Google Sheet displays as &amp;lt;code&amp;gt;«GoogleSheetsWorkbook»,&amp;lt;/code&amp;gt; but an Excel file stored in Google Drive displays as &amp;lt;code&amp;gt;«LibXlWorkbook»&amp;lt;/code&amp;gt;.}}&lt;br /&gt;
&lt;br /&gt;
Unless you include a complete file path in «filename», Analytica looks for the file in the [[CurrentDataFolder]]. You can also provide the name of a workbook that is currently open in Excel, even if it has not yet been saved to disk.&lt;br /&gt;
&lt;br /&gt;
If you omit the optional parameter «showDialog», the file browser dialog opens only if the specified file cannot be found.&lt;br /&gt;
* Set «showDialog» to True (&amp;lt;code&amp;gt;1&amp;lt;/code&amp;gt;) to force the file browser even if the file exists.&lt;br /&gt;
* Set «showDialog» to False (&amp;lt;code&amp;gt;0&amp;lt;/code&amp;gt;) to suppress the dialog entirely.&lt;br /&gt;
&lt;br /&gt;
If no file is successfully opened, the function flags an error. You can customize the file dialog caption by passing text to the optional «title» parameter.&lt;br /&gt;
&lt;br /&gt;
[[SpreadsheetOpen]] can return two values: the workbook object and the full path to the file that was opened. This is particularly useful when the user selects a file via the dialog: &lt;br /&gt;
::&amp;lt;code&amp;gt;Local (wb, filePath) := SpreadsheetOpen(&amp;quot;Data.xlsx&amp;quot;);&amp;lt;/code&amp;gt;&lt;br /&gt;
{{Release|1=7.0|2=|3=&lt;br /&gt;
{{Release|1=7.1|2=|3=&lt;br /&gt;
=== Creating a new workbook ===&lt;br /&gt;
If you pass &amp;lt;code&amp;gt;&amp;quot;New&amp;quot;&amp;lt;/code&amp;gt; as the «filename», SpreadsheetOpen creates a new blank workbook with a single empty sheet, without saving to any file. This is useful for building a workbook from scratch before saving it with [[Functions_to_Write_Data_to_Excel_Worksheets#SpreadsheetSave|SpreadsheetSave]].&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;code&amp;gt;SpreadsheetOpen(&amp;quot;New&amp;quot;)&amp;lt;/code&amp;gt; — Creates a new workbook using the Excel backend.&lt;br /&gt;
:&amp;lt;code&amp;gt;SpreadsheetOpen(&amp;quot;New&amp;quot;, backend:&#039;LibXl&#039;)&amp;lt;/code&amp;gt; — Creates a new workbook using the LibXl backend.&lt;br /&gt;
&lt;br /&gt;
You can then add sheets using the &amp;lt;code&amp;gt;+&amp;lt;/code&amp;gt; prefix in [[Functions_to_Write_Data_to_Excel_Worksheets#SpreadsheetSetRange|SpreadsheetSetRange]] or [[Functions_to_Write_Data_to_Excel_Worksheets#SpreadsheetSetCell|SpreadsheetSetCell]], and save with SpreadsheetSave when done.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== Backend === &lt;br /&gt;
&#039;&#039;(New to [[Analytica 7.0]])&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The optional «backend» parameter determines which underlying engine Analytica uses to handle the spreadsheet.&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;&#039;Excel&#039;&amp;lt;/code&amp;gt;: (Default) Uses the Microsoft Excel COM interface. &lt;br /&gt;
*;Requirements: Requires Microsoft Excel to be installed locally.  &lt;br /&gt;
*;Capabilities: This backend includes the full Excel calculation engine. If you change cell values using [[SpreadsheetSetCell]] or [[SpreadsheetSetRange]], formulas within the workbook will be recalculated, allowing you to read back computed results. It supports all standard Excel file formats and features. &lt;br /&gt;
*;Return type: Returns an «ExcelWorkbook» object.&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;&#039;LibXl&#039;&amp;lt;/code&amp;gt;: Uses a built-in library for direct file access.&lt;br /&gt;
*; Requirements: Does not require Microsoft Excel to be installed. &lt;br /&gt;
*; Capabilities: Offers high performance for reading and writing raw data. It is ideal for automated environments (like servers) where Excel might not be present. Note that it does &#039;&#039;&#039;&#039;&#039;not&#039;&#039;&#039;&#039;&#039; include a calculation engine; it reads literal values and formulas from the file but cannot &amp;quot;re-calc&amp;quot; a workbook after data is changed. &lt;br /&gt;
*; Return type: Returns a «LibXlWorkbook» object.&lt;br /&gt;
{{Release|1=7.2|2=|3=&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;&#039;GoogleSheets&#039;&amp;lt;/code&amp;gt;: Opens a Google Sheets spreadsheet, or an Excel workbook stored in Google Drive, from its link. It is selected automatically when «filename» is a &amp;lt;code&amp;gt;https://docs.google.com/spreadsheets/d/...&amp;lt;/code&amp;gt; link (copy it from your browser&#039;s address bar while the sheet is open), so «backend» can be omitted.&lt;br /&gt;
*; Requirements: A Google account with access to the spreadsheet. The first time, Analytica opens your web browser so you can sign in to Google and select the spreadsheet in Google&#039;s file chooser; the connection is remembered on your computer under your Windows account, never in the model, and you can revoke it from your Google account settings. Analytica can open only the spreadsheets you select in that chooser (plus ones it creates itself), so a link to a spreadsheet the chooser did not show cannot be opened. Pass an empty «filename» (or «showDialog»: True) to browse for a spreadsheet.&lt;br /&gt;
*; Capabilities: Reads come from a snapshot taken when the workbook is opened. Writes made with [[SpreadsheetSetCell]] and [[SpreadsheetSetRange]], and sheets added or removed, are sent to Google when the computation finishes, when you call [[Functions_to_Write_Data_to_Excel_Worksheets#SpreadsheetSave|SpreadsheetSave]](wb), or when the workbook is refreshed: &amp;lt;code&amp;gt;SpreadsheetSetInfo(wb, &#039;Refresh&#039;, true)&amp;lt;/code&amp;gt; sends the pending writes and re-downloads the spreadsheet, so the values Google computed (and other people&#039;s edits) are seen. A native Google Sheet is exported by Google as an .xlsx snapshot (Google limits the export to 10 MB); an Excel workbook stored in Drive is downloaded as-is and written back as a whole file. The tab names the model sees (&amp;lt;code&amp;gt;SpreadsheetInfo(wb, &#039;Sheets&#039;)&amp;lt;/code&amp;gt;, and «sheet» given by name) are the names in the exported snapshot, truncated to 31 characters, while writes are addressed to Google&#039;s real tab titles. &amp;lt;code&amp;gt;SpreadsheetOpen(&amp;quot;New&amp;quot;, backend:&#039;GoogleSheets&#039;, title: &amp;quot;My sheet&amp;quot;)&amp;lt;/code&amp;gt; creates a new Google Sheet in your Drive. The second return value is the spreadsheet&#039;s link.&lt;br /&gt;
*; Return type: Returns a «GoogleSheetsWorkbook» object for a native Google Sheet, or a «LibXlWorkbook» object for an Excel file stored in Drive. &amp;lt;code&amp;gt;SpreadsheetInfo(wb, &#039;Backend&#039;)&amp;lt;/code&amp;gt; returns &amp;lt;code&amp;gt;&#039;GoogleSheets&#039;&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;&#039;LibXl&#039;&amp;lt;/code&amp;gt; accordingly, and &amp;lt;code&amp;gt;SpreadsheetInfo(wb, &#039;URL&#039;)&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;SpreadsheetInfo(wb, &#039;Account&#039;)&amp;lt;/code&amp;gt; give its link and the Google account it was opened with.&lt;br /&gt;
*; Advanced: Analytica identifies itself to Google as an application registered by Lumina. An organization that would rather it identified itself as an application of their own -- because their Google Workspace administrator controls which outside applications may reach their data, for example -- can arrange that; see [[Using your own Google OAuth client]], which also covers asking Google for access to every spreadsheet so that the chooser is never shown.&lt;br /&gt;
*; Unattended use: A server, a scheduled job or [[Analytica Decision Engine|ADE]] on a machine where nobody is present cannot answer a browser. Give «account» a Google service account instead, and share the spreadsheet with its e-mail address -- see [[Using a Google service account]].&lt;br /&gt;
*; Managing the connection: &amp;lt;code&amp;gt;SysLib_Internal::GoogleAccountEmail()&amp;lt;/code&amp;gt; answers the Google account Analytica is connected to, or Null when there is none. &amp;lt;code&amp;gt;SysLib_Internal::DisconnectGoogleAccount()&amp;lt;/code&amp;gt; revokes that access at Google and forgets the stored connection, returning the address it disconnected; it is a side effect, so evaluate it from a button&#039;s OnClick or the [[Typescript Window|Typescript window]] rather than in a Definition. Opening a spreadsheet again then reconnects, and &amp;lt;code&amp;gt;showDialog: True&amp;lt;/code&amp;gt; forces the connect dialog if you want to switch accounts. You can also revoke Analytica&#039;s access from [https://myaccount.google.com/permissions your Google account&#039;s permissions page].&lt;br /&gt;
}}&lt;br /&gt;
}}&lt;br /&gt;
=== Example ===&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;code&amp;gt;SpreadsheetOpen(&amp;quot;C:\MyModels\Sales Numbers.xlsx&amp;quot;) &amp;amp;rarr; &#039;&#039;«ExcelWorkbook»&#039;&#039;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;SpreadsheetCell&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Getting the file name actually opened ===&lt;br /&gt;
Your code may want to know the file path for which file was actually opened. This may differ from «filename» when the specified file is not found, or when  «showDialog» forces a dialog, allowing the user to select a different file. [[SpreadsheetOpen]] returns the file path as a second return value, which you can optionally capture using, e.g.,&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;code&amp;gt;[[Local]] (contents, filepath) := [[SpreadsheetOpen]]( ... );&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A common pattern is that you may want to save the filename in a variable such that when the evaluation is repeated in the future, it can supply the file selected by the user to the «filename» parameter. This can be coded by supplying first a global variable to hold the filename defined using [[ComputedBy]] with the default filename as follows:&lt;br /&gt;
&lt;br /&gt;
:Variable TheFilename ::= &lt;br /&gt;
::&amp;lt;code&amp;gt;[[ComputedBy]]( TheFileContetns, &amp;quot;defaultFilename.xlsx&amp;quot; ) &amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
:Variable TheWorkbook::= &lt;br /&gt;
::&amp;lt;code&amp;gt;( , TheFilename ) := [[SpreadsheetOpen]]( TheFilename )&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The assignment to &amp;lt;code&amp;gt;( , TheFilename )&amp;lt;/code&amp;gt; passes through the first parameter as the result of the assignment expression, but assigns the second return value the &amp;lt;code&amp;gt;TheFilename&amp;lt;/code&amp;gt;. The assignment to &amp;lt;code&amp;gt;TheFilename&amp;lt;/code&amp;gt; is a [[side-effect]] that is allowed only because &amp;lt;code&amp;gt;TheFilename&amp;lt;/code&amp;gt; is defined as a [[ComputedBy]]. The assignment changes the value, but also rewrites the second parameter of the call to [[ComputedBy]], thus permanently preserving the filename selected. The one line definition of &amp;lt;code&amp;gt;TheWorkbook&amp;lt;/code&amp;gt; is locally equivalent to:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;code&amp;gt;[[Local]] (wb, filename ) := [[SpreadsheetOpen]]( TheFilename );&amp;lt;/code&amp;gt;&lt;br /&gt;
:&amp;lt;code&amp;gt;TheFilename := filename;&amp;lt;/code&amp;gt;&lt;br /&gt;
:&amp;lt;code&amp;gt;wb&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Use with Office 2010 ===&lt;br /&gt;
&lt;br /&gt;
If you have installed the &amp;quot;Click-to-Run&amp;quot; version of Office 2010 from a web download, these spreadsheet functions may not work, due to a &amp;quot;feature&amp;quot; introduced in Office 2010 that apparently disables several common operations.  In this case, you may need to re-install Office using the MSI-based edition.  See how to do this at:&lt;br /&gt;
&lt;br /&gt;
[http://office.microsoft.com/en-gb/excel-help/click-to-run-switch-to-using-a-full-office-edition-HA101850538.aspx http://office.microsoft.com/en-gb/excel-help/click-to-run-switch-to-using-a-full-office-edition-HA101850538.aspx]&lt;br /&gt;
&lt;br /&gt;
=== Excel 64-bit requires Analytica 64-bit ===&lt;br /&gt;
&lt;br /&gt;
Analytica 32-bit cannot launch Excel 64-bit. (The other way around works). Thus, if you have installed Excel 64-bit (which we recommend), make sure you have installed Analytica 64-bit.  If you are a [[Free Edition]] user, you probably have 32-bit installed, but you can install Analytica 64-bit from the [https://www.lumina.com/support/downloads/ Analytica Downloads page].&lt;br /&gt;
&lt;br /&gt;
=== Remembering the selected filename ===&lt;br /&gt;
&lt;br /&gt;
When [[SpreadsheetOpen]]() shows the file dialog and you select a file, it does not save the file name. So, the next time you load the model, you&#039;ll have to select the file again.  If you want the model to remember the selected file, so it will just load it without asking, prompt using that file name as the default, you can use the &#039;&#039;&#039;SpreadsheetOpenEx&#039;&#039;&#039; function in the [[Analytica_Libraries_and_Templates#Spreadsheet_Helper_Library|Spreadsheet Helper Library]].&lt;br /&gt;
&lt;br /&gt;
=== Having same spreadsheet open in Excel at the same time ===&lt;br /&gt;
&lt;br /&gt;
It is often useful to have the spreadsheet you are working with open in Excel at the same time your model is working with it. When you want to do this, is it best to open it Excel first, before evaluating [[SpreadsheetOpen]], in which case [[SpreadsheetOpen]] connects to the existing Excel process and to the currently open spreadsheet. If you change cells in Excel, then evaluate a spreadsheet read functions, you&#039;ll read the new values, and if your model writes to the spreadsheet, you&#039;ll see those values reflected immediately in the Excel interface.&lt;br /&gt;
&lt;br /&gt;
When you call [[SpreadsheetOpen]] before opening the model in Excel, the situation is more complex. To understand what happens and how to view the same model in the Excel UI at the same time, see [[Simultaneously opening a spreadsheet in Excel and Analytica]].&lt;br /&gt;
&lt;br /&gt;
=== SpreadsheetOpenFlags ===&lt;br /&gt;
A registry setting named &amp;lt;code&amp;gt;SpreadsheetOpenFlags&amp;lt;/code&amp;gt; can be set to alter how [[SpreadsheetOpen]] connects to Excel and the initial settings in Excel. There is usually no reason to fiddle with these flags unless you encounter a specific problem. It has been more common to set these flags in server-based applications using ADE than from desktop Analytica.&lt;br /&gt;
&lt;br /&gt;
You&#039;ll need to modify the sitting from RegEdit.  You can set it in either&lt;br /&gt;
:&amp;lt;code&amp;gt;HKEY_LOCAL_MACHINE/Software/Lumina Decision Systems/Analytica/{{#svarget:anarelease|6.1}}&amp;lt;/code&amp;gt;&lt;br /&gt;
or&lt;br /&gt;
:&amp;lt;code&amp;gt;HKEY_CURRENT_USER/Software/Lumina Decision Systems/Analytica/{{#svarget:anarelease|6.1}}&amp;lt;/code&amp;gt;&lt;br /&gt;
For ADE, set it in one of these hives:&lt;br /&gt;
:&amp;lt;code&amp;gt;HKEY_LOCAL_MACHINE/Software/Lumina Decision Systems/Analytica/{{#svarget:anarelease|6.1}}&amp;lt;/code&amp;gt;&lt;br /&gt;
:&amp;lt;code&amp;gt;HKEY_CURRENT_USER/Software/Lumina Decision Systems/Analytica/{{#svarget:anarelease|6.1}}&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Setting it in HKLM causes it to apply from any account on your computer, while setting it from HKCU causes it to apply only to your own account.  A setting in HKCU takes precedence over the same setting in HKLM.&lt;br /&gt;
&lt;br /&gt;
Initially the value &amp;lt;code&amp;gt;SpreadsheetOpenFlags&amp;lt;/code&amp;gt; will not be present. Create a new 32-bit DWORD with this name.  The set the numeric value to an addition of any of these flags that you want:&lt;br /&gt;
* 1 = Launch using a COMCreateObject mechanism.  (unset)=Launch using a BindToObject method. &lt;br /&gt;
*: A BindToObject method (the default for Desktop Analytica) makes it possible to connect to a Workbook running in an active Excel UI. A COMCreateObject mechanism launches a separate instance of Excel every time.   &lt;br /&gt;
* 2 = Turn off Excel&#039;s Interactive flag.&lt;br /&gt;
* 4 = Turn off Excel&#039;s &amp;quot;Ask to update OLE links&amp;quot; flag.&lt;br /&gt;
* 8 = Turn off Excel&#039;s &amp;quot;Display Alerts&amp;quot;&lt;br /&gt;
* 16 = Disable Excel macros (for security)&lt;br /&gt;
* 32 = Close when visible. &lt;br /&gt;
*:Normally, if the workbook is currently visible in an Excel UI, Analytica simply disconnects from it, but doesn&#039;t force the workbook to close.  The Excel UI is then responsible for eventually closing it.  This overrides this and forces the workbook to close when the model releases it, even if it is visible.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== From ADE ===&lt;br /&gt;
When using from ADE on a Web Server, we strongly advise against using Excel 2016 on the server. Excel 2010 works fairly well, but Excel 2016 is extremely unstable and has a tendency to fail unpredictably and lock up all other Excel instances. Microsoft responds by saying that Excel 2016 is not supported nor licensed for use on a web server.&lt;br /&gt;
&lt;br /&gt;
When [[SpreadsheetOpen]] is evaluated in [[ADE|the Analytica Decision Engine (ADE)]] and a dialog needs to be shown to the end-user, it calls [[IAdeUICallbacks::GetFilename]](...). From within that callback, the parent application can interact with the end-user to resolve the file path, and a web applications can instruct the end-user to upload a file. Once complete, the callback returns the full path to the file which is then read. To receive this callback, the parent application must have previously registered the callback with ADE using [[CAEngine::SetCallbackObject]]( ). If it has not registered a callback and the file doesn&#039;t exist, returns an empty text.&lt;br /&gt;
&lt;br /&gt;
Once the open completes, it calls [[IAdeUICallbacks::FileOpenCompleted]]().&lt;br /&gt;
&lt;br /&gt;
=== Debugging Errors ===&lt;br /&gt;
This section documents failures when &#039;&#039;&#039;SpreadsheetOpen&#039;&#039;&#039; has been unable to open Excel, and solutions.&lt;br /&gt;
* &#039;&#039;&#039;&#039;&#039;Library not registered&#039;&#039;&#039;&#039;&#039;: &lt;br /&gt;
*:If this error occurs when &#039;&#039;&#039;SpreadsheetOpen&#039;&#039;&#039; is evaluated...&lt;br /&gt;
** The article [https://excel.tips.net/T002952_Library_Not_Registered_Error.html Library not registered error] explains how to solve this problem when it is caused by an Excel plug-in. It may be caused by a bad Excel add-in library.  You should also run &amp;lt;code&amp;gt;excel.exe /regserver&amp;lt;/code&amp;gt;.&lt;br /&gt;
** In one case, an Analytica user concluded that an older version of Excel was interfering with his newer 32-bit version of Excel. He uninstalled both and re-installed Excel 64-bit and the problem corrected itself.  But, for a different user with this problem, these steps did not correct the problem.&lt;br /&gt;
** A common cause of this problem is when stray registry settings from Excel versions that had been installed and uninstalled interfere with your current version of Excel. This is most common after you roll back to an earlier release after uninstalling a later release. To test for this cause, start Power Shell and run:&lt;br /&gt;
**::&amp;lt;code&amp;gt;get-childitem -Path &amp;quot;HKLM:\Software\Classes\TypeLib\{00020813-0000-0000-C000-000000000046}&amp;quot;&amp;lt;/code&amp;gt;&lt;br /&gt;
::If you see more than one version listed, with the most recent version number missing its mapping to Excel, then this is probably the cause. To fix, use &amp;lt;code&amp;gt;RegEdit&amp;lt;/code&amp;gt; to delete the hive for the later version number.&lt;br /&gt;
&lt;br /&gt;
== SpreadsheetCell(workbook, sheet, column, row&#039;&#039;, what&#039;&#039;) ==&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Returns the value (or other information) of a cell of a worksheet given its coordinates.  The function fully array abstracts, so you can get a range of cells by specifying the column and/or row as an array.&lt;br /&gt;
&lt;br /&gt;
Parameters:&lt;br /&gt;
;«workbook»: A workbook object returned by [[SpreadsheetOpen]]()&lt;br /&gt;
;«sheet»: The name or number of a worksheet from the workbook. Number 1 is the first worksheet, etc.&lt;br /&gt;
::  If you specify &amp;lt;code&amp;gt;sheet: &amp;quot;*&amp;quot;&amp;lt;/code&amp;gt;, it returns the cell value from &#039;&#039;column, row&#039;&#039; for all sheets in the workbook, indexed by &amp;lt;code&amp;gt;.Sheet&amp;lt;/code&amp;gt;, a local index containing the names of the worksheets. This is a way to get a list of all the worksheets in the workbook. If you specify column and/or rows as arrays, you can also use this to get a 3D array for a range over all worksheets.&lt;br /&gt;
;«column»: The column label, e.g., &amp;lt;code&amp;gt;&amp;quot;A&amp;quot;, &amp;quot;B&amp;quot;&amp;lt;/code&amp;gt;, or &amp;lt;code&amp;gt;&amp;quot;AB&amp;quot;&amp;lt;/code&amp;gt;, or the column number as an integer.&lt;br /&gt;
;«row»: The row number as an integer&lt;br /&gt;
;«what»: optional. Let&#039;s you get the formula or format information from the cell. See below under [[SpreadsheetRange]] for details. &lt;br /&gt;
&lt;br /&gt;
If the worksheet cell is empty, it returns [[Null]]. It flags an error if «workbook» is not a valid workbook, if it does not contain «sheet», or if the coordinates are invalid.&lt;br /&gt;
&lt;br /&gt;
=== Examples ===&lt;br /&gt;
These expressions are different ways to get the same result, the value from cell &#039;&#039;C7&#039;&#039; in the first sheet, &amp;lt;code&amp;gt;&amp;quot;Sheet1&amp;quot;&amp;lt;/code&amp;gt; of workbook:&lt;br /&gt;
:&amp;lt;code&amp;gt;SpreadsheetCell(workbook, &amp;quot;Sheet1&amp;quot;, &amp;quot;C&amp;quot;, 7)&amp;lt;/code&amp;gt;&lt;br /&gt;
:&amp;lt;code&amp;gt;SpreadsheetCell(workbook, &amp;quot;Sheet1&amp;quot;, 3, 7)&amp;lt;/code&amp;gt;&lt;br /&gt;
:&amp;lt;code&amp;gt;SpreadsheetCell(workbook, 1, 3, 7)&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Suppose the spreadsheet contains a 2-D table in the region &#039;&#039;C4:J19&#039;&#039;.  The columns of this table correspond to the years 2008..2015.  The rows correspond to different assets.  It is easier to refer to the columns by number, so that the columns &amp;quot;C&amp;quot; thru &amp;quot;J&amp;quot; are columns 3 thru 10.  To hold this 2-D table, we need two indexes in Analytica, &amp;lt;code&amp;gt;Time&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;Asset&amp;lt;/code&amp;gt;.  &lt;br /&gt;
&lt;br /&gt;
:&amp;lt;code&amp;gt;Index Time := 2008..2015&amp;lt;/code&amp;gt;&lt;br /&gt;
:&amp;lt;code&amp;gt;Index Asset := 1..16&amp;lt;/code&amp;gt;&lt;br /&gt;
:&amp;lt;code&amp;gt;Variable Workbook := SpreadsheetOpen(&amp;quot;C:\Asset Data.xls&amp;quot;)&amp;lt;/code&amp;gt;&lt;br /&gt;
:&amp;lt;code&amp;gt;Variable Data := SpreadsheetCell( workbook, &amp;quot;Sheet1&amp;quot;, @Time+2, @Asset+3)&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;SpreadsheetRange&amp;quot;&amp;gt;&lt;br /&gt;
== SpreadsheetRange(workbook, range&#039;&#039;, colIndex, rowIndex, howToIndex, sheet, what&#039;&#039;) ==&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Returns the values (or other information) for a range of cells from an Excel worksheet.  The  «range» can be can be a cell address such as &amp;lt;code&amp;gt;&amp;quot;C7&amp;quot;&amp;lt;/code&amp;gt; or cell range &amp;lt;code&amp;gt;&amp;quot;C7:F12&amp;quot;&amp;lt;/code&amp;gt;, or the name of a range defined in the spreadsheet.  If you want to read or write several cells or ranges in a spreadsheet, it is often convenient to use Excel&#039;s name mechanism and refer to them by name in Analytica.&lt;br /&gt;
&lt;br /&gt;
If the range has multiple columns, the result has local index &amp;lt;code&amp;gt;.Column&amp;lt;/code&amp;gt; unless you specify «colindex» as a parameter. Similarly, if the range has multiple rows, the result has local index &amp;lt;code&amp;gt;.Row&amp;lt;/code&amp;gt; unless you specify «rowindex» as a parameter. Flags in «howToIndex» let you control whether the first row (column) should be used as labels for local index  &amp;lt;code&amp;gt;.Row&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;.Column&amp;lt;/code&amp;gt;.  &lt;br /&gt;
&lt;br /&gt;
If you specify a sheet name with no cells, e.g.  &amp;lt;code&amp;gt;&amp;quot;Inputs!&amp;quot;&amp;lt;/code&amp;gt;, it returns a table that includes all cells from that sheet that contain anything.&lt;br /&gt;
&lt;br /&gt;
By default, it returns the number or text values from the range (or &amp;lt;code&amp;gt;NULL&amp;lt;/code&amp;gt; if the cell is empty). You can use the «what» parameter to obtain the cell formula, address, format, styles, precedent, and dependent cells for each cell.&lt;br /&gt;
&lt;br /&gt;
=== SpreadsheetRange Parameters ===&lt;br /&gt;
&lt;br /&gt;
SpreadsheetRange has two required parameters:&lt;br /&gt;
&lt;br /&gt;
;«workbook»: A workbook object returned by [[SpreadsheetOpen]]()&lt;br /&gt;
; «range»: A cell range.  It may be a single cell address, e.g. &amp;lt;code&amp;gt;&amp;quot;B10&amp;quot;&amp;lt;/code&amp;gt;, a range, e.g. &amp;lt;code&amp;gt;&amp;quot;A1:BC99&amp;quot;&amp;lt;/code&amp;gt;, optionally with sheet name, e.g.  &amp;lt;code&amp;gt;&amp;quot;Sheet1!A1:BC99&amp;quot;&amp;lt;/code&amp;gt;, or a named range, e.g. &amp;lt;code&amp;gt;&amp;quot;Discount_rate&amp;quot;&amp;lt;/code&amp;gt; defined in the spreadsheet. If the «range» doesn&#039;t mention the sheet name, you must specify «sheet» as a separate parameter.&lt;br /&gt;
:: If you specify the range as &amp;lt;code&amp;gt;&amp;quot;Sheet1!&amp;quot;&amp;lt;/code&amp;gt;, with nothing after the &amp;quot;!&amp;quot;, or omit «range» and specify only «sheet», it returns the smallest rectangular range that includes all used cells within the sheet. &lt;br /&gt;
&lt;br /&gt;
SpreadsheetRange has four optional parameters relating to the indexes for a range with multiple columns or rows, or over multiple sheets:&lt;br /&gt;
;«colIndex»: (optional) An index to use for the column dimension of the result.&lt;br /&gt;
;«rowIndex»: (optional) An index to use for the row dimension of the result.&lt;br /&gt;
;«howToIndex»: (optional) Flags controlling how to index the result when «colIndex» or «rowIndex» are not specified.  You can add any of these values to combine their effects:&lt;br /&gt;
::&amp;lt;code&amp;gt;1&amp;lt;/code&amp;gt;: Force a column index even if the range spans only a single column. Has no effect if you specify «colIndex».&lt;br /&gt;
::&amp;lt;code&amp;gt; 2&amp;lt;/code&amp;gt;: Force a row index even if the range spans only a single row.  Has no effect if you specify «rowIndex».&lt;br /&gt;
::&amp;lt;code&amp;gt;4&amp;lt;/code&amp;gt;: Use the first row of «range» as column labels in the local index &amp;lt;code&amp;gt;.Column&amp;lt;/code&amp;gt;. Exclude this first row in the result returned.&lt;br /&gt;
::&amp;lt;code&amp;gt;8&amp;lt;/code&amp;gt;: Use the first column of «range» as labels in the local index &amp;lt;code&amp;gt;.Row&amp;lt;/code&amp;gt;. Exclude this first column in the result returned..&lt;br /&gt;
::&amp;lt;code&amp;gt;16&amp;lt;/code&amp;gt;: Suppress the error message that is otherwise given if the sizes of «colIndex» or «rowIndex» do not match the size of the range.&lt;br /&gt;
;«sheet»: (optional) The name or number of a worksheet inside the workbook. It can be a list of sheets, in which case, the function will return a 3D table, indexed by this list as the third dimension.&lt;br /&gt;
;«what»: (optional)  See below for details on this parameter.&lt;br /&gt;
&lt;br /&gt;
=== Indexes of a cell range ===&lt;br /&gt;
&lt;br /&gt;
The result may be a scalar (single cell), a column vector, a row vector, or a 2-D array, depending on the dimensions of the cell range.  If the range has more than one row (or column),  it will use a local index .Row (.Column) by default. By default, the elements of the .Row index contain the range&#039;s row numbers and elements of the column index contain its column labels.  For example, if the range is &amp;lt;code&amp;gt;&amp;quot;C7:E12&amp;quot;&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;.Row&amp;lt;/code&amp;gt; would contain the elements &amp;lt;code&amp;gt;[7, 8, 9, 10, 11, 12]&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;.Column&amp;lt;/code&amp;gt; would contain &amp;lt;code&amp;gt;[&#039;C&#039;, &#039;D&#039;, &#039;E&#039;]&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Or, you can use the first column (row) of the range as the values for the local index .Row (.Column), by specifying &amp;lt;code&amp;gt;howToIndex: 8&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;howToIndex: 4&amp;lt;/code&amp;gt;, or &amp;lt;code&amp;gt;howToIndex: 12&amp;lt;/code&amp;gt; for both .Row and .Column.)   If you use, the first row (column) of the range as values of the local indexe(es), they will not be included in the value of the array returned. So, in that case, the range must have at least two rows (columns).  &lt;br /&gt;
&lt;br /&gt;
Alternatively, if you already have index(es), you can supply them to the  «rowIndex» («colIndex») parameters.  If you specify a «rowIndex» or «colIndex», that is shorter than the number of rows (columns) in the range, it  truncates the result. If an index is too long, it pads the result with [[Null]].  In these cases, it gives a warning message unless you set flag &amp;lt;code&amp;gt;&#039;&#039;howToIndex: 16&#039;&#039;&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
If the range has just one  column, the result normally will not have a local .Column index. But, you can force it to use a .Column with one element by setting &amp;lt;code&amp;gt;howToIndex: 1&amp;lt;/code&amp;gt;.  If you are using a named range and don&#039;t know how many columns it has, you might use this option to prevent an error occurring if you use [[Dot_operator::A.I|result.Column]] in an expression. Similarly, you can force it to use local &amp;lt;code&amp;gt;.Row&amp;lt;/code&amp;gt; index even when the result has only a single row by specifying &amp;lt;code&amp;gt;howToIndex: 2&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
You can obtain the entire range of a worksheet with all cells that contain anything named &amp;lt;code&amp;gt;&amp;quot;Sheet1&amp;quot;&amp;lt;/code&amp;gt; by specifying the «range» as &amp;lt;code&amp;gt;&amp;quot;Sheet1!&amp;quot;&amp;lt;/code&amp;gt; or by omitting the «range» parameter and specifying just the «sheet» parameter.&lt;br /&gt;
&lt;br /&gt;
=== Examples ===&lt;br /&gt;
&lt;br /&gt;
The following examples use this spreadsheet:&lt;br /&gt;
&lt;br /&gt;
:[[Image:WorksheetRange ExcelShot.jpg]]&lt;br /&gt;
&lt;br /&gt;
This spreadsheet contains these named ranges:&lt;br /&gt;
:{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Label !! Range &lt;br /&gt;
|-&lt;br /&gt;
| Rate || B1&lt;br /&gt;
|-&lt;br /&gt;
| Year || B3:F3&lt;br /&gt;
|-&lt;br /&gt;
| Cash_flow || B4:F4&lt;br /&gt;
|-&lt;br /&gt;
| Divisions || A7:A9&lt;br /&gt;
|-&lt;br /&gt;
| Employee_count || B7:F9&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;code&amp;gt;SpreadsheetRange(wb, &amp;quot;Rate&amp;quot;) &amp;amp;rarr; 0.08&amp;lt;/code&amp;gt;&lt;br /&gt;
:&amp;lt;code&amp;gt;SpreadsheetRange(wb, &amp;quot;Sheet1!B1&amp;quot;) &amp;amp;rarr; 0.08&amp;lt;/code&amp;gt;&lt;br /&gt;
:&amp;lt;code&amp;gt;SpreadsheetRange(wb, &amp;quot;Sheet1!B3:F3&amp;quot;) &amp;amp;rarr;&amp;lt;/code&amp;gt;&lt;br /&gt;
:{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
 ! .Column &amp;amp;rarr; !! &#039;B&#039; !! &#039;C&#039; !! &#039;D&#039; !! &#039;E&#039; !! &#039;F&#039;&lt;br /&gt;
 |-&lt;br /&gt;
 | || 2008 || 2009 || 2010 || 2011 || 2012&lt;br /&gt;
 |}&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;code&amp;gt;Index Year := CopyIndex( SpreadsheetRange(wb, &amp;quot;Year&amp;quot;, howToIndex: 1));&amp;lt;/code&amp;gt;&lt;br /&gt;
:&amp;lt;code&amp;gt;SpreadsheetRange(wb, &amp;quot;Cash_flow&amp;quot;, colIndex: Year) &amp;amp;rarr;&amp;lt;/code&amp;gt;&lt;br /&gt;
:{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
 ! .Year &amp;amp;rarr; !! 2008 !! 2009 !! 2010 !! 2011 !! 2012&lt;br /&gt;
 |-&lt;br /&gt;
 | || -100 || 10 || 30 || 50 || 60&lt;br /&gt;
 |}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Note&#039;&#039;: &amp;lt;code&amp;gt;howToIndex: 1&amp;lt;/code&amp;gt; was specified for &amp;lt;code&amp;gt;Year&amp;lt;/code&amp;gt; here so that we would have a 1-D array even if only one year were present in the spreadsheet.&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;code&amp;gt;SpreadsheetRange(wb, &amp;quot;Employee_count&amp;quot;) &amp;amp;rarr;&amp;lt;/code&amp;gt;&lt;br /&gt;
:{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
 ! !! .Column &amp;amp;rarr; !! &#039;B&#039; !! &#039;C&#039; !! &#039;D&#039; !! &#039;E&#039; !! &#039;F&#039;&lt;br /&gt;
 |-&lt;br /&gt;
 ! rowspan=&amp;quot;3&amp;quot; | .Row&amp;lt;br&amp;gt;&amp;amp;darr; !! 7 &lt;br /&gt;
 | 24 || 27 || 28 || 32 || 35&lt;br /&gt;
 |-&lt;br /&gt;
 ! 8 &lt;br /&gt;
 | 13 || 13 || 13 || 13 || 13&lt;br /&gt;
 |-&lt;br /&gt;
 ! 9 &lt;br /&gt;
 | 25 || 22 || 21 || 19 || 16&lt;br /&gt;
 |}&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;code&amp;gt;Index Time := [2008, 2009, 2010, 2011, 2012];&amp;lt;/code&amp;gt;&lt;br /&gt;
:&amp;lt;code&amp;gt;SpreadsheetRange(wb, &amp;quot;A7:F9&amp;quot;, colIndex: Time, howToIndex: 8, sheet: 1)  &amp;amp;rarr;&amp;lt;/code&amp;gt;&lt;br /&gt;
:{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
 ! !! Time &amp;amp;rarr; !! 2008 !! 2009 !! 2010 !! 2011 !! 2012&lt;br /&gt;
 |-&lt;br /&gt;
 ! rowspan=&amp;quot;3&amp;quot; | .Row&amp;lt;br&amp;gt;&amp;amp;darr; !! &amp;quot;Div A&amp;quot; &lt;br /&gt;
 | 24 || 27 || 28 || 32 || 35&lt;br /&gt;
 |-&lt;br /&gt;
 ! &amp;quot;Div B&amp;quot; &lt;br /&gt;
 | 13 || 13 || 13 || 13 || 13&lt;br /&gt;
 |-&lt;br /&gt;
 ! &amp;quot;Div C&amp;quot; &lt;br /&gt;
 | 25 || 22 || 21 || 19 || 16&lt;br /&gt;
 |}&lt;br /&gt;
&lt;br /&gt;
To obtain the list of worksheet names:&lt;br /&gt;
:&amp;lt;code&amp;gt;SpreadsheetCell(wb, &amp;quot;*&amp;quot;, 1, 1).Sheet&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To obtain all used cells in sheet named &amp;quot;Sheet2&amp;quot;:&lt;br /&gt;
:&amp;lt;code&amp;gt;SpreadsheetRange(wb, sheet:&amp;quot;Sheet2&amp;quot;)&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To obtain the number format of all cells in &amp;quot;Sheet2&amp;quot;:&lt;br /&gt;
:&amp;lt;code&amp;gt;SpreadsheetRange(wb, sheet:&amp;quot;Sheet2&amp;quot;, what:&amp;quot;NumberFormat&amp;quot;)&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===  SpreadsheetRange «what» parameter === &lt;br /&gt;
&lt;br /&gt;
By default, SpreadsheetRange() returns the value of the cell(s) in the range, but you can use the «what» parameter to obtain the formula,  cell style and formats, cell address, predecessor or dependent cells of each cell:&lt;br /&gt;
;«what»: (optional). By default, SpreadsheetRange returns the value of the range, but you can use this parameter to obtain its formula, or cell style parameters.  Possible values: &lt;br /&gt;
::&amp;lt;code&amp;gt;&amp;quot;Value&amp;quot;&amp;lt;/code&amp;gt;: (Default) The computed value.  Excel dates become Analytica date-time numbers, which display as dates.&lt;br /&gt;
::&amp;lt;code&amp;gt;&amp;quot;NumericValue&amp;quot;&amp;lt;/code&amp;gt;: The computed value, but dates are returned as numbers.&lt;br /&gt;
::&amp;lt;code&amp;gt;&amp;quot;Formula&amp;quot;&amp;lt;/code&amp;gt;: The cell formula as a text value in the normal Excel format starting with &amp;quot;=&amp;quot;, e.g., &amp;lt;code&amp;gt;&amp;quot;=Sum(D4:D10)&amp;quot;&amp;lt;/code&amp;gt;&lt;br /&gt;
::&amp;lt;code&amp;gt;&amp;quot;RelativeFormula&amp;quot;&amp;lt;/code&amp;gt;: The cell formula using relative offset format, e.g., &amp;lt;code&amp;gt;&amp;quot;=Sum(RC[-9]:R[+6]C[-9])&amp;quot;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== «what» parameter cell formats  ==== &lt;br /&gt;
::&amp;lt;code&amp;gt;&amp;quot;NumberFormat&amp;quot;&amp;lt;/code&amp;gt;: The cell number format as text.&lt;br /&gt;
::&amp;lt;code&amp;gt;&amp;quot;BackColor&amp;quot;&amp;lt;/code&amp;gt;: Cell background color as integer: &#039;&#039;red*65536 + green*256 + blue&#039;&#039;&lt;br /&gt;
::&amp;lt;code&amp;gt;&amp;quot;Text Color&amp;quot;&amp;lt;/code&amp;gt;: Font color as an integer: &#039;&#039;red*65536 + green*256 + blue&#039;&#039;&lt;br /&gt;
::&amp;lt;code&amp;gt;&amp;quot;FontName&amp;quot;&amp;lt;/code&amp;gt;: Name of the font used to display the cell&lt;br /&gt;
::&amp;lt;code&amp;gt;&amp;quot;FontSize&amp;quot;&amp;lt;/code&amp;gt;: Point size of the font displayed in the cell&lt;br /&gt;
::&amp;lt;code&amp;gt;&amp;quot;FontStyle&amp;quot;&amp;lt;/code&amp;gt;: Special font styles for cell separated by spaces, may include &amp;quot;bold italic underline strikethrough subscript superscript outline shadow&amp;quot;&lt;br /&gt;
::&amp;lt;code&amp;gt;&amp;quot;HorizontalAlignment&amp;quot;&amp;lt;/code&amp;gt;: Text justification, one of: &amp;lt;code&amp;gt;&#039;Left&#039;, &#039;Center&#039;, &#039;Right&#039;, &#039;Justify&#039;, &#039;Distributed&#039;, &#039;Fill&#039;&amp;lt;/code&amp;gt;.  (&#039;&#039;new to [[Analytica 5.0]]&#039;&#039;)&lt;br /&gt;
::&amp;lt;code&amp;gt;&amp;quot;VerticalAlignment&amp;quot;&amp;lt;/code&amp;gt;: Text vertical justification, one of: &amp;lt;code&amp;gt;&#039;Top&#039;, &#039;Middle&#039;, &#039;Bottom&#039;, &#039;Justify&#039;, &#039;Distributed&#039;&amp;lt;/code&amp;gt;.  (&#039;&#039;new to [[Analytica 5.0]]&#039;&#039;)&lt;br /&gt;
::&amp;lt;code&amp;gt;&amp;quot;WrapText&amp;quot;&amp;lt;/code&amp;gt;: &amp;lt;code&amp;gt;0&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;1&amp;lt;/code&amp;gt; controls whether text is word wrapped to fit in the cell.&lt;br /&gt;
::&amp;lt;code&amp;gt;&amp;quot;Border(Left|Right|Up|Down)&amp;quot;&amp;lt;/code&amp;gt; show a border to left, right, above, or below the cell.&lt;br /&gt;
::&amp;lt;code&amp;gt;&amp;quot;Border(Left|Right|Up|Down)Color&amp;quot;&amp;lt;/code&amp;gt;: Return the color of the specified side of the border as an RGB number --  E.g., &amp;lt;code&amp;gt;&amp;quot;BorderLeftColor&amp;quot;&amp;lt;/code&amp;gt; returns an integer equal to &#039;&#039;red*65535+green*256+blue&#039;&#039;&lt;br /&gt;
::&amp;lt;code&amp;gt;&amp;quot;Border (Left|Right|Up|Down) Style&amp;quot;&amp;lt;/code&amp;gt;: Style of indicated border, or [[Null]] if not set. May be &amp;lt;code&amp;gt;&amp;quot;Solid&amp;quot;, &amp;quot;Dash&amp;quot;, &amp;quot;DashDot&amp;quot;, &amp;quot;DashDotDot&amp;quot;, &amp;quot;Dot&amp;quot;, &amp;quot;Double&amp;quot;&amp;lt;/code&amp;gt;, or &amp;lt;code&amp;gt;&amp;quot;SlantDashDot&amp;quot;&amp;lt;/code&amp;gt;.&lt;br /&gt;
::&amp;lt;code&amp;gt;&amp;quot;Border (Left|Right|Up|Down) Weight&amp;quot;&amp;lt;/code&amp;gt;: Thickness of indicated border, usually between 1 and 4&lt;br /&gt;
&lt;br /&gt;
==== «what» parameter cell addresses  ====&lt;br /&gt;
::&amp;lt;code&amp;gt;&amp;quot;Address&amp;quot;&amp;lt;/code&amp;gt;: The address of the cell range, e.g., &amp;lt;code&amp;gt;&amp;quot;B12:C13&amp;quot;&amp;lt;/code&amp;gt;&lt;br /&gt;
::&amp;lt;code&amp;gt;&amp;quot;AddressR1C1&amp;quot;&amp;lt;/code&amp;gt;: The address of the cell range in R1C1 format, e.g., &amp;lt;code&amp;gt;&amp;quot;R12C2:R13C3&amp;quot;&amp;lt;/code&amp;gt;&lt;br /&gt;
::&amp;lt;code&amp;gt;&amp;quot;Sheet&amp;quot;&amp;lt;/code&amp;gt;: The sheet name where the cell range exists.&lt;br /&gt;
::&amp;lt;code&amp;gt;&amp;quot;RangeName&amp;quot;&amp;lt;/code&amp;gt;: The name of the range, if it is a named range. &lt;br /&gt;
&lt;br /&gt;
==== «what» parameter cell precedents and dependents  ==== &lt;br /&gt;
&lt;br /&gt;
::&amp;lt;code&amp;gt;&amp;quot;DirectPrecedents&amp;quot;&amp;lt;/code&amp;gt;: Addresses of all cells mentioned in the cell formula, separated by commas.  Unfortunately, Excel lists only cells in the same sheet, but not precedents in other sheets.  &lt;br /&gt;
::&amp;lt;code&amp;gt;&amp;quot;DirectPrecedentsRelative&amp;quot;&amp;lt;/code&amp;gt;: Same as &amp;quot;DirectPrecedents&amp;quot;, but cells are given by their offset relative to the current cell, e.g., &amp;lt;code&amp;gt;R[-3]C[6]&amp;lt;/code&amp;gt;.&lt;br /&gt;
::&amp;lt;code&amp;gt;&amp;quot;DirectDependents&amp;quot;&amp;lt;/code&amp;gt;: Addresses of all cells whose formula mentions this cell, separated by commas.  Unfortunately, Excel lists only cells in the same sheet, but not dependents in other sheets. &lt;br /&gt;
::&amp;lt;code&amp;gt;&amp;quot;DirectDependentsRelative&amp;quot;&amp;lt;/code&amp;gt;: Same as &amp;lt;code&amp;gt;&amp;quot;DirectDependents&amp;quot;&amp;lt;/code&amp;gt;, but cells are identified by their offset relative to the current cell, e.g., &amp;lt;code&amp;gt;R[-3]C[6]&amp;lt;/code&amp;gt;.&lt;br /&gt;
::&amp;lt;code&amp;gt;&amp;quot;Precedents&amp;quot;&amp;lt;/code&amp;gt;:  Addresses of all cells in the current worksheet mentioned in the formula of this cell and the formulas of its direct precedents.  It does not include cells reached by paths passing through other sheets.&lt;br /&gt;
::&amp;lt;code&amp;gt;&amp;quot;PrecedentsRelative&amp;quot;&amp;lt;/code&amp;gt;: Same as &amp;lt;code&amp;gt;&amp;quot;Precedents&amp;quot;&amp;lt;/code&amp;gt;, but cells are identified by their offset relative to the current cell, e.g., &amp;lt;code&amp;gt;R[-3]C[6]&amp;lt;/code&amp;gt;.&lt;br /&gt;
::&amp;lt;code&amp;gt;&amp;quot;Descendants&amp;quot;&amp;lt;/code&amp;gt;: Description of all cells in the current worksheet that depend directly or indirectly on the given cell.  It does not include cells reached by paths passing through other sheets.&lt;br /&gt;
::&amp;lt;code&amp;gt;&amp;quot;DirectDescendantsRelative&amp;quot;&amp;lt;/code&amp;gt;: Same as &amp;lt;code&amp;gt;&amp;quot;DirectDescendants&amp;quot;&amp;lt;/code&amp;gt;, but cells are identified by their offset relative to the current cell, e.g., &amp;lt;code&amp;gt;R[-3]C[6]&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Errors in SpreadsheetRange parameters ===&lt;br /&gt;
In a call to SpreadsheetRange(wb, range):&lt;br /&gt;
* If range refers to a sheet, e.g. &amp;quot;sheet!x&amp;quot; that does not exist in workbook wb, it gives an error message saying &amp;quot;the worksheet &#039;sheet&#039; was not found.&amp;quot;&lt;br /&gt;
* If range refers to a named range, e.g. &amp;quot;sheet!x&amp;quot; that does not exist in workbook wb, it gives an error message saying &amp;quot;the indicated named cell range, &#039;x&#039;, was not found.&amp;quot;&lt;br /&gt;
* If range refers to a cell address with bad syntax, e.g. &amp;quot;ted!A1:R3C6&amp;quot;, it gives an error message saying &amp;quot;the range named A1:R3C6 was not found in Excel worksheet &#039;ted&#039;.&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;SpreadsheetInfo&amp;quot;&amp;gt;&lt;br /&gt;
== SpreadsheetInfo(workbook, item) == &lt;br /&gt;
&amp;lt;/div&amp;gt;  &lt;br /&gt;
&lt;br /&gt;
SpreadsheetInfo gets various kinds of information about the spreadsheet («workbook») specified by parameter «item»:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! item !! Description&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;&amp;quot;AcceptLabelsInFormulas&amp;quot;&amp;lt;/code&amp;gt; || True when you can use labels in worksheet formulas. This is usually false.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;&amp;quot;Account&amp;quot;&amp;lt;/code&amp;gt; || &#039;&#039;([[Analytica 7.2|7.2]])&#039;&#039; The Google account a workbook opened from Google Sheets or Google Drive is connected with. Null for other workbooks.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;&amp;quot;ActiveSheet&amp;quot;&amp;lt;/code&amp;gt; || The number of the active (displayed) worksheet.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;&amp;quot;Author&amp;quot;&amp;lt;/code&amp;gt; || The name of the author, usually the name of the person who created the spreadsheet as recorded by Windows OS.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;&amp;quot;Backend&amp;quot;&amp;lt;/code&amp;gt; || &#039;&#039;([[Analytica 7.2|7.2]])&#039;&#039; Which engine holds the workbook: &amp;lt;code&amp;gt;&#039;Excel&#039;&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;&#039;LibXl&#039;&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;&#039;GoogleSheets&#039;&amp;lt;/code&amp;gt; (see the «backend» parameter of [[SpreadsheetOpen]]).&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;&amp;quot;CalculationMode&amp;quot;&amp;lt;/code&amp;gt;   || The calculation mode set for the workbook, which may be &amp;quot;Automatic&amp;quot;, &amp;quot;Manual&amp;quot; or &amp;quot;Semiautomatic&amp;quot;, meaning automatic except for data tables.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;&amp;quot;CalculationState&amp;quot;&amp;lt;/code&amp;gt; || &#039;&#039;([[Analytica 5.0|5.0]])&#039;&#039; The current state of Excel&#039;s calculation engine, either &amp;lt;code&amp;gt;&amp;quot;Calculating&amp;quot;&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;&amp;quot;Pending&amp;quot;&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;&amp;quot;Done&amp;quot;&amp;lt;/code&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;&amp;quot;CalculationVersion&amp;quot;&amp;lt;/code&amp;gt; || &#039;&#039;([[Analytica 5.0|5.0]])&#039;&#039; Number equal to 10000 * major + minor, encoding the version of the Excel calculation engine that the current workbook was last calculated in. If it was saved in an earlier version of Excel and hasn&#039;t yet been fully calculated, the value is 0. You can compare this to the &amp;quot;Excel.CalculationVersion&amp;quot; to determine whether it was last re-calculated using the same calculation engine as your current installed Excel.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;&amp;quot;CodeName&amp;quot;&amp;lt;/code&amp;gt; ||&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;&amp;quot;Date1904&amp;quot;&amp;lt;/code&amp;gt; || The base for dates used in the workbook.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;&amp;quot;DecimalSeparator&amp;quot;&amp;lt;/code&amp;gt; || &#039;&#039;([[Analytica 5.0|5.0]])&#039;&#039; The character used to separate a whole number from its fractional part. In English-speaking countries this is &#039;.&#039; (a dot).&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;&amp;quot;Excel.CalculationVersion&amp;quot;&amp;lt;/code&amp;gt; || &#039;&#039;([[Analytica 5.0|5.0]])&#039;&#039; Number equal to 10000 * major + minor, encoding the version of calculation engine for your installed version of Excel. &lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;&amp;quot;Filename&amp;quot;&amp;lt;/code&amp;gt;   || The name of the file, including the full file path.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;&amp;quot;Name&amp;quot;&amp;lt;/code&amp;gt;         || The name of the file, without the file path.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;&amp;quot;Names&amp;quot;&amp;lt;/code&amp;gt;          || A list of all the named ranges.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;&amp;quot;OperatingSystem&amp;quot;&amp;lt;/code&amp;gt; || &#039;&#039;([[Analytica 5.0|5.0]])&#039;&#039; The name of the operating system that your Excel instance is running on, as reported by Excel. &lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;&amp;quot;ReadOnly&amp;quot;&amp;lt;/code&amp;gt; || True (1) if the file is saved as Readonly.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;&amp;quot;Saved&amp;quot;&amp;lt;/code&amp;gt;      || False (0) if it has unsaved changes.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;&amp;quot;SelectedRange&amp;quot;&amp;lt;/code&amp;gt; || The currently selected Range.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;&amp;quot;SelectedRangeR1C1&amp;quot;&amp;lt;/code&amp;gt; || The currently selected Range specified by row and column number.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;&amp;quot;Sheets&amp;quot;&amp;lt;/code&amp;gt;     || A list of the names of all the worksheets&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;&amp;quot;ThousandsSeparator&amp;quot;&amp;lt;/code&amp;gt; || &#039;&#039;([[Analytica 5.0|5.0]])&#039;&#039; The character Excel uses to group thousands when displaying a large number. In English-speaking countries this is &#039;,&#039; (a comma). For example, in the number &amp;lt;code&amp;gt;1,234,456.78&amp;lt;/code&amp;gt;, groups of thousands are separated by commas.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;&amp;quot;Title&amp;quot;&amp;lt;/code&amp;gt; || The title of the spreadsheet&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;&amp;quot;URL&amp;quot;&amp;lt;/code&amp;gt; || &#039;&#039;([[Analytica 7.2|7.2]])&#039;&#039; The link of a workbook opened from Google Sheets or Google Drive. Null for other workbooks.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;&amp;quot;UseSystemSeparators&amp;quot;&amp;lt;/code&amp;gt; || &#039;&#039;([[Analytica 5.0|5.0]])&#039;&#039; True when Excel uses &amp;lt;code&amp;gt;&amp;quot;DecimalSeparator&amp;quot;&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;&amp;quot;ThousandsSeparator&amp;quot;&amp;lt;/code&amp;gt; for displaying numbers.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;&amp;quot;Version&amp;quot;&amp;lt;/code&amp;gt; || &#039;&#039;([[Analytica 5.0|5.0]])&#039;&#039; The version number (text) for the installed release of Excel. Excel 2010 is &amp;quot;14.0&amp;quot;, Excel 2013 is &amp;quot;15.0&amp;quot; and Excel 2016 is &amp;quot;16.0&amp;quot;.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;&amp;quot;Visible&amp;quot;&amp;lt;/code&amp;gt; || True when the Excel UI is visible.&lt;br /&gt;
|}  The items above marked with &#039;&#039;([[Analytica 5.0|5.0]])&#039;&#039;  require [[Analytica 5.0]] or better; those marked &#039;&#039;([[Analytica 7.2|7.2]])&#039;&#039; require [[Analytica 7.2]].&lt;br /&gt;
&lt;br /&gt;
== History== &lt;br /&gt;
&lt;br /&gt;
Functions for reading cells from Excel were first present in Analytica 4.1 with functions named [[OpenExcelFile]], [[WorksheetCell]] and [[WorksheetRange]], although these were labelled as &#039;&#039;experimental&#039;&#039;, and the present functions were not officially available until 4.2.0.    The old names are now deprecated, replaced with [[SpreadsheetOpen]], [[SpreadsheetCell]] and [[SpreadsheetRange]].  The old functions still work, but may be removed in future Analytica releases.  The parameters have changed slightly from [[WorksheetRange]] to [[SpreadsheetRange]], with the sheet parameter moved from being the second to being the last parameter and now optional -- no longer required for named ranges or ranges of the form &amp;lt;code&amp;gt;&amp;quot;Sheet1!A1:Z99&amp;quot;&amp;lt;/code&amp;gt;.  &lt;br /&gt;
&lt;br /&gt;
[[SpreadsheetInfo]] was introduced in [[Analytica 4.5]]. These options to [[SpreadsheetInfo]] were added in [[Analytica 5.0]]: &amp;quot;Version&amp;quot;, &amp;quot;CalculationVersion&amp;quot;, &amp;quot;Excel.CalculationVersion&amp;quot;, &amp;quot;UseSystemSeparators&amp;quot;, &amp;quot;DecimalSeparator&amp;quot;, &amp;quot;ThousandsSeparator&amp;quot;, and &amp;quot;CalculationState&amp;quot;.  &lt;br /&gt;
&lt;br /&gt;
The color options for «what» incorrectly returned numbers in 0x00bbggrr order, instead of 0x00rrggbb order prior to [[Analytica 5.0]]. (This was a bug -- the documentation stated it should be 0x00rrggbb).  Various options to the «what» parameter of [[SpreadsheetCell]] and [[SpreadsheetRange]] have appeared at different releases. The options &amp;lt;code&amp;gt;&#039;HorizontalAlignment&#039;&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;&#039;VerticalAlignment&#039;&amp;lt;/code&amp;gt; appeared in [[Analytica 5.0]]. Options &amp;lt;code&amp;gt;&#039;Address&#039;&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;&#039;AddressR1C1&#039;&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;&#039;Sheet&#039;&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;&#039;RangeName&#039;&amp;lt;/code&amp;gt; appeared in [[Analytica 4.6]]. The remaining options appeared in [[Analytica 4.4]], except for &amp;lt;code&amp;gt;&#039;Value&#039;&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;&#039;NumericValue&#039;&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;&#039;Formula&#039;&amp;lt;/code&amp;gt;&#039; and &amp;lt;code&amp;gt;&#039;RelativeFormula&#039;&amp;lt;/code&amp;gt;, which appeared when the «what» parameter was introduced in [[Analytica 4.3]].&lt;br /&gt;
&lt;br /&gt;
{{Release|1=7.2|2=|3=The &amp;lt;code&amp;gt;&#039;GoogleSheets&#039;&amp;lt;/code&amp;gt; «backend» of [[SpreadsheetOpen]] (Google Sheets spreadsheets and Excel workbooks stored in Google Drive, opened from their link), and the &amp;quot;Backend&amp;quot;, &amp;quot;URL&amp;quot; and &amp;quot;Account&amp;quot; items of [[SpreadsheetInfo]], were added in [[Analytica 7.2]].}}&lt;br /&gt;
&lt;br /&gt;
== See Also == &lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;column-count:2;-moz-column-count:2;-webkit-column-count:2&amp;quot;&amp;gt;&lt;br /&gt;
*  [[media:Spreadsheet Helper lib.ana|Spreadsheet Helper lib.ana]]&lt;br /&gt;
* [[Analytica_Libraries_and_Templates#Spreadsheet_Helper_Library|Spreadsheet Helper Library]]&lt;br /&gt;
* [[Media:Functions for Reading Excel Worksheets.ana|Reading Excel Worksheets.ana]]&lt;br /&gt;
* [[Read and Write Spreadsheets]]&lt;br /&gt;
* [[Excel spreadsheets read and write]]&lt;br /&gt;
* [[Functions to Write Data to Excel Worksheets]] -- [[SpreadsheetSetCell]], [[SpreadsheetSetRange]] and [[SpreadsheetSave]]&lt;br /&gt;
* {{Release|1=7.2|2=|3=[[Using your own Google OAuth client]] -- connecting to Google Sheets through your own Google Cloud registration}}&lt;br /&gt;
* {{Release|1=7.2|2=|3=[[Using a Google service account]] -- opening a Google Sheet with no browser and nobody present}}&lt;br /&gt;
* You can also use [[DbQuery| ODBC]] -- a standard database access method to read from Excel spreadsheets.&lt;br /&gt;
* [[SuppressExcelAlerts]]&lt;br /&gt;
* [[Excel to Analytica Translation]]&lt;br /&gt;
* [[Excel to Analytica Mappings]]&lt;br /&gt;
* [[ReadTextFile]]&lt;br /&gt;
* [[ReadCsvFile]]&lt;br /&gt;
* You can use these functions from  [[Excel Functions from ADE| ADE]].&lt;br /&gt;
* These spreadsheet functions above are more flexible than [[OLE linking]] which is also available.&lt;br /&gt;
* [[OLE linking]] &amp;lt;/div&amp;gt;&lt;/div&gt;</summary>
		<author><name>Lchrisman</name></author>
	</entry>
	<entry>
		<id>https://docs.analytica.com/index.php?title=Category:Analytica_MCP_Platform&amp;diff=64576</id>
		<title>Category:Analytica MCP Platform</title>
		<link rel="alternate" type="text/html" href="https://docs.analytica.com/index.php?title=Category:Analytica_MCP_Platform&amp;diff=64576"/>
		<updated>2026-09-18T18:07:15Z</updated>

		<summary type="html">&lt;p&gt;Lchrisman: Created page with &amp;quot;The Analytica MCP Platform (AMP) is a Lumina product offering that runs Analytica models headlessly while exposing a Model Context Protocol (MCP) API. It is designed especially for use cases involving a front-end language model interface.  This category page lists pages specifically related to AMP.&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;The Analytica MCP Platform (AMP) is a Lumina product offering that runs Analytica models headlessly while exposing a Model Context Protocol (MCP) API. It is designed especially for use cases involving a front-end language model interface.&lt;br /&gt;
&lt;br /&gt;
This category page lists pages specifically related to AMP.&lt;/div&gt;</summary>
		<author><name>Lchrisman</name></author>
	</entry>
	<entry>
		<id>https://docs.analytica.com/index.php?title=Using_a_Google_service_account&amp;diff=64575</id>
		<title>Using a Google service account</title>
		<link rel="alternate" type="text/html" href="https://docs.analytica.com/index.php?title=Using_a_Google_service_account&amp;diff=64575"/>
		<updated>2026-09-18T18:01:37Z</updated>

		<summary type="html">&lt;p&gt;Lchrisman: categories ADE &amp;amp; AMP&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:Integration Functions]]&lt;br /&gt;
[[Category:Analytica Decision Engine]]&lt;br /&gt;
[[Category:Analytica MCP Platform]]&lt;br /&gt;
&lt;br /&gt;
{{ReleaseBar}}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;New to [[Analytica 7.2]]&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{{Release|1=7.2|2=|3=&lt;br /&gt;
Normally the [[Functions To Read Excel Worksheets#Backend|GoogleSheets backend]] opens a spreadsheet as &#039;&#039;you&#039;&#039;: the first time, your web browser opens, you sign in to Google and choose the spreadsheet, and the connection is remembered on your computer.&lt;br /&gt;
&lt;br /&gt;
That cannot work where there is nobody at a browser -- a server, a scheduled task, a build, a model running under [[Analytica Decision Engine|ADE]] on a machine nobody signs in to. For those, Analytica can open a spreadsheet as a Google &#039;&#039;&#039;service account&#039;&#039;&#039;: an account that belongs to a program rather than a person, and that proves who it is with a key file instead of a sign-in. You share the spreadsheet with the service account&#039;s e-mail address, exactly as you would share it with a colleague, and pass its key to [[Functions To Read Excel Worksheets#SpreadsheetOpen|SpreadsheetOpen]] in the «account» parameter.&lt;br /&gt;
&lt;br /&gt;
== When to use one ==&lt;br /&gt;
&lt;br /&gt;
* An unattended machine: a scheduled model run, a server, a continuous-integration job, or ADE on a machine where nobody has ever run the desktop connect flow.&lt;br /&gt;
* A spreadsheet that should be read as &#039;&#039;itself&#039;&#039; rather than as whoever happens to run the model -- a reference dataset the model&#039;s author maintains, where you do not want every reader to need their own access to it.&lt;br /&gt;
&lt;br /&gt;
Do &#039;&#039;&#039;not&#039;&#039;&#039; use one for an ordinary desktop model. There the browser connection is simpler, it keeps each person reading with their own permissions, and it needs no key to look after.&lt;br /&gt;
&lt;br /&gt;
== Creating the service account ==&lt;br /&gt;
&lt;br /&gt;
# In the [https://console.cloud.google.com/ Google Cloud console], choose or create a project, and enable the &#039;&#039;&#039;Google Drive API&#039;&#039;&#039; and the &#039;&#039;&#039;Google Sheets API&#039;&#039;&#039; for it. (The Picker API is not needed -- a service account never sees a file chooser.)&lt;br /&gt;
# Go to IAM and admin, &#039;&#039;&#039;Service accounts&#039;&#039;&#039;, and create one. Give it a name you will recognize later. It needs no project roles: the only access that matters is the sharing you do in step 4.&lt;br /&gt;
# Open the new account, go to &#039;&#039;&#039;Keys&#039;&#039;&#039;, &#039;&#039;&#039;Add key&#039;&#039;&#039;, &#039;&#039;&#039;Create new key&#039;&#039;&#039;, type &#039;&#039;&#039;JSON&#039;&#039;&#039;. Google downloads a small .json file, once -- it cannot be downloaded again. This file is the account&#039;s password; treat it like one.&lt;br /&gt;
# Copy the account&#039;s e-mail address, which looks like &amp;lt;code&amp;gt;something@&#039;&#039;your-project&#039;&#039;.iam.gserviceaccount.com&amp;lt;/code&amp;gt;, and &#039;&#039;&#039;share each spreadsheet with it&#039;&#039;&#039; in Google Sheets exactly as you would with a person: Share, paste the address, Viewer for read-only or Editor if the model writes. A service account sees nothing that has not been shared with it.&lt;br /&gt;
&lt;br /&gt;
The service account needs no consent screen, no Google verification, and no security assessment, because nobody is being asked to grant it anything: it holds its own credentials, and you decide what it can reach by sharing.&lt;br /&gt;
&lt;br /&gt;
== Giving Analytica the key ==&lt;br /&gt;
&lt;br /&gt;
Put the contents of the .json file in a [[Secret]], and pass the Secret as the «account» parameter:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;code&amp;gt;SpreadsheetOpen(&amp;quot;https://docs.google.com/spreadsheets/d/&#039;&#039;id&#039;&#039;/edit&amp;quot;, account: Sheets_Robot_Key)&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
where &amp;lt;code&amp;gt;Sheets_Robot_Key&amp;lt;/code&amp;gt; is a Secret whose value is the whole JSON text. Set its &#039;&#039;&#039;Sinks&#039;&#039;&#039; to &amp;lt;code&amp;gt;SpreadsheetOpen&amp;lt;/code&amp;gt; and its &#039;&#039;&#039;Destination&#039;&#039;&#039; to &amp;lt;code&amp;gt;https://oauth2.googleapis.com/&amp;lt;/code&amp;gt;, so the key can be used for this and nothing else; Analytica refuses the substitution otherwise. Which storage kind the Secret uses -- in the model, an environment variable, per machine user, or a vault -- is your choice, and is the thing that decides who else can run the model.&lt;br /&gt;
&lt;br /&gt;
Passing the JSON as a plain text literal works too, and is the wrong thing to do in a saved model: anyone who opens the file then has the key.&lt;br /&gt;
&lt;br /&gt;
The «backend» parameter is not needed; a Google Sheets link selects the backend by itself.&lt;br /&gt;
&lt;br /&gt;
== What is different with a service account ==&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Nothing interactive ever happens.&#039;&#039;&#039; No browser opens, no dialog appears, and nothing waits for a person. If the account cannot reach the spreadsheet the call reports that and stops.&lt;br /&gt;
* &#039;&#039;&#039;The file chooser is not involved.&#039;&#039;&#039; Any spreadsheet shared with the account opens directly from its link. There is no &amp;quot;pick one&amp;quot; mode, so &amp;lt;code&amp;gt;SpreadsheetOpen(&amp;quot;&amp;quot;)&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;showDialog: True&amp;lt;/code&amp;gt; have nothing to offer.&lt;br /&gt;
* &#039;&#039;&#039;It ignores the desktop&#039;s connection entirely.&#039;&#039;&#039; The Windows user&#039;s own Google connection, and the [[Using your own Google OAuth client|client-registration settings]], play no part; the key carries its own identity and its own permissions. You can use a service account for one spreadsheet and your own connection for another in the same model.&lt;br /&gt;
* &#039;&#039;&#039;Reads and writes behave exactly as they do otherwise&#039;&#039;&#039;: a snapshot on open, writes sent when the computation finishes, on &amp;lt;code&amp;gt;SpreadsheetSave(wb)&amp;lt;/code&amp;gt;, or on &amp;lt;code&amp;gt;SpreadsheetSetInfo(wb, &#039;Refresh&#039;, true)&amp;lt;/code&amp;gt;. Give the account &#039;&#039;&#039;Editor&#039;&#039;&#039; on the spreadsheet if the model writes to it.&lt;br /&gt;
* &amp;lt;code&amp;gt;SpreadsheetInfo(wb, &#039;Account&#039;)&amp;lt;/code&amp;gt; reports the service account&#039;s address, so a model can say which identity a workbook was opened with.&lt;br /&gt;
* &amp;lt;code&amp;gt;SpreadsheetOpen(&amp;quot;New&amp;quot;, backend:&#039;GoogleSheets&#039;, account: &#039;&#039;key&#039;&#039;)&amp;lt;/code&amp;gt; does work, but the new spreadsheet is created in the &#039;&#039;&#039;service account&#039;s own Drive&#039;&#039;&#039;, where no person can see it. That is occasionally what you want for scratch space and almost never what you want otherwise; a service account has no Drive anyone can browse.&lt;br /&gt;
&lt;br /&gt;
== When it does not work ==&lt;br /&gt;
&lt;br /&gt;
The most common failure is the one that has nothing to do with Analytica: the spreadsheet was never shared with the service account. Analytica says so by name --&lt;br /&gt;
&lt;br /&gt;
:&#039;&#039;The Google service account &#039;&#039;name&#039;&#039;@&#039;&#039;project&#039;&#039;.iam.gserviceaccount.com cannot open the spreadsheet ...&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
-- and the cure is to share it at that address. Note that sharing a &#039;&#039;folder&#039;&#039; with the account shares what is in it, which is usually the tidier arrangement for a model that reads several spreadsheets.&lt;br /&gt;
&lt;br /&gt;
A key that is damaged, truncated, or not a service-account key at all is reported when the call is evaluated, before any network request, so a mistyped Secret fails immediately rather than halfway through a run.&lt;br /&gt;
&lt;br /&gt;
Some organizations&#039; Workspace policies block service-account keys from being created at all, or disable them after a period. If the console will not produce a key, that is an administrator&#039;s setting, not a Google-wide limit.&lt;br /&gt;
&lt;br /&gt;
=== Not supported ===&lt;br /&gt;
&lt;br /&gt;
Domain-wide delegation -- a service account impersonating a member of your Workspace -- is not supported. The service account acts as itself, and sees what has been shared with it.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
== See Also ==&lt;br /&gt;
&lt;br /&gt;
* [[Functions To Read Excel Worksheets]] -- [[Functions To Read Excel Worksheets#SpreadsheetOpen|SpreadsheetOpen]] and the [[Functions To Read Excel Worksheets#Backend|GoogleSheets backend]]&lt;br /&gt;
* [[Using your own Google OAuth client]]&lt;br /&gt;
* [[Secret]]&lt;br /&gt;
* [[Analytica Decision Engine]]&lt;/div&gt;</summary>
		<author><name>Lchrisman</name></author>
	</entry>
	<entry>
		<id>https://docs.analytica.com/index.php?title=Using_a_Google_service_account&amp;diff=64574</id>
		<title>Using a Google service account</title>
		<link rel="alternate" type="text/html" href="https://docs.analytica.com/index.php?title=Using_a_Google_service_account&amp;diff=64574"/>
		<updated>2026-09-18T17:59:53Z</updated>

		<summary type="html">&lt;p&gt;Lchrisman: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:Integration Functions]]&lt;br /&gt;
{{ReleaseBar}}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;New to [[Analytica 7.2]]&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{{Release|1=7.2|2=|3=&lt;br /&gt;
Normally the [[Functions To Read Excel Worksheets#Backend|GoogleSheets backend]] opens a spreadsheet as &#039;&#039;you&#039;&#039;: the first time, your web browser opens, you sign in to Google and choose the spreadsheet, and the connection is remembered on your computer.&lt;br /&gt;
&lt;br /&gt;
That cannot work where there is nobody at a browser -- a server, a scheduled task, a build, a model running under [[Analytica Decision Engine|ADE]] on a machine nobody signs in to. For those, Analytica can open a spreadsheet as a Google &#039;&#039;&#039;service account&#039;&#039;&#039;: an account that belongs to a program rather than a person, and that proves who it is with a key file instead of a sign-in. You share the spreadsheet with the service account&#039;s e-mail address, exactly as you would share it with a colleague, and pass its key to [[Functions To Read Excel Worksheets#SpreadsheetOpen|SpreadsheetOpen]] in the «account» parameter.&lt;br /&gt;
&lt;br /&gt;
== When to use one ==&lt;br /&gt;
&lt;br /&gt;
* An unattended machine: a scheduled model run, a server, a continuous-integration job, or ADE on a machine where nobody has ever run the desktop connect flow.&lt;br /&gt;
* A spreadsheet that should be read as &#039;&#039;itself&#039;&#039; rather than as whoever happens to run the model -- a reference dataset the model&#039;s author maintains, where you do not want every reader to need their own access to it.&lt;br /&gt;
&lt;br /&gt;
Do &#039;&#039;&#039;not&#039;&#039;&#039; use one for an ordinary desktop model. There the browser connection is simpler, it keeps each person reading with their own permissions, and it needs no key to look after.&lt;br /&gt;
&lt;br /&gt;
== Creating the service account ==&lt;br /&gt;
&lt;br /&gt;
# In the [https://console.cloud.google.com/ Google Cloud console], choose or create a project, and enable the &#039;&#039;&#039;Google Drive API&#039;&#039;&#039; and the &#039;&#039;&#039;Google Sheets API&#039;&#039;&#039; for it. (The Picker API is not needed -- a service account never sees a file chooser.)&lt;br /&gt;
# Go to IAM and admin, &#039;&#039;&#039;Service accounts&#039;&#039;&#039;, and create one. Give it a name you will recognize later. It needs no project roles: the only access that matters is the sharing you do in step 4.&lt;br /&gt;
# Open the new account, go to &#039;&#039;&#039;Keys&#039;&#039;&#039;, &#039;&#039;&#039;Add key&#039;&#039;&#039;, &#039;&#039;&#039;Create new key&#039;&#039;&#039;, type &#039;&#039;&#039;JSON&#039;&#039;&#039;. Google downloads a small .json file, once -- it cannot be downloaded again. This file is the account&#039;s password; treat it like one.&lt;br /&gt;
# Copy the account&#039;s e-mail address, which looks like &amp;lt;code&amp;gt;something@&#039;&#039;your-project&#039;&#039;.iam.gserviceaccount.com&amp;lt;/code&amp;gt;, and &#039;&#039;&#039;share each spreadsheet with it&#039;&#039;&#039; in Google Sheets exactly as you would with a person: Share, paste the address, Viewer for read-only or Editor if the model writes. A service account sees nothing that has not been shared with it.&lt;br /&gt;
&lt;br /&gt;
The service account needs no consent screen, no Google verification, and no security assessment, because nobody is being asked to grant it anything: it holds its own credentials, and you decide what it can reach by sharing.&lt;br /&gt;
&lt;br /&gt;
== Giving Analytica the key ==&lt;br /&gt;
&lt;br /&gt;
Put the contents of the .json file in a [[Secret]], and pass the Secret as the «account» parameter:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;code&amp;gt;SpreadsheetOpen(&amp;quot;https://docs.google.com/spreadsheets/d/&#039;&#039;id&#039;&#039;/edit&amp;quot;, account: Sheets_Robot_Key)&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
where &amp;lt;code&amp;gt;Sheets_Robot_Key&amp;lt;/code&amp;gt; is a Secret whose value is the whole JSON text. Set its &#039;&#039;&#039;Sinks&#039;&#039;&#039; to &amp;lt;code&amp;gt;SpreadsheetOpen&amp;lt;/code&amp;gt; and its &#039;&#039;&#039;Destination&#039;&#039;&#039; to &amp;lt;code&amp;gt;https://oauth2.googleapis.com/&amp;lt;/code&amp;gt;, so the key can be used for this and nothing else; Analytica refuses the substitution otherwise. Which storage kind the Secret uses -- in the model, an environment variable, per machine user, or a vault -- is your choice, and is the thing that decides who else can run the model.&lt;br /&gt;
&lt;br /&gt;
Passing the JSON as a plain text literal works too, and is the wrong thing to do in a saved model: anyone who opens the file then has the key.&lt;br /&gt;
&lt;br /&gt;
The «backend» parameter is not needed; a Google Sheets link selects the backend by itself.&lt;br /&gt;
&lt;br /&gt;
== What is different with a service account ==&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Nothing interactive ever happens.&#039;&#039;&#039; No browser opens, no dialog appears, and nothing waits for a person. If the account cannot reach the spreadsheet the call reports that and stops.&lt;br /&gt;
* &#039;&#039;&#039;The file chooser is not involved.&#039;&#039;&#039; Any spreadsheet shared with the account opens directly from its link. There is no &amp;quot;pick one&amp;quot; mode, so &amp;lt;code&amp;gt;SpreadsheetOpen(&amp;quot;&amp;quot;)&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;showDialog: True&amp;lt;/code&amp;gt; have nothing to offer.&lt;br /&gt;
* &#039;&#039;&#039;It ignores the desktop&#039;s connection entirely.&#039;&#039;&#039; The Windows user&#039;s own Google connection, and the [[Using your own Google OAuth client|client-registration settings]], play no part; the key carries its own identity and its own permissions. You can use a service account for one spreadsheet and your own connection for another in the same model.&lt;br /&gt;
* &#039;&#039;&#039;Reads and writes behave exactly as they do otherwise&#039;&#039;&#039;: a snapshot on open, writes sent when the computation finishes, on &amp;lt;code&amp;gt;SpreadsheetSave(wb)&amp;lt;/code&amp;gt;, or on &amp;lt;code&amp;gt;SpreadsheetSetInfo(wb, &#039;Refresh&#039;, true)&amp;lt;/code&amp;gt;. Give the account &#039;&#039;&#039;Editor&#039;&#039;&#039; on the spreadsheet if the model writes to it.&lt;br /&gt;
* &amp;lt;code&amp;gt;SpreadsheetInfo(wb, &#039;Account&#039;)&amp;lt;/code&amp;gt; reports the service account&#039;s address, so a model can say which identity a workbook was opened with.&lt;br /&gt;
* &amp;lt;code&amp;gt;SpreadsheetOpen(&amp;quot;New&amp;quot;, backend:&#039;GoogleSheets&#039;, account: &#039;&#039;key&#039;&#039;)&amp;lt;/code&amp;gt; does work, but the new spreadsheet is created in the &#039;&#039;&#039;service account&#039;s own Drive&#039;&#039;&#039;, where no person can see it. That is occasionally what you want for scratch space and almost never what you want otherwise; a service account has no Drive anyone can browse.&lt;br /&gt;
&lt;br /&gt;
== When it does not work ==&lt;br /&gt;
&lt;br /&gt;
The most common failure is the one that has nothing to do with Analytica: the spreadsheet was never shared with the service account. Analytica says so by name --&lt;br /&gt;
&lt;br /&gt;
:&#039;&#039;The Google service account &#039;&#039;name&#039;&#039;@&#039;&#039;project&#039;&#039;.iam.gserviceaccount.com cannot open the spreadsheet ...&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
-- and the cure is to share it at that address. Note that sharing a &#039;&#039;folder&#039;&#039; with the account shares what is in it, which is usually the tidier arrangement for a model that reads several spreadsheets.&lt;br /&gt;
&lt;br /&gt;
A key that is damaged, truncated, or not a service-account key at all is reported when the call is evaluated, before any network request, so a mistyped Secret fails immediately rather than halfway through a run.&lt;br /&gt;
&lt;br /&gt;
Some organizations&#039; Workspace policies block service-account keys from being created at all, or disable them after a period. If the console will not produce a key, that is an administrator&#039;s setting, not a Google-wide limit.&lt;br /&gt;
&lt;br /&gt;
=== Not supported ===&lt;br /&gt;
&lt;br /&gt;
Domain-wide delegation -- a service account impersonating a member of your Workspace -- is not supported. The service account acts as itself, and sees what has been shared with it.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
== See Also ==&lt;br /&gt;
&lt;br /&gt;
* [[Functions To Read Excel Worksheets]] -- [[Functions To Read Excel Worksheets#SpreadsheetOpen|SpreadsheetOpen]] and the [[Functions To Read Excel Worksheets#Backend|GoogleSheets backend]]&lt;br /&gt;
* [[Using your own Google OAuth client]]&lt;br /&gt;
* [[Secret]]&lt;br /&gt;
* [[Analytica Decision Engine]]&lt;/div&gt;</summary>
		<author><name>Lchrisman</name></author>
	</entry>
	<entry>
		<id>https://docs.analytica.com/index.php?title=Using_your_own_Google_OAuth_client&amp;diff=64573</id>
		<title>Using your own Google OAuth client</title>
		<link rel="alternate" type="text/html" href="https://docs.analytica.com/index.php?title=Using_your_own_Google_OAuth_client&amp;diff=64573"/>
		<updated>2026-09-18T16:24:46Z</updated>

		<summary type="html">&lt;p&gt;Lchrisman: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:Integration Functions]]&lt;br /&gt;
{{ReleaseBar}}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Requires [[Analytica 7.2]]&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{{Release|1=7.2|2=|3=&lt;br /&gt;
When the [[Functions To Read Excel Worksheets#Backend|GoogleSheets backend]] of [[Functions To Read Excel Worksheets#SpreadsheetOpen|SpreadsheetOpen]] connects to Google, Analytica identifies itself to Google as an application named &#039;&#039;&#039;Analytica&#039;&#039;&#039;, registered by Lumina Decision Systems. That needs no setup and is the right arrangement for nearly everyone.&lt;br /&gt;
&lt;br /&gt;
This page is for the few organizations that would rather have Analytica identify itself as an application registered by &#039;&#039;&#039;them&#039;&#039;&#039;. It describes what that does and does not change, when it is worth the trouble, and how to set it up. If what you need instead is for a server, a scheduled job or [[Analytica Decision Engine|ADE]] to open a spreadsheet with nobody present, see [[Using a Google service account]].&lt;br /&gt;
&lt;br /&gt;
== What the application identity is, and is not ==&lt;br /&gt;
&lt;br /&gt;
The client ID and client secret that Analytica sends to Google say &#039;&#039;which program is asking&#039;&#039;. They say nothing about &#039;&#039;who you are&#039;&#039;, and nothing about &#039;&#039;which spreadsheets&#039;&#039; it may touch. In particular, using Lumina&#039;s registration does &#039;&#039;&#039;not&#039;&#039;&#039; restrict Analytica to non-confidential spreadsheets:&lt;br /&gt;
&lt;br /&gt;
* Analytica asks Google for one permission, &amp;lt;code&amp;gt;drive.file&amp;lt;/code&amp;gt;, which lets it see only the spreadsheets you yourself select in Google&#039;s file chooser, plus ones it creates. A confidential spreadsheet is no different from any other: select it in the chooser and Analytica can read and write it.&lt;br /&gt;
* No spreadsheet content and no Google credential ever passes through Lumina. Your browser talks to Google, your computer exchanges tokens with Google directly, and the resulting connection is kept in the Windows Credential Manager under your Windows account. Registering your own application changes none of that.&lt;br /&gt;
* On its own, your own registration does not remove the browser step, and does not change which spreadsheets the chooser offers you. It is what makes the &#039;&#039;broader permission&#039;&#039; below possible, and that is what removes the chooser.&lt;br /&gt;
&lt;br /&gt;
== Why you might want your own ==&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Your organization controls which third-party applications may use its Google Workspace data.&#039;&#039;&#039; A Workspace administrator can allow or block individual applications (Admin console, under Security, API controls). If Analytica has not been allowed, connecting fails, or Google&#039;s chooser offers nothing you can select. An application registered inside your own organization is under your administrator&#039;s control from the start, instead of needing an exception made for an outside one.&lt;br /&gt;
* &#039;&#039;&#039;You want to skip the file chooser altogether.&#039;&#039;&#039; Google will only grant an application permission to &#039;&#039;every&#039;&#039; spreadsheet after a review, and for an outside application that review is an annual paid security assessment. An application registered as &#039;&#039;&#039;Internal&#039;&#039;&#039; to your own Google Workspace skips it entirely. See [[#Asking for a broader permission]] below.&lt;br /&gt;
* &#039;&#039;&#039;You want the consent screen, the grants and the audit trail inside your own organization.&#039;&#039;&#039; The browser names &#039;&#039;your&#039;&#039; application, the grants appear in your own Google Cloud project, and an administrator can review or revoke them centrally.&lt;br /&gt;
* &#039;&#039;&#039;You want your own API quota.&#039;&#039;&#039; Google counts Drive and Sheets API usage per Cloud project. Your own project gets its own quota, rather than sharing a pool with other Analytica users.&lt;br /&gt;
* &#039;&#039;&#039;You would rather Analytica&#039;s access to your data not depend on an application registered by another company at all.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
If none of these apply to you, use the built-in registration and ignore this page.&lt;br /&gt;
&lt;br /&gt;
== Registering the application with Google ==&lt;br /&gt;
&lt;br /&gt;
You need a Google Cloud project, which is free.&lt;br /&gt;
&lt;br /&gt;
# In the [https://console.cloud.google.com/ Google Cloud console], create a project, or choose one your organization already uses.&lt;br /&gt;
# Enable three APIs for it (APIs and services, then Enable APIs and services): &#039;&#039;&#039;Google Drive API&#039;&#039;&#039;, &#039;&#039;&#039;Google Sheets API&#039;&#039;&#039; and &#039;&#039;&#039;Google Picker API&#039;&#039;&#039;. The Picker API is the one that presents the file chooser; without it the connection cannot finish.&lt;br /&gt;
# Configure the OAuth consent screen (Google Auth Platform, then Branding). Give it an application name -- this is the name the person sees in the browser, so use one they will recognize -- a user-support e-mail address, and a developer contact address.&lt;br /&gt;
#* Choose the &#039;&#039;&#039;Internal&#039;&#039;&#039; user type if your organization uses Google Workspace and only its own members need to connect. Internal applications skip Google&#039;s verification process entirely, which also makes the broader permission below available to you.&lt;br /&gt;
#* &#039;&#039;&#039;External&#039;&#039;&#039; also works for the default permission. Because Analytica asks only for the non-sensitive &amp;lt;code&amp;gt;drive.file&amp;lt;/code&amp;gt; permission, an External application can be published without a Google review. While it is left in &#039;&#039;Testing&#039;&#039;, though, only the accounts listed as test users can connect, and their connections stop working after seven days.&lt;br /&gt;
# Create the credential: APIs and services, Credentials, Create credentials, &#039;&#039;&#039;OAuth client ID&#039;&#039;&#039;, application type &#039;&#039;&#039;Desktop app&#039;&#039;&#039;. Copy the &#039;&#039;&#039;Client ID&#039;&#039;&#039; and the &#039;&#039;&#039;Client secret&#039;&#039;&#039; that Google shows. There is no redirect URI to fill in -- a desktop client may use the local loopback address that Analytica uses.&lt;br /&gt;
&lt;br /&gt;
Google calls the second value a secret, but for a desktop application it is not a true secret: it is distributed inside the program, and Google&#039;s own documentation treats installed applications as unable to keep it confidential. Look after it as you would any configuration value, but it is not a password, and on its own it grants access to nothing.&lt;br /&gt;
&lt;br /&gt;
== Telling Analytica to use it ==&lt;br /&gt;
&lt;br /&gt;
Set two values in the registry, under&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;code&amp;gt;HKEY_CURRENT_USER\Software\Lumina Decision Systems&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
for one Windows user, or under&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;code&amp;gt;HKEY_LOCAL_MACHINE\SOFTWARE\Lumina Decision Systems&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
for everyone on the computer. If a value appears in both, the HKEY_CURRENT_USER one wins.&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;code&amp;gt;GoogleOAuthClientId&amp;lt;/code&amp;gt; (REG_SZ)&lt;br /&gt;
:The full client ID, ending in &amp;lt;code&amp;gt;.apps.googleusercontent.com&amp;lt;/code&amp;gt;&lt;br /&gt;
;&amp;lt;code&amp;gt;GoogleOAuthClientSecret&amp;lt;/code&amp;gt; (REG_SZ)&lt;br /&gt;
:The client secret&lt;br /&gt;
;&amp;lt;code&amp;gt;GoogleOAuthScope&amp;lt;/code&amp;gt; (REG_SZ)&lt;br /&gt;
:&#039;&#039;Optional.&#039;&#039; See [[#Asking for a broader permission]] below. Leave it unset to keep the default.&lt;br /&gt;
&lt;br /&gt;
That key is deliberately the one &#039;&#039;&#039;above&#039;&#039;&#039; the per-product keys, so a single setting covers Analytica, ADE and the other Analytica products alike. Use it: one Google connection is shared by every product running as the same Windows user, and it is remembered under the client ID, so a client ID set for only one of them would leave the others unable to find the connection. (A value under a product&#039;s own key -- &amp;lt;code&amp;gt;...\Lumina Decision Systems\Analytica&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;...\ADEW&amp;lt;/code&amp;gt; -- still wins for that product, if you ever need to make one of them differ.) None of these keys has a release number in it, so the setting applies to every installed release.&lt;br /&gt;
&lt;br /&gt;
Set both the ID and the secret. If the ID is given without the secret, Google refuses the connection. As a .reg file:&lt;br /&gt;
&lt;br /&gt;
 Windows Registry Editor Version 5.00&lt;br /&gt;
 &lt;br /&gt;
 [HKEY_CURRENT_USER\Software\Lumina Decision Systems]&lt;br /&gt;
 &amp;quot;GoogleOAuthClientId&amp;quot;=&amp;quot;123456789012-examplexample.apps.googleusercontent.com&amp;quot;&lt;br /&gt;
 &amp;quot;GoogleOAuthClientSecret&amp;quot;=&amp;quot;GOCSPX-exampleexampleexample&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Removing the values restores the built-in registration.&lt;br /&gt;
&lt;br /&gt;
The change takes effect at the next connection; there is no need to restart Analytica. Connections are remembered separately for each client ID, so the first spreadsheet you open after the change asks you to connect once more. Your earlier connection is left untouched, and is used again if you remove the values.&lt;br /&gt;
&lt;br /&gt;
== Asking for a broader permission ==&lt;br /&gt;
&lt;br /&gt;
By default Analytica asks Google for one permission, &amp;lt;code&amp;gt;https://www.googleapis.com/auth/drive.file&amp;lt;/code&amp;gt;, which covers only the spreadsheets you pick in Google&#039;s chooser. That is why a spreadsheet you have not used with Analytica before sends you to the browser once.&lt;br /&gt;
&lt;br /&gt;
The optional &amp;lt;code&amp;gt;GoogleOAuthScope&amp;lt;/code&amp;gt; value replaces that request. Setting it to&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;code&amp;gt;https://www.googleapis.com/auth/drive&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
asks instead for access to your Drive as a whole. The effect on everyday use is that &#039;&#039;&#039;the file chooser disappears&#039;&#039;&#039;: you connect once, and after that every spreadsheet link you paste simply opens, in any model, with no further browser trips. The trade is the single consent screen at the start, which now asks for a great deal more, so it is a decision to take deliberately rather than a convenience to switch on by reflex.&lt;br /&gt;
&lt;br /&gt;
Two things to know before you use it:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;In practice this needs an Internal application.&#039;&#039;&#039; Google classes whole-Drive access as a &#039;&#039;restricted&#039;&#039; scope: an External application must pass an annual, paid third-party security assessment before Google will allow it, which is why Analytica&#039;s built-in registration does not offer this. An &#039;&#039;&#039;Internal&#039;&#039;&#039; application in your own Workspace is exempt from verification, so the setting works immediately there. Do not set it while using Lumina&#039;s built-in registration.&lt;br /&gt;
* &#039;&#039;&#039;&amp;lt;code&amp;gt;.../auth/spreadsheets&amp;lt;/code&amp;gt; alone is not enough.&#039;&#039;&#039; Analytica reads a native Google Sheet through Drive&#039;s export, and an Excel workbook stored in Drive through a Drive download, so it needs a Drive permission and not only a Sheets one. Give the value &amp;lt;code&amp;gt;https://www.googleapis.com/auth/drive&amp;lt;/code&amp;gt;, or a space-separated list containing it.&lt;br /&gt;
&lt;br /&gt;
Changing this value makes Analytica ask you to connect again, because the connection it has was granted under the old permission. With any value that does not include &amp;lt;code&amp;gt;drive.file&amp;lt;/code&amp;gt; there is no chooser to browse in, so &amp;lt;code&amp;gt;SpreadsheetOpen(&amp;quot;&amp;quot;)&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;showDialog: True&amp;lt;/code&amp;gt; no longer offer one -- give a spreadsheet link instead.&lt;br /&gt;
&lt;br /&gt;
== Checking that it worked, and disconnecting ==&lt;br /&gt;
&lt;br /&gt;
Open any Google Sheets link with [[Functions To Read Excel Worksheets#SpreadsheetOpen|SpreadsheetOpen]]. The browser page that asks for permission should now name &#039;&#039;your&#039;&#039; application rather than Analytica. Afterwards:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;code&amp;gt;SysLib_Internal::GoogleAccountEmail()&amp;lt;/code&amp;gt; -- the Google account now connected, or Null when there is none.&lt;br /&gt;
:&amp;lt;code&amp;gt;SpreadsheetInfo(wb, &#039;Account&#039;)&amp;lt;/code&amp;gt; -- the account an open workbook was opened with.&lt;br /&gt;
&lt;br /&gt;
To disconnect -- which revokes the access at Google and forgets the stored connection -- evaluate&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;code&amp;gt;SysLib_Internal::DisconnectGoogleAccount()&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
from a button&#039;s OnClick, or in the [[Typescript Window|Typescript window]]. It is a side effect, so it is refused inside a variable&#039;s Definition. It returns the e-mail address of the account that was disconnected, or Null when none was connected. You can also revoke Analytica&#039;s access at any time from [https://myaccount.google.com/permissions your Google account&#039;s permissions page].&lt;br /&gt;
&lt;br /&gt;
== Notes and limits ==&lt;br /&gt;
&lt;br /&gt;
* One Google connection is remembered at a time for a given client ID, for the Windows user who made it. [[Analytica Decision Engine|ADE]] scripts running as the same Windows user share that connection, which is why connecting once in Analytica is enough for them -- and why the settings above belong in the shared registry key.&lt;br /&gt;
* A model published to the [[Analytica Cloud Platform]] does not use these settings. There, each person connects their own Google account through the ACP server&#039;s own registration, and the settings on the author&#039;s computer play no part.&lt;br /&gt;
* These settings say how the person at the browser connects. They have no effect on [[Using a Google service account|a service account]] given to &amp;lt;code&amp;gt;SpreadsheetOpen&amp;lt;/code&amp;gt;&#039;s «account» parameter, which carries its own credentials and its own permissions.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
== See Also ==&lt;br /&gt;
&lt;br /&gt;
* [[Functions To Read Excel Worksheets]] -- [[Functions To Read Excel Worksheets#SpreadsheetOpen|SpreadsheetOpen]] and the [[Functions To Read Excel Worksheets#Backend|GoogleSheets backend]]&lt;br /&gt;
* [[Using a Google service account]]&lt;br /&gt;
* [[Functions to Write Data to Excel Worksheets]]&lt;br /&gt;
* [[SpreadsheetInfo]]&lt;br /&gt;
* [[Analytica Cloud Platform]]&lt;/div&gt;</summary>
		<author><name>Lchrisman</name></author>
	</entry>
	<entry>
		<id>https://docs.analytica.com/index.php?title=Functions_To_Read_Excel_Worksheets&amp;diff=64572</id>
		<title>Functions To Read Excel Worksheets</title>
		<link rel="alternate" type="text/html" href="https://docs.analytica.com/index.php?title=Functions_To_Read_Excel_Worksheets&amp;diff=64572"/>
		<updated>2026-09-18T15:47:06Z</updated>

		<summary type="html">&lt;p&gt;Lchrisman: ER 21588 Phase 4: link the service-account page (re-applying; a cached action=raw fetch reverted it)&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:Excel to Analytica mappings]]&lt;br /&gt;
[[Category:Integration Functions]]&lt;br /&gt;
&lt;br /&gt;
__TOC__&lt;br /&gt;
{{ReleaseBar}}&lt;br /&gt;
&lt;br /&gt;
These functions let you open an Excel spreadsheet file, and read cells and ranges from it. For writing to a spreadsheet, see [[Functions to Write Data to Excel Worksheets]].&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;SpreadsheetOpen&amp;quot; &amp;gt;&lt;br /&gt;
== SpreadsheetOpen(filename&#039;&#039;, showDialog, title{{Release|7.0||, backend}}{{Release|7.2||, account}}&#039;&#039;) ==&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Opens a spreadsheet file and returns a workbook object for use by other functions (such as [[SpreadsheetCell]] or [[SpreadsheetRange]]) to read from or write to the file.&lt;br /&gt;
&lt;br /&gt;
The returned object displays in a result table as &amp;lt;code&amp;gt;«ExcelWorkbook»&amp;lt;/code&amp;gt;{{Release|7.2||, &amp;lt;code&amp;gt;«GoogleSheetsWorkbook»&amp;lt;/code&amp;gt; }}{{Release|7.0|| or &amp;lt;code&amp;gt;«LibXlWorkbook»&amp;lt;/code&amp;gt;, depending on the «backend» used.}} {{Release|1=7.2|2=|3=A workbook opened from a native Google Sheet displays as &amp;lt;code&amp;gt;«GoogleSheetsWorkbook»,&amp;lt;/code&amp;gt; but an Excel file stored in Google Drive displays as &amp;lt;code&amp;gt;«LibXlWorkbook»&amp;lt;/code&amp;gt;.}}&lt;br /&gt;
&lt;br /&gt;
Unless you include a complete file path in «filename», Analytica looks for the file in the [[CurrentDataFolder]]. You can also provide the name of a workbook that is currently open in Excel, even if it has not yet been saved to disk.&lt;br /&gt;
&lt;br /&gt;
If you omit the optional parameter «showDialog», the file browser dialog opens only if the specified file cannot be found.&lt;br /&gt;
* Set «showDialog» to True (&amp;lt;code&amp;gt;1&amp;lt;/code&amp;gt;) to force the file browser even if the file exists.&lt;br /&gt;
* Set «showDialog» to False (&amp;lt;code&amp;gt;0&amp;lt;/code&amp;gt;) to suppress the dialog entirely.&lt;br /&gt;
&lt;br /&gt;
If no file is successfully opened, the function flags an error. You can customize the file dialog caption by passing text to the optional «title» parameter.&lt;br /&gt;
&lt;br /&gt;
[[SpreadsheetOpen]] can return two values: the workbook object and the full path to the file that was opened. This is particularly useful when the user selects a file via the dialog: &lt;br /&gt;
::&amp;lt;code&amp;gt;Local (wb, filePath) := SpreadsheetOpen(&amp;quot;Data.xlsx&amp;quot;);&amp;lt;/code&amp;gt;&lt;br /&gt;
{{Release|1=7.0|2=|3=&lt;br /&gt;
{{Release|1=7.1|2=|3=&lt;br /&gt;
=== Creating a new workbook ===&lt;br /&gt;
If you pass &amp;lt;code&amp;gt;&amp;quot;New&amp;quot;&amp;lt;/code&amp;gt; as the «filename», SpreadsheetOpen creates a new blank workbook with a single empty sheet, without saving to any file. This is useful for building a workbook from scratch before saving it with [[Functions_to_Write_Data_to_Excel_Worksheets#SpreadsheetSave|SpreadsheetSave]].&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;code&amp;gt;SpreadsheetOpen(&amp;quot;New&amp;quot;)&amp;lt;/code&amp;gt; — Creates a new workbook using the Excel backend.&lt;br /&gt;
:&amp;lt;code&amp;gt;SpreadsheetOpen(&amp;quot;New&amp;quot;, backend:&#039;LibXl&#039;)&amp;lt;/code&amp;gt; — Creates a new workbook using the LibXl backend.&lt;br /&gt;
&lt;br /&gt;
You can then add sheets using the &amp;lt;code&amp;gt;+&amp;lt;/code&amp;gt; prefix in [[Functions_to_Write_Data_to_Excel_Worksheets#SpreadsheetSetRange|SpreadsheetSetRange]] or [[Functions_to_Write_Data_to_Excel_Worksheets#SpreadsheetSetCell|SpreadsheetSetCell]], and save with SpreadsheetSave when done.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== Backend === &lt;br /&gt;
&#039;&#039;(New to [[Analytica 7.0]])&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The optional «backend» parameter determines which underlying engine Analytica uses to handle the spreadsheet.&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;&#039;Excel&#039;&amp;lt;/code&amp;gt;: (Default) Uses the Microsoft Excel COM interface. &lt;br /&gt;
*;Requirements: Requires Microsoft Excel to be installed locally.  &lt;br /&gt;
*;Capabilities: This backend includes the full Excel calculation engine. If you change cell values using [[SpreadsheetSetCell]] or [[SpreadsheetSetRange]], formulas within the workbook will be recalculated, allowing you to read back computed results. It supports all standard Excel file formats and features. &lt;br /&gt;
*;Return type: Returns an «ExcelWorkbook» object.&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;&#039;LibXl&#039;&amp;lt;/code&amp;gt;: Uses a built-in library for direct file access.&lt;br /&gt;
*; Requirements: Does not require Microsoft Excel to be installed. &lt;br /&gt;
*; Capabilities: Offers high performance for reading and writing raw data. It is ideal for automated environments (like servers) where Excel might not be present. Note that it does &#039;&#039;&#039;&#039;&#039;not&#039;&#039;&#039;&#039;&#039; include a calculation engine; it reads literal values and formulas from the file but cannot &amp;quot;re-calc&amp;quot; a workbook after data is changed. &lt;br /&gt;
*; Return type: Returns a «LibXlWorkbook» object.&lt;br /&gt;
{{Release|1=7.2|2=|3=&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;&#039;GoogleSheets&#039;&amp;lt;/code&amp;gt;: Opens a Google Sheets spreadsheet, or an Excel workbook stored in Google Drive, from its link. It is selected automatically when «filename» is a &amp;lt;code&amp;gt;https://docs.google.com/spreadsheets/d/...&amp;lt;/code&amp;gt; link (copy it from your browser&#039;s address bar while the sheet is open), so «backend» can be omitted.&lt;br /&gt;
*; Requirements: A Google account with access to the spreadsheet. The first time, Analytica opens your web browser so you can sign in to Google and select the spreadsheet in Google&#039;s file chooser; the connection is remembered on your computer under your Windows account, never in the model, and you can revoke it from your Google account settings. Analytica can open only the spreadsheets you select in that chooser (plus ones it creates itself), so a link to a spreadsheet the chooser did not show cannot be opened. Pass an empty «filename» (or «showDialog»: True) to browse for a spreadsheet.&lt;br /&gt;
*; Capabilities: Reads come from a snapshot taken when the workbook is opened. Writes made with [[SpreadsheetSetCell]] and [[SpreadsheetSetRange]], and sheets added or removed, are sent to Google when the computation finishes, when you call [[Functions_to_Write_Data_to_Excel_Worksheets#SpreadsheetSave|SpreadsheetSave]](wb), or when the workbook is refreshed: &amp;lt;code&amp;gt;SpreadsheetSetInfo(wb, &#039;Refresh&#039;, true)&amp;lt;/code&amp;gt; sends the pending writes and re-downloads the spreadsheet, so the values Google computed (and other people&#039;s edits) are seen. A native Google Sheet is exported by Google as an .xlsx snapshot (Google limits the export to 10 MB); an Excel workbook stored in Drive is downloaded as-is and written back as a whole file. The tab names the model sees (&amp;lt;code&amp;gt;SpreadsheetInfo(wb, &#039;Sheets&#039;)&amp;lt;/code&amp;gt;, and «sheet» given by name) are the names in the exported snapshot, truncated to 31 characters, while writes are addressed to Google&#039;s real tab titles. &amp;lt;code&amp;gt;SpreadsheetOpen(&amp;quot;New&amp;quot;, backend:&#039;GoogleSheets&#039;, title: &amp;quot;My sheet&amp;quot;)&amp;lt;/code&amp;gt; creates a new Google Sheet in your Drive. The second return value is the spreadsheet&#039;s link.&lt;br /&gt;
*; Return type: Returns a «GoogleSheetsWorkbook» object for a native Google Sheet, or a «LibXlWorkbook» object for an Excel file stored in Drive. &amp;lt;code&amp;gt;SpreadsheetInfo(wb, &#039;Backend&#039;)&amp;lt;/code&amp;gt; returns &amp;lt;code&amp;gt;&#039;GoogleSheets&#039;&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;&#039;LibXl&#039;&amp;lt;/code&amp;gt; accordingly, and &amp;lt;code&amp;gt;SpreadsheetInfo(wb, &#039;URL&#039;)&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;SpreadsheetInfo(wb, &#039;Account&#039;)&amp;lt;/code&amp;gt; give its link and the Google account it was opened with.&lt;br /&gt;
*; Advanced: Analytica identifies itself to Google as an application registered by Lumina. An organization that would rather it identified itself as an application of their own -- because their Google Workspace administrator controls which outside applications may reach their data, for example -- can arrange that; see [[Using your own Google OAuth client]], which also covers asking Google for access to every spreadsheet so that the chooser is never shown.&lt;br /&gt;
*; Unattended use: A server, a scheduled job or [[Analytica Decision Engine|ADE]] on a machine where nobody is present cannot answer a browser. Give «account» a Google service account instead, and share the spreadsheet with its e-mail address -- see [[Using a Google service account]].&lt;br /&gt;
}}&lt;br /&gt;
}}&lt;br /&gt;
=== Example ===&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;code&amp;gt;SpreadsheetOpen(&amp;quot;C:\MyModels\Sales Numbers.xlsx&amp;quot;) &amp;amp;rarr; &#039;&#039;«ExcelWorkbook»&#039;&#039;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;SpreadsheetCell&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Getting the file name actually opened ===&lt;br /&gt;
Your code may want to know the file path for which file was actually opened. This may differ from «filename» when the specified file is not found, or when  «showDialog» forces a dialog, allowing the user to select a different file. [[SpreadsheetOpen]] returns the file path as a second return value, which you can optionally capture using, e.g.,&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;code&amp;gt;[[Local]] (contents, filepath) := [[SpreadsheetOpen]]( ... );&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A common pattern is that you may want to save the filename in a variable such that when the evaluation is repeated in the future, it can supply the file selected by the user to the «filename» parameter. This can be coded by supplying first a global variable to hold the filename defined using [[ComputedBy]] with the default filename as follows:&lt;br /&gt;
&lt;br /&gt;
:Variable TheFilename ::= &lt;br /&gt;
::&amp;lt;code&amp;gt;[[ComputedBy]]( TheFileContetns, &amp;quot;defaultFilename.xlsx&amp;quot; ) &amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
:Variable TheWorkbook::= &lt;br /&gt;
::&amp;lt;code&amp;gt;( , TheFilename ) := [[SpreadsheetOpen]]( TheFilename )&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The assignment to &amp;lt;code&amp;gt;( , TheFilename )&amp;lt;/code&amp;gt; passes through the first parameter as the result of the assignment expression, but assigns the second return value the &amp;lt;code&amp;gt;TheFilename&amp;lt;/code&amp;gt;. The assignment to &amp;lt;code&amp;gt;TheFilename&amp;lt;/code&amp;gt; is a [[side-effect]] that is allowed only because &amp;lt;code&amp;gt;TheFilename&amp;lt;/code&amp;gt; is defined as a [[ComputedBy]]. The assignment changes the value, but also rewrites the second parameter of the call to [[ComputedBy]], thus permanently preserving the filename selected. The one line definition of &amp;lt;code&amp;gt;TheWorkbook&amp;lt;/code&amp;gt; is locally equivalent to:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;code&amp;gt;[[Local]] (wb, filename ) := [[SpreadsheetOpen]]( TheFilename );&amp;lt;/code&amp;gt;&lt;br /&gt;
:&amp;lt;code&amp;gt;TheFilename := filename;&amp;lt;/code&amp;gt;&lt;br /&gt;
:&amp;lt;code&amp;gt;wb&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Use with Office 2010 ===&lt;br /&gt;
&lt;br /&gt;
If you have installed the &amp;quot;Click-to-Run&amp;quot; version of Office 2010 from a web download, these spreadsheet functions may not work, due to a &amp;quot;feature&amp;quot; introduced in Office 2010 that apparently disables several common operations.  In this case, you may need to re-install Office using the MSI-based edition.  See how to do this at:&lt;br /&gt;
&lt;br /&gt;
[http://office.microsoft.com/en-gb/excel-help/click-to-run-switch-to-using-a-full-office-edition-HA101850538.aspx http://office.microsoft.com/en-gb/excel-help/click-to-run-switch-to-using-a-full-office-edition-HA101850538.aspx]&lt;br /&gt;
&lt;br /&gt;
=== Excel 64-bit requires Analytica 64-bit ===&lt;br /&gt;
&lt;br /&gt;
Analytica 32-bit cannot launch Excel 64-bit. (The other way around works). Thus, if you have installed Excel 64-bit (which we recommend), make sure you have installed Analytica 64-bit.  If you are a [[Free Edition]] user, you probably have 32-bit installed, but you can install Analytica 64-bit from the [https://www.lumina.com/support/downloads/ Analytica Downloads page].&lt;br /&gt;
&lt;br /&gt;
=== Remembering the selected filename ===&lt;br /&gt;
&lt;br /&gt;
When [[SpreadsheetOpen]]() shows the file dialog and you select a file, it does not save the file name. So, the next time you load the model, you&#039;ll have to select the file again.  If you want the model to remember the selected file, so it will just load it without asking, prompt using that file name as the default, you can use the &#039;&#039;&#039;SpreadsheetOpenEx&#039;&#039;&#039; function in the [[Analytica_Libraries_and_Templates#Spreadsheet_Helper_Library|Spreadsheet Helper Library]].&lt;br /&gt;
&lt;br /&gt;
=== Having same spreadsheet open in Excel at the same time ===&lt;br /&gt;
&lt;br /&gt;
It is often useful to have the spreadsheet you are working with open in Excel at the same time your model is working with it. When you want to do this, is it best to open it Excel first, before evaluating [[SpreadsheetOpen]], in which case [[SpreadsheetOpen]] connects to the existing Excel process and to the currently open spreadsheet. If you change cells in Excel, then evaluate a spreadsheet read functions, you&#039;ll read the new values, and if your model writes to the spreadsheet, you&#039;ll see those values reflected immediately in the Excel interface.&lt;br /&gt;
&lt;br /&gt;
When you call [[SpreadsheetOpen]] before opening the model in Excel, the situation is more complex. To understand what happens and how to view the same model in the Excel UI at the same time, see [[Simultaneously opening a spreadsheet in Excel and Analytica]].&lt;br /&gt;
&lt;br /&gt;
=== SpreadsheetOpenFlags ===&lt;br /&gt;
A registry setting named &amp;lt;code&amp;gt;SpreadsheetOpenFlags&amp;lt;/code&amp;gt; can be set to alter how [[SpreadsheetOpen]] connects to Excel and the initial settings in Excel. There is usually no reason to fiddle with these flags unless you encounter a specific problem. It has been more common to set these flags in server-based applications using ADE than from desktop Analytica.&lt;br /&gt;
&lt;br /&gt;
You&#039;ll need to modify the sitting from RegEdit.  You can set it in either&lt;br /&gt;
:&amp;lt;code&amp;gt;HKEY_LOCAL_MACHINE/Software/Lumina Decision Systems/Analytica/{{#svarget:anarelease|6.1}}&amp;lt;/code&amp;gt;&lt;br /&gt;
or&lt;br /&gt;
:&amp;lt;code&amp;gt;HKEY_CURRENT_USER/Software/Lumina Decision Systems/Analytica/{{#svarget:anarelease|6.1}}&amp;lt;/code&amp;gt;&lt;br /&gt;
For ADE, set it in one of these hives:&lt;br /&gt;
:&amp;lt;code&amp;gt;HKEY_LOCAL_MACHINE/Software/Lumina Decision Systems/Analytica/{{#svarget:anarelease|6.1}}&amp;lt;/code&amp;gt;&lt;br /&gt;
:&amp;lt;code&amp;gt;HKEY_CURRENT_USER/Software/Lumina Decision Systems/Analytica/{{#svarget:anarelease|6.1}}&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Setting it in HKLM causes it to apply from any account on your computer, while setting it from HKCU causes it to apply only to your own account.  A setting in HKCU takes precedence over the same setting in HKLM.&lt;br /&gt;
&lt;br /&gt;
Initially the value &amp;lt;code&amp;gt;SpreadsheetOpenFlags&amp;lt;/code&amp;gt; will not be present. Create a new 32-bit DWORD with this name.  The set the numeric value to an addition of any of these flags that you want:&lt;br /&gt;
* 1 = Launch using a COMCreateObject mechanism.  (unset)=Launch using a BindToObject method. &lt;br /&gt;
*: A BindToObject method (the default for Desktop Analytica) makes it possible to connect to a Workbook running in an active Excel UI. A COMCreateObject mechanism launches a separate instance of Excel every time.   &lt;br /&gt;
* 2 = Turn off Excel&#039;s Interactive flag.&lt;br /&gt;
* 4 = Turn off Excel&#039;s &amp;quot;Ask to update OLE links&amp;quot; flag.&lt;br /&gt;
* 8 = Turn off Excel&#039;s &amp;quot;Display Alerts&amp;quot;&lt;br /&gt;
* 16 = Disable Excel macros (for security)&lt;br /&gt;
* 32 = Close when visible. &lt;br /&gt;
*:Normally, if the workbook is currently visible in an Excel UI, Analytica simply disconnects from it, but doesn&#039;t force the workbook to close.  The Excel UI is then responsible for eventually closing it.  This overrides this and forces the workbook to close when the model releases it, even if it is visible.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== From ADE ===&lt;br /&gt;
When using from ADE on a Web Server, we strongly advise against using Excel 2016 on the server. Excel 2010 works fairly well, but Excel 2016 is extremely unstable and has a tendency to fail unpredictably and lock up all other Excel instances. Microsoft responds by saying that Excel 2016 is not supported nor licensed for use on a web server.&lt;br /&gt;
&lt;br /&gt;
When [[SpreadsheetOpen]] is evaluated in [[ADE|the Analytica Decision Engine (ADE)]] and a dialog needs to be shown to the end-user, it calls [[IAdeUICallbacks::GetFilename]](...). From within that callback, the parent application can interact with the end-user to resolve the file path, and a web applications can instruct the end-user to upload a file. Once complete, the callback returns the full path to the file which is then read. To receive this callback, the parent application must have previously registered the callback with ADE using [[CAEngine::SetCallbackObject]]( ). If it has not registered a callback and the file doesn&#039;t exist, returns an empty text.&lt;br /&gt;
&lt;br /&gt;
Once the open completes, it calls [[IAdeUICallbacks::FileOpenCompleted]]().&lt;br /&gt;
&lt;br /&gt;
=== Debugging Errors ===&lt;br /&gt;
This section documents failures when &#039;&#039;&#039;SpreadsheetOpen&#039;&#039;&#039; has been unable to open Excel, and solutions.&lt;br /&gt;
* &#039;&#039;&#039;&#039;&#039;Library not registered&#039;&#039;&#039;&#039;&#039;: &lt;br /&gt;
*:If this error occurs when &#039;&#039;&#039;SpreadsheetOpen&#039;&#039;&#039; is evaluated...&lt;br /&gt;
** The article [https://excel.tips.net/T002952_Library_Not_Registered_Error.html Library not registered error] explains how to solve this problem when it is caused by an Excel plug-in. It may be caused by a bad Excel add-in library.  You should also run &amp;lt;code&amp;gt;excel.exe /regserver&amp;lt;/code&amp;gt;.&lt;br /&gt;
** In one case, an Analytica user concluded that an older version of Excel was interfering with his newer 32-bit version of Excel. He uninstalled both and re-installed Excel 64-bit and the problem corrected itself.  But, for a different user with this problem, these steps did not correct the problem.&lt;br /&gt;
** A common cause of this problem is when stray registry settings from Excel versions that had been installed and uninstalled interfere with your current version of Excel. This is most common after you roll back to an earlier release after uninstalling a later release. To test for this cause, start Power Shell and run:&lt;br /&gt;
**::&amp;lt;code&amp;gt;get-childitem -Path &amp;quot;HKLM:\Software\Classes\TypeLib\{00020813-0000-0000-C000-000000000046}&amp;quot;&amp;lt;/code&amp;gt;&lt;br /&gt;
::If you see more than one version listed, with the most recent version number missing its mapping to Excel, then this is probably the cause. To fix, use &amp;lt;code&amp;gt;RegEdit&amp;lt;/code&amp;gt; to delete the hive for the later version number.&lt;br /&gt;
&lt;br /&gt;
== SpreadsheetCell(workbook, sheet, column, row&#039;&#039;, what&#039;&#039;) ==&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Returns the value (or other information) of a cell of a worksheet given its coordinates.  The function fully array abstracts, so you can get a range of cells by specifying the column and/or row as an array.&lt;br /&gt;
&lt;br /&gt;
Parameters:&lt;br /&gt;
;«workbook»: A workbook object returned by [[SpreadsheetOpen]]()&lt;br /&gt;
;«sheet»: The name or number of a worksheet from the workbook. Number 1 is the first worksheet, etc.&lt;br /&gt;
::  If you specify &amp;lt;code&amp;gt;sheet: &amp;quot;*&amp;quot;&amp;lt;/code&amp;gt;, it returns the cell value from &#039;&#039;column, row&#039;&#039; for all sheets in the workbook, indexed by &amp;lt;code&amp;gt;.Sheet&amp;lt;/code&amp;gt;, a local index containing the names of the worksheets. This is a way to get a list of all the worksheets in the workbook. If you specify column and/or rows as arrays, you can also use this to get a 3D array for a range over all worksheets.&lt;br /&gt;
;«column»: The column label, e.g., &amp;lt;code&amp;gt;&amp;quot;A&amp;quot;, &amp;quot;B&amp;quot;&amp;lt;/code&amp;gt;, or &amp;lt;code&amp;gt;&amp;quot;AB&amp;quot;&amp;lt;/code&amp;gt;, or the column number as an integer.&lt;br /&gt;
;«row»: The row number as an integer&lt;br /&gt;
;«what»: optional. Let&#039;s you get the formula or format information from the cell. See below under [[SpreadsheetRange]] for details. &lt;br /&gt;
&lt;br /&gt;
If the worksheet cell is empty, it returns [[Null]]. It flags an error if «workbook» is not a valid workbook, if it does not contain «sheet», or if the coordinates are invalid.&lt;br /&gt;
&lt;br /&gt;
=== Examples ===&lt;br /&gt;
These expressions are different ways to get the same result, the value from cell &#039;&#039;C7&#039;&#039; in the first sheet, &amp;lt;code&amp;gt;&amp;quot;Sheet1&amp;quot;&amp;lt;/code&amp;gt; of workbook:&lt;br /&gt;
:&amp;lt;code&amp;gt;SpreadsheetCell(workbook, &amp;quot;Sheet1&amp;quot;, &amp;quot;C&amp;quot;, 7)&amp;lt;/code&amp;gt;&lt;br /&gt;
:&amp;lt;code&amp;gt;SpreadsheetCell(workbook, &amp;quot;Sheet1&amp;quot;, 3, 7)&amp;lt;/code&amp;gt;&lt;br /&gt;
:&amp;lt;code&amp;gt;SpreadsheetCell(workbook, 1, 3, 7)&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Suppose the spreadsheet contains a 2-D table in the region &#039;&#039;C4:J19&#039;&#039;.  The columns of this table correspond to the years 2008..2015.  The rows correspond to different assets.  It is easier to refer to the columns by number, so that the columns &amp;quot;C&amp;quot; thru &amp;quot;J&amp;quot; are columns 3 thru 10.  To hold this 2-D table, we need two indexes in Analytica, &amp;lt;code&amp;gt;Time&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;Asset&amp;lt;/code&amp;gt;.  &lt;br /&gt;
&lt;br /&gt;
:&amp;lt;code&amp;gt;Index Time := 2008..2015&amp;lt;/code&amp;gt;&lt;br /&gt;
:&amp;lt;code&amp;gt;Index Asset := 1..16&amp;lt;/code&amp;gt;&lt;br /&gt;
:&amp;lt;code&amp;gt;Variable Workbook := SpreadsheetOpen(&amp;quot;C:\Asset Data.xls&amp;quot;)&amp;lt;/code&amp;gt;&lt;br /&gt;
:&amp;lt;code&amp;gt;Variable Data := SpreadsheetCell( workbook, &amp;quot;Sheet1&amp;quot;, @Time+2, @Asset+3)&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;SpreadsheetRange&amp;quot;&amp;gt;&lt;br /&gt;
== SpreadsheetRange(workbook, range&#039;&#039;, colIndex, rowIndex, howToIndex, sheet, what&#039;&#039;) ==&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Returns the values (or other information) for a range of cells from an Excel worksheet.  The  «range» can be can be a cell address such as &amp;lt;code&amp;gt;&amp;quot;C7&amp;quot;&amp;lt;/code&amp;gt; or cell range &amp;lt;code&amp;gt;&amp;quot;C7:F12&amp;quot;&amp;lt;/code&amp;gt;, or the name of a range defined in the spreadsheet.  If you want to read or write several cells or ranges in a spreadsheet, it is often convenient to use Excel&#039;s name mechanism and refer to them by name in Analytica.&lt;br /&gt;
&lt;br /&gt;
If the range has multiple columns, the result has local index &amp;lt;code&amp;gt;.Column&amp;lt;/code&amp;gt; unless you specify «colindex» as a parameter. Similarly, if the range has multiple rows, the result has local index &amp;lt;code&amp;gt;.Row&amp;lt;/code&amp;gt; unless you specify «rowindex» as a parameter. Flags in «howToIndex» let you control whether the first row (column) should be used as labels for local index  &amp;lt;code&amp;gt;.Row&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;.Column&amp;lt;/code&amp;gt;.  &lt;br /&gt;
&lt;br /&gt;
If you specify a sheet name with no cells, e.g.  &amp;lt;code&amp;gt;&amp;quot;Inputs!&amp;quot;&amp;lt;/code&amp;gt;, it returns a table that includes all cells from that sheet that contain anything.&lt;br /&gt;
&lt;br /&gt;
By default, it returns the number or text values from the range (or &amp;lt;code&amp;gt;NULL&amp;lt;/code&amp;gt; if the cell is empty). You can use the «what» parameter to obtain the cell formula, address, format, styles, precedent, and dependent cells for each cell.&lt;br /&gt;
&lt;br /&gt;
=== SpreadsheetRange Parameters ===&lt;br /&gt;
&lt;br /&gt;
SpreadsheetRange has two required parameters:&lt;br /&gt;
&lt;br /&gt;
;«workbook»: A workbook object returned by [[SpreadsheetOpen]]()&lt;br /&gt;
; «range»: A cell range.  It may be a single cell address, e.g. &amp;lt;code&amp;gt;&amp;quot;B10&amp;quot;&amp;lt;/code&amp;gt;, a range, e.g. &amp;lt;code&amp;gt;&amp;quot;A1:BC99&amp;quot;&amp;lt;/code&amp;gt;, optionally with sheet name, e.g.  &amp;lt;code&amp;gt;&amp;quot;Sheet1!A1:BC99&amp;quot;&amp;lt;/code&amp;gt;, or a named range, e.g. &amp;lt;code&amp;gt;&amp;quot;Discount_rate&amp;quot;&amp;lt;/code&amp;gt; defined in the spreadsheet. If the «range» doesn&#039;t mention the sheet name, you must specify «sheet» as a separate parameter.&lt;br /&gt;
:: If you specify the range as &amp;lt;code&amp;gt;&amp;quot;Sheet1!&amp;quot;&amp;lt;/code&amp;gt;, with nothing after the &amp;quot;!&amp;quot;, or omit «range» and specify only «sheet», it returns the smallest rectangular range that includes all used cells within the sheet. &lt;br /&gt;
&lt;br /&gt;
SpreadsheetRange has four optional parameters relating to the indexes for a range with multiple columns or rows, or over multiple sheets:&lt;br /&gt;
;«colIndex»: (optional) An index to use for the column dimension of the result.&lt;br /&gt;
;«rowIndex»: (optional) An index to use for the row dimension of the result.&lt;br /&gt;
;«howToIndex»: (optional) Flags controlling how to index the result when «colIndex» or «rowIndex» are not specified.  You can add any of these values to combine their effects:&lt;br /&gt;
::&amp;lt;code&amp;gt;1&amp;lt;/code&amp;gt;: Force a column index even if the range spans only a single column. Has no effect if you specify «colIndex».&lt;br /&gt;
::&amp;lt;code&amp;gt; 2&amp;lt;/code&amp;gt;: Force a row index even if the range spans only a single row.  Has no effect if you specify «rowIndex».&lt;br /&gt;
::&amp;lt;code&amp;gt;4&amp;lt;/code&amp;gt;: Use the first row of «range» as column labels in the local index &amp;lt;code&amp;gt;.Column&amp;lt;/code&amp;gt;. Exclude this first row in the result returned.&lt;br /&gt;
::&amp;lt;code&amp;gt;8&amp;lt;/code&amp;gt;: Use the first column of «range» as labels in the local index &amp;lt;code&amp;gt;.Row&amp;lt;/code&amp;gt;. Exclude this first column in the result returned..&lt;br /&gt;
::&amp;lt;code&amp;gt;16&amp;lt;/code&amp;gt;: Suppress the error message that is otherwise given if the sizes of «colIndex» or «rowIndex» do not match the size of the range.&lt;br /&gt;
;«sheet»: (optional) The name or number of a worksheet inside the workbook. It can be a list of sheets, in which case, the function will return a 3D table, indexed by this list as the third dimension.&lt;br /&gt;
;«what»: (optional)  See below for details on this parameter.&lt;br /&gt;
&lt;br /&gt;
=== Indexes of a cell range ===&lt;br /&gt;
&lt;br /&gt;
The result may be a scalar (single cell), a column vector, a row vector, or a 2-D array, depending on the dimensions of the cell range.  If the range has more than one row (or column),  it will use a local index .Row (.Column) by default. By default, the elements of the .Row index contain the range&#039;s row numbers and elements of the column index contain its column labels.  For example, if the range is &amp;lt;code&amp;gt;&amp;quot;C7:E12&amp;quot;&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;.Row&amp;lt;/code&amp;gt; would contain the elements &amp;lt;code&amp;gt;[7, 8, 9, 10, 11, 12]&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;.Column&amp;lt;/code&amp;gt; would contain &amp;lt;code&amp;gt;[&#039;C&#039;, &#039;D&#039;, &#039;E&#039;]&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Or, you can use the first column (row) of the range as the values for the local index .Row (.Column), by specifying &amp;lt;code&amp;gt;howToIndex: 8&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;howToIndex: 4&amp;lt;/code&amp;gt;, or &amp;lt;code&amp;gt;howToIndex: 12&amp;lt;/code&amp;gt; for both .Row and .Column.)   If you use, the first row (column) of the range as values of the local indexe(es), they will not be included in the value of the array returned. So, in that case, the range must have at least two rows (columns).  &lt;br /&gt;
&lt;br /&gt;
Alternatively, if you already have index(es), you can supply them to the  «rowIndex» («colIndex») parameters.  If you specify a «rowIndex» or «colIndex», that is shorter than the number of rows (columns) in the range, it  truncates the result. If an index is too long, it pads the result with [[Null]].  In these cases, it gives a warning message unless you set flag &amp;lt;code&amp;gt;&#039;&#039;howToIndex: 16&#039;&#039;&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
If the range has just one  column, the result normally will not have a local .Column index. But, you can force it to use a .Column with one element by setting &amp;lt;code&amp;gt;howToIndex: 1&amp;lt;/code&amp;gt;.  If you are using a named range and don&#039;t know how many columns it has, you might use this option to prevent an error occurring if you use [[Dot_operator::A.I|result.Column]] in an expression. Similarly, you can force it to use local &amp;lt;code&amp;gt;.Row&amp;lt;/code&amp;gt; index even when the result has only a single row by specifying &amp;lt;code&amp;gt;howToIndex: 2&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
You can obtain the entire range of a worksheet with all cells that contain anything named &amp;lt;code&amp;gt;&amp;quot;Sheet1&amp;quot;&amp;lt;/code&amp;gt; by specifying the «range» as &amp;lt;code&amp;gt;&amp;quot;Sheet1!&amp;quot;&amp;lt;/code&amp;gt; or by omitting the «range» parameter and specifying just the «sheet» parameter.&lt;br /&gt;
&lt;br /&gt;
=== Examples ===&lt;br /&gt;
&lt;br /&gt;
The following examples use this spreadsheet:&lt;br /&gt;
&lt;br /&gt;
:[[Image:WorksheetRange ExcelShot.jpg]]&lt;br /&gt;
&lt;br /&gt;
This spreadsheet contains these named ranges:&lt;br /&gt;
:{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Label !! Range &lt;br /&gt;
|-&lt;br /&gt;
| Rate || B1&lt;br /&gt;
|-&lt;br /&gt;
| Year || B3:F3&lt;br /&gt;
|-&lt;br /&gt;
| Cash_flow || B4:F4&lt;br /&gt;
|-&lt;br /&gt;
| Divisions || A7:A9&lt;br /&gt;
|-&lt;br /&gt;
| Employee_count || B7:F9&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;code&amp;gt;SpreadsheetRange(wb, &amp;quot;Rate&amp;quot;) &amp;amp;rarr; 0.08&amp;lt;/code&amp;gt;&lt;br /&gt;
:&amp;lt;code&amp;gt;SpreadsheetRange(wb, &amp;quot;Sheet1!B1&amp;quot;) &amp;amp;rarr; 0.08&amp;lt;/code&amp;gt;&lt;br /&gt;
:&amp;lt;code&amp;gt;SpreadsheetRange(wb, &amp;quot;Sheet1!B3:F3&amp;quot;) &amp;amp;rarr;&amp;lt;/code&amp;gt;&lt;br /&gt;
:{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
 ! .Column &amp;amp;rarr; !! &#039;B&#039; !! &#039;C&#039; !! &#039;D&#039; !! &#039;E&#039; !! &#039;F&#039;&lt;br /&gt;
 |-&lt;br /&gt;
 | || 2008 || 2009 || 2010 || 2011 || 2012&lt;br /&gt;
 |}&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;code&amp;gt;Index Year := CopyIndex( SpreadsheetRange(wb, &amp;quot;Year&amp;quot;, howToIndex: 1));&amp;lt;/code&amp;gt;&lt;br /&gt;
:&amp;lt;code&amp;gt;SpreadsheetRange(wb, &amp;quot;Cash_flow&amp;quot;, colIndex: Year) &amp;amp;rarr;&amp;lt;/code&amp;gt;&lt;br /&gt;
:{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
 ! .Year &amp;amp;rarr; !! 2008 !! 2009 !! 2010 !! 2011 !! 2012&lt;br /&gt;
 |-&lt;br /&gt;
 | || -100 || 10 || 30 || 50 || 60&lt;br /&gt;
 |}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Note&#039;&#039;: &amp;lt;code&amp;gt;howToIndex: 1&amp;lt;/code&amp;gt; was specified for &amp;lt;code&amp;gt;Year&amp;lt;/code&amp;gt; here so that we would have a 1-D array even if only one year were present in the spreadsheet.&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;code&amp;gt;SpreadsheetRange(wb, &amp;quot;Employee_count&amp;quot;) &amp;amp;rarr;&amp;lt;/code&amp;gt;&lt;br /&gt;
:{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
 ! !! .Column &amp;amp;rarr; !! &#039;B&#039; !! &#039;C&#039; !! &#039;D&#039; !! &#039;E&#039; !! &#039;F&#039;&lt;br /&gt;
 |-&lt;br /&gt;
 ! rowspan=&amp;quot;3&amp;quot; | .Row&amp;lt;br&amp;gt;&amp;amp;darr; !! 7 &lt;br /&gt;
 | 24 || 27 || 28 || 32 || 35&lt;br /&gt;
 |-&lt;br /&gt;
 ! 8 &lt;br /&gt;
 | 13 || 13 || 13 || 13 || 13&lt;br /&gt;
 |-&lt;br /&gt;
 ! 9 &lt;br /&gt;
 | 25 || 22 || 21 || 19 || 16&lt;br /&gt;
 |}&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;code&amp;gt;Index Time := [2008, 2009, 2010, 2011, 2012];&amp;lt;/code&amp;gt;&lt;br /&gt;
:&amp;lt;code&amp;gt;SpreadsheetRange(wb, &amp;quot;A7:F9&amp;quot;, colIndex: Time, howToIndex: 8, sheet: 1)  &amp;amp;rarr;&amp;lt;/code&amp;gt;&lt;br /&gt;
:{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
 ! !! Time &amp;amp;rarr; !! 2008 !! 2009 !! 2010 !! 2011 !! 2012&lt;br /&gt;
 |-&lt;br /&gt;
 ! rowspan=&amp;quot;3&amp;quot; | .Row&amp;lt;br&amp;gt;&amp;amp;darr; !! &amp;quot;Div A&amp;quot; &lt;br /&gt;
 | 24 || 27 || 28 || 32 || 35&lt;br /&gt;
 |-&lt;br /&gt;
 ! &amp;quot;Div B&amp;quot; &lt;br /&gt;
 | 13 || 13 || 13 || 13 || 13&lt;br /&gt;
 |-&lt;br /&gt;
 ! &amp;quot;Div C&amp;quot; &lt;br /&gt;
 | 25 || 22 || 21 || 19 || 16&lt;br /&gt;
 |}&lt;br /&gt;
&lt;br /&gt;
To obtain the list of worksheet names:&lt;br /&gt;
:&amp;lt;code&amp;gt;SpreadsheetCell(wb, &amp;quot;*&amp;quot;, 1, 1).Sheet&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To obtain all used cells in sheet named &amp;quot;Sheet2&amp;quot;:&lt;br /&gt;
:&amp;lt;code&amp;gt;SpreadsheetRange(wb, sheet:&amp;quot;Sheet2&amp;quot;)&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To obtain the number format of all cells in &amp;quot;Sheet2&amp;quot;:&lt;br /&gt;
:&amp;lt;code&amp;gt;SpreadsheetRange(wb, sheet:&amp;quot;Sheet2&amp;quot;, what:&amp;quot;NumberFormat&amp;quot;)&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===  SpreadsheetRange «what» parameter === &lt;br /&gt;
&lt;br /&gt;
By default, SpreadsheetRange() returns the value of the cell(s) in the range, but you can use the «what» parameter to obtain the formula,  cell style and formats, cell address, predecessor or dependent cells of each cell:&lt;br /&gt;
;«what»: (optional). By default, SpreadsheetRange returns the value of the range, but you can use this parameter to obtain its formula, or cell style parameters.  Possible values: &lt;br /&gt;
::&amp;lt;code&amp;gt;&amp;quot;Value&amp;quot;&amp;lt;/code&amp;gt;: (Default) The computed value.  Excel dates become Analytica date-time numbers, which display as dates.&lt;br /&gt;
::&amp;lt;code&amp;gt;&amp;quot;NumericValue&amp;quot;&amp;lt;/code&amp;gt;: The computed value, but dates are returned as numbers.&lt;br /&gt;
::&amp;lt;code&amp;gt;&amp;quot;Formula&amp;quot;&amp;lt;/code&amp;gt;: The cell formula as a text value in the normal Excel format starting with &amp;quot;=&amp;quot;, e.g., &amp;lt;code&amp;gt;&amp;quot;=Sum(D4:D10)&amp;quot;&amp;lt;/code&amp;gt;&lt;br /&gt;
::&amp;lt;code&amp;gt;&amp;quot;RelativeFormula&amp;quot;&amp;lt;/code&amp;gt;: The cell formula using relative offset format, e.g., &amp;lt;code&amp;gt;&amp;quot;=Sum(RC[-9]:R[+6]C[-9])&amp;quot;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== «what» parameter cell formats  ==== &lt;br /&gt;
::&amp;lt;code&amp;gt;&amp;quot;NumberFormat&amp;quot;&amp;lt;/code&amp;gt;: The cell number format as text.&lt;br /&gt;
::&amp;lt;code&amp;gt;&amp;quot;BackColor&amp;quot;&amp;lt;/code&amp;gt;: Cell background color as integer: &#039;&#039;red*65536 + green*256 + blue&#039;&#039;&lt;br /&gt;
::&amp;lt;code&amp;gt;&amp;quot;Text Color&amp;quot;&amp;lt;/code&amp;gt;: Font color as an integer: &#039;&#039;red*65536 + green*256 + blue&#039;&#039;&lt;br /&gt;
::&amp;lt;code&amp;gt;&amp;quot;FontName&amp;quot;&amp;lt;/code&amp;gt;: Name of the font used to display the cell&lt;br /&gt;
::&amp;lt;code&amp;gt;&amp;quot;FontSize&amp;quot;&amp;lt;/code&amp;gt;: Point size of the font displayed in the cell&lt;br /&gt;
::&amp;lt;code&amp;gt;&amp;quot;FontStyle&amp;quot;&amp;lt;/code&amp;gt;: Special font styles for cell separated by spaces, may include &amp;quot;bold italic underline strikethrough subscript superscript outline shadow&amp;quot;&lt;br /&gt;
::&amp;lt;code&amp;gt;&amp;quot;HorizontalAlignment&amp;quot;&amp;lt;/code&amp;gt;: Text justification, one of: &amp;lt;code&amp;gt;&#039;Left&#039;, &#039;Center&#039;, &#039;Right&#039;, &#039;Justify&#039;, &#039;Distributed&#039;, &#039;Fill&#039;&amp;lt;/code&amp;gt;.  (&#039;&#039;new to [[Analytica 5.0]]&#039;&#039;)&lt;br /&gt;
::&amp;lt;code&amp;gt;&amp;quot;VerticalAlignment&amp;quot;&amp;lt;/code&amp;gt;: Text vertical justification, one of: &amp;lt;code&amp;gt;&#039;Top&#039;, &#039;Middle&#039;, &#039;Bottom&#039;, &#039;Justify&#039;, &#039;Distributed&#039;&amp;lt;/code&amp;gt;.  (&#039;&#039;new to [[Analytica 5.0]]&#039;&#039;)&lt;br /&gt;
::&amp;lt;code&amp;gt;&amp;quot;WrapText&amp;quot;&amp;lt;/code&amp;gt;: &amp;lt;code&amp;gt;0&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;1&amp;lt;/code&amp;gt; controls whether text is word wrapped to fit in the cell.&lt;br /&gt;
::&amp;lt;code&amp;gt;&amp;quot;Border(Left|Right|Up|Down)&amp;quot;&amp;lt;/code&amp;gt; show a border to left, right, above, or below the cell.&lt;br /&gt;
::&amp;lt;code&amp;gt;&amp;quot;Border(Left|Right|Up|Down)Color&amp;quot;&amp;lt;/code&amp;gt;: Return the color of the specified side of the border as an RGB number --  E.g., &amp;lt;code&amp;gt;&amp;quot;BorderLeftColor&amp;quot;&amp;lt;/code&amp;gt; returns an integer equal to &#039;&#039;red*65535+green*256+blue&#039;&#039;&lt;br /&gt;
::&amp;lt;code&amp;gt;&amp;quot;Border (Left|Right|Up|Down) Style&amp;quot;&amp;lt;/code&amp;gt;: Style of indicated border, or [[Null]] if not set. May be &amp;lt;code&amp;gt;&amp;quot;Solid&amp;quot;, &amp;quot;Dash&amp;quot;, &amp;quot;DashDot&amp;quot;, &amp;quot;DashDotDot&amp;quot;, &amp;quot;Dot&amp;quot;, &amp;quot;Double&amp;quot;&amp;lt;/code&amp;gt;, or &amp;lt;code&amp;gt;&amp;quot;SlantDashDot&amp;quot;&amp;lt;/code&amp;gt;.&lt;br /&gt;
::&amp;lt;code&amp;gt;&amp;quot;Border (Left|Right|Up|Down) Weight&amp;quot;&amp;lt;/code&amp;gt;: Thickness of indicated border, usually between 1 and 4&lt;br /&gt;
&lt;br /&gt;
==== «what» parameter cell addresses  ====&lt;br /&gt;
::&amp;lt;code&amp;gt;&amp;quot;Address&amp;quot;&amp;lt;/code&amp;gt;: The address of the cell range, e.g., &amp;lt;code&amp;gt;&amp;quot;B12:C13&amp;quot;&amp;lt;/code&amp;gt;&lt;br /&gt;
::&amp;lt;code&amp;gt;&amp;quot;AddressR1C1&amp;quot;&amp;lt;/code&amp;gt;: The address of the cell range in R1C1 format, e.g., &amp;lt;code&amp;gt;&amp;quot;R12C2:R13C3&amp;quot;&amp;lt;/code&amp;gt;&lt;br /&gt;
::&amp;lt;code&amp;gt;&amp;quot;Sheet&amp;quot;&amp;lt;/code&amp;gt;: The sheet name where the cell range exists.&lt;br /&gt;
::&amp;lt;code&amp;gt;&amp;quot;RangeName&amp;quot;&amp;lt;/code&amp;gt;: The name of the range, if it is a named range. &lt;br /&gt;
&lt;br /&gt;
==== «what» parameter cell precedents and dependents  ==== &lt;br /&gt;
&lt;br /&gt;
::&amp;lt;code&amp;gt;&amp;quot;DirectPrecedents&amp;quot;&amp;lt;/code&amp;gt;: Addresses of all cells mentioned in the cell formula, separated by commas.  Unfortunately, Excel lists only cells in the same sheet, but not precedents in other sheets.  &lt;br /&gt;
::&amp;lt;code&amp;gt;&amp;quot;DirectPrecedentsRelative&amp;quot;&amp;lt;/code&amp;gt;: Same as &amp;quot;DirectPrecedents&amp;quot;, but cells are given by their offset relative to the current cell, e.g., &amp;lt;code&amp;gt;R[-3]C[6]&amp;lt;/code&amp;gt;.&lt;br /&gt;
::&amp;lt;code&amp;gt;&amp;quot;DirectDependents&amp;quot;&amp;lt;/code&amp;gt;: Addresses of all cells whose formula mentions this cell, separated by commas.  Unfortunately, Excel lists only cells in the same sheet, but not dependents in other sheets. &lt;br /&gt;
::&amp;lt;code&amp;gt;&amp;quot;DirectDependentsRelative&amp;quot;&amp;lt;/code&amp;gt;: Same as &amp;lt;code&amp;gt;&amp;quot;DirectDependents&amp;quot;&amp;lt;/code&amp;gt;, but cells are identified by their offset relative to the current cell, e.g., &amp;lt;code&amp;gt;R[-3]C[6]&amp;lt;/code&amp;gt;.&lt;br /&gt;
::&amp;lt;code&amp;gt;&amp;quot;Precedents&amp;quot;&amp;lt;/code&amp;gt;:  Addresses of all cells in the current worksheet mentioned in the formula of this cell and the formulas of its direct precedents.  It does not include cells reached by paths passing through other sheets.&lt;br /&gt;
::&amp;lt;code&amp;gt;&amp;quot;PrecedentsRelative&amp;quot;&amp;lt;/code&amp;gt;: Same as &amp;lt;code&amp;gt;&amp;quot;Precedents&amp;quot;&amp;lt;/code&amp;gt;, but cells are identified by their offset relative to the current cell, e.g., &amp;lt;code&amp;gt;R[-3]C[6]&amp;lt;/code&amp;gt;.&lt;br /&gt;
::&amp;lt;code&amp;gt;&amp;quot;Descendants&amp;quot;&amp;lt;/code&amp;gt;: Description of all cells in the current worksheet that depend directly or indirectly on the given cell.  It does not include cells reached by paths passing through other sheets.&lt;br /&gt;
::&amp;lt;code&amp;gt;&amp;quot;DirectDescendantsRelative&amp;quot;&amp;lt;/code&amp;gt;: Same as &amp;lt;code&amp;gt;&amp;quot;DirectDescendants&amp;quot;&amp;lt;/code&amp;gt;, but cells are identified by their offset relative to the current cell, e.g., &amp;lt;code&amp;gt;R[-3]C[6]&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Errors in SpreadsheetRange parameters ===&lt;br /&gt;
In a call to SpreadsheetRange(wb, range):&lt;br /&gt;
* If range refers to a sheet, e.g. &amp;quot;sheet!x&amp;quot; that does not exist in workbook wb, it gives an error message saying &amp;quot;the worksheet &#039;sheet&#039; was not found.&amp;quot;&lt;br /&gt;
* If range refers to a named range, e.g. &amp;quot;sheet!x&amp;quot; that does not exist in workbook wb, it gives an error message saying &amp;quot;the indicated named cell range, &#039;x&#039;, was not found.&amp;quot;&lt;br /&gt;
* If range refers to a cell address with bad syntax, e.g. &amp;quot;ted!A1:R3C6&amp;quot;, it gives an error message saying &amp;quot;the range named A1:R3C6 was not found in Excel worksheet &#039;ted&#039;.&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;SpreadsheetInfo&amp;quot;&amp;gt;&lt;br /&gt;
== SpreadsheetInfo(workbook, item) == &lt;br /&gt;
&amp;lt;/div&amp;gt;  &lt;br /&gt;
&lt;br /&gt;
SpreadsheetInfo gets various kinds of information about the spreadsheet («workbook») specified by parameter «item»:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! item !! Description&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;&amp;quot;AcceptLabelsInFormulas&amp;quot;&amp;lt;/code&amp;gt; || True when you can use labels in worksheet formulas. This is usually false.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;&amp;quot;Account&amp;quot;&amp;lt;/code&amp;gt; || &#039;&#039;([[Analytica 7.2|7.2]])&#039;&#039; The Google account a workbook opened from Google Sheets or Google Drive is connected with. Null for other workbooks.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;&amp;quot;ActiveSheet&amp;quot;&amp;lt;/code&amp;gt; || The number of the active (displayed) worksheet.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;&amp;quot;Author&amp;quot;&amp;lt;/code&amp;gt; || The name of the author, usually the name of the person who created the spreadsheet as recorded by Windows OS.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;&amp;quot;Backend&amp;quot;&amp;lt;/code&amp;gt; || &#039;&#039;([[Analytica 7.2|7.2]])&#039;&#039; Which engine holds the workbook: &amp;lt;code&amp;gt;&#039;Excel&#039;&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;&#039;LibXl&#039;&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;&#039;GoogleSheets&#039;&amp;lt;/code&amp;gt; (see the «backend» parameter of [[SpreadsheetOpen]]).&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;&amp;quot;CalculationMode&amp;quot;&amp;lt;/code&amp;gt;   || The calculation mode set for the workbook, which may be &amp;quot;Automatic&amp;quot;, &amp;quot;Manual&amp;quot; or &amp;quot;Semiautomatic&amp;quot;, meaning automatic except for data tables.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;&amp;quot;CalculationState&amp;quot;&amp;lt;/code&amp;gt; || &#039;&#039;([[Analytica 5.0|5.0]])&#039;&#039; The current state of Excel&#039;s calculation engine, either &amp;lt;code&amp;gt;&amp;quot;Calculating&amp;quot;&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;&amp;quot;Pending&amp;quot;&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;&amp;quot;Done&amp;quot;&amp;lt;/code&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;&amp;quot;CalculationVersion&amp;quot;&amp;lt;/code&amp;gt; || &#039;&#039;([[Analytica 5.0|5.0]])&#039;&#039; Number equal to 10000 * major + minor, encoding the version of the Excel calculation engine that the current workbook was last calculated in. If it was saved in an earlier version of Excel and hasn&#039;t yet been fully calculated, the value is 0. You can compare this to the &amp;quot;Excel.CalculationVersion&amp;quot; to determine whether it was last re-calculated using the same calculation engine as your current installed Excel.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;&amp;quot;CodeName&amp;quot;&amp;lt;/code&amp;gt; ||&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;&amp;quot;Date1904&amp;quot;&amp;lt;/code&amp;gt; || The base for dates used in the workbook.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;&amp;quot;DecimalSeparator&amp;quot;&amp;lt;/code&amp;gt; || &#039;&#039;([[Analytica 5.0|5.0]])&#039;&#039; The character used to separate a whole number from its fractional part. In English-speaking countries this is &#039;.&#039; (a dot).&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;&amp;quot;Excel.CalculationVersion&amp;quot;&amp;lt;/code&amp;gt; || &#039;&#039;([[Analytica 5.0|5.0]])&#039;&#039; Number equal to 10000 * major + minor, encoding the version of calculation engine for your installed version of Excel. &lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;&amp;quot;Filename&amp;quot;&amp;lt;/code&amp;gt;   || The name of the file, including the full file path.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;&amp;quot;Name&amp;quot;&amp;lt;/code&amp;gt;         || The name of the file, without the file path.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;&amp;quot;Names&amp;quot;&amp;lt;/code&amp;gt;          || A list of all the named ranges.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;&amp;quot;OperatingSystem&amp;quot;&amp;lt;/code&amp;gt; || &#039;&#039;([[Analytica 5.0|5.0]])&#039;&#039; The name of the operating system that your Excel instance is running on, as reported by Excel. &lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;&amp;quot;ReadOnly&amp;quot;&amp;lt;/code&amp;gt; || True (1) if the file is saved as Readonly.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;&amp;quot;Saved&amp;quot;&amp;lt;/code&amp;gt;      || False (0) if it has unsaved changes.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;&amp;quot;SelectedRange&amp;quot;&amp;lt;/code&amp;gt; || The currently selected Range.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;&amp;quot;SelectedRangeR1C1&amp;quot;&amp;lt;/code&amp;gt; || The currently selected Range specified by row and column number.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;&amp;quot;Sheets&amp;quot;&amp;lt;/code&amp;gt;     || A list of the names of all the worksheets&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;&amp;quot;ThousandsSeparator&amp;quot;&amp;lt;/code&amp;gt; || &#039;&#039;([[Analytica 5.0|5.0]])&#039;&#039; The character Excel uses to group thousands when displaying a large number. In English-speaking countries this is &#039;,&#039; (a comma). For example, in the number &amp;lt;code&amp;gt;1,234,456.78&amp;lt;/code&amp;gt;, groups of thousands are separated by commas.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;&amp;quot;Title&amp;quot;&amp;lt;/code&amp;gt; || The title of the spreadsheet&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;&amp;quot;URL&amp;quot;&amp;lt;/code&amp;gt; || &#039;&#039;([[Analytica 7.2|7.2]])&#039;&#039; The link of a workbook opened from Google Sheets or Google Drive. Null for other workbooks.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;&amp;quot;UseSystemSeparators&amp;quot;&amp;lt;/code&amp;gt; || &#039;&#039;([[Analytica 5.0|5.0]])&#039;&#039; True when Excel uses &amp;lt;code&amp;gt;&amp;quot;DecimalSeparator&amp;quot;&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;&amp;quot;ThousandsSeparator&amp;quot;&amp;lt;/code&amp;gt; for displaying numbers.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;&amp;quot;Version&amp;quot;&amp;lt;/code&amp;gt; || &#039;&#039;([[Analytica 5.0|5.0]])&#039;&#039; The version number (text) for the installed release of Excel. Excel 2010 is &amp;quot;14.0&amp;quot;, Excel 2013 is &amp;quot;15.0&amp;quot; and Excel 2016 is &amp;quot;16.0&amp;quot;.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;&amp;quot;Visible&amp;quot;&amp;lt;/code&amp;gt; || True when the Excel UI is visible.&lt;br /&gt;
|}  The items above marked with &#039;&#039;([[Analytica 5.0|5.0]])&#039;&#039;  require [[Analytica 5.0]] or better; those marked &#039;&#039;([[Analytica 7.2|7.2]])&#039;&#039; require [[Analytica 7.2]].&lt;br /&gt;
&lt;br /&gt;
== History== &lt;br /&gt;
&lt;br /&gt;
Functions for reading cells from Excel were first present in Analytica 4.1 with functions named [[OpenExcelFile]], [[WorksheetCell]] and [[WorksheetRange]], although these were labelled as &#039;&#039;experimental&#039;&#039;, and the present functions were not officially available until 4.2.0.    The old names are now deprecated, replaced with [[SpreadsheetOpen]], [[SpreadsheetCell]] and [[SpreadsheetRange]].  The old functions still work, but may be removed in future Analytica releases.  The parameters have changed slightly from [[WorksheetRange]] to [[SpreadsheetRange]], with the sheet parameter moved from being the second to being the last parameter and now optional -- no longer required for named ranges or ranges of the form &amp;lt;code&amp;gt;&amp;quot;Sheet1!A1:Z99&amp;quot;&amp;lt;/code&amp;gt;.  &lt;br /&gt;
&lt;br /&gt;
[[SpreadsheetInfo]] was introduced in [[Analytica 4.5]]. These options to [[SpreadsheetInfo]] were added in [[Analytica 5.0]]: &amp;quot;Version&amp;quot;, &amp;quot;CalculationVersion&amp;quot;, &amp;quot;Excel.CalculationVersion&amp;quot;, &amp;quot;UseSystemSeparators&amp;quot;, &amp;quot;DecimalSeparator&amp;quot;, &amp;quot;ThousandsSeparator&amp;quot;, and &amp;quot;CalculationState&amp;quot;.  &lt;br /&gt;
&lt;br /&gt;
The color options for «what» incorrectly returned numbers in 0x00bbggrr order, instead of 0x00rrggbb order prior to [[Analytica 5.0]]. (This was a bug -- the documentation stated it should be 0x00rrggbb).  Various options to the «what» parameter of [[SpreadsheetCell]] and [[SpreadsheetRange]] have appeared at different releases. The options &amp;lt;code&amp;gt;&#039;HorizontalAlignment&#039;&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;&#039;VerticalAlignment&#039;&amp;lt;/code&amp;gt; appeared in [[Analytica 5.0]]. Options &amp;lt;code&amp;gt;&#039;Address&#039;&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;&#039;AddressR1C1&#039;&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;&#039;Sheet&#039;&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;&#039;RangeName&#039;&amp;lt;/code&amp;gt; appeared in [[Analytica 4.6]]. The remaining options appeared in [[Analytica 4.4]], except for &amp;lt;code&amp;gt;&#039;Value&#039;&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;&#039;NumericValue&#039;&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;&#039;Formula&#039;&amp;lt;/code&amp;gt;&#039; and &amp;lt;code&amp;gt;&#039;RelativeFormula&#039;&amp;lt;/code&amp;gt;, which appeared when the «what» parameter was introduced in [[Analytica 4.3]].&lt;br /&gt;
&lt;br /&gt;
{{Release|1=7.2|2=|3=The &amp;lt;code&amp;gt;&#039;GoogleSheets&#039;&amp;lt;/code&amp;gt; «backend» of [[SpreadsheetOpen]] (Google Sheets spreadsheets and Excel workbooks stored in Google Drive, opened from their link), and the &amp;quot;Backend&amp;quot;, &amp;quot;URL&amp;quot; and &amp;quot;Account&amp;quot; items of [[SpreadsheetInfo]], were added in [[Analytica 7.2]].}}&lt;br /&gt;
&lt;br /&gt;
== See Also == &lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;column-count:2;-moz-column-count:2;-webkit-column-count:2&amp;quot;&amp;gt;&lt;br /&gt;
*  [[media:Spreadsheet Helper lib.ana|Spreadsheet Helper lib.ana]]&lt;br /&gt;
* [[Analytica_Libraries_and_Templates#Spreadsheet_Helper_Library|Spreadsheet Helper Library]]&lt;br /&gt;
* [[Media:Functions for Reading Excel Worksheets.ana|Reading Excel Worksheets.ana]]&lt;br /&gt;
* [[Read and Write Spreadsheets]]&lt;br /&gt;
* [[Excel spreadsheets read and write]]&lt;br /&gt;
* [[Functions to Write Data to Excel Worksheets]] -- [[SpreadsheetSetCell]], [[SpreadsheetSetRange]] and [[SpreadsheetSave]]&lt;br /&gt;
* {{Release|1=7.2|2=|3=[[Using your own Google OAuth client]] -- connecting to Google Sheets through your own Google Cloud registration}}&lt;br /&gt;
* {{Release|1=7.2|2=|3=[[Using a Google service account]] -- opening a Google Sheet with no browser and nobody present}}&lt;br /&gt;
* You can also use [[DbQuery| ODBC]] -- a standard database access method to read from Excel spreadsheets.&lt;br /&gt;
* [[SuppressExcelAlerts]]&lt;br /&gt;
* [[Excel to Analytica Translation]]&lt;br /&gt;
* [[Excel to Analytica Mappings]]&lt;br /&gt;
* [[ReadTextFile]]&lt;br /&gt;
* [[ReadCsvFile]]&lt;br /&gt;
* You can use these functions from  [[Excel Functions from ADE| ADE]].&lt;br /&gt;
* These spreadsheet functions above are more flexible than [[OLE linking]] which is also available.&lt;br /&gt;
* [[OLE linking]] &amp;lt;/div&amp;gt;&lt;/div&gt;</summary>
		<author><name>Lchrisman</name></author>
	</entry>
	<entry>
		<id>https://docs.analytica.com/index.php?title=Functions_To_Read_Excel_Worksheets&amp;diff=64571</id>
		<title>Functions To Read Excel Worksheets</title>
		<link rel="alternate" type="text/html" href="https://docs.analytica.com/index.php?title=Functions_To_Read_Excel_Worksheets&amp;diff=64571"/>
		<updated>2026-09-18T15:46:10Z</updated>

		<summary type="html">&lt;p&gt;Lchrisman: ER 21588 Phase 4: name the new account parameter in the SpreadsheetOpen heading&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:Excel to Analytica mappings]]&lt;br /&gt;
[[Category:Integration Functions]]&lt;br /&gt;
&lt;br /&gt;
__TOC__&lt;br /&gt;
{{ReleaseBar}}&lt;br /&gt;
&lt;br /&gt;
These functions let you open an Excel spreadsheet file, and read cells and ranges from it. For writing to a spreadsheet, see [[Functions to Write Data to Excel Worksheets]].&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;SpreadsheetOpen&amp;quot; &amp;gt;&lt;br /&gt;
== SpreadsheetOpen(filename&#039;&#039;, showDialog, title{{Release|7.0||, backend}}{{Release|7.2||, account}}&#039;&#039;) ==&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Opens a spreadsheet file and returns a workbook object for use by other functions (such as [[SpreadsheetCell]] or [[SpreadsheetRange]]) to read from or write to the file.&lt;br /&gt;
&lt;br /&gt;
The returned object displays in a result table as &amp;lt;code&amp;gt;«ExcelWorkbook»&amp;lt;/code&amp;gt;{{Release|7.2||, &amp;lt;code&amp;gt;«GoogleSheetsWorkbook»&amp;lt;/code&amp;gt; }}{{Release|7.0|| or &amp;lt;code&amp;gt;«LibXlWorkbook»&amp;lt;/code&amp;gt;, depending on the «backend» used.}} {{Release|1=7.2|2=|3=A workbook opened from a native Google Sheet displays as &amp;lt;code&amp;gt;«GoogleSheetsWorkbook»,&amp;lt;/code&amp;gt; but an Excel file stored in Google Drive displays as &amp;lt;code&amp;gt;«LibXlWorkbook»&amp;lt;/code&amp;gt;.}}&lt;br /&gt;
&lt;br /&gt;
Unless you include a complete file path in «filename», Analytica looks for the file in the [[CurrentDataFolder]]. You can also provide the name of a workbook that is currently open in Excel, even if it has not yet been saved to disk.&lt;br /&gt;
&lt;br /&gt;
If you omit the optional parameter «showDialog», the file browser dialog opens only if the specified file cannot be found.&lt;br /&gt;
* Set «showDialog» to True (&amp;lt;code&amp;gt;1&amp;lt;/code&amp;gt;) to force the file browser even if the file exists.&lt;br /&gt;
* Set «showDialog» to False (&amp;lt;code&amp;gt;0&amp;lt;/code&amp;gt;) to suppress the dialog entirely.&lt;br /&gt;
&lt;br /&gt;
If no file is successfully opened, the function flags an error. You can customize the file dialog caption by passing text to the optional «title» parameter.&lt;br /&gt;
&lt;br /&gt;
[[SpreadsheetOpen]] can return two values: the workbook object and the full path to the file that was opened. This is particularly useful when the user selects a file via the dialog: &lt;br /&gt;
::&amp;lt;code&amp;gt;Local (wb, filePath) := SpreadsheetOpen(&amp;quot;Data.xlsx&amp;quot;);&amp;lt;/code&amp;gt;&lt;br /&gt;
{{Release|1=7.0|2=|3=&lt;br /&gt;
{{Release|1=7.1|2=|3=&lt;br /&gt;
=== Creating a new workbook ===&lt;br /&gt;
If you pass &amp;lt;code&amp;gt;&amp;quot;New&amp;quot;&amp;lt;/code&amp;gt; as the «filename», SpreadsheetOpen creates a new blank workbook with a single empty sheet, without saving to any file. This is useful for building a workbook from scratch before saving it with [[Functions_to_Write_Data_to_Excel_Worksheets#SpreadsheetSave|SpreadsheetSave]].&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;code&amp;gt;SpreadsheetOpen(&amp;quot;New&amp;quot;)&amp;lt;/code&amp;gt; — Creates a new workbook using the Excel backend.&lt;br /&gt;
:&amp;lt;code&amp;gt;SpreadsheetOpen(&amp;quot;New&amp;quot;, backend:&#039;LibXl&#039;)&amp;lt;/code&amp;gt; — Creates a new workbook using the LibXl backend.&lt;br /&gt;
&lt;br /&gt;
You can then add sheets using the &amp;lt;code&amp;gt;+&amp;lt;/code&amp;gt; prefix in [[Functions_to_Write_Data_to_Excel_Worksheets#SpreadsheetSetRange|SpreadsheetSetRange]] or [[Functions_to_Write_Data_to_Excel_Worksheets#SpreadsheetSetCell|SpreadsheetSetCell]], and save with SpreadsheetSave when done.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== Backend === &lt;br /&gt;
&#039;&#039;(New to [[Analytica 7.0]])&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The optional «backend» parameter determines which underlying engine Analytica uses to handle the spreadsheet.&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;&#039;Excel&#039;&amp;lt;/code&amp;gt;: (Default) Uses the Microsoft Excel COM interface. &lt;br /&gt;
*;Requirements: Requires Microsoft Excel to be installed locally.  &lt;br /&gt;
*;Capabilities: This backend includes the full Excel calculation engine. If you change cell values using [[SpreadsheetSetCell]] or [[SpreadsheetSetRange]], formulas within the workbook will be recalculated, allowing you to read back computed results. It supports all standard Excel file formats and features. &lt;br /&gt;
*;Return type: Returns an «ExcelWorkbook» object.&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;&#039;LibXl&#039;&amp;lt;/code&amp;gt;: Uses a built-in library for direct file access.&lt;br /&gt;
*; Requirements: Does not require Microsoft Excel to be installed. &lt;br /&gt;
*; Capabilities: Offers high performance for reading and writing raw data. It is ideal for automated environments (like servers) where Excel might not be present. Note that it does &#039;&#039;&#039;&#039;&#039;not&#039;&#039;&#039;&#039;&#039; include a calculation engine; it reads literal values and formulas from the file but cannot &amp;quot;re-calc&amp;quot; a workbook after data is changed. &lt;br /&gt;
*; Return type: Returns a «LibXlWorkbook» object.&lt;br /&gt;
{{Release|1=7.2|2=|3=&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;&#039;GoogleSheets&#039;&amp;lt;/code&amp;gt;: Opens a Google Sheets spreadsheet, or an Excel workbook stored in Google Drive, from its link. It is selected automatically when «filename» is a &amp;lt;code&amp;gt;https://docs.google.com/spreadsheets/d/...&amp;lt;/code&amp;gt; link (copy it from your browser&#039;s address bar while the sheet is open), so «backend» can be omitted.&lt;br /&gt;
*; Requirements: A Google account with access to the spreadsheet. The first time, Analytica opens your web browser so you can sign in to Google and select the spreadsheet in Google&#039;s file chooser; the connection is remembered on your computer under your Windows account, never in the model, and you can revoke it from your Google account settings. Analytica can open only the spreadsheets you select in that chooser (plus ones it creates itself), so a link to a spreadsheet the chooser did not show cannot be opened. Pass an empty «filename» (or «showDialog»: True) to browse for a spreadsheet.&lt;br /&gt;
*; Capabilities: Reads come from a snapshot taken when the workbook is opened. Writes made with [[SpreadsheetSetCell]] and [[SpreadsheetSetRange]], and sheets added or removed, are sent to Google when the computation finishes, when you call [[Functions_to_Write_Data_to_Excel_Worksheets#SpreadsheetSave|SpreadsheetSave]](wb), or when the workbook is refreshed: &amp;lt;code&amp;gt;SpreadsheetSetInfo(wb, &#039;Refresh&#039;, true)&amp;lt;/code&amp;gt; sends the pending writes and re-downloads the spreadsheet, so the values Google computed (and other people&#039;s edits) are seen. A native Google Sheet is exported by Google as an .xlsx snapshot (Google limits the export to 10 MB); an Excel workbook stored in Drive is downloaded as-is and written back as a whole file. The tab names the model sees (&amp;lt;code&amp;gt;SpreadsheetInfo(wb, &#039;Sheets&#039;)&amp;lt;/code&amp;gt;, and «sheet» given by name) are the names in the exported snapshot, truncated to 31 characters, while writes are addressed to Google&#039;s real tab titles. &amp;lt;code&amp;gt;SpreadsheetOpen(&amp;quot;New&amp;quot;, backend:&#039;GoogleSheets&#039;, title: &amp;quot;My sheet&amp;quot;)&amp;lt;/code&amp;gt; creates a new Google Sheet in your Drive. The second return value is the spreadsheet&#039;s link.&lt;br /&gt;
*; Return type: Returns a «GoogleSheetsWorkbook» object for a native Google Sheet, or a «LibXlWorkbook» object for an Excel file stored in Drive. &amp;lt;code&amp;gt;SpreadsheetInfo(wb, &#039;Backend&#039;)&amp;lt;/code&amp;gt; returns &amp;lt;code&amp;gt;&#039;GoogleSheets&#039;&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;&#039;LibXl&#039;&amp;lt;/code&amp;gt; accordingly, and &amp;lt;code&amp;gt;SpreadsheetInfo(wb, &#039;URL&#039;)&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;SpreadsheetInfo(wb, &#039;Account&#039;)&amp;lt;/code&amp;gt; give its link and the Google account it was opened with.&lt;br /&gt;
*; Advanced: Analytica identifies itself to Google as an application registered by Lumina. An organization that would rather it identified itself as an application of their own -- because their Google Workspace administrator controls which outside applications may reach their data, for example -- can arrange that; see [[Using your own Google OAuth client]].&lt;br /&gt;
}}&lt;br /&gt;
}}&lt;br /&gt;
=== Example ===&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;code&amp;gt;SpreadsheetOpen(&amp;quot;C:\MyModels\Sales Numbers.xlsx&amp;quot;) &amp;amp;rarr; &#039;&#039;«ExcelWorkbook»&#039;&#039;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;SpreadsheetCell&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Getting the file name actually opened ===&lt;br /&gt;
Your code may want to know the file path for which file was actually opened. This may differ from «filename» when the specified file is not found, or when  «showDialog» forces a dialog, allowing the user to select a different file. [[SpreadsheetOpen]] returns the file path as a second return value, which you can optionally capture using, e.g.,&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;code&amp;gt;[[Local]] (contents, filepath) := [[SpreadsheetOpen]]( ... );&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A common pattern is that you may want to save the filename in a variable such that when the evaluation is repeated in the future, it can supply the file selected by the user to the «filename» parameter. This can be coded by supplying first a global variable to hold the filename defined using [[ComputedBy]] with the default filename as follows:&lt;br /&gt;
&lt;br /&gt;
:Variable TheFilename ::= &lt;br /&gt;
::&amp;lt;code&amp;gt;[[ComputedBy]]( TheFileContetns, &amp;quot;defaultFilename.xlsx&amp;quot; ) &amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
:Variable TheWorkbook::= &lt;br /&gt;
::&amp;lt;code&amp;gt;( , TheFilename ) := [[SpreadsheetOpen]]( TheFilename )&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The assignment to &amp;lt;code&amp;gt;( , TheFilename )&amp;lt;/code&amp;gt; passes through the first parameter as the result of the assignment expression, but assigns the second return value the &amp;lt;code&amp;gt;TheFilename&amp;lt;/code&amp;gt;. The assignment to &amp;lt;code&amp;gt;TheFilename&amp;lt;/code&amp;gt; is a [[side-effect]] that is allowed only because &amp;lt;code&amp;gt;TheFilename&amp;lt;/code&amp;gt; is defined as a [[ComputedBy]]. The assignment changes the value, but also rewrites the second parameter of the call to [[ComputedBy]], thus permanently preserving the filename selected. The one line definition of &amp;lt;code&amp;gt;TheWorkbook&amp;lt;/code&amp;gt; is locally equivalent to:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;code&amp;gt;[[Local]] (wb, filename ) := [[SpreadsheetOpen]]( TheFilename );&amp;lt;/code&amp;gt;&lt;br /&gt;
:&amp;lt;code&amp;gt;TheFilename := filename;&amp;lt;/code&amp;gt;&lt;br /&gt;
:&amp;lt;code&amp;gt;wb&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Use with Office 2010 ===&lt;br /&gt;
&lt;br /&gt;
If you have installed the &amp;quot;Click-to-Run&amp;quot; version of Office 2010 from a web download, these spreadsheet functions may not work, due to a &amp;quot;feature&amp;quot; introduced in Office 2010 that apparently disables several common operations.  In this case, you may need to re-install Office using the MSI-based edition.  See how to do this at:&lt;br /&gt;
&lt;br /&gt;
[http://office.microsoft.com/en-gb/excel-help/click-to-run-switch-to-using-a-full-office-edition-HA101850538.aspx http://office.microsoft.com/en-gb/excel-help/click-to-run-switch-to-using-a-full-office-edition-HA101850538.aspx]&lt;br /&gt;
&lt;br /&gt;
=== Excel 64-bit requires Analytica 64-bit ===&lt;br /&gt;
&lt;br /&gt;
Analytica 32-bit cannot launch Excel 64-bit. (The other way around works). Thus, if you have installed Excel 64-bit (which we recommend), make sure you have installed Analytica 64-bit.  If you are a [[Free Edition]] user, you probably have 32-bit installed, but you can install Analytica 64-bit from the [https://www.lumina.com/support/downloads/ Analytica Downloads page].&lt;br /&gt;
&lt;br /&gt;
=== Remembering the selected filename ===&lt;br /&gt;
&lt;br /&gt;
When [[SpreadsheetOpen]]() shows the file dialog and you select a file, it does not save the file name. So, the next time you load the model, you&#039;ll have to select the file again.  If you want the model to remember the selected file, so it will just load it without asking, prompt using that file name as the default, you can use the &#039;&#039;&#039;SpreadsheetOpenEx&#039;&#039;&#039; function in the [[Analytica_Libraries_and_Templates#Spreadsheet_Helper_Library|Spreadsheet Helper Library]].&lt;br /&gt;
&lt;br /&gt;
=== Having same spreadsheet open in Excel at the same time ===&lt;br /&gt;
&lt;br /&gt;
It is often useful to have the spreadsheet you are working with open in Excel at the same time your model is working with it. When you want to do this, is it best to open it Excel first, before evaluating [[SpreadsheetOpen]], in which case [[SpreadsheetOpen]] connects to the existing Excel process and to the currently open spreadsheet. If you change cells in Excel, then evaluate a spreadsheet read functions, you&#039;ll read the new values, and if your model writes to the spreadsheet, you&#039;ll see those values reflected immediately in the Excel interface.&lt;br /&gt;
&lt;br /&gt;
When you call [[SpreadsheetOpen]] before opening the model in Excel, the situation is more complex. To understand what happens and how to view the same model in the Excel UI at the same time, see [[Simultaneously opening a spreadsheet in Excel and Analytica]].&lt;br /&gt;
&lt;br /&gt;
=== SpreadsheetOpenFlags ===&lt;br /&gt;
A registry setting named &amp;lt;code&amp;gt;SpreadsheetOpenFlags&amp;lt;/code&amp;gt; can be set to alter how [[SpreadsheetOpen]] connects to Excel and the initial settings in Excel. There is usually no reason to fiddle with these flags unless you encounter a specific problem. It has been more common to set these flags in server-based applications using ADE than from desktop Analytica.&lt;br /&gt;
&lt;br /&gt;
You&#039;ll need to modify the sitting from RegEdit.  You can set it in either&lt;br /&gt;
:&amp;lt;code&amp;gt;HKEY_LOCAL_MACHINE/Software/Lumina Decision Systems/Analytica/{{#svarget:anarelease|6.1}}&amp;lt;/code&amp;gt;&lt;br /&gt;
or&lt;br /&gt;
:&amp;lt;code&amp;gt;HKEY_CURRENT_USER/Software/Lumina Decision Systems/Analytica/{{#svarget:anarelease|6.1}}&amp;lt;/code&amp;gt;&lt;br /&gt;
For ADE, set it in one of these hives:&lt;br /&gt;
:&amp;lt;code&amp;gt;HKEY_LOCAL_MACHINE/Software/Lumina Decision Systems/Analytica/{{#svarget:anarelease|6.1}}&amp;lt;/code&amp;gt;&lt;br /&gt;
:&amp;lt;code&amp;gt;HKEY_CURRENT_USER/Software/Lumina Decision Systems/Analytica/{{#svarget:anarelease|6.1}}&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Setting it in HKLM causes it to apply from any account on your computer, while setting it from HKCU causes it to apply only to your own account.  A setting in HKCU takes precedence over the same setting in HKLM.&lt;br /&gt;
&lt;br /&gt;
Initially the value &amp;lt;code&amp;gt;SpreadsheetOpenFlags&amp;lt;/code&amp;gt; will not be present. Create a new 32-bit DWORD with this name.  The set the numeric value to an addition of any of these flags that you want:&lt;br /&gt;
* 1 = Launch using a COMCreateObject mechanism.  (unset)=Launch using a BindToObject method. &lt;br /&gt;
*: A BindToObject method (the default for Desktop Analytica) makes it possible to connect to a Workbook running in an active Excel UI. A COMCreateObject mechanism launches a separate instance of Excel every time.   &lt;br /&gt;
* 2 = Turn off Excel&#039;s Interactive flag.&lt;br /&gt;
* 4 = Turn off Excel&#039;s &amp;quot;Ask to update OLE links&amp;quot; flag.&lt;br /&gt;
* 8 = Turn off Excel&#039;s &amp;quot;Display Alerts&amp;quot;&lt;br /&gt;
* 16 = Disable Excel macros (for security)&lt;br /&gt;
* 32 = Close when visible. &lt;br /&gt;
*:Normally, if the workbook is currently visible in an Excel UI, Analytica simply disconnects from it, but doesn&#039;t force the workbook to close.  The Excel UI is then responsible for eventually closing it.  This overrides this and forces the workbook to close when the model releases it, even if it is visible.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== From ADE ===&lt;br /&gt;
When using from ADE on a Web Server, we strongly advise against using Excel 2016 on the server. Excel 2010 works fairly well, but Excel 2016 is extremely unstable and has a tendency to fail unpredictably and lock up all other Excel instances. Microsoft responds by saying that Excel 2016 is not supported nor licensed for use on a web server.&lt;br /&gt;
&lt;br /&gt;
When [[SpreadsheetOpen]] is evaluated in [[ADE|the Analytica Decision Engine (ADE)]] and a dialog needs to be shown to the end-user, it calls [[IAdeUICallbacks::GetFilename]](...). From within that callback, the parent application can interact with the end-user to resolve the file path, and a web applications can instruct the end-user to upload a file. Once complete, the callback returns the full path to the file which is then read. To receive this callback, the parent application must have previously registered the callback with ADE using [[CAEngine::SetCallbackObject]]( ). If it has not registered a callback and the file doesn&#039;t exist, returns an empty text.&lt;br /&gt;
&lt;br /&gt;
Once the open completes, it calls [[IAdeUICallbacks::FileOpenCompleted]]().&lt;br /&gt;
&lt;br /&gt;
=== Debugging Errors ===&lt;br /&gt;
This section documents failures when &#039;&#039;&#039;SpreadsheetOpen&#039;&#039;&#039; has been unable to open Excel, and solutions.&lt;br /&gt;
* &#039;&#039;&#039;&#039;&#039;Library not registered&#039;&#039;&#039;&#039;&#039;: &lt;br /&gt;
*:If this error occurs when &#039;&#039;&#039;SpreadsheetOpen&#039;&#039;&#039; is evaluated...&lt;br /&gt;
** The article [https://excel.tips.net/T002952_Library_Not_Registered_Error.html Library not registered error] explains how to solve this problem when it is caused by an Excel plug-in. It may be caused by a bad Excel add-in library.  You should also run &amp;lt;code&amp;gt;excel.exe /regserver&amp;lt;/code&amp;gt;.&lt;br /&gt;
** In one case, an Analytica user concluded that an older version of Excel was interfering with his newer 32-bit version of Excel. He uninstalled both and re-installed Excel 64-bit and the problem corrected itself.  But, for a different user with this problem, these steps did not correct the problem.&lt;br /&gt;
** A common cause of this problem is when stray registry settings from Excel versions that had been installed and uninstalled interfere with your current version of Excel. This is most common after you roll back to an earlier release after uninstalling a later release. To test for this cause, start Power Shell and run:&lt;br /&gt;
**::&amp;lt;code&amp;gt;get-childitem -Path &amp;quot;HKLM:\Software\Classes\TypeLib\{00020813-0000-0000-C000-000000000046}&amp;quot;&amp;lt;/code&amp;gt;&lt;br /&gt;
::If you see more than one version listed, with the most recent version number missing its mapping to Excel, then this is probably the cause. To fix, use &amp;lt;code&amp;gt;RegEdit&amp;lt;/code&amp;gt; to delete the hive for the later version number.&lt;br /&gt;
&lt;br /&gt;
== SpreadsheetCell(workbook, sheet, column, row&#039;&#039;, what&#039;&#039;) ==&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Returns the value (or other information) of a cell of a worksheet given its coordinates.  The function fully array abstracts, so you can get a range of cells by specifying the column and/or row as an array.&lt;br /&gt;
&lt;br /&gt;
Parameters:&lt;br /&gt;
;«workbook»: A workbook object returned by [[SpreadsheetOpen]]()&lt;br /&gt;
;«sheet»: The name or number of a worksheet from the workbook. Number 1 is the first worksheet, etc.&lt;br /&gt;
::  If you specify &amp;lt;code&amp;gt;sheet: &amp;quot;*&amp;quot;&amp;lt;/code&amp;gt;, it returns the cell value from &#039;&#039;column, row&#039;&#039; for all sheets in the workbook, indexed by &amp;lt;code&amp;gt;.Sheet&amp;lt;/code&amp;gt;, a local index containing the names of the worksheets. This is a way to get a list of all the worksheets in the workbook. If you specify column and/or rows as arrays, you can also use this to get a 3D array for a range over all worksheets.&lt;br /&gt;
;«column»: The column label, e.g., &amp;lt;code&amp;gt;&amp;quot;A&amp;quot;, &amp;quot;B&amp;quot;&amp;lt;/code&amp;gt;, or &amp;lt;code&amp;gt;&amp;quot;AB&amp;quot;&amp;lt;/code&amp;gt;, or the column number as an integer.&lt;br /&gt;
;«row»: The row number as an integer&lt;br /&gt;
;«what»: optional. Let&#039;s you get the formula or format information from the cell. See below under [[SpreadsheetRange]] for details. &lt;br /&gt;
&lt;br /&gt;
If the worksheet cell is empty, it returns [[Null]]. It flags an error if «workbook» is not a valid workbook, if it does not contain «sheet», or if the coordinates are invalid.&lt;br /&gt;
&lt;br /&gt;
=== Examples ===&lt;br /&gt;
These expressions are different ways to get the same result, the value from cell &#039;&#039;C7&#039;&#039; in the first sheet, &amp;lt;code&amp;gt;&amp;quot;Sheet1&amp;quot;&amp;lt;/code&amp;gt; of workbook:&lt;br /&gt;
:&amp;lt;code&amp;gt;SpreadsheetCell(workbook, &amp;quot;Sheet1&amp;quot;, &amp;quot;C&amp;quot;, 7)&amp;lt;/code&amp;gt;&lt;br /&gt;
:&amp;lt;code&amp;gt;SpreadsheetCell(workbook, &amp;quot;Sheet1&amp;quot;, 3, 7)&amp;lt;/code&amp;gt;&lt;br /&gt;
:&amp;lt;code&amp;gt;SpreadsheetCell(workbook, 1, 3, 7)&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Suppose the spreadsheet contains a 2-D table in the region &#039;&#039;C4:J19&#039;&#039;.  The columns of this table correspond to the years 2008..2015.  The rows correspond to different assets.  It is easier to refer to the columns by number, so that the columns &amp;quot;C&amp;quot; thru &amp;quot;J&amp;quot; are columns 3 thru 10.  To hold this 2-D table, we need two indexes in Analytica, &amp;lt;code&amp;gt;Time&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;Asset&amp;lt;/code&amp;gt;.  &lt;br /&gt;
&lt;br /&gt;
:&amp;lt;code&amp;gt;Index Time := 2008..2015&amp;lt;/code&amp;gt;&lt;br /&gt;
:&amp;lt;code&amp;gt;Index Asset := 1..16&amp;lt;/code&amp;gt;&lt;br /&gt;
:&amp;lt;code&amp;gt;Variable Workbook := SpreadsheetOpen(&amp;quot;C:\Asset Data.xls&amp;quot;)&amp;lt;/code&amp;gt;&lt;br /&gt;
:&amp;lt;code&amp;gt;Variable Data := SpreadsheetCell( workbook, &amp;quot;Sheet1&amp;quot;, @Time+2, @Asset+3)&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;SpreadsheetRange&amp;quot;&amp;gt;&lt;br /&gt;
== SpreadsheetRange(workbook, range&#039;&#039;, colIndex, rowIndex, howToIndex, sheet, what&#039;&#039;) ==&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Returns the values (or other information) for a range of cells from an Excel worksheet.  The  «range» can be can be a cell address such as &amp;lt;code&amp;gt;&amp;quot;C7&amp;quot;&amp;lt;/code&amp;gt; or cell range &amp;lt;code&amp;gt;&amp;quot;C7:F12&amp;quot;&amp;lt;/code&amp;gt;, or the name of a range defined in the spreadsheet.  If you want to read or write several cells or ranges in a spreadsheet, it is often convenient to use Excel&#039;s name mechanism and refer to them by name in Analytica.&lt;br /&gt;
&lt;br /&gt;
If the range has multiple columns, the result has local index &amp;lt;code&amp;gt;.Column&amp;lt;/code&amp;gt; unless you specify «colindex» as a parameter. Similarly, if the range has multiple rows, the result has local index &amp;lt;code&amp;gt;.Row&amp;lt;/code&amp;gt; unless you specify «rowindex» as a parameter. Flags in «howToIndex» let you control whether the first row (column) should be used as labels for local index  &amp;lt;code&amp;gt;.Row&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;.Column&amp;lt;/code&amp;gt;.  &lt;br /&gt;
&lt;br /&gt;
If you specify a sheet name with no cells, e.g.  &amp;lt;code&amp;gt;&amp;quot;Inputs!&amp;quot;&amp;lt;/code&amp;gt;, it returns a table that includes all cells from that sheet that contain anything.&lt;br /&gt;
&lt;br /&gt;
By default, it returns the number or text values from the range (or &amp;lt;code&amp;gt;NULL&amp;lt;/code&amp;gt; if the cell is empty). You can use the «what» parameter to obtain the cell formula, address, format, styles, precedent, and dependent cells for each cell.&lt;br /&gt;
&lt;br /&gt;
=== SpreadsheetRange Parameters ===&lt;br /&gt;
&lt;br /&gt;
SpreadsheetRange has two required parameters:&lt;br /&gt;
&lt;br /&gt;
;«workbook»: A workbook object returned by [[SpreadsheetOpen]]()&lt;br /&gt;
; «range»: A cell range.  It may be a single cell address, e.g. &amp;lt;code&amp;gt;&amp;quot;B10&amp;quot;&amp;lt;/code&amp;gt;, a range, e.g. &amp;lt;code&amp;gt;&amp;quot;A1:BC99&amp;quot;&amp;lt;/code&amp;gt;, optionally with sheet name, e.g.  &amp;lt;code&amp;gt;&amp;quot;Sheet1!A1:BC99&amp;quot;&amp;lt;/code&amp;gt;, or a named range, e.g. &amp;lt;code&amp;gt;&amp;quot;Discount_rate&amp;quot;&amp;lt;/code&amp;gt; defined in the spreadsheet. If the «range» doesn&#039;t mention the sheet name, you must specify «sheet» as a separate parameter.&lt;br /&gt;
:: If you specify the range as &amp;lt;code&amp;gt;&amp;quot;Sheet1!&amp;quot;&amp;lt;/code&amp;gt;, with nothing after the &amp;quot;!&amp;quot;, or omit «range» and specify only «sheet», it returns the smallest rectangular range that includes all used cells within the sheet. &lt;br /&gt;
&lt;br /&gt;
SpreadsheetRange has four optional parameters relating to the indexes for a range with multiple columns or rows, or over multiple sheets:&lt;br /&gt;
;«colIndex»: (optional) An index to use for the column dimension of the result.&lt;br /&gt;
;«rowIndex»: (optional) An index to use for the row dimension of the result.&lt;br /&gt;
;«howToIndex»: (optional) Flags controlling how to index the result when «colIndex» or «rowIndex» are not specified.  You can add any of these values to combine their effects:&lt;br /&gt;
::&amp;lt;code&amp;gt;1&amp;lt;/code&amp;gt;: Force a column index even if the range spans only a single column. Has no effect if you specify «colIndex».&lt;br /&gt;
::&amp;lt;code&amp;gt; 2&amp;lt;/code&amp;gt;: Force a row index even if the range spans only a single row.  Has no effect if you specify «rowIndex».&lt;br /&gt;
::&amp;lt;code&amp;gt;4&amp;lt;/code&amp;gt;: Use the first row of «range» as column labels in the local index &amp;lt;code&amp;gt;.Column&amp;lt;/code&amp;gt;. Exclude this first row in the result returned.&lt;br /&gt;
::&amp;lt;code&amp;gt;8&amp;lt;/code&amp;gt;: Use the first column of «range» as labels in the local index &amp;lt;code&amp;gt;.Row&amp;lt;/code&amp;gt;. Exclude this first column in the result returned..&lt;br /&gt;
::&amp;lt;code&amp;gt;16&amp;lt;/code&amp;gt;: Suppress the error message that is otherwise given if the sizes of «colIndex» or «rowIndex» do not match the size of the range.&lt;br /&gt;
;«sheet»: (optional) The name or number of a worksheet inside the workbook. It can be a list of sheets, in which case, the function will return a 3D table, indexed by this list as the third dimension.&lt;br /&gt;
;«what»: (optional)  See below for details on this parameter.&lt;br /&gt;
&lt;br /&gt;
=== Indexes of a cell range ===&lt;br /&gt;
&lt;br /&gt;
The result may be a scalar (single cell), a column vector, a row vector, or a 2-D array, depending on the dimensions of the cell range.  If the range has more than one row (or column),  it will use a local index .Row (.Column) by default. By default, the elements of the .Row index contain the range&#039;s row numbers and elements of the column index contain its column labels.  For example, if the range is &amp;lt;code&amp;gt;&amp;quot;C7:E12&amp;quot;&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;.Row&amp;lt;/code&amp;gt; would contain the elements &amp;lt;code&amp;gt;[7, 8, 9, 10, 11, 12]&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;.Column&amp;lt;/code&amp;gt; would contain &amp;lt;code&amp;gt;[&#039;C&#039;, &#039;D&#039;, &#039;E&#039;]&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Or, you can use the first column (row) of the range as the values for the local index .Row (.Column), by specifying &amp;lt;code&amp;gt;howToIndex: 8&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;howToIndex: 4&amp;lt;/code&amp;gt;, or &amp;lt;code&amp;gt;howToIndex: 12&amp;lt;/code&amp;gt; for both .Row and .Column.)   If you use, the first row (column) of the range as values of the local indexe(es), they will not be included in the value of the array returned. So, in that case, the range must have at least two rows (columns).  &lt;br /&gt;
&lt;br /&gt;
Alternatively, if you already have index(es), you can supply them to the  «rowIndex» («colIndex») parameters.  If you specify a «rowIndex» or «colIndex», that is shorter than the number of rows (columns) in the range, it  truncates the result. If an index is too long, it pads the result with [[Null]].  In these cases, it gives a warning message unless you set flag &amp;lt;code&amp;gt;&#039;&#039;howToIndex: 16&#039;&#039;&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
If the range has just one  column, the result normally will not have a local .Column index. But, you can force it to use a .Column with one element by setting &amp;lt;code&amp;gt;howToIndex: 1&amp;lt;/code&amp;gt;.  If you are using a named range and don&#039;t know how many columns it has, you might use this option to prevent an error occurring if you use [[Dot_operator::A.I|result.Column]] in an expression. Similarly, you can force it to use local &amp;lt;code&amp;gt;.Row&amp;lt;/code&amp;gt; index even when the result has only a single row by specifying &amp;lt;code&amp;gt;howToIndex: 2&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
You can obtain the entire range of a worksheet with all cells that contain anything named &amp;lt;code&amp;gt;&amp;quot;Sheet1&amp;quot;&amp;lt;/code&amp;gt; by specifying the «range» as &amp;lt;code&amp;gt;&amp;quot;Sheet1!&amp;quot;&amp;lt;/code&amp;gt; or by omitting the «range» parameter and specifying just the «sheet» parameter.&lt;br /&gt;
&lt;br /&gt;
=== Examples ===&lt;br /&gt;
&lt;br /&gt;
The following examples use this spreadsheet:&lt;br /&gt;
&lt;br /&gt;
:[[Image:WorksheetRange ExcelShot.jpg]]&lt;br /&gt;
&lt;br /&gt;
This spreadsheet contains these named ranges:&lt;br /&gt;
:{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Label !! Range &lt;br /&gt;
|-&lt;br /&gt;
| Rate || B1&lt;br /&gt;
|-&lt;br /&gt;
| Year || B3:F3&lt;br /&gt;
|-&lt;br /&gt;
| Cash_flow || B4:F4&lt;br /&gt;
|-&lt;br /&gt;
| Divisions || A7:A9&lt;br /&gt;
|-&lt;br /&gt;
| Employee_count || B7:F9&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;code&amp;gt;SpreadsheetRange(wb, &amp;quot;Rate&amp;quot;) &amp;amp;rarr; 0.08&amp;lt;/code&amp;gt;&lt;br /&gt;
:&amp;lt;code&amp;gt;SpreadsheetRange(wb, &amp;quot;Sheet1!B1&amp;quot;) &amp;amp;rarr; 0.08&amp;lt;/code&amp;gt;&lt;br /&gt;
:&amp;lt;code&amp;gt;SpreadsheetRange(wb, &amp;quot;Sheet1!B3:F3&amp;quot;) &amp;amp;rarr;&amp;lt;/code&amp;gt;&lt;br /&gt;
:{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
 ! .Column &amp;amp;rarr; !! &#039;B&#039; !! &#039;C&#039; !! &#039;D&#039; !! &#039;E&#039; !! &#039;F&#039;&lt;br /&gt;
 |-&lt;br /&gt;
 | || 2008 || 2009 || 2010 || 2011 || 2012&lt;br /&gt;
 |}&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;code&amp;gt;Index Year := CopyIndex( SpreadsheetRange(wb, &amp;quot;Year&amp;quot;, howToIndex: 1));&amp;lt;/code&amp;gt;&lt;br /&gt;
:&amp;lt;code&amp;gt;SpreadsheetRange(wb, &amp;quot;Cash_flow&amp;quot;, colIndex: Year) &amp;amp;rarr;&amp;lt;/code&amp;gt;&lt;br /&gt;
:{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
 ! .Year &amp;amp;rarr; !! 2008 !! 2009 !! 2010 !! 2011 !! 2012&lt;br /&gt;
 |-&lt;br /&gt;
 | || -100 || 10 || 30 || 50 || 60&lt;br /&gt;
 |}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Note&#039;&#039;: &amp;lt;code&amp;gt;howToIndex: 1&amp;lt;/code&amp;gt; was specified for &amp;lt;code&amp;gt;Year&amp;lt;/code&amp;gt; here so that we would have a 1-D array even if only one year were present in the spreadsheet.&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;code&amp;gt;SpreadsheetRange(wb, &amp;quot;Employee_count&amp;quot;) &amp;amp;rarr;&amp;lt;/code&amp;gt;&lt;br /&gt;
:{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
 ! !! .Column &amp;amp;rarr; !! &#039;B&#039; !! &#039;C&#039; !! &#039;D&#039; !! &#039;E&#039; !! &#039;F&#039;&lt;br /&gt;
 |-&lt;br /&gt;
 ! rowspan=&amp;quot;3&amp;quot; | .Row&amp;lt;br&amp;gt;&amp;amp;darr; !! 7 &lt;br /&gt;
 | 24 || 27 || 28 || 32 || 35&lt;br /&gt;
 |-&lt;br /&gt;
 ! 8 &lt;br /&gt;
 | 13 || 13 || 13 || 13 || 13&lt;br /&gt;
 |-&lt;br /&gt;
 ! 9 &lt;br /&gt;
 | 25 || 22 || 21 || 19 || 16&lt;br /&gt;
 |}&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;code&amp;gt;Index Time := [2008, 2009, 2010, 2011, 2012];&amp;lt;/code&amp;gt;&lt;br /&gt;
:&amp;lt;code&amp;gt;SpreadsheetRange(wb, &amp;quot;A7:F9&amp;quot;, colIndex: Time, howToIndex: 8, sheet: 1)  &amp;amp;rarr;&amp;lt;/code&amp;gt;&lt;br /&gt;
:{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
 ! !! Time &amp;amp;rarr; !! 2008 !! 2009 !! 2010 !! 2011 !! 2012&lt;br /&gt;
 |-&lt;br /&gt;
 ! rowspan=&amp;quot;3&amp;quot; | .Row&amp;lt;br&amp;gt;&amp;amp;darr; !! &amp;quot;Div A&amp;quot; &lt;br /&gt;
 | 24 || 27 || 28 || 32 || 35&lt;br /&gt;
 |-&lt;br /&gt;
 ! &amp;quot;Div B&amp;quot; &lt;br /&gt;
 | 13 || 13 || 13 || 13 || 13&lt;br /&gt;
 |-&lt;br /&gt;
 ! &amp;quot;Div C&amp;quot; &lt;br /&gt;
 | 25 || 22 || 21 || 19 || 16&lt;br /&gt;
 |}&lt;br /&gt;
&lt;br /&gt;
To obtain the list of worksheet names:&lt;br /&gt;
:&amp;lt;code&amp;gt;SpreadsheetCell(wb, &amp;quot;*&amp;quot;, 1, 1).Sheet&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To obtain all used cells in sheet named &amp;quot;Sheet2&amp;quot;:&lt;br /&gt;
:&amp;lt;code&amp;gt;SpreadsheetRange(wb, sheet:&amp;quot;Sheet2&amp;quot;)&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To obtain the number format of all cells in &amp;quot;Sheet2&amp;quot;:&lt;br /&gt;
:&amp;lt;code&amp;gt;SpreadsheetRange(wb, sheet:&amp;quot;Sheet2&amp;quot;, what:&amp;quot;NumberFormat&amp;quot;)&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===  SpreadsheetRange «what» parameter === &lt;br /&gt;
&lt;br /&gt;
By default, SpreadsheetRange() returns the value of the cell(s) in the range, but you can use the «what» parameter to obtain the formula,  cell style and formats, cell address, predecessor or dependent cells of each cell:&lt;br /&gt;
;«what»: (optional). By default, SpreadsheetRange returns the value of the range, but you can use this parameter to obtain its formula, or cell style parameters.  Possible values: &lt;br /&gt;
::&amp;lt;code&amp;gt;&amp;quot;Value&amp;quot;&amp;lt;/code&amp;gt;: (Default) The computed value.  Excel dates become Analytica date-time numbers, which display as dates.&lt;br /&gt;
::&amp;lt;code&amp;gt;&amp;quot;NumericValue&amp;quot;&amp;lt;/code&amp;gt;: The computed value, but dates are returned as numbers.&lt;br /&gt;
::&amp;lt;code&amp;gt;&amp;quot;Formula&amp;quot;&amp;lt;/code&amp;gt;: The cell formula as a text value in the normal Excel format starting with &amp;quot;=&amp;quot;, e.g., &amp;lt;code&amp;gt;&amp;quot;=Sum(D4:D10)&amp;quot;&amp;lt;/code&amp;gt;&lt;br /&gt;
::&amp;lt;code&amp;gt;&amp;quot;RelativeFormula&amp;quot;&amp;lt;/code&amp;gt;: The cell formula using relative offset format, e.g., &amp;lt;code&amp;gt;&amp;quot;=Sum(RC[-9]:R[+6]C[-9])&amp;quot;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== «what» parameter cell formats  ==== &lt;br /&gt;
::&amp;lt;code&amp;gt;&amp;quot;NumberFormat&amp;quot;&amp;lt;/code&amp;gt;: The cell number format as text.&lt;br /&gt;
::&amp;lt;code&amp;gt;&amp;quot;BackColor&amp;quot;&amp;lt;/code&amp;gt;: Cell background color as integer: &#039;&#039;red*65536 + green*256 + blue&#039;&#039;&lt;br /&gt;
::&amp;lt;code&amp;gt;&amp;quot;Text Color&amp;quot;&amp;lt;/code&amp;gt;: Font color as an integer: &#039;&#039;red*65536 + green*256 + blue&#039;&#039;&lt;br /&gt;
::&amp;lt;code&amp;gt;&amp;quot;FontName&amp;quot;&amp;lt;/code&amp;gt;: Name of the font used to display the cell&lt;br /&gt;
::&amp;lt;code&amp;gt;&amp;quot;FontSize&amp;quot;&amp;lt;/code&amp;gt;: Point size of the font displayed in the cell&lt;br /&gt;
::&amp;lt;code&amp;gt;&amp;quot;FontStyle&amp;quot;&amp;lt;/code&amp;gt;: Special font styles for cell separated by spaces, may include &amp;quot;bold italic underline strikethrough subscript superscript outline shadow&amp;quot;&lt;br /&gt;
::&amp;lt;code&amp;gt;&amp;quot;HorizontalAlignment&amp;quot;&amp;lt;/code&amp;gt;: Text justification, one of: &amp;lt;code&amp;gt;&#039;Left&#039;, &#039;Center&#039;, &#039;Right&#039;, &#039;Justify&#039;, &#039;Distributed&#039;, &#039;Fill&#039;&amp;lt;/code&amp;gt;.  (&#039;&#039;new to [[Analytica 5.0]]&#039;&#039;)&lt;br /&gt;
::&amp;lt;code&amp;gt;&amp;quot;VerticalAlignment&amp;quot;&amp;lt;/code&amp;gt;: Text vertical justification, one of: &amp;lt;code&amp;gt;&#039;Top&#039;, &#039;Middle&#039;, &#039;Bottom&#039;, &#039;Justify&#039;, &#039;Distributed&#039;&amp;lt;/code&amp;gt;.  (&#039;&#039;new to [[Analytica 5.0]]&#039;&#039;)&lt;br /&gt;
::&amp;lt;code&amp;gt;&amp;quot;WrapText&amp;quot;&amp;lt;/code&amp;gt;: &amp;lt;code&amp;gt;0&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;1&amp;lt;/code&amp;gt; controls whether text is word wrapped to fit in the cell.&lt;br /&gt;
::&amp;lt;code&amp;gt;&amp;quot;Border(Left|Right|Up|Down)&amp;quot;&amp;lt;/code&amp;gt; show a border to left, right, above, or below the cell.&lt;br /&gt;
::&amp;lt;code&amp;gt;&amp;quot;Border(Left|Right|Up|Down)Color&amp;quot;&amp;lt;/code&amp;gt;: Return the color of the specified side of the border as an RGB number --  E.g., &amp;lt;code&amp;gt;&amp;quot;BorderLeftColor&amp;quot;&amp;lt;/code&amp;gt; returns an integer equal to &#039;&#039;red*65535+green*256+blue&#039;&#039;&lt;br /&gt;
::&amp;lt;code&amp;gt;&amp;quot;Border (Left|Right|Up|Down) Style&amp;quot;&amp;lt;/code&amp;gt;: Style of indicated border, or [[Null]] if not set. May be &amp;lt;code&amp;gt;&amp;quot;Solid&amp;quot;, &amp;quot;Dash&amp;quot;, &amp;quot;DashDot&amp;quot;, &amp;quot;DashDotDot&amp;quot;, &amp;quot;Dot&amp;quot;, &amp;quot;Double&amp;quot;&amp;lt;/code&amp;gt;, or &amp;lt;code&amp;gt;&amp;quot;SlantDashDot&amp;quot;&amp;lt;/code&amp;gt;.&lt;br /&gt;
::&amp;lt;code&amp;gt;&amp;quot;Border (Left|Right|Up|Down) Weight&amp;quot;&amp;lt;/code&amp;gt;: Thickness of indicated border, usually between 1 and 4&lt;br /&gt;
&lt;br /&gt;
==== «what» parameter cell addresses  ====&lt;br /&gt;
::&amp;lt;code&amp;gt;&amp;quot;Address&amp;quot;&amp;lt;/code&amp;gt;: The address of the cell range, e.g., &amp;lt;code&amp;gt;&amp;quot;B12:C13&amp;quot;&amp;lt;/code&amp;gt;&lt;br /&gt;
::&amp;lt;code&amp;gt;&amp;quot;AddressR1C1&amp;quot;&amp;lt;/code&amp;gt;: The address of the cell range in R1C1 format, e.g., &amp;lt;code&amp;gt;&amp;quot;R12C2:R13C3&amp;quot;&amp;lt;/code&amp;gt;&lt;br /&gt;
::&amp;lt;code&amp;gt;&amp;quot;Sheet&amp;quot;&amp;lt;/code&amp;gt;: The sheet name where the cell range exists.&lt;br /&gt;
::&amp;lt;code&amp;gt;&amp;quot;RangeName&amp;quot;&amp;lt;/code&amp;gt;: The name of the range, if it is a named range. &lt;br /&gt;
&lt;br /&gt;
==== «what» parameter cell precedents and dependents  ==== &lt;br /&gt;
&lt;br /&gt;
::&amp;lt;code&amp;gt;&amp;quot;DirectPrecedents&amp;quot;&amp;lt;/code&amp;gt;: Addresses of all cells mentioned in the cell formula, separated by commas.  Unfortunately, Excel lists only cells in the same sheet, but not precedents in other sheets.  &lt;br /&gt;
::&amp;lt;code&amp;gt;&amp;quot;DirectPrecedentsRelative&amp;quot;&amp;lt;/code&amp;gt;: Same as &amp;quot;DirectPrecedents&amp;quot;, but cells are given by their offset relative to the current cell, e.g., &amp;lt;code&amp;gt;R[-3]C[6]&amp;lt;/code&amp;gt;.&lt;br /&gt;
::&amp;lt;code&amp;gt;&amp;quot;DirectDependents&amp;quot;&amp;lt;/code&amp;gt;: Addresses of all cells whose formula mentions this cell, separated by commas.  Unfortunately, Excel lists only cells in the same sheet, but not dependents in other sheets. &lt;br /&gt;
::&amp;lt;code&amp;gt;&amp;quot;DirectDependentsRelative&amp;quot;&amp;lt;/code&amp;gt;: Same as &amp;lt;code&amp;gt;&amp;quot;DirectDependents&amp;quot;&amp;lt;/code&amp;gt;, but cells are identified by their offset relative to the current cell, e.g., &amp;lt;code&amp;gt;R[-3]C[6]&amp;lt;/code&amp;gt;.&lt;br /&gt;
::&amp;lt;code&amp;gt;&amp;quot;Precedents&amp;quot;&amp;lt;/code&amp;gt;:  Addresses of all cells in the current worksheet mentioned in the formula of this cell and the formulas of its direct precedents.  It does not include cells reached by paths passing through other sheets.&lt;br /&gt;
::&amp;lt;code&amp;gt;&amp;quot;PrecedentsRelative&amp;quot;&amp;lt;/code&amp;gt;: Same as &amp;lt;code&amp;gt;&amp;quot;Precedents&amp;quot;&amp;lt;/code&amp;gt;, but cells are identified by their offset relative to the current cell, e.g., &amp;lt;code&amp;gt;R[-3]C[6]&amp;lt;/code&amp;gt;.&lt;br /&gt;
::&amp;lt;code&amp;gt;&amp;quot;Descendants&amp;quot;&amp;lt;/code&amp;gt;: Description of all cells in the current worksheet that depend directly or indirectly on the given cell.  It does not include cells reached by paths passing through other sheets.&lt;br /&gt;
::&amp;lt;code&amp;gt;&amp;quot;DirectDescendantsRelative&amp;quot;&amp;lt;/code&amp;gt;: Same as &amp;lt;code&amp;gt;&amp;quot;DirectDescendants&amp;quot;&amp;lt;/code&amp;gt;, but cells are identified by their offset relative to the current cell, e.g., &amp;lt;code&amp;gt;R[-3]C[6]&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Errors in SpreadsheetRange parameters ===&lt;br /&gt;
In a call to SpreadsheetRange(wb, range):&lt;br /&gt;
* If range refers to a sheet, e.g. &amp;quot;sheet!x&amp;quot; that does not exist in workbook wb, it gives an error message saying &amp;quot;the worksheet &#039;sheet&#039; was not found.&amp;quot;&lt;br /&gt;
* If range refers to a named range, e.g. &amp;quot;sheet!x&amp;quot; that does not exist in workbook wb, it gives an error message saying &amp;quot;the indicated named cell range, &#039;x&#039;, was not found.&amp;quot;&lt;br /&gt;
* If range refers to a cell address with bad syntax, e.g. &amp;quot;ted!A1:R3C6&amp;quot;, it gives an error message saying &amp;quot;the range named A1:R3C6 was not found in Excel worksheet &#039;ted&#039;.&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;SpreadsheetInfo&amp;quot;&amp;gt;&lt;br /&gt;
== SpreadsheetInfo(workbook, item) == &lt;br /&gt;
&amp;lt;/div&amp;gt;  &lt;br /&gt;
&lt;br /&gt;
SpreadsheetInfo gets various kinds of information about the spreadsheet («workbook») specified by parameter «item»:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! item !! Description&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;&amp;quot;AcceptLabelsInFormulas&amp;quot;&amp;lt;/code&amp;gt; || True when you can use labels in worksheet formulas. This is usually false.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;&amp;quot;Account&amp;quot;&amp;lt;/code&amp;gt; || &#039;&#039;([[Analytica 7.2|7.2]])&#039;&#039; The Google account a workbook opened from Google Sheets or Google Drive is connected with. Null for other workbooks.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;&amp;quot;ActiveSheet&amp;quot;&amp;lt;/code&amp;gt; || The number of the active (displayed) worksheet.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;&amp;quot;Author&amp;quot;&amp;lt;/code&amp;gt; || The name of the author, usually the name of the person who created the spreadsheet as recorded by Windows OS.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;&amp;quot;Backend&amp;quot;&amp;lt;/code&amp;gt; || &#039;&#039;([[Analytica 7.2|7.2]])&#039;&#039; Which engine holds the workbook: &amp;lt;code&amp;gt;&#039;Excel&#039;&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;&#039;LibXl&#039;&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;&#039;GoogleSheets&#039;&amp;lt;/code&amp;gt; (see the «backend» parameter of [[SpreadsheetOpen]]).&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;&amp;quot;CalculationMode&amp;quot;&amp;lt;/code&amp;gt;   || The calculation mode set for the workbook, which may be &amp;quot;Automatic&amp;quot;, &amp;quot;Manual&amp;quot; or &amp;quot;Semiautomatic&amp;quot;, meaning automatic except for data tables.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;&amp;quot;CalculationState&amp;quot;&amp;lt;/code&amp;gt; || &#039;&#039;([[Analytica 5.0|5.0]])&#039;&#039; The current state of Excel&#039;s calculation engine, either &amp;lt;code&amp;gt;&amp;quot;Calculating&amp;quot;&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;&amp;quot;Pending&amp;quot;&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;&amp;quot;Done&amp;quot;&amp;lt;/code&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;&amp;quot;CalculationVersion&amp;quot;&amp;lt;/code&amp;gt; || &#039;&#039;([[Analytica 5.0|5.0]])&#039;&#039; Number equal to 10000 * major + minor, encoding the version of the Excel calculation engine that the current workbook was last calculated in. If it was saved in an earlier version of Excel and hasn&#039;t yet been fully calculated, the value is 0. You can compare this to the &amp;quot;Excel.CalculationVersion&amp;quot; to determine whether it was last re-calculated using the same calculation engine as your current installed Excel.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;&amp;quot;CodeName&amp;quot;&amp;lt;/code&amp;gt; ||&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;&amp;quot;Date1904&amp;quot;&amp;lt;/code&amp;gt; || The base for dates used in the workbook.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;&amp;quot;DecimalSeparator&amp;quot;&amp;lt;/code&amp;gt; || &#039;&#039;([[Analytica 5.0|5.0]])&#039;&#039; The character used to separate a whole number from its fractional part. In English-speaking countries this is &#039;.&#039; (a dot).&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;&amp;quot;Excel.CalculationVersion&amp;quot;&amp;lt;/code&amp;gt; || &#039;&#039;([[Analytica 5.0|5.0]])&#039;&#039; Number equal to 10000 * major + minor, encoding the version of calculation engine for your installed version of Excel. &lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;&amp;quot;Filename&amp;quot;&amp;lt;/code&amp;gt;   || The name of the file, including the full file path.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;&amp;quot;Name&amp;quot;&amp;lt;/code&amp;gt;         || The name of the file, without the file path.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;&amp;quot;Names&amp;quot;&amp;lt;/code&amp;gt;          || A list of all the named ranges.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;&amp;quot;OperatingSystem&amp;quot;&amp;lt;/code&amp;gt; || &#039;&#039;([[Analytica 5.0|5.0]])&#039;&#039; The name of the operating system that your Excel instance is running on, as reported by Excel. &lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;&amp;quot;ReadOnly&amp;quot;&amp;lt;/code&amp;gt; || True (1) if the file is saved as Readonly.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;&amp;quot;Saved&amp;quot;&amp;lt;/code&amp;gt;      || False (0) if it has unsaved changes.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;&amp;quot;SelectedRange&amp;quot;&amp;lt;/code&amp;gt; || The currently selected Range.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;&amp;quot;SelectedRangeR1C1&amp;quot;&amp;lt;/code&amp;gt; || The currently selected Range specified by row and column number.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;&amp;quot;Sheets&amp;quot;&amp;lt;/code&amp;gt;     || A list of the names of all the worksheets&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;&amp;quot;ThousandsSeparator&amp;quot;&amp;lt;/code&amp;gt; || &#039;&#039;([[Analytica 5.0|5.0]])&#039;&#039; The character Excel uses to group thousands when displaying a large number. In English-speaking countries this is &#039;,&#039; (a comma). For example, in the number &amp;lt;code&amp;gt;1,234,456.78&amp;lt;/code&amp;gt;, groups of thousands are separated by commas.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;&amp;quot;Title&amp;quot;&amp;lt;/code&amp;gt; || The title of the spreadsheet&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;&amp;quot;URL&amp;quot;&amp;lt;/code&amp;gt; || &#039;&#039;([[Analytica 7.2|7.2]])&#039;&#039; The link of a workbook opened from Google Sheets or Google Drive. Null for other workbooks.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;&amp;quot;UseSystemSeparators&amp;quot;&amp;lt;/code&amp;gt; || &#039;&#039;([[Analytica 5.0|5.0]])&#039;&#039; True when Excel uses &amp;lt;code&amp;gt;&amp;quot;DecimalSeparator&amp;quot;&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;&amp;quot;ThousandsSeparator&amp;quot;&amp;lt;/code&amp;gt; for displaying numbers.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;&amp;quot;Version&amp;quot;&amp;lt;/code&amp;gt; || &#039;&#039;([[Analytica 5.0|5.0]])&#039;&#039; The version number (text) for the installed release of Excel. Excel 2010 is &amp;quot;14.0&amp;quot;, Excel 2013 is &amp;quot;15.0&amp;quot; and Excel 2016 is &amp;quot;16.0&amp;quot;.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;&amp;quot;Visible&amp;quot;&amp;lt;/code&amp;gt; || True when the Excel UI is visible.&lt;br /&gt;
|}  The items above marked with &#039;&#039;([[Analytica 5.0|5.0]])&#039;&#039;  require [[Analytica 5.0]] or better; those marked &#039;&#039;([[Analytica 7.2|7.2]])&#039;&#039; require [[Analytica 7.2]].&lt;br /&gt;
&lt;br /&gt;
== History== &lt;br /&gt;
&lt;br /&gt;
Functions for reading cells from Excel were first present in Analytica 4.1 with functions named [[OpenExcelFile]], [[WorksheetCell]] and [[WorksheetRange]], although these were labelled as &#039;&#039;experimental&#039;&#039;, and the present functions were not officially available until 4.2.0.    The old names are now deprecated, replaced with [[SpreadsheetOpen]], [[SpreadsheetCell]] and [[SpreadsheetRange]].  The old functions still work, but may be removed in future Analytica releases.  The parameters have changed slightly from [[WorksheetRange]] to [[SpreadsheetRange]], with the sheet parameter moved from being the second to being the last parameter and now optional -- no longer required for named ranges or ranges of the form &amp;lt;code&amp;gt;&amp;quot;Sheet1!A1:Z99&amp;quot;&amp;lt;/code&amp;gt;.  &lt;br /&gt;
&lt;br /&gt;
[[SpreadsheetInfo]] was introduced in [[Analytica 4.5]]. These options to [[SpreadsheetInfo]] were added in [[Analytica 5.0]]: &amp;quot;Version&amp;quot;, &amp;quot;CalculationVersion&amp;quot;, &amp;quot;Excel.CalculationVersion&amp;quot;, &amp;quot;UseSystemSeparators&amp;quot;, &amp;quot;DecimalSeparator&amp;quot;, &amp;quot;ThousandsSeparator&amp;quot;, and &amp;quot;CalculationState&amp;quot;.  &lt;br /&gt;
&lt;br /&gt;
The color options for «what» incorrectly returned numbers in 0x00bbggrr order, instead of 0x00rrggbb order prior to [[Analytica 5.0]]. (This was a bug -- the documentation stated it should be 0x00rrggbb).  Various options to the «what» parameter of [[SpreadsheetCell]] and [[SpreadsheetRange]] have appeared at different releases. The options &amp;lt;code&amp;gt;&#039;HorizontalAlignment&#039;&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;&#039;VerticalAlignment&#039;&amp;lt;/code&amp;gt; appeared in [[Analytica 5.0]]. Options &amp;lt;code&amp;gt;&#039;Address&#039;&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;&#039;AddressR1C1&#039;&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;&#039;Sheet&#039;&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;&#039;RangeName&#039;&amp;lt;/code&amp;gt; appeared in [[Analytica 4.6]]. The remaining options appeared in [[Analytica 4.4]], except for &amp;lt;code&amp;gt;&#039;Value&#039;&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;&#039;NumericValue&#039;&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;&#039;Formula&#039;&amp;lt;/code&amp;gt;&#039; and &amp;lt;code&amp;gt;&#039;RelativeFormula&#039;&amp;lt;/code&amp;gt;, which appeared when the «what» parameter was introduced in [[Analytica 4.3]].&lt;br /&gt;
&lt;br /&gt;
{{Release|1=7.2|2=|3=The &amp;lt;code&amp;gt;&#039;GoogleSheets&#039;&amp;lt;/code&amp;gt; «backend» of [[SpreadsheetOpen]] (Google Sheets spreadsheets and Excel workbooks stored in Google Drive, opened from their link), and the &amp;quot;Backend&amp;quot;, &amp;quot;URL&amp;quot; and &amp;quot;Account&amp;quot; items of [[SpreadsheetInfo]], were added in [[Analytica 7.2]].}}&lt;br /&gt;
&lt;br /&gt;
== See Also == &lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;column-count:2;-moz-column-count:2;-webkit-column-count:2&amp;quot;&amp;gt;&lt;br /&gt;
*  [[media:Spreadsheet Helper lib.ana|Spreadsheet Helper lib.ana]]&lt;br /&gt;
* [[Analytica_Libraries_and_Templates#Spreadsheet_Helper_Library|Spreadsheet Helper Library]]&lt;br /&gt;
* [[Media:Functions for Reading Excel Worksheets.ana|Reading Excel Worksheets.ana]]&lt;br /&gt;
* [[Read and Write Spreadsheets]]&lt;br /&gt;
* [[Excel spreadsheets read and write]]&lt;br /&gt;
* [[Functions to Write Data to Excel Worksheets]] -- [[SpreadsheetSetCell]], [[SpreadsheetSetRange]] and [[SpreadsheetSave]]&lt;br /&gt;
* {{Release|1=7.2|2=|3=[[Using your own Google OAuth client]] -- connecting to Google Sheets through your own Google Cloud registration}}&lt;br /&gt;
* You can also use [[DbQuery| ODBC]] -- a standard database access method to read from Excel spreadsheets.&lt;br /&gt;
* [[SuppressExcelAlerts]]&lt;br /&gt;
* [[Excel to Analytica Translation]]&lt;br /&gt;
* [[Excel to Analytica Mappings]]&lt;br /&gt;
* [[ReadTextFile]]&lt;br /&gt;
* [[ReadCsvFile]]&lt;br /&gt;
* You can use these functions from  [[Excel Functions from ADE| ADE]].&lt;br /&gt;
* These spreadsheet functions above are more flexible than [[OLE linking]] which is also available.&lt;br /&gt;
* [[OLE linking]] &amp;lt;/div&amp;gt;&lt;/div&gt;</summary>
		<author><name>Lchrisman</name></author>
	</entry>
</feed>