NAV Navbar
Home icon
SAS Viya REST APIs
shell javascript python go

Getting Started

Introduction

SAS Viya REST APIs are based on resource-oriented URLs, use HTTP authentication and HTTP verbs, and return HTTP response codes to indicate API errors. With SAS Viya REST APIs, you can create and access SAS resources using any client technology.

The following documentation covers how to register clients, create access tokens, and make basic API calls. You’ll also find guidelines on collections, links, pagination, filtering, and other general usage information on SAS Viya REST APIs. More detailed information is available in the following resources, most of which are referenced in the sections below.

Authentication to SAS Viya

SAS Viya provides authentication services with the SAS Logon Manager. These services are built around Open Authorization (OAuth) and OpenID Connect. SAS Logon Manager uses OAuth policy to enforce the access token to allow access to APIs and protected resources. SAS Viya integrates with the identity management system that your organization uses.

The steps below outline the process of obtaining an access token.

  1. Developers contact the organization's SAS administrator to register a client application.
  2. The SAS administrator registers the client and provides a client id and secret.
  3. Developers implement code to acquire the token used for API calls. The request for the token must include the registered client id and client secret.
  4. The resulting access token is passed on API calls.

Steps for SAS Administrator

All applications and scripts that make use of the SAS Viya REST APIs must be registered with the SAS environment.

Register the client

A SAS administrator uses the SAS Logon OAuth API to configure a client with the Oauth service in SAS Logon Manager using the following steps.

Get access token for client registration


A user in the SAS Administrators group makes and API call to SAS Logon to generate an access token, which will in turn be used to register a client (application). Below is an example of the command where the resulting token is stored in the BEARER_TOKEN variable.

Request client registration OAuth token


curl

export BEARER_TOKEN=`curl -sk -X POST "https://<sas-server>/SASLogon/oauth/token" \
   -u "sas.cli:"
   -H "Content-Type: application/x-www-form-urlencoded" \
   -d "grant_type=password&username=user&password=password"`

Python


import requests
url="http://<sas-server>/SASLogon/oauth/token"
payload=grant_type=password&username=username&password=password
headers={
'Content-Type':'application/x-www-form-urlencoded',
'Authorization':'Basic c2FzLmNsaTo='}

response= requests.request("POST", url,headers=headers,data=payload) bearer_token= json.loads(response.text)['access_token']

Grant types


A grant is a method of acquiring an access token. Deciding which grants to implement depends on the type of client the end user will be using. SAS supports three grant types (not including refresh_token, which is covered later). These include authorization_code, client_credentials, and password. Each grant is described in the table below.


authorized_grant_type Description
authorization_code User-centric grant flow – facilitates the client obtaining an access token without ever handling the user's credentials
client_credentials Group-based grant flow – based on SAS Viya custom group-based access
password Not for use outside of test or dev instances. Easiest to configure, but is the least secure grant type

Choosing the proper grant type depends on your environment and configuraiton. This choice plays out in client registration and access token requests. For detailed information, see this article on choosing an OAuth flow for SAS Viya.


Client registration


Use the value of access_token returned in the response JSON from the earlier section to register the new client.
The following sections outline the code required to register the client, listed by grant type. Parameters for each grant type are defined in the following table.


Query parameter Parameter Value Required Description
Authorization Bearer eyJhbGciOiJSUzI1NiIsI... Yes access token returned in the response from the previous step
client_id app Yes name of the client to register; this must match the serviceId from the previous step
client_secret secret Yes secret string used for authenticating as the client; assigned by the admin registering the client
scope openid Yes scopes allowed for the client to obtain on behalf of users; most SAS Viya APIs, "openid" and "uaa.user" are sufficient; for client_credentials grant type, use the default scope "uaa.none
authorized_grant_types authorization_code, client_credentials, or password Yes method of acquiring an access token
authorities <group name(s)> client_credentials specifies the groups tokens get using the client_credentials grant_type
redirect_uri urn:ietf:wg:oauth:2.0:oob authorization_code allowed URI pattern for redirect during authorization
access_token_validity No Controls access token ttl; can be set to any number of seconds. If not specified, will use the system default.

A note on scope and authorities. Scopes are included in all three grant types, while authorities only pertain to client_credentials. Scopes are internal and only understood by the saslogon service. The authorities assigned to an OAuth client are the groups the client will itself have when obtaining access tokens using its own client credentials. See this Community thread for more details.


Authorization Code

Use the following code to register a client using the authorization_code grant type:


curl

curl -k -X POST "https://<sas-server>/SASLogon/oauth/clients" \
   -H "Content-Type: application/json" \
   -H "Authorization: Bearer eyJhbGciOiJSUzI1NiIsIm..." \
   -d '{"client_id": "myclientid",
     "client_secret": "myclientsecret",
     "scope": ["openid","uaa.user"],
     "authorized_grant_types": "authorization_code",>
     "redirect_uri": "urn:ietf:wg:oauth:2.0:oob",
     "access_token_validity": 43199}'

Python

import requests
url = "http://<sas-server>/SASLogon/oauth/clients"
payload = '{"client_id": "myclientid", "client_secret": "myclientsecret", "scope": ["openid", "uaa.user"], "authorized_grant_types": ["authorization_code", "refresh_token"], "redirect_uri": "urn:ietf:wg:oauth:2.0:oob"}'
headers = {'Content-Type': 'application/json', 'Authorization': 'Bearer eyJhbGciOiJSUzI1...'}
response = requests.request("POST", url, headers=headers, data=payload)
print(response.text)

