curl --request POST \
--url https://iam.basaltic.sh/v1/assume-role \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"role": "crn:iam::production:role/reports",
"duration_seconds": 3600,
"policy": {
"version": "2024-01-01",
"statements": [
{
"effect": "allow",
"actions": [
"storage:GetObject"
],
"resources": [
"crn:storage:sa-saopaulo-1::bucket/reports/*"
]
}
]
}
}
'import requests
url = "https://iam.basaltic.sh/v1/assume-role"
payload = {
"role": "crn:iam::production:role/reports",
"duration_seconds": 3600,
"policy": {
"version": "2024-01-01",
"statements": [
{
"effect": "allow",
"actions": ["storage:GetObject"],
"resources": ["crn:storage:sa-saopaulo-1::bucket/reports/*"]
}
]
}
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
role: 'crn:iam::production:role/reports',
duration_seconds: 3600,
policy: {
version: '2024-01-01',
statements: [
{
effect: 'allow',
actions: ['storage:GetObject'],
resources: ['crn:storage:sa-saopaulo-1::bucket/reports/*']
}
]
}
})
};
fetch('https://iam.basaltic.sh/v1/assume-role', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://iam.basaltic.sh/v1/assume-role",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'role' => 'crn:iam::production:role/reports',
'duration_seconds' => 3600,
'policy' => [
'version' => '2024-01-01',
'statements' => [
[
'effect' => 'allow',
'actions' => [
'storage:GetObject'
],
'resources' => [
'crn:storage:sa-saopaulo-1::bucket/reports/*'
]
]
]
]
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://iam.basaltic.sh/v1/assume-role"
payload := strings.NewReader("{\n \"role\": \"crn:iam::production:role/reports\",\n \"duration_seconds\": 3600,\n \"policy\": {\n \"version\": \"2024-01-01\",\n \"statements\": [\n {\n \"effect\": \"allow\",\n \"actions\": [\n \"storage:GetObject\"\n ],\n \"resources\": [\n \"crn:storage:sa-saopaulo-1::bucket/reports/*\"\n ]\n }\n ]\n }\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://iam.basaltic.sh/v1/assume-role")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"role\": \"crn:iam::production:role/reports\",\n \"duration_seconds\": 3600,\n \"policy\": {\n \"version\": \"2024-01-01\",\n \"statements\": [\n {\n \"effect\": \"allow\",\n \"actions\": [\n \"storage:GetObject\"\n ],\n \"resources\": [\n \"crn:storage:sa-saopaulo-1::bucket/reports/*\"\n ]\n }\n ]\n }\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://iam.basaltic.sh/v1/assume-role")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"role\": \"crn:iam::production:role/reports\",\n \"duration_seconds\": 3600,\n \"policy\": {\n \"version\": \"2024-01-01\",\n \"statements\": [\n {\n \"effect\": \"allow\",\n \"actions\": [\n \"storage:GetObject\"\n ],\n \"resources\": [\n \"crn:storage:sa-saopaulo-1::bucket/reports/*\"\n ]\n }\n ]\n }\n}"
response = http.request(request)
puts response.read_body{
"access_token": "eyJhbGciOiJFUzI1NiIsInR5cCI6ImF0K2p3dCIsImtpZCI6Ii4uLiJ9...",
"token_type": "Bearer",
"expires_in": 3600,
"access_key_id": "AKIA...",
"secret_access_key": "wJalrXUtnFEMI...",
"session_token": "FwoGZXIvYXdzE...",
"expiration": "2026-01-15T09:30:00Z",
"account_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"account_handle": "<string>",
"role_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a"
}{
"error": {
"code": "INVALID_INPUT",
"message": "Invalid request parameters",
"request_id": "550e8400-e29b-41d4-a716-446655440000"
}
}{
"error": {
"code": "UNAUTHORIZED",
"message": "Authentication required",
"request_id": "550e8400-e29b-41d4-a716-446655440000"
}
}{
"error": {
"code": "INVALID_INPUT",
"message": "Invalid request parameters",
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"params": {
"instances": 2,
"pools": 0
}
}
}{
"error": {
"code": "NOT_FOUND",
"message": "Resource not found",
"request_id": "550e8400-e29b-41d4-a716-446655440000"
}
}{
"error": {
"code": "INTERNAL_ERROR",
"message": "An internal error occurred",
"request_id": "550e8400-e29b-41d4-a716-446655440000"
}
}{
"error": {
"code": "SERVICE_UNAVAILABLE",
"message": "The service is temporarily unavailable. Please try again later.",
"request_id": "550e8400-e29b-41d4-a716-446655440000"
}
}Assume role
Assume a role in this account or another account in the same organization. The caller needs iam:AssumeRole permission (or an effective Workspace account-role assignment), and the target role must independently trust the caller.
The session account comes from the target role, not the selected-account header. The session uses the target role permissions and delegated organization policies, subject to applicable boundaries, explicit denies, and an optional narrowing session policy. Source principal permissions are not unioned into the session.
curl --request POST \
--url https://iam.basaltic.sh/v1/assume-role \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"role": "crn:iam::production:role/reports",
"duration_seconds": 3600,
"policy": {
"version": "2024-01-01",
"statements": [
{
"effect": "allow",
"actions": [
"storage:GetObject"
],
"resources": [
"crn:storage:sa-saopaulo-1::bucket/reports/*"
]
}
]
}
}
'import requests
url = "https://iam.basaltic.sh/v1/assume-role"
payload = {
"role": "crn:iam::production:role/reports",
"duration_seconds": 3600,
"policy": {
"version": "2024-01-01",
"statements": [
{
"effect": "allow",
"actions": ["storage:GetObject"],
"resources": ["crn:storage:sa-saopaulo-1::bucket/reports/*"]
}
]
}
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
role: 'crn:iam::production:role/reports',
duration_seconds: 3600,
policy: {
version: '2024-01-01',
statements: [
{
effect: 'allow',
actions: ['storage:GetObject'],
resources: ['crn:storage:sa-saopaulo-1::bucket/reports/*']
}
]
}
})
};
fetch('https://iam.basaltic.sh/v1/assume-role', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://iam.basaltic.sh/v1/assume-role",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'role' => 'crn:iam::production:role/reports',
'duration_seconds' => 3600,
'policy' => [
'version' => '2024-01-01',
'statements' => [
[
'effect' => 'allow',
'actions' => [
'storage:GetObject'
],
'resources' => [
'crn:storage:sa-saopaulo-1::bucket/reports/*'
]
]
]
]
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://iam.basaltic.sh/v1/assume-role"
payload := strings.NewReader("{\n \"role\": \"crn:iam::production:role/reports\",\n \"duration_seconds\": 3600,\n \"policy\": {\n \"version\": \"2024-01-01\",\n \"statements\": [\n {\n \"effect\": \"allow\",\n \"actions\": [\n \"storage:GetObject\"\n ],\n \"resources\": [\n \"crn:storage:sa-saopaulo-1::bucket/reports/*\"\n ]\n }\n ]\n }\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://iam.basaltic.sh/v1/assume-role")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"role\": \"crn:iam::production:role/reports\",\n \"duration_seconds\": 3600,\n \"policy\": {\n \"version\": \"2024-01-01\",\n \"statements\": [\n {\n \"effect\": \"allow\",\n \"actions\": [\n \"storage:GetObject\"\n ],\n \"resources\": [\n \"crn:storage:sa-saopaulo-1::bucket/reports/*\"\n ]\n }\n ]\n }\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://iam.basaltic.sh/v1/assume-role")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"role\": \"crn:iam::production:role/reports\",\n \"duration_seconds\": 3600,\n \"policy\": {\n \"version\": \"2024-01-01\",\n \"statements\": [\n {\n \"effect\": \"allow\",\n \"actions\": [\n \"storage:GetObject\"\n ],\n \"resources\": [\n \"crn:storage:sa-saopaulo-1::bucket/reports/*\"\n ]\n }\n ]\n }\n}"
response = http.request(request)
puts response.read_body{
"access_token": "eyJhbGciOiJFUzI1NiIsInR5cCI6ImF0K2p3dCIsImtpZCI6Ii4uLiJ9...",
"token_type": "Bearer",
"expires_in": 3600,
"access_key_id": "AKIA...",
"secret_access_key": "wJalrXUtnFEMI...",
"session_token": "FwoGZXIvYXdzE...",
"expiration": "2026-01-15T09:30:00Z",
"account_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"account_handle": "<string>",
"role_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a"
}{
"error": {
"code": "INVALID_INPUT",
"message": "Invalid request parameters",
"request_id": "550e8400-e29b-41d4-a716-446655440000"
}
}{
"error": {
"code": "UNAUTHORIZED",
"message": "Authentication required",
"request_id": "550e8400-e29b-41d4-a716-446655440000"
}
}{
"error": {
"code": "INVALID_INPUT",
"message": "Invalid request parameters",
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"params": {
"instances": 2,
"pools": 0
}
}
}{
"error": {
"code": "NOT_FOUND",
"message": "Resource not found",
"request_id": "550e8400-e29b-41d4-a716-446655440000"
}
}{
"error": {
"code": "INTERNAL_ERROR",
"message": "An internal error occurred",
"request_id": "550e8400-e29b-41d4-a716-446655440000"
}
}{
"error": {
"code": "SERVICE_UNAVAILABLE",
"message": "The service is temporarily unavailable. Please try again later.",
"request_id": "550e8400-e29b-41d4-a716-446655440000"
}
}iam:AssumeRole. See IAM permissions for the full list, what each one covers, and an example policy.Authorizations
An OAuth 2.0 bearer token, sent as Authorization: Bearer <token>.
This is the recommended way to authenticate.
Get one by exchanging a service account's access key pair at
POST /v1/oauth/token with grant_type=client_credentials. It is the
standard client-credentials grant, so any OAuth-aware library will
obtain and refresh it for you.
curl -s -u "$KEY_ID:$SECRET" -d grant_type=client_credentials \ https://iam.basaltic.sh/v1/oauth/token
Tokens last an hour by default. The same access key pair is separately your AWS SigV4 credential for the S3-compatible object endpoint, which speaks nothing else.
Body
Account role UUID, immutable name in the selected account, or crn:iam:::role/. AssumeRole may use a qualified CRN to target another account in the same organization. The resulting session is bound to the target role account.
"crn:iam::production:role/reports"
Credential validity duration (15 min to 12 hours)
900 <= x <= 432003600
An inline policy that scopes down the credentials being minted. It grants nothing on its own: every request made with the resulting credentials must be allowed by the assumed role's effective policies and by this document, so it can only narrow the permissions of the assumed role.
The document is validated on the way in and stored with the session — an
invalid one fails the call with INVALID_INPUT rather than being ignored.
Statements take the same shape as in a managed policy but carry no
conditions; a session policy fences on actions and resources only.
Show child attributes
Show child attributes
{ "version": "2024-01-01", "statements": [ { "effect": "allow", "actions": ["storage:GetObject"], "resources": [ "crn:storage:sa-saopaulo-1::bucket/reports/*" ] } ] }
Response
Role assumed successfully
A role session's credentials, in both forms it can be presented.
access_token is a bearer token for this API — send it as
Authorization: Bearer <token>. The other four fields are AWS SigV4
credentials for the S3-compatible object storage endpoint, which speaks
nothing else.
Both come from the same session and share its expiry, so revoking the session stops both at once. Use whichever the endpoint you are calling needs; there is no need to choose one at request time.
Bearer token for the Basaltic API. Present on every role session.
"eyJhbGciOiJFUzI1NiIsInR5cCI6ImF0K2p3dCIsImtpZCI6Ii4uLiJ9..."
Always Bearer when access_token is present.
"Bearer"
Seconds until access_token expires.
3600
SigV4 access key id, for the S3 endpoint.
"AKIA..."
SigV4 secret, for the S3 endpoint.
"wJalrXUtnFEMI..."
SigV4 session token, for the S3 endpoint. Send as
X-Amz-Security-Token and include it in SignedHeaders.
"FwoGZXIvYXdzE..."
When the session — and therefore both credential forms — expires.
"2026-01-15T09:30:00Z"
Owning account UUID of the target role.
Owning account handle of the target role.
Immutable UUID of the assumed role.

