# REST API Quickstart
URL: https://docs.autociso.io/api/quickstart/
Description: Make your first AutoCISO REST call in cURL, TypeScript, Go or Java — list SBOMs, upload a manifest, and read leaver items that need attention.

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.

**Warning — API tokens reach only a short list of endpoints**

Examples 1 and 2 target `/api/v1/sscs/*`; Example 3 targets the read-only off-boarding endpoints under `/api/v1/jml/*`. An API token calling any other path returns `403 FORBIDDEN` — see [Access model](https://docs.autociso.io/developers/access-model/). To give an AI client broad read access to compliance data, use [MCP](https://docs.autociso.io/developers/mcp/overview/) instead.

## Before you start

- An API token — see [API tokens](https://docs.autociso.io/developers/api-tokens/)
- The token needs `sbom:read` to read and `sbom:write` to upload; Example 3 needs `jml: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](https://docs.autociso.io/getting-started/plans-and-features/)

Set your token as an environment variable so you don't hard-code credentials:

```bash
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:

```http
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](https://docs.autociso.io/console/supply-chain/sboms/) in the console. Pass `q` to filter by name. Endpoint reference:
[List SBOMs](https://docs.autociso.io/api/reference/sboms/list/).

**cURL**

```bash
curl -s \
  -H "Authorization: Bearer $AUTOCISO_API_TOKEN" \
  "https://api.autociso.io/api/v1/sscs/sboms"
```

**TypeScript**

```typescript
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);
```

**Go**

```go
package main

	"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)
}
```

**Java**

```java
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:

```json
{
  "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](https://docs.autociso.io/api/reference/sboms/upload/).

**Tip — Upload the manifest you already have**

The endpoint takes a dependency manifest — `go.sum`, `pom.xml`, `build.gradle(.kts)`, `package.json` or `requirements.txt` — and picks the parser from the file name. See [Upload an SBOM from CI](https://docs.autociso.io/api/upload-sbom/).

**Note — Form field is `name`, not `assetName`**

The multipart form accepts `file` (required), `name` (optional — defaults to the uploaded filename), and `version` (optional). An unrecognised field is ignored silently, so a typo means your SBOM is named after the file instead.

**cURL**

```bash
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"
```

**TypeScript**

```typescript
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);
```

**Go**

```go
package main

	"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)
}
```

**Java**

```java
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**

```bash
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"
```

**TypeScript**

```typescript
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:

```json
{
  "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](https://docs.autociso.io/api/reference/offboarding-cases/list/) for statuses, filters and errors, and [Poll off-boarding discrepancies nightly](https://docs.autociso.io/api/poll-offboarding/) 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:

```json
{
  "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](https://docs.autociso.io/developers/errors-and-limits/).

## Next steps

- [Access model & scopes](https://docs.autociso.io/developers/access-model/) — what a token can and cannot reach
- [Upload an SBOM from CI](https://docs.autociso.io/api/upload-sbom/) — a ready-made pipeline step
- [Supply Chain API](https://docs.autociso.io/api/reference/) — every SBOM, finding and vendor endpoint
- [Poll leaver items nightly](https://docs.autociso.io/api/poll-offboarding/) — a complete off-boarding recipe
- [MCP quickstart](https://docs.autociso.io/developers/mcp/quickstart/) — broad read access for AI clients