Sample response:

{
  "scope":["openid","uaa.user"],
  "client_id":"myclientid",
  "resource_ids":["none"],
  "authorized_grant_types":["authorization_code"],
  "redirect_uri":["urn:ietf:wg:oauth:2.0:oob"],
  "autoapprove":[],
  "authorities":["uaa.none"],
  "lastModified":1547138692523,
  "required_user_groups":[]
}

Client Credentials

Use the following code to register a client using the client_credentials grant type:


curl

curl -k -X POST "https://<sas-server>/SASLogon/oauth/clients" \
   -H "Content-Type: application/json" \
   -H "Authorization: Bearer eyJhbGciOiJSUzI1NiIsIm..." \
   -d '{"client_id": "myclientid",
     "client_secret": "myclientsecret",
     "authorized_grant_types": "client_credentials",
     "authorities": "<CUSTOM_GROUP>",
     "scope": ["openid"]}'

Python

import requests
url = "http://<sas-server>/SASLogon/oauth/clients"
payload="{"client_id": "myclientid", "client_secret": "myclientsecret", "authorized_grant_types": "client_credentials", "authorities": "<custom-group>"}"
headers = {'Content-Type': 'application/json', 'Authorization': 'Bearer eyJhbGciOiJSUzI1...'}
response = requests.request("POST", url, headers=headers, data=payload)
print(response.text)

Sample response:

{
  "client_id":"app",
  "resource_ids":["none"],
  "authorized_grant_types":["client_credentials"],
  "autoapprove":[],"authorities":["<CUSTOM_GROUP>"],
  "lastModified":1626724462841,
  "required_user_groups":[]
}

Password

Use the following code, to register a client using the password grant type:

curl

curl -k -X POST "https://<sas-server>/SASLogon/oauth/clients" \
    -H "Content-Type: application/json" \
    -H "Authorization: Bearer eyJhbGciOiJSUzI1NiIsIm..." \
    -d '{"client_id": "myclientid",
      "client_secret": "myclientsecret",
      "scope": ["openid","uaa.user"],
      "authorized_grant_types": "password",
      "access_token_validity": 43199}'

Python

import requests
url = "http://<sas-server>/SASLogon/oauth/clients"
payload="{"client_id": "myclientid", "client_secret": "myclientsecret", "authorized_grant_types": "password", "scope": ["openid", "uaa.user"]}"
headers = {'Content-Type': 'application/json', 'Authorization': 'Bearer eyJhbGciOiJSUzI1...'}
response = requests.request("POST", url, headers=headers, data=payload)
print(response.text)

Sample response:

{
  "scope":["openid","group1"],
  "client_id":"myclientid",
  "resource_ids":["none"],
  "authorized_grant_types":["password"],
  "redirect_uri":["urn:ietf:wg:oauth:2.0:oob"],
  "autoapprove":[],
  "authorities":["uaa.none"],
  "lastModified":1547801596527,
  "required_user_groups":[]
}

Once the client is registered, the SAS administrator provides the client identifier and client secret to the developer/user.


Setting Cross-Origin Resource Sharing (CORS)

If you are developing a web application using SAS Viya REST APIs, you need your SAS administrator to configure CORS settings on the SAS servers. Cross-origin resource sharing is a W3C specification supported by most browsers to allow you to specify how cross domain requests are authorized.

More Information about CORS


SAS Viya servers can provide support for Cross-Origin Resource Sharing (CORS), a technique enables Javascript on a web page to consume a REST API served from a different origin. CORS is implemented when a server is configured to include additional HTTP headers to let a user agent access selected resources from a server on a different origin (or domain) than the site currently in use. A user agent makes a cross-origin HTTP request when it requests a resource from a different domain, protocol, or port than the one from which the current document originated.


In the simplest scenario, cross-origin communications starts with a client making a GET, POST, or HEAD request against a resource endpoint on the server. The request includes an Origin header that indicates the origin of the client code. The server considers the request's Origin and either allows or disallows the request. If the server allows the request, then it responds with the requested resource and an Access-Control-Allow-Origin header in the response. This header indicates to the client which client origins should be permitted to access the resource. Assuming that the Access-Control-Allow-Origin header matches the request's Origin, the browser allows the request.


If Access-Control-Allow-Origin is missing in the response or if its value does not match the request's Origin, the browser disallows the request.


If you want to enable developers to utilize SAS Viya REST APIs in web applications, you must enable CORS support in the SAS Viya environment using SAS Environment Manager. To configure or update the setting:


Log into SAS Environment Manager and assume administrator privileges. Note that you must have an account that is authorized as a SAS Administrator to perform these actions.


  1. Select Configuration on the main SAS Environment Manager page.
  2. Select Definitions from the View menu on the Configuration page.
  3. Select sas.commons.web.security.cors from the list of configuration definitions.
  4. Click the New Configuration button if you are setting the CORS options for the first time.
  5. Or click the pencil icon to edit an existing CORS definition.
  6. Make sure allowCredentials is enabled. Set the allowHeaders, allowMethods, and allowOrigins properties to * (or wildcard) to accept all values. Specify a comma-separated list of values for each setting that are enabled by default in cross-origin requests.
  7. Specify the allowOrigins property as a comma-separated list of origins that are allowed. Note: do not use wildcards.
  8. Click Save to complete the configuration.

