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 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.
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 3600Tokens 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_token1.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))