Admin - Machine Collections Beta

Machine collections let an MSP admin create a temporary upload run for collecting machine inventory or diagnostic output from customer endpoints.

The admin creates a run with an MSP admin API key. The create response returns a constrained upload_token and an upload_url_endpoint. RMM scripts should use that constrained token to request one short-lived upload URL per machine. Do not deploy the MSP admin API key to customer machines.

This is a beta API. Your MSP must be enabled for the machine collections beta before these endpoints are available.

Flow

  1. Create a machine collection run.
  2. Deploy the returned upload_token through your RMM tool.
  3. Each endpoint calls the run-token upload URL endpoint with its machine metadata and intended filename.
  4. Each endpoint uploads its result file directly to the returned presigned URL.
  5. The MSP admin lists uploads and generates download URLs for files that are ready.

Supported default file extensions are json, csv, and txt. Upload URLs expire after 15 minutes. Runs expire after 24 hours by default and cannot be extended beyond 30 days.

Authentication

Admin endpoints require an MSP admin API key with the manage_machine_collections permission.

curl 'https://ai.hatz.ai/v1/admin/machine-collection-runs' \
  -H 'X-API-Key: $HATZ_API_KEY'

The endpoint used by RMM scripts does not use an MSP admin API key. It is authorized by the constrained run token in the path:

POST /v1/machine-collection-runs/{upload_token}/upload-url

Create a Run

Create a run and receive the one-time upload token.

Endpoint

POST /v1/admin/machine-collection-runs

Request Body

Field Required Description
name Yes Human-readable run name.
description No Optional run notes.
target_tenant_id No Tenant to associate with the run. Must belong to the MSP.
sell_ai_customer_id No Sell-AI customer to associate with the run. Must belong to the MSP.
allowed_extensions No Allowed file extensions. Defaults to ["json", "csv", "txt"].
allowed_mime_types No Optional allowed MIME types. If omitted, platform file policy is used.
max_file_size_bytes No Per-file max size. Defaults to 50 MB and cannot exceed 50 MB.
max_file_count No Max files accepted by the run. Defaults to 5000.
expires_at No ISO timestamp. Defaults to 24 hours from creation; max is 30 days.

Example Request

curl -X POST 'https://ai.hatz.ai/v1/admin/machine-collection-runs' \
  -H 'X-API-Key: $HATZ_API_KEY' \
  -H 'Content-Type: application/json' \
  -d '{
    "name": "Acme endpoint inventory",
    "description": "Inventory collected through RMM",
    "allowed_extensions": ["json"],
    "allowed_mime_types": ["application/json"],
    "max_file_size_bytes": 1048576,
    "max_file_count": 1000
  }'

Response

{
  "id": "4e3f9f1a-6c6f-4b45-9db6-6b66eac1a7f2",
  "name": "Acme endpoint inventory",
  "description": "Inventory collected through RMM",
  "target_tenant_id": null,
  "sell_ai_customer_id": null,
  "allowed_extensions": ["json"],
  "allowed_mime_types": ["application/json"],
  "max_file_size_bytes": 1048576,
  "max_file_count": 1000,
  "expires_at": "2026-06-09T15:30:00Z",
  "disabled_at": null,
  "created_at": "2026-06-08T15:30:00Z",
  "updated_at": "2026-06-08T15:30:00Z",
  "upload_token": "mcr_example_run_token",
  "upload_url_endpoint": "https://ai.hatz.ai/v1/machine-collection-runs/mcr_example_run_token/upload-url"
}

Store id for admin retrieval. Pass only upload_token or upload_url_endpoint to endpoint machines.

Request an Upload URL

Each endpoint requests its own upload URL before uploading its result file.

Endpoint

POST /v1/machine-collection-runs/{upload_token}/upload-url

Request Body

Field Required Description
file_name Yes Result filename. The extension must be allowed by the run.
file_size Yes File size in bytes. Must be within the run's limit.
mime_type Yes File MIME type.
source_tool No RMM or script source, such as ninjaone or connectwise-automate.
script_version No Version of the collection script.
machine_hostname No Endpoint hostname.
machine_serial No Endpoint serial number.
machine_external_id No RMM/device identifier.
os_name No Operating system name.
os_version No Operating system version.
customer_name No Customer name hint.
customer_domain No Customer domain hint.
metadata No Additional JSON metadata, max 8 KB.

Example Request

curl -X POST 'https://ai.hatz.ai/v1/machine-collection-runs/mcr_example_run_token/upload-url' \
  -H 'Content-Type: application/json' \
  -d '{
    "file_name": "acme-laptop-042.json",
    "file_size": 2048,
    "mime_type": "application/json",
    "source_tool": "rmm",
    "script_version": "1.0.0",
    "machine_hostname": "ACME-LAPTOP-042",
    "machine_serial": "ABC123456",
    "machine_external_id": "rmm-device-123",
    "os_name": "Windows",
    "os_version": "11 Pro",
    "customer_name": "Acme Inc",
    "customer_domain": "acme.example",
    "metadata": {
      "site": "HQ"
    }
  }'

Response

{
  "upload_id": "49c1df20-cb82-4d35-9cc4-39102afdf724",
  "file_id": "69a6b435-6bbd-4bc2-b2aa-d4c6006c67a3",
  "presigned_url": "https://uploads.hatz.ai/uploads/...",
  "expires_in": 900,
  "required_headers": {
    "Content-Type": "application/json"
  }
}