That's it. The CORS setting is in effect for all services supporting the SAS Viya REST APIs.


Steps for Developers

Contact the SAS Administrator to get a registered client id and the client secret (steps outlined in previous section).

Registered clients request an access token using the SAS Logon OAuth API token endpoint and passing the grant_type used in client registration.

Retrieve access token

Access tokens, by default, are valid for a length of time determined at the system level (unless set to another value during registration). Depending on the grant_type, use the following procedures to obtain an access token.


Authorization code grant type


To get the access token using the authorization code grant type for the myclientid client, first place the following URL in a browser (be sure to replace hostname and clientid accordingly):


https://<sas-server>/SASLogon/oauth/authorize?client_id=clientid&response_type=code


A SAS Viya log in screen appears. Enter valid SAS credentials. You may get presented with a list of scopes. Select access as appropriate for the client and then click the Authorize Access button. An Authorization code is presented (YZuKQUg10Z for example) on the screen. The authorization code is also appended to the URL in the browser address bar. Copy the code value for use in the next step.


Next, using the authorization code, send the following command to get the access token, specifying parameters from the table below:


Query parameter Description
-d grant_type, code authorization_code grant type and authorization code value
-u myclientid:myclientsecret registered clientid and secret

curl

curl -k -X POST https://<sas-server>/SASLogon/oauth/token \
   -H "Accept: application/json" \
   -H "Content-Type: application/x-www-form-urlencoded" \
   -d "grant_type=authorization_code&code=YZuKQUg10Z" \
   -u "myclientid:myclientsecret"


Python

import requests
url = "http://<sas-server>/SASLogon/oauth/token"
payload={"client_id": "myclientid", "client_secret": "myclientsecret", "grant_type": "authorization_code", "code": "bPbbF0QU5u"}
headers = {'Content-Type': 'application/x-www-form-urlencoded', 'Accept': 'application/json'}
response = requests.request("POST", url, headers=headers, data=payload)
print(response.text)


Sample response:

{
  "access_token":"eyJhbGciOiJSUzI1NiIsImtpZ...",
  "token_type":"bearer",
  "refresh_token":"eyJhbGciOiJSUzI1NiIsImtpZC...",
  "expires_in":35999,
  "scope":"openid",
  "jti":"b35f26197fa849b6a1856eea1c722933"
}


Client credentials grant type


To get the access token using the client_credentials grant type for the app client, send the following command, specifying parameters from the table below:

Query parameter Description
-d grant_type, code client_credentials grant type
-u myclientid:myclientsecret registered clientid and secret

curl

curl -k -X POST "https://<sas-server>/SASLogon/oauth/token" \
   -H "Content-Type: application/x-www-form-urlencoded" \
   -d "grant_type=client_credentials" \
   -u "myclientid:myclientsecret"


Python

import requests
url = "http://<sas-server>/SASLogon/oauth/token"
payload="{"grant_type": "client_credentials"}"
headers = {'Content-Type': 'application/json', 'Accept': 'application/json'}
response = requests.request("POST", url, headers=headers, data=payload)
print(response.text)

Sample response:

{
  "access_token":"eyJhbGciOiJSUzI1NiIsImtpZ...",
  "token_type":"bearer",
  "refresh_token":"eyJhbGciOiJSUzI1NiIsImtpZC...",
  "expires_in":35999,
  "scope":"openid",
  "jti":"b35f26197fa849b6a1856eea1c722933"
}

Password grant type


To get the access token using the password grant type for the user sasdemo and app client, send the following command, specifying parameters from the table below:


Query parameter Description
-d grant_type, username, password password grant type, SAS username, and password
-u myclientid:myclientsecret registered clientid and secret

curl

curl -k -X POST https://<sas-server>/SASLogon/oauth/token \
   -H "Content-Type: application/x-www-form-urlencoded" \
   -d "grant_type=password&username=sasdemo&password=mypassword" \
   -u "myclientid:myclientsecret"


Python

import requests
url = "http://<sas-server>/SASLogon/oauth/token"
payload="{"grant_type": "password"}"
headers = {'Content-Type': 'application/json'}
response = requests.request("POST", url, headers=headers, data=payload)
print(response.text)


Sample response:

{
  "access_token":"eyJhbGciOiJSUzI1NiIsImtpZCI6Imx...",
  "token_type":"bearer",
  "expires_in":43199,
  "scope":" openid",
  "jti":"073bdcbc6dc94384bcf9b47dc8b7e479"
}


Refresh Token

The access token has a relatively short lifetime before it expires. To avoid the hassle of repeating the access token retrieval process, use the refresh token to get a new access token. The refresh token is valid for much longer. Most applications deal with expiring and refreshing tokens programmatically.

More information about refresh tokens


The refresh token is valid for the password and authorization code grant types only. The refresh token creation is determined during the client registration process by adding it to the grant_type parameter as follows:


"authorized_grant_types": "authorization_code refresh_token",


The refresh token is then returned in the subsequent access token call:


{
  "access_token":" eyJhbGciOiJSUzI1NiIsImtpZ...", 
  "token_type":"bearer", 
  "refresh_token":"eyJhbGciOiJSUzI1NiIsImtpZC..."
}

The refresh token is valid for relatively longer timeframes and can be used to get a new access token using a command like the following:


curl

