1  Authentication

Before AutoDeskR can talk to any APS endpoint, it needs a token — a temporary credential that proves your app is who it says it is. This chapter covers how to get one, keep it safe, and deal with the inevitable moment when it expires.

APS uses 2-legged OAuth 2.0 (client credentials flow): your app sends its Client ID and Secret, and AutoDesk hands back an access token. No user login involved, perfect for server-side scripts and automated pipelines.

sequenceDiagram
  participant R as Your R Script
  participant APS as AutoDesk APS
  R->>APS: POST /authentication/v2/token<br/>(client_id + client_secret + scope)
  APS-->>R: access_token (expires in 3600 s)
  R->>APS: GET /oss/v2/buckets (Authorization: Bearer token)
  APS-->>R: 200 OK + response data

1.1 Store Credentials Safely

Your Client ID and Secret are like a username and password for your app. Keep them out of your source files and version control. The cleanest approach in R is to stash them in ~/.Renviron:

client_id = "YOUR_CLIENT_ID"
client_secret = "YOUR_CLIENT_SECRET"
token = "YOUR_ACCESS_TOKEN"
urn = "YOUR_URN"

Reload the file without restarting R:

readRenviron("~/.Renviron")

Then pull values at runtime with Sys.getenv() — credentials never appear in your code. See Wrapping APIs in the httr2 docs for more on this pattern.

1.2 Get an Access Token

One function call does it all:

resp    <- getToken(id = Sys.getenv("client_id"), secret = Sys.getenv("client_secret"))
myToken <- resp$content$access_token
myToken
#> [1] "eyJhbGciOiJSUzI1NiIsImtpZCI6IlU3c1BKNVVpOWNaVjlIR2FhX1pIeDhEd3VYSHcifQ.
#>       eyJjbGllbnRfaWQiOiJBQkNERUYxMjM0NTYiLCJleHAiOjE3NDU0MDA0MDAsInNjb3BlIjpb
#>       ImRhdGE6cmVhZCJdfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c"

The full response also tells you what you’re working with:

str(resp$content)
#> List of 3
#>  $ access_token: chr "eyJhbGciOiJSUzI1NiIsImtpZCI6IlU3c1BKNVVpOWNaVjlIR..."
#>  $ token_type  : chr "Bearer"
#>  $ expires_in  : int 3600
Warning

Tokens expire after 1 hour. A 401 Unauthorized mid-session almost always means your token timed out — just call getToken() again.

resp$status_code
#> [1] 401
resp$content$errorCode
#> [1] "AUTH-001"
resp$content$developerMessage
#> [1] "The provided access token has expired."

1.3 Scopes — Only Ask for What You Need

The scope parameter controls which APIs the token can access. It’s good practice to request the minimum scopes your task actually requires:

Scope What it unlocks
data:read Read files and derivatives
data:write Upload and modify files
data:create Create new files
data:search Search within files
bucket:create Create storage buckets
bucket:read View bucket details
bucket:update Modify bucket settings
bucket:delete Delete buckets
code:all Design Automation APIs
account:read Read account information
account:write Modify account settings
user-profile:read Read user profile data

Pass multiple scopes as a space-separated string:

resp <- getToken(
  id     = Sys.getenv("client_id"),
  secret = Sys.getenv("client_secret"),
  scope  = "bucket:create bucket:read data:write"
)
myToken <- resp$content$access_token

1.4 Rate Limits

APS enforces per-application rate limits (typically 60–300 requests per minute depending on the endpoint). Hit the ceiling and you get HTTP 429:

resp$status_code
#> [1] 429
resp$content$errorCode
#> [1] "TOO-MANY-REQUESTS"
resp$content$developerMessage
#> [1] "Rate limit exceeded. Retry after 60 seconds."

This simple retry helper wraps any function call that might get rate-limited:

aps_retry <- function(fn, max_tries = 3, wait = 10) {
  for (i in seq_len(max_tries)) {
    resp <- fn()
    if (!identical(resp$status_code, 429L)) return(resp)
    message("Rate limited — waiting ", wait, "s (attempt ", i, "/", max_tries, ")")
    Sys.sleep(wait)
  }
  stop("Max retries exceeded after ", max_tries, " attempts.")
}

# Example: poll translation status with automatic backoff
resp <- aps_retry(function() checkFile(urn = myEncodedUrn, token = myToken))