REST API Quickstart
The AutoCISO REST API lets you push software composition data into your compliance workspace from CI/CD pipelines and internal tools. This guide walks you through your first authenticated request.
Before you start
- An API token — see API tokens
- The token needs
sbom:readto read andsbom:writeto upload; Example 3 needsjml:read - Your plan must include Supply Chain Security, The Growth plan or higher (Examples 1 and 2), or Employee lifecycle, The Seed plan or higher (Example 3) — see Plans & features
Set your token as an environment variable so you don’t hard-code credentials:
export AUTOCISO_API_TOKEN="aci_your_token_here"
Base URL
All REST endpoints are under:
https://api.autociso.io/api/v1
Use api.autociso.io for every integration. It carries only the API, so it is
unaffected by changes to the marketing site or the web console.
The apex host autociso.io routes to the same backend and stays supported, so
older integrations keep working unchanged. Every example on this site uses
api.autociso.io.
Authentication
Every request must include your token in the Authorization header:
Authorization: Bearer aci_your_token_here
Example 1 — List your SBOMs
A simple GET that returns the Software Bills of Materials registered in your organisation — the same list as
SBOM Inventory in the console. Pass q to filter by name. Endpoint reference:
List SBOMs.
curl -s \
-H "Authorization: Bearer $AUTOCISO_API_TOKEN" \
"https://api.autociso.io/api/v1/sscs/sboms"const API_BASE = 'https://api.autociso.io/api/v1';
const token = process.env.AUTOCISO_API_TOKEN!;
const res = await fetch(`${API_BASE}/sscs/sboms`, {
headers: { Authorization: `Bearer ${token}` },
});
if (!res.ok) {
throw new Error(`API error ${res.status}: ${await res.text()}`);
}
const { data } = await res.json();
console.log(`Found ${data.length} SBOMs:`, data);package main
import (
"encoding/json"
"fmt"
"net/http"
"os"
)
func main() {
token := os.Getenv("AUTOCISO_API_TOKEN")
apiURL := "https://api.autociso.io/api/v1/sscs/sboms"
req, _ := http.NewRequest(http.MethodGet, apiURL, nil)
req.Header.Set("Authorization", "Bearer "+token)
resp, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer resp.Body.Close()
var result map[string]any
json.NewDecoder(resp.Body).Decode(&result)
fmt.Printf("%+v\n", result)
}import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
public class ListSboms {
private static final String API_BASE = "https://api.autociso.io/api/v1";
private static final String TOKEN = System.getenv("AUTOCISO_API_TOKEN");
public static void main(String[] args) throws Exception {
var client = HttpClient.newHttpClient();
var request = HttpRequest.newBuilder()
.uri(URI.create(API_BASE + "/sscs/sboms"))
.header("Authorization", "Bearer " + TOKEN)
.GET()
.build();
var response = client.send(request, HttpResponse.BodyHandlers.ofString());
if (response.statusCode() != 200) {
throw new RuntimeException("API error " + response.statusCode()
+ ": " + response.body());
}
System.out.println(response.body());
}
}A successful response looks like:
{
"data": [
{
"sbomId": "sbom_1hgyun41rx48",
"name": "payment-service",
"version": "1.4.2",
"source": "package.json",
"componentCount": 412,
"createdAt": "2026-03-01T09:00:00Z"
}
],
"error": null,
"meta": null
}
Example 2 — Upload a manifest
Upload a dependency manifest so AutoCISO can inventory its components and match them against known vulnerabilities. Requires the sbom:write scope. Endpoint reference:
Upload SBOM.
curl -s -X POST \
-H "Authorization: Bearer $AUTOCISO_API_TOKEN" \
-F "file=@package.json" \
-F "name=payment-service" \
-F "version=1.4.2" \
"https://api.autociso.io/api/v1/sscs/sboms/upload"import { readFileSync } from 'node:fs';
const API_BASE = 'https://api.autociso.io/api/v1';
const token = process.env.AUTOCISO_API_TOKEN!;
const form = new FormData();
form.append(
'file',
new Blob([readFileSync('package.json')], { type: 'application/json' }),
'package.json',
);
form.append('name', 'payment-service');
form.append('version', '1.4.2');
const res = await fetch(`${API_BASE}/sscs/sboms/upload`, {
method: 'POST',
headers: { Authorization: `Bearer ${token}` },
body: form,
});
const { data, error } = await res.json();
if (error) throw new Error(error.message);
console.log('Uploaded SBOM:', data.id);package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"mime/multipart"
"net/http"
"os"
"path/filepath"
)
func main() {
token := os.Getenv("AUTOCISO_API_TOKEN")
uploadSBOM(token, "package.json", "payment-service", "1.4.2")
}
func uploadSBOM(token, filePath, name, version string) {
f, err := os.Open(filePath)
if err != nil {
panic(err)
}
defer f.Close()
var body bytes.Buffer
w := multipart.NewWriter(&body)
fw, _ := w.CreateFormFile("file", filepath.Base(filePath))
io.Copy(fw, f)
w.WriteField("name", name)
w.WriteField("version", version)
w.Close()
req, _ := http.NewRequest(http.MethodPost,
"https://api.autociso.io/api/v1/sscs/sboms/upload", &body)
req.Header.Set("Authorization", "Bearer "+token)
req.Header.Set("Content-Type", w.FormDataContentType())
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
var result map[string]any
json.NewDecoder(resp.Body).Decode(&result)
fmt.Printf("%+v\n", result)
}import java.io.IOException;
import java.net.URI;
import java.net.http.*;
import java.nio.file.*;
public class UploadSBOM {
private static final String API_BASE = "https://api.autociso.io/api/v1";
private static final String TOKEN = System.getenv("AUTOCISO_API_TOKEN");
public static void main(String[] args) throws Exception {
uploadSBOM(
Path.of("package.json"),
"payment-service",
"1.4.2"
);
}
static void uploadSBOM(Path sbomFile, String name, String version)
throws IOException, InterruptedException {
var boundary = "----AutoCISOBoundary" + System.currentTimeMillis();
var fileBytes = Files.readAllBytes(sbomFile);
// Build multipart/form-data body manually
var body = new StringBuilder();
body.append("--").append(boundary).append("\r\n");
body.append("Content-Disposition: form-data; name=\"name\"\r\n\r\n");
body.append(name).append("\r\n");
body.append("--").append(boundary).append("\r\n");
body.append("Content-Disposition: form-data; name=\"version\"\r\n\r\n");
body.append(version).append("\r\n");
body.append("--").append(boundary).append("\r\n");
body.append("Content-Disposition: form-data; name=\"file\"; filename=\"")
.append(sbomFile.getFileName()).append("\"\r\n");
body.append("Content-Type: application/json\r\n\r\n");
var prefix = body.toString().getBytes();
var suffix = ("\r\n--" + boundary + "--\r\n").getBytes();
var full = new byte[prefix.length + fileBytes.length + suffix.length];
System.arraycopy(prefix, 0, full, 0, prefix.length);
System.arraycopy(fileBytes, 0, full, prefix.length, fileBytes.length);
System.arraycopy(suffix, 0, full, prefix.length + fileBytes.length, suffix.length);
var request = HttpRequest.newBuilder()
.uri(URI.create(API_BASE + "/sscs/sboms/upload"))
.header("Authorization", "Bearer " + TOKEN)
.header("Content-Type", "multipart/form-data; boundary=" + boundary)
.POST(HttpRequest.BodyPublishers.ofByteArray(full))
.build();
var response = HttpClient.newHttpClient()
.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println(response.body());
}
}Example 3 — List leaver items that still need attention
Read the leaver items whose access is still outstanding or contradicts a completed task. Requires the jml:read scope and a plan that includes Employee lifecycle. The response is pseudonymised personal data: it carries employee IDs, not names, unless the token also holds pii:read. Call it from a server, never from a browser.
curl -s -G \
-H "Authorization: Bearer $AUTOCISO_API_TOKEN" \
--data-urlencode "filter[status][in]=OUTSTANDING,DISCREPANCY" \
--data-urlencode "limit=100" \
"https://api.autociso.io/api/v1/jml/offboarding/items"const API_BASE = 'https://api.autociso.io/api/v1';
const token = process.env.AUTOCISO_API_TOKEN!;
const url = new URL(`${API_BASE}/jml/offboarding/items`);
url.searchParams.set('filter[status][in]', 'OUTSTANDING,DISCREPANCY');
url.searchParams.set('limit', '100');
let res = await fetch(url, { headers: { Authorization: `Bearer ${token}` } });
if (res.status === 429) {
// Wait as long as the server says, then try once more.
const wait = Number(res.headers.get('Retry-After') ?? '1');
await new Promise(r => setTimeout(r, wait * 1000));
res = await fetch(url, { headers: { Authorization: `Bearer ${token}` } });
}
if (!res.ok) throw new Error(`API error ${res.status}: ${await res.text()}`);
const { data, meta } = await res.json();
console.log(`${meta.total} items need attention (showing ${data.length})`);A successful response looks like this. itemKey is the stable key to de-duplicate on between runs:
{
"data": [
{
"itemId": "ltsk_example000001",
"itemKey": "lfc_example000001:ast_example000001",
"taskId": "ltsk_example000001",
"caseId": "lfc_example000001",
"employeeId": "empl_example000001",
"assetId": "ast_example000001",
"assetName": "Example CRM",
"status": "DISCREPANCY",
"taskStatus": "COMPLETED",
"accessStatus": "ACTIVE",
"source": "TASK",
"openedAt": "2026-09-15T08:00:00Z",
"taskCompletedAt": "2026-09-16T10:30:00Z",
"accessLastDetectedAt": "2026-09-29T02:00:00Z"
}
],
"error": null,
"meta": {
"limit": 100,
"offset": 0,
"total": 1,
"summary": { "total": 1, "revoked": 0, "outstanding": 0, "discrepancy": 1, "attested": 0 },
"accessDataAsOf": "2026-09-29T02:00:00Z"
}
}
If you exceed the rate limit (5 requests per second, burst of 30, per token) the API answers 429 RATE_LIMITED with a Retry-After header; honour it. See the Off-boarding API for statuses, filters and errors, and Poll off-boarding discrepancies nightly for a complete recipe.
Error handling
Every response uses the same envelope. On failure, data is null and error.code is a stable, machine-readable
code:
{
"data": null,
"error": { "code": "MISSING_FILE", "message": "multipart field 'file' is required" }
}
The codes you will meet first are 401 INVALID_TOKEN (expired or revoked token), 403 INSUFFICIENT_SCOPE (the
token lacks a scope), 403 FORBIDDEN (the endpoint does not accept tokens at all) and 402 FEATURE_GATED (your plan
does not include the feature). The full table is in Errors & rate limits.
Next steps
- Access model & scopes — what a token can and cannot reach
- Upload an SBOM from CI — a ready-made pipeline step
- Supply Chain API — every SBOM, finding and vendor endpoint
- Poll leaver items nightly — a complete off-boarding recipe
- MCP quickstart — broad read access for AI clients
Was this page helpful?