curl -k https://<sas-server>/SASLogon/oauth/token -H "Accept: application/json" \
   -H "Content-Type: application/x-www-form-urlencoded" \
   -u "myclientid:myclientsecret" \
   -d "grant_type=refresh_token&refresh_token=eyJhbGciOiJSUzI1NiIsImtpZC..."

Python

import requests
url = "http://<sas-server>/SASLogon/oauth/token"
payload="{"grant_type": "authoriztion_code", "code": "YZuKQUg10Z"
headers = {'Content-Type': 'application/json', 'Accept': 'application/json'}
response = requests.request("POST", url, headers=headers, data=payload)
print(response.text)

Sample response:

{
  "access_token":"eyJhbGciOiJSUzI1NiIsImtpZ...",
  "token_type":"bearer",
  "refresh_token":"eyJhbGciOiJSUzI1NiIsImtpZC...",
  "expires_in":35999,
  "scope":"openid",
  "jti":"b35f26197fa849b6a1856eea1c722933"
}

Note, the access token is new, and the refresh token remains static.


Making an API call

Once a client has a valid access token, use it to make requests to SAS Viya REST APIs.


The authenticated token is passed on requests to identify the caller. If a valid OAuth2 token is not provided on the call, the operation fails with a standard 401 Unauthorized response code. SAS REST APIs use the OAuth2 tokens to authorize specific REST API operations. For example, an authenticated user may have authorization to read a resource via a GET method, but not have authorization update or delete a resource via a PUT or a DELETE methods.


The following call lists top level folders:


curl

curl -k -X GET "https://<sas-server>/folders/folders/" \
     -H "Accept: application/json" \
     -H "Authorization: Bearer eyJhbGciOiJSUzI1NiIsImtpZ..."


Python

import requests
url = "http://<sas-server>/folders/folders/"
payload="{"grant_type": "authoriztion_code", "code": "YZuKQUg10Z"
headers = {'Accept': 'application/json', "Authorization: Bearer eyJhbGciOiJSUzI1NiIsImtpZ..."}
response = requests.request("POST", url, headers=headers)
print(response.text)


Sample response

{
  "links": [
   {
      "method": "GET",
      "rel": "collection",
      "href": "/folders/folders",
      "uri": "/folders/folders",
      "type": "application/vnd.sas.collection"
    },
    {
      "method": "GET",
      ...
    }
  ]
}


For more examples and details on making REST API calls, refer to this series of articles.


API organization and use details

The following sections provide details on SAS Viya API organiztion, usage notes, and defines options when sending calls.

Collections


In SAS Viya REST APIs, collections are aggregations of zero or more resources. Most collections are represented as sets of resources rather than as ordered. Alternatively, you can think of collections as a map from resource IDs to a resource.


Most SAS Viya APIs manage collections of resources. For example, the Reports API has a collection of report resources as a top-level resource, and the Files API a collection of file resources as a top-level resource. Each collection is identified with a URL that typically uses the plural form of the noun, such as /reports/reports or /files/files. That means that in the URI /reports/reports, the first 'reports' is the namespace of the API and the second 'reports' represents the name of the collection of reports.


Collection resources are represented with the application/vnd.sas.collection media type which provides support for paginating, filtering, and sorting those collections. APIs are grouped for convenience into the following API categories:


API Category Description
Visualization Provide access to reports and report images
Compute Act on SAS compute and analytic servers, including Cloud Analytic Services (CAS)
Text Analytics (Deprecated) Provide analysis and categorization of text documents
Data Management Enable data manipulation and data quality operations on data sources
Decision Management Provide access to machine scoring and business rules
Core Services Provide operations for shared resources such as files and folders
Automated Machine Learning Automate machine learning capabilities


SAS Viya REST APIs follow REST conventions including links in REST responses. Just like an HTML page can include links that connect the page to related pages, links in REST responses connect the resources of the API to operations available for that resource or to related resources.


For example, if using a report API to retrieve a report:

GET http://www.example.com/reports/reports/4ebf1327-c72b-4857-a8f4-421a555ac7e7


the response includes a set of links, such as:

{
  "id": "4ebf1327-c72b-4857-a8f4-421a555ac7e7",
  "name": "monthly-sales-analysis",
  "description": "Monthly sales analysis report",
  ....,
  "links": [{
      "method": "GET",
      "rel": "self",
      "href": "/reports/reports/4ebf1327-c72b-4857-a8f4-421a555ac7e7",
      "uri": "/reports/reports/4ebf1327-c72b-4857-a8f4-421a555ac7e7",
      "type": "application/vnd.sas.report"},
    {
      "method": "PUT",
      "rel": "update",
      "href": "/reports/reports/4ebf1327-c72b-4857-a8f4-421a555ac7e7",
      "uri": "/reports/reports/4ebf1327-c72b-4857-a8f4-421a555ac7e7",
      "type": "application/vnd.sas.report",
      "responseType": "application/vnd.sas.report"},
    {
      "method": "DELETE",
      "rel": "delete",
      "href": "/reports/reports/4ebf1327-c72b-4857-a8f4-421a555ac7e7",
      "uri": "/reports/reports/4ebf1327-c72b-4857-a8f4-421a555ac7e7"},
    {
      "method": "GET",
      "rel": "content",
      "href": "/reports/reports/4ebf1327-c72b-4857-a8f4-421a555ac7e7/content",
      "uri": "/reports/reports/4ebf1327-c72b-4857-a8f4-421a555ac7e7/content",
      "type": "application/vnd.sas.report.content"},
  ],
  ....
}

