Documentation
SDKs
Call the DeviceQuest REST API from Go or JavaScript/TypeScript with typed clients that handle authentication, pagination and errors.
Overview
DeviceQuest provides two client libraries for the REST API:
| SDK | Package | Client |
|---|---|---|
| Go | github.com/devicequest/platform/sdk/go | devicequest.NewClient |
| JavaScript / TypeScript | @devicequest/sdk | DeviceQuestClient |
Both SDKs send your API key in the X-API-Key header, read the pagination headers for you, and turn error responses into typed errors. The API key needs the scopes of the endpoints you call. See Scopes.
Set the base URL to the address you use to open DeviceQuest, without /api/v1; the SDKs add the path:
export DEVICEQUEST_BASE_URL="https://devicequest.example.com"
export DEVICEQUEST_API_KEY="<your 64-character API key>"
Go
Install
go get github.com/devicequest/platform/sdk/go
The package name is devicequest.
Create a client
client, err := devicequest.NewClient(
os.Getenv("DEVICEQUEST_API_KEY"),
devicequest.WithBaseURL(os.Getenv("DEVICEQUEST_BASE_URL")),
devicequest.WithTimeout(60*time.Second),
)
if err != nil {
log.Fatal(err)
}
| Option | Default | Description |
|---|---|---|
WithBaseURL(url) | http://localhost:8081 | Address of your DeviceQuest installation. Always set this. |
WithTimeout(d) | 30 seconds | Timeout for each request. |
WithTransport(rt) | Go default | A custom http.RoundTripper, for example for a proxy or custom CA. |
NewClient returns an error if the API key is empty. The client doesn’t follow redirects.
Resource accessors
| Accessor | Methods | REST endpoints |
|---|---|---|
client.Assets() | List(ctx, *AssetListOptions), Get(ctx, id), Search(ctx, query, *AssetListOptions), ExportCSV(ctx) | /assets, /assets/{id}, /assets/search, /assets/export |
client.Vulnerabilities() | List(ctx, *VulnerabilityListOptions), Get(ctx, id), ExportCSV(ctx) | /vulnerabilities, /vulnerabilities/{id}, /vulnerabilities/export |
client.CloudResources() | List(ctx, *ListOptions) | /cloud-resources |
client.Flows() | List(ctx, *ListOptions) | /flows |
client.Reports() | List(ctx, *ListOptions), Get(ctx, id), Generate(ctx, *GenerateReportInput) | /reports, /reports/{id}, POST /reports |
client.Scans() | List(ctx, *ScanListOptions), Trigger(ctx, *ScanInput), Cancel(ctx, scanID) | /scans, POST /scans, DELETE /scans/{id} |
client.Recognition() | Recognize(ctx, RecognitionRequest), RecognizeByMAC(ctx, mac), RecognizeByIP(ctx, ip), RecognizeByFingerprint(ctx, map[string]any), ListDeviceTypes(ctx) | /recognition/* |
List methods return the page of items and a *Pagination with Total, Page, PerPage, TotalPages and NextCursor, read from the X-Total-Count, X-Page, X-Per-Page, X-Total-Pages and X-Next-Cursor headers. Pass nil options for the first page at the default size; Limit and Offset page through the rest.
Example
package main
import (
"context"
"errors"
"fmt"
"log"
"os"
devicequest "github.com/devicequest/platform/sdk/go"
)
func main() {
ctx := context.Background()
client, err := devicequest.NewClient(
os.Getenv("DEVICEQUEST_API_KEY"),
devicequest.WithBaseURL(os.Getenv("DEVICEQUEST_BASE_URL")),
)
if err != nil {
log.Fatal(err)
}
// Read every asset, 200 at a time.
opts := &devicequest.AssetListOptions{Limit: 200}
for {
assets, page, err := client.Assets().List(ctx, opts)
if err != nil {
log.Fatal(err)
}
for _, a := range assets {
fmt.Println(a.ID, a.Name, a.IPAddress, a.RiskScore)
}
if page.NextCursor == "" {
break
}
opts.Offset += opts.Limit
}
// Queue a scan of an existing scan target (needs the write scope).
scan, err := client.Scans().Trigger(ctx, &devicequest.ScanInput{
Type: "sweep",
Target: "8a3e5c71-2f4b-4d9a-b6e8-1c0d7f2a9e45",
})
var apiErr *devicequest.APIError
switch {
case errors.As(err, &apiErr):
log.Fatalf("scan rejected: %d %s: %s", apiErr.StatusCode, apiErr.Code, apiErr.Message)
case err != nil:
log.Fatal(err)
}
fmt.Println("queued scan", scan.ScanID, scan.Status)
}
Errors
Any response with status 300 or higher returns a *devicequest.APIError with StatusCode, Code (the error field, such as forbidden or not_found) and Message. Network failures and timeouts return the underlying Go error. See Errors for the codes.
JavaScript and TypeScript
Install
npm install @devicequest/sdk
The package ships CommonJS, ES module and TypeScript type definitions. It uses the standard fetch API, so it runs on Node.js 18 or later and in other runtimes with fetch.
Create a client
import { DeviceQuestClient } from '@devicequest/sdk';
const client = new DeviceQuestClient({
baseUrl: process.env.DEVICEQUEST_BASE_URL!,
apiKey: process.env.DEVICEQUEST_API_KEY!,
timeout: 60_000,
});
| Option | Default | Description |
|---|---|---|
baseUrl | http://localhost:8081 | Address of your DeviceQuest installation. Only the origin (scheme, host and port) is used. |
apiKey | Your API key. Required. | |
timeout | 30000 | Timeout for each request, in milliseconds. |
headers | {} | Extra headers to send with every request. |
You can also write new DeviceQuestClient(apiKey, { baseUrl }) or createClient({ baseUrl, apiKey }). client.setApiKey(key) switches to another key, for example after a rotation.
Resource clients
| Property | Methods | REST endpoints |
|---|---|---|
client.assets | list(options?), get(id), search(query, options?), exportCSV() | /assets, /assets/{id}, /assets/search, /assets/export |
client.vulnerabilities | list(options?), get(id), exportCSV() | /vulnerabilities, /vulnerabilities/{id}, /vulnerabilities/export |
client.cloudResources | list(options?) | /cloud-resources |
client.flows | list(options?) | /flows |
client.reports | list(options?), get(id), create(input) | /reports, /reports/{id}, POST /reports |
client.scans | list(options?), create(input), cancel(id) | /scans, POST /scans, DELETE /scans/{id} |
client.recognition | recognize(input), recognizeByMAC(mac), recognizeByIP(ip), recognizeByFingerprint(fingerprint), listDeviceTypes() | /recognition/* |
options is { limit?, offset? }. List methods return a Page<T>: { data, total, page, perPage, totalPages, nextCursor? }.
Example
import { ApiError, DeviceQuestClient } from '@devicequest/sdk';
const client = new DeviceQuestClient({
baseUrl: process.env.DEVICEQUEST_BASE_URL!,
apiKey: process.env.DEVICEQUEST_API_KEY!,
});
async function main(): Promise<void> {
// Read every asset, 200 at a time.
let offset = 0;
for (;;) {
const page = await client.assets.list({ limit: 200, offset });
for (const asset of page.data) {
console.log(asset.id, asset.name, asset.ipAddress, asset.riskScore);
}
if (!page.nextCursor) break;
offset += 200;
}
// Queue a report (needs the write scope) and check its status later.
const report = await client.reports.create({
name: 'Weekly inventory',
type: 'inventory_summary',
format: 'pdf',
});
console.log(report.id, report.status);
}
main().catch((err: unknown) => {
if (err instanceof ApiError) {
console.error(`DeviceQuest API error ${err.statusCode} ${err.code ?? ''}: ${err.message}`);
} else {
console.error(err);
}
process.exit(1);
});
Errors
A non-2xx response throws an ApiError with statusCode, code (the error field) and message. A request that exceeds the timeout is aborted and throws an AbortError.