curl --request GET \
--url https://audit.basaltic.sh/v1/audit-logs \
--header 'Authorization: Bearer <token>'import requests
url = "https://audit.basaltic.sh/v1/audit-logs"
headers = {"Authorization": "Bearer <token>"}
response = requests.get(url, headers=headers)
print(response.text)const options = {method: 'GET', headers: {Authorization: 'Bearer <token>'}};
fetch('https://audit.basaltic.sh/v1/audit-logs', 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://audit.basaltic.sh/v1/audit-logs",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://audit.basaltic.sh/v1/audit-logs"
req, _ := http.NewRequest("GET", url, nil)
req.Header.Add("Authorization", "Bearer <token>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.get("https://audit.basaltic.sh/v1/audit-logs")
.header("Authorization", "Bearer <token>")
.asString();require 'uri'
require 'net/http'
url = URI("https://audit.basaltic.sh/v1/audit-logs")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Get.new(url)
request["Authorization"] = 'Bearer <token>'
response = http.request(request)
puts response.read_body{
"audit_logs": [
{
"crn": "crn:audit:::log/550e8400-e29b-41d4-a716-446655440000",
"id": "e5f6a7b8-c9d0-1234-5678-90abcdef1234",
"timestamp": "2026-01-15T09:30:00Z",
"actor_crn": "crn:workspace:::user/jane",
"resource_crn": "crn:workspace:::organization/550e8400-e29b-41d4-a716-446655440000",
"actor_name": "John Doe",
"actor_email": "john.doe@acme.com",
"action": "iam.policy.create",
"status": "success",
"resource_name": "web-server-1",
"ip_address": "192.168.1.100",
"user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36",
"request_id": "req-7c9e6679742540de",
"details": {
"auth_chain": [
{
"principal_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"principal_type": "<string>",
"principal_crn": "<string>",
"name": "<string>",
"issuer": "<string>",
"subject": "<string>",
"email": "<string>",
"session_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"session_crn": "<string>",
"session_name": "<string>",
"grant_type": "<string>",
"issued_at": "2023-11-07T05:31:56Z"
}
]
},
"error_code": "access_denied",
"error_message": "Principal is not authorized to perform this action"
}
],
"meta": {
"total": 150,
"limit": 20,
"marker": "550e8400-e29b-41d4-a716-446655440000",
"has_more": true
}
}{
"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": "ACCESS_DENIED",
"message": "You don't have permission to perform this action",
"request_id": "550e8400-e29b-41d4-a716-446655440000"
}
}List audit logs
List audit logs for the current organization with filtering and pagination.
Requires audit:ListLogs permission.
curl --request GET \
--url https://audit.basaltic.sh/v1/audit-logs \
--header 'Authorization: Bearer <token>'import requests
url = "https://audit.basaltic.sh/v1/audit-logs"
headers = {"Authorization": "Bearer <token>"}
response = requests.get(url, headers=headers)
print(response.text)const options = {method: 'GET', headers: {Authorization: 'Bearer <token>'}};
fetch('https://audit.basaltic.sh/v1/audit-logs', 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://audit.basaltic.sh/v1/audit-logs",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://audit.basaltic.sh/v1/audit-logs"
req, _ := http.NewRequest("GET", url, nil)
req.Header.Add("Authorization", "Bearer <token>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.get("https://audit.basaltic.sh/v1/audit-logs")
.header("Authorization", "Bearer <token>")
.asString();require 'uri'
require 'net/http'
url = URI("https://audit.basaltic.sh/v1/audit-logs")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Get.new(url)
request["Authorization"] = 'Bearer <token>'
response = http.request(request)
puts response.read_body{
"audit_logs": [
{
"crn": "crn:audit:::log/550e8400-e29b-41d4-a716-446655440000",
"id": "e5f6a7b8-c9d0-1234-5678-90abcdef1234",
"timestamp": "2026-01-15T09:30:00Z",
"actor_crn": "crn:workspace:::user/jane",
"resource_crn": "crn:workspace:::organization/550e8400-e29b-41d4-a716-446655440000",
"actor_name": "John Doe",
"actor_email": "john.doe@acme.com",
"action": "iam.policy.create",
"status": "success",
"resource_name": "web-server-1",
"ip_address": "192.168.1.100",
"user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36",
"request_id": "req-7c9e6679742540de",
"details": {
"auth_chain": [
{
"principal_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"principal_type": "<string>",
"principal_crn": "<string>",
"name": "<string>",
"issuer": "<string>",
"subject": "<string>",
"email": "<string>",
"session_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"session_crn": "<string>",
"session_name": "<string>",
"grant_type": "<string>",
"issued_at": "2023-11-07T05:31:56Z"
}
]
},
"error_code": "access_denied",
"error_message": "Principal is not authorized to perform this action"
}
],
"meta": {
"total": 150,
"limit": 20,
"marker": "550e8400-e29b-41d4-a716-446655440000",
"has_more": true
}
}{
"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": "ACCESS_DENIED",
"message": "You don't have permission to perform this action",
"request_id": "550e8400-e29b-41d4-a716-446655440000"
}
}audit:ListLogs. See AUDIT 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.
Query Parameters
Exact audit event CRN (crn:audit:::log/UUID). Foreign or mismatched identities yield an empty page. This selects the event itself, independently of resource and actor snapshots.
Filter by a canonical UUID or an exact event-time actor CRN, including Workspace users, account IAM identities, and retained historical CRNs. Matches identities stored with events in the current organization, including deleted actors. Bare names and wildcard CRNs are unsupported. Events without a stored CRN remain searchable by UUID.
"550e8400-e29b-41d4-a716-446655440000"
Filter by actor type
user, service_account, system "user"
Filter by action (exact match or prefix with wildcard, e.g., "iam.*")
"iam.policy.create"
Filter by resource type
"instance"
Filter by a canonical UUID or an event-time resource CRN, without
looking up a live resource. A CRN matches exactly and also matches
descendants at a literal / boundary: crn:network:region:account:vpc/prod
matches stored crn:network:region:account:vpc/prod/subnet/private.
Account and region are matched exactly within the current organization.
Percent and underscore characters are literal, not wildcards.
Bare names and wildcard CRNs are unsupported. Events without a stored
CRN remain searchable by UUID. The resource_type filter applies
independently, including to descendant events.
"crn:network:sa-saopaulo-1:acme:vpc/prod"
Filter by status
success, failure, denied "success"
Filter by IP address
"192.168.1.100"
Filter logs from this timestamp (inclusive)
"2026-01-01T00:00:00Z"
Filter logs until this timestamp (exclusive)
"2026-02-01T00:00:00Z"
Maximum number of items to return. A value above the maximum is clamped to it rather than rejected, so a page shorter than the one you asked for is normal — page until meta.has_more is false, not until a page looks short.
1 <= x <= 100Opaque pagination cursor. Echo back the meta.marker value from the previous page to fetch the next one; do not construct or parse it. The token's internal form varies by endpoint (a resource ID, a timestamp, …) and is not guaranteed stable across releases.