The returned links indicate actions, operations, or state transitions that the client can make from the current resource or related resources. In the example above, the links with the "rel" values of "update" and "delete" indicate state transitions available from the current resource. Specifically, the links define operations to update or delete the report.


Links may also represent an object's relationships to other resources. For example, the link with the value "content" for the "rel" member in the above example (called "the content link") associates the report with the report's content.


Pagination


Many collections in SAS Viya APIs can contain very large numbers of resources, and it is not useful or reasonable to request all of them at once. Instead, most collections return pages of items, where each page represents reasonably-sized, consecutive subsets of the full collection. Each page may also contain links to other pages, such as the first and last page, or the previous and next page. Pagination may also be combined with sorting and filtering.

Pagination Query Parameters

For collections, APIs use start and limit query parameters to fetch (GET) subsets of collections. Given that a client application may only display or operate on a small subset of the collection, the pagination query parameters prevent transmission of entire, large collections across networks. Consider the following:


GET https://www.example.com/''collection''?start=item-index&limit=max-item-count

GET https://www.example.com/''collection''?start=item-index

GET https://www.example.com/''collection''?limit=max-item-count

Parameter Description
start Start is a zero-based integer index (the first item is index == 0) The default start is 0, or item-index.
limit An integer that specifies the maximum number of items to return in the response.

Consult the documentation for each API to determine the default limit for its paginated collections.


Collection results return hypermedia controls that may include the following links:


Parameter Description
first ("rel" : "first") A link to the first page in the collection (?start=0).
previous ("rel" : "prev") A link to the previous page in the collection, if there is one.
next ("rel" : "next") A link to the next page in the collection, if there is one.
last ("rel" : "last") A link to the last page in the collection.
self ("rel" : "self") A link to the current page in the collection, using the same filter and sort criteria.

Sorting


When making a request for a collection or applying a query on its subsetting, a client often requires that the response data is returned in a sorted order.


Sort criteria are specified by the sortBy query parameter.


GET https://www.example.com/reports/reports?sortBy=name,description


This API request results in a paginated collection view that is sorted -- first by the report name, then by the description.


The value of the sortBy parameter is one or more sort criteria, separated by commas. Sort criteria must include one or more keys that match the members of a resource. Each sort criteria may also have sort options which specify sort order or collation strength.


Note: Not all APIs support collation strength options. In those cases, values specified in the sortBy parameter are not honored by the underlying service.


When invoking a query on an orderable collection, the client often requires the data in a sorted order for presentation to the user. Sort criteria are specified by the sortBy query parameter.


GET https://www.example.com/reports/reports?sortBy=name,description


This call results in a paginated collection view that is sorted first by the report name, then by the description.


The value of the sortBy parameter is one or more sort criteria, separated by commas. Sort criteria consist of a key which is a member of a resource. Each sort criteria may also have sort options which specify sort order or collation strength. Note: Not all APIs support collation strength options. (The values may be specified in the sortBy parameter but are not honored by the underlying service.)


sortBy syntax

The syntax of of the sortBy criteria is defined by the following Backus–Naur form syntax:


sortCriteria ::= criteria [ ',' sortCriteria ]


criteria ::= key [ ':' options ]


options ::= option [ ':' options ]


option ::= 'ascending' | 'descending'


option ::= 'primary' | 'secondary' | 'tertiary' | 'quaternary' | 'identical'


The key is the name of a member (also known as a field) in the resources that are being sorted. For example, if a collection resource /someApi/models contains a collection of models, and each model has members with names model, type, modifiedTimeStamp and modifiedBy, a client can request the collection of models sorted first by modified date (most recent first), then who last modified the resource, then by type, then by name:


GET https://www.example.com/someApi/models?sortBy=modifiedTimeStamp:descending,modifiedBy,type,name


If more than one criteria exist, they are considered subgroup sorting criteria. The order is important: the second sort criteria, if present, applies to items that are equal according to the first sort criteria, and so on. It is unlikely that many resources will have the same modifiedTimeStamp. For the


GET https://www.example.com/someApi/models?sortBy=modifiedBy,type,modifiedTimeStamp:descending


there are likely to be several models modified by each user, and for each user, several models with the same type. Within those subgroups, the items will be sorted by descending modified timestamp.


sortBy sort criteria options

Ordering options are:


Option Description
ascending sort in ascending order; this is the default order default if descending is omitted
descending sort in descending order
primary Typically, this is used to denote differences between base characters (for example, "a" < "b", "a" == "A"). It is the strongest difference. For example, dictionaries are divided into different sections by base character. Case is ignored.
secondary Accents in the characters are considered differences (for example, "as" < "às" < "at", , "at" == "At"). Other differences between letters can also be considered secondary differences, depending on the language. A secondary difference is ignored when there is a primary difference anywhere in the strings. Case is ignored.
tertiary Upper and lower case differences in characters are distinguished at tertiary strength (for example, "ao" < "Ao" < "aò"). In addition, a variant of a letter differs from the base form on the tertiary strength (such as "A" and "Ⓐ"). Another example is the difference between large and small Kana. A tertiary difference is ignored when there is a primary or secondary difference anywhere in the strings.
quaternary When punctuation is ignored at primary to tertiary strength, an additional strength level can be used to distinguish words with and without punctuation (for example, "ab" < "a-b" < "aB").
identical When all other strengths are equal, the identical strength is used as a tiebreaker. The Unicode code point values of the NFD form of each string are compared, just in case there is no difference. For example, Hebrew cantillation marks are only distinguished at strength. This strength should be used sparingly, as only code point value differences between two strings is an extremely rare occurrence. Using this strength substantially decreases the performance for both comparison and collation key generation APIs.