Upload the File

Use the exact headers returned in required_headers when uploading to the presigned URL.

curl -X PUT "$PRESIGNED_URL" \
  -H 'Content-Type: application/json' \
  --data-binary '@acme-laptop-042.json'

List Runs

List recent runs for the MSP.

Endpoint

GET /v1/admin/machine-collection-runs

Query Parameters

Parameter Required Description
limit No Number of runs to return, 1-100. Defaults to 50.
offset No Number of runs to skip. Defaults to 0.

Example Request

curl 'https://ai.hatz.ai/v1/admin/machine-collection-runs?limit=50&offset=0' \
  -H 'X-API-Key: $HATZ_API_KEY'

Response

{
  "runs": [
    {
      "id": "4e3f9f1a-6c6f-4b45-9db6-6b66eac1a7f2",
      "name": "Acme endpoint inventory",
      "description": "Inventory collected through RMM",
      "target_tenant_id": null,
      "sell_ai_customer_id": null,
      "allowed_extensions": ["json"],
      "allowed_mime_types": ["application/json"],
      "max_file_size_bytes": 1048576,
      "max_file_count": 1000,
      "expires_at": "2026-06-09T15:30:00Z",
      "disabled_at": null,
      "created_at": "2026-06-08T15:30:00Z",
      "updated_at": "2026-06-08T15:30:00Z"
    }
  ],
  "has_more": true,
  "next_offset": 50
}

Get a Run

Retrieve a single run by ID.

Endpoint

GET /v1/admin/machine-collection-runs/{run_id}

Example Request

curl 'https://ai.hatz.ai/v1/admin/machine-collection-runs/4e3f9f1a-6c6f-4b45-9db6-6b66eac1a7f2' \
  -H 'X-API-Key: $HATZ_API_KEY'

Update or Disable a Run

Patch run settings. To stop accepting uploads, set disabled_at to the current timestamp.

Endpoint

PATCH /v1/admin/machine-collection-runs/{run_id}

Example Request

curl -X PATCH 'https://ai.hatz.ai/v1/admin/machine-collection-runs/4e3f9f1a-6c6f-4b45-9db6-6b66eac1a7f2' \
  -H 'X-API-Key: $HATZ_API_KEY' \
  -H 'Content-Type: application/json' \
  -d '{
    "disabled_at": "2026-06-08T18:00:00Z"
  }'

List Uploads

List files received for a run.

Endpoint

GET /v1/admin/machine-collection-runs/{run_id}/uploads

Query Parameters

Parameter Required Description
limit No Number of uploads to return, 1-500. Defaults to 100.
offset No Number of uploads to skip. Defaults to 0.

Example Request

curl 'https://ai.hatz.ai/v1/admin/machine-collection-runs/4e3f9f1a-6c6f-4b45-9db6-6b66eac1a7f2/uploads?limit=100&offset=0' \
  -H 'X-API-Key: $HATZ_API_KEY'

Response

{
  "uploads": [
    {
      "id": "49c1df20-cb82-4d35-9cc4-39102afdf724",
      "file_uuid": "69a6b435-6bbd-4bc2-b2aa-d4c6006c67a3",
      "file_status": "ready",
      "processing_error": null,
      "filename": "acme-laptop-042.json",
      "mime_type": "application/json",
      "bytes": 2048,
      "machine_hostname": "ACME-LAPTOP-042",
      "machine_serial": "ABC123456",
      "machine_external_id": "rmm-device-123",
      "customer_name": "Acme Inc",
      "customer_domain": "acme.example",
      "target_tenant_id": null,
      "sell_ai_customer_id": null,
      "created_at": "2026-06-08T15:35:00Z"
    }
  ],
  "has_more": true,
  "next_offset": 100
}

When has_more is true, request the next page with offset set to next_offset.

File statuses follow the file upload pipeline: uploading, processing, ready, failed, or deleted.

Generate a Download URL

Generate a short-lived URL for a ready upload. Download URLs expire after 5 minutes.

Endpoint

POST /v1/admin/machine-collection-runs/{run_id}/uploads/{upload_id}/download-url

Example Request

curl -X POST 'https://ai.hatz.ai/v1/admin/machine-collection-runs/4e3f9f1a-6c6f-4b45-9db6-6b66eac1a7f2/uploads/49c1df20-cb82-4d35-9cc4-39102afdf724/download-url' \
  -H 'X-API-Key: $HATZ_API_KEY'

Ready Response

{
  "url": "https://uploads.hatz.ai/...",
  "expires_in": 300,
  "status": "ready",
  "processing_error": null
}

Non-Ready Response

{
  "url": null,
  "expires_in": null,
  "status": "processing",
  "processing_error": null
}

Common Errors

Status Cause
400 The run expiry is invalid, too far in the future, or an update body is empty.
403 The admin key does not have manage_machine_collections.
404 The beta is not enabled, or the run, upload, customer, tenant, or upload token was not found.
409 The run reached its configured file count limit.
410 The run-token upload endpoint is disabled or expired.
413 The requested file is larger than the run's max file size.
422 The filename extension, MIME type, or metadata is invalid.

When the beta is not enabled for your MSP, endpoints return:

{
  "detail": {
    "code": "machine_collections_beta_not_enabled",
    "message": "Machine collection uploads are currently in beta and are not enabled for this tenant."
  }
}