If you want case-sensitive comparison, use tertiary. For case-insensitive comparison, use either primary or secondary; for example depending on whether you want á to be grouped with â.


If more than one of {ascending,descending} appear, the last occurrence is used. If more than one of {primary,secondary,tertiary,identical} appear, the last occurrence is used. The default sorting strength is tertiary.


Consult the documentation for each API to determine the default sort order for its orderable collections. If it is not documented, you can assume the collection order is not guaranteed and the service MAY return items in a different order in future requests. For pagination, the first, next, prev, and last link relations, if present, preserve the sortBy query parameter if it was used. For pagination, the start query parameters are relative to sorted collection indexes.


The sort keys are selected from the full set of fields in the collection's underlying resource, which may be a superset of the fields in the requested representation on any specific query.


Filters


SAS Viya APIs support filtering, which is a way to subset a collection to only those resources for which a provided Boolean condition is true. In this way, filtering is analogous to a SQL query with a WHERE clause. SAS REST APIs MAY support two forms of filtering: basic filtering and/or the use of a filter query parameter. If both are available, the two forms may be combined. Each individual API documents which filtering options are supported for that particular API.


Filtering is the application of a Boolean condition against a collection of resources in order to subset the collection to ony those resources for which the condition is true. (For those familiar with SQL, filtering is analogous to a SQL query with a WHERE clause.) SAS REST APIs support two forms of filtering: basic filtering and a filter query parameter. The two forms may be combined.


Consider, for example, a collection of report resources. Given the following members:


Member Type Description
id string The report's unique identifier.
name string The localizable report name.
description string The localizable report description.
createdBy string The name of the user who created this report.
creationTimeStamp date-time string The date and time the report wast first created.
modifiedBy string The name of the last user who modified this report.
modifiedTimeStamp date-time string The date and time the report was last modified.
imageUris object The map of images of the report object.

and this JSON representation of a report resource:


{
  "id": "4eb3b675-e107-4857-a8f4-51aa555ac7e7",
  "name": "Production Report",
  "description": "Production forecasts by region and market segment.",
  "createdBy": "dale",
  "creationTimeStamp": "2017-04-19T14:54:04.705Z",
  "modifiedBy": "dale",
  "modifiedTimeStamp": "2017-04-19T14:55:11.643Z",
  "links": [ ... ],
  "imageUris": {
    "icon": "/reports/icons/report.gif"
  },
  "version": 1
}

The filtering explanations below use this example scenario.

Basic Filtering

Basic filtering allows selecting resources by matching one or more members of the resource to values passed as query parameters. For example, to find all reports created by the user dale, a basic filter is:


GET /reports/reports?createdBy=dale


The names of members in the resource representation (such as description, name, createdBy, modifiedBy etc) are used as the name of the corresponding query parameters. The general form is


GET /reports/reports?memberName=value&...&memberName=value


Basic filtering only supports exact matches of members or simple set containment. Note that quotes are not used around either the names or the string values when using basic filtering.


Some APIs support nested resources, so you can use dot notation to select a nested member, such as imageUris.icon to filter based on the icon member of the imageUris object in the above example resource.


When multiple basic query parameters are used, there is an implicit and operation. That is, *all* the basic filter values must match in order for a resource to be included in the response. For example, a request


GET /reports/reports?createdBy=dale&name=Production%20Report


will match reports that were created by dale and that have the name Production Report. Note that the values must be URL encoded using standard query parameter encoding rules; for example, a space in the value string has an encoded value of %20.


Some members are not well-suited to exact matching, such as the modifiedTimeStamp which is a date-time value with sub-second precision: ?modifiedTimeStamp=2017-04-19T14:55:11.643Z. For such requirements, you should use the filter parameter described below to select resources based on ranges, comparisons, and other selection criteria.

Basic set containment filtering

When using basic filtering, you may pass a pipe-separated set of values. The basic filter matches if the named member matches any of the values in the set. As with simple query parameters, do not use quote characters around the values.

For example, to find reports created by users dale or elaine or jules, use the pipe-separated set containment notation to express the set of values, such as dale|elaine|jules. Of course, the | character must be URL-encoded as %7C when using set containment notation in a query parameter. That means:


GET /reports/reports?createdBy=dale|elaine|jules

is URL encoded as:


GET /reports/reports?createdBy=dale%7Celaine%7Cjules

The values must be URL encoded as well.

Filtering with the filter query parameter

The filter query parameter provides a flexible way to subset the resources from collections by combining comparison and other functions.


?filter=filterExpression


Unlike basic filtering, the filterExpression enables you to:


  • combine multiple expressions with logical operations (and, or, and, not)
  • make comparisons using relational operators (eq, ne, lt, le, gt, ge)
  • use other functions such as contains, startsWith, in, match, etc


The simple (and somewhat useless) filter ?filter=true evaluates to true for all items in the collection — this is the default behavior if no filter is provided.


The filter query parameter names a filter expression which uses a very simple expression notation. The filter expression syntax expresses the same condition as a combination of function calls:


and(or(e,not(b)),gt(C,0))


Parentheses surround the comma-separated arguments to each function and are required to differentiate a function from a member name. Figure 1 shows the structure of the expression; ovals represent functions or operations and rectangle represent members or literal values.

Filter expression syntax

The syntax for filter expressions:


expression := literal
expression := name
expression := function
function := identifier '(' [ arguments ] ')'
arguments := expression [ ',' arguments ]
literal := 'true' | 'false'
literal := number
literal := date
literal := time
literal := date-time
literal := string
string := "'" characters "'"
string := '"' characters '"'
name := identifier [ '.' name ]

For example, the basic query used above,


GET /reports/reports?createdBy=dale&name=Production%20Report


can be expressed by combining two equality test functions on the members createdBy and name, combined with the and function:


GET /reports/reports?filter=and(eq(createdBy,'dale'),eq(name,'Production%20Report'))

Filter expressions

There are three types of expressions that may be used in the filterExpression a filter query: literals, names, and functions.

Filter literals

Literals (also known as constants) are fixed values, such as


the string 'dale'
the string 'Production Report'
Boolean literals true and false
numbers such as 100 or -5.75
date, time, and date-time literals

Literals are typically used as arguments to functions.


String literals are sequences of zero or more characters enclosed in quotes. In filter query parameters, strings may be enclosed in either single or double quotes. The string literal 'dale' is the same as the string literal "dale"; only the contents of the string is significant. If the string contains quote characters, use one of two representations:


To include single quotes (') in a string literal, use double quotes to quote the string: "Dale Smith's ID is 'dale'".


To include double quotes (") in a string literal, use single quotes to quote the string: 'Dale chose the ID "dale".'.


Alternatively, you can uniformly double each embedded quote character:


'IT assigned the user ID ''dale'' to Dale Smith.'


"IT assigned the user ID 'dale' to Dale Smith."


are identical and have the value:


IT assigned the user ID 'dale' to Dale Smith.

Names

Names in filter expressions refer to members of the items in the collection, such as the members name, createdBy, modifiedBy, or imageUris.icon from the example report resource above. Names are typically used as arguments to functions, such as comparing the members to literal values, as described below. An example is the expression


eq(createdBy,'dale')


which is true for any item in the collection that has the exact value 'dale' as its createdBy member.


Date, time, and date-time literals are other name filters.


  • A date literal uses the form yyyy-MM-dd such as 2017-07-27.
  • A time literal uses the form HH:mm:ss and HH:mm:ss.SSSZ where the hours HH range from 00 to 24 and the fractional seconds and time zone are optional. The time zone code Z means UTC (GMT) time zone. Time zone offsets if +HH:mm and -HH:mm are also allowed instead of Z.
  • A date-time literal uses the form yyyy-MM-ddTHH:mm:ss.SSSZ where the fractional seconds and time zone are optional; for example, 2017-06-28T03:18:53.0717Z

Note: date, time, and date-time values are not quoted as strings.

Filter function expressions

Functions operate on one or more values which form the arguments to the function. A function expression has the form:


function := identifier '(' [ arguments ] ')'


arguments := expression [ ',' arguments ]


such as eq(name,'dale'). Here, the function name is eq and the two function arguments (a name and a string literal) are encoded in parentheses and separated by commas.


Below is the base set of functions that may be used in filter expressions.

Logical functions

Logical functions combine other boolean expressions.


Expression Rule
and(e0, e1 [,..., en]) true if and only if all expressions are true; at least two expressions required. This uses short-circuit semantics (does not evaluate remaining ei, ..., e
or(e0, e1, [... en]) true if and only if one or more expressions are true; at least two expressions required. or uses short-circuit semantics (does not evaluate remaining expressions if any ei is true).
not(e) true if and only if expression e is false
isNull(e) true if and only if e is null or unset

Relational functions

Relational functions compare two or more expressions. Most functions accept two or more arguments, in which case the relation must hold for consecutive values; i.e. lt(a,b,c,d) is equivalent to and(lt(a,b),lt(b,c),lt(c,d)) (that is, a < b && b < c && c < d.


This syntax cannot express combinations of relational operators, such as minInclusive ≤ x < maxExclusive; one must use and(le(minInclusive,x),lt(x,maxExclusive)).


Expression Rule
eq(e0, e1 [,..., en]) true if and only if all expressions are equal.
ne(e0, e1 true if and only if the e0 is not equal to e1
lt(e0, e1 [,..., en]) true if and only if each expression ei is less than ei+1
le(e0, e1 [,..., en]) true if and only if each expression ei is less than or equal to ei+1
gt(e0, e1 [,..., en]) true if and only if each expression ei is greater than ei+1
ge(e0, e1 [,..., en]) true if and only if each expression ei is greater than ei+1
in(e, v0, v1 [,..., vn]) Set containment, true if and only if the value e is equal to at least one of the vi values.

Relational functions may be combined with date, time, and date-time literals. To find all reports created between January and March 2017 (using GMT).


GET /reports/reports?filter=le(2017-01-01T00:00:00Z,creationTimeStamp,2017-03-31T24:00:00Z)

String functions

Functions which manipulate string values


Expression Rule
match(e,regularExpression) true if and only if the string e matches a regular expression. The regularExpression is a quoted string literal or other expression that yields a string pattern.
matchAll(regularExpression, exp1,...,expn) true if all expressions match the regular expression. Uses short-circuit semantics (do not evaluate remaining expressions if any preceding expression does not match).
matchAny(regularExpression, exp1,...,expn) true if and only if the member map is a Map or Properties object which contains an item (key,value) such that and(match(key,name),match(value,regularExpression) is true
match(map,name,regularExpression) true if and only if each expression ei is less than or equal to ei+1
contains(e,substring) true if and only if the string e contains the substring, or if e is a list/set/collection, true if and only if an element of e equals substring
startsWith(e,prefix) true if and only if the string e starts with prefix
endsWith(e,suffix) true if and only if the string e ends with the suffix
blank(e) true if and only if the string a consists of only whitespace
length(e) the integer length of the string expression e
substr(e,start,len) the substring of e, starting at start (zero-based index?) and including len chars. Negative index start counts backwards from the end. If len is omitted, the substring will extend to the end of the string.
upCase(s) | convert all character in s to upper case.
downCase(s) convert all character in s to lower case.

Locale-sensitive collation

Several of the above functions require locale-sensitive collation, where the locale is derived by the Accept-Language header passed by the client.


Locale-sensitive collation strength is passed to these functions using one of several reserved identifiers, as optional first arguments. The identifier name is obtained by prefixing the collation options below with a dollar sign ('$'):


Locale-sensitive collation Description
$primary normal case-insensitive comparison
$secondary only different accent characters are significant
$tertiary normal case-sensitive comparison
$quaternary punctuation is considered
$identical case, accents, and punctuation differences are significant

If the first parameter is not one of these special identifiers, the default is $identical. These reserved identifiers are ignored when comparing numeric and boolean values.


Examples of locale-sensitive functions, using $tertiary as an example:


Expression
eq($tertiary, a, b,...) (equal)
ne($tertiary, a, b) (not equal)
lt($tertiary, a, b,...) (less than)
le($tertiary, a, b,...) (less than or equal)
gt($tertiary, a, b,...) (greater than)
ge($tertiary, a, b,...) (greater than or equal)
in($tertiary, e, v0, v1 [,..., vn])
contains($tertiary, e, substring)
startsWith($tertiary, e, prefix)
endsWith($tertiary, e, suffix)

Combining basic filtering and the filter query parameter

An API request may combine basic filtering and the filter query parameter. When both are used, the request combines them by adding an implicit and function to the existing filter query parameter. For example, for


GET .../resource?name=dale&format=ruled&filter=or(bounded,lt(shipDate,2017-07-27))


The basic filters (name=dale and format=ruled) will be merged into the filter expression, or(bounded,lt(shipDate,2017-07-27)), yielding the effective filter query


GET .../resource?filter=and(eq(name,'dale'),eq(format,'ruled'),or(bounded,lt(shipDate,2017-07-27)))


Note that the resulting filter expression is always false if the basic filter and the filter specify mutually exclusive values:


GET .../resource?name=dale&format=ruled&filter=eq(name,'jack')


The effective filter query:


GET .../resource?filter=and(eq(name,'dale'),eq(format,'ruled'),eq(name,'robert')


This is always false (resulting in an empty collection result) because name cannot be 'dale' *and* 'robert' at the same time.


Conditional Operations

SAS Viya REST APIs may use conditional operations as defined with HTTP/1.1. Conditional operations support many concurrent reads of resources, and accepts updates to those resources if request satisfies a precondition. Preconditions are expressed with request headers to ensure that the update request (such as a PUT or PATCH and perhaps a POST) is based on the current state of the resource being updated.


Note that not all SAS Viya APIs support preconditions. SAS APIs use preconditions in operations where concurrent updates are likely. These SAS APIs use either entity tags or last-modified timestamps to satisfy update preconditions.

Entity Tags

An entity tag is an opaque string that marks (or "tags") the current state of a resource. The entity tag changes each time the resource is updated and are returned in the ETag response headers from supporting operations.


SAS APIs that use entity tags must pass the If-Match request header using the value of the entity tag received from the latest operation on the resource. This acts as a precondition: the update operation fails if the value of the If-Match header on the request does not match the current entity tag of the resource. A precondition failure means that a client has stale data and the resource has been updated since the current client last obtained the entity tag. Clients can simply make a GET request to the resource to obtain a new entity tag, then retry the operation if appropriate.

Last-Modified Timestamps

APIs that use a Last-Modified timestamp return a timestamp that changes each time a resource is updated. Preconditions based on "last-modified" return a Last-Modified response header. Resources expect a If-Unmodified-Since precondition request header with the timestamp value based on the previous Last-Modified timestamp. Timestamps use the format EEE, dd MMM yyyy HH:mm:ss GMT, such as Thu, 13 Sep 2016 07:27:08 GMT.

Precondition Status

SAS Viya APIs return standard precondition HTTP status codes:


Code Status Description
412 Precondition Failed Returned if an API operation specifies preconditions and the precondition is not satisfied
428 Precondition Required Returned if an operation requires preconditions but the client does not pass the required preconditions request headers

Precondition failures occur when a client has stale data and the resource has been updated since the current client last obtained the entity tag. Clients can simply make a GET request to the resource to obtain a new entity tag, then retry the operation if appropriate.


Note that if an operation accepts and is passed both If-Match and If-Unmodified-Since preconditions, only the If-Match precondition is checked and If-Unmodified-Since is ignored.