API Reference
Complete reference for using the AICR API Server.
Overview
The AICR API Server provides HTTP REST access to recipe generation and bundle creation for GPU-accelerated infrastructure. Use the API for programmatic access to configuration recommendations and deployment artifacts.
Version numbers in the sample requests and responses below (server version, chart versions, driver versions) are illustrative. The authoritative, current versions are in the Component Catalog and the Container Images BOM.
API vs CLI
- Use the API for remote recipe generation and bundle creation
- Use the CLI for local operations, snapshot capture, and ConfigMap integration
Base URL
Local development (example):
Start the local server:
Quick Start
Get a Recipe
Generate an optimized configuration recipe for your environment:
Generate Bundles
Create deployment bundles from a recipe:
Endpoints
GET /
Service information and available routes.
Response:
GET /v1/recipe
Generate an optimized configuration recipe based on environment parameters.
Query Parameters:
service=rke2andaccelerator=vr200are Preview. They publish an early-adopter recipe path without the full production support and lifecycle qualification required for Supported status. See the published validation evidence at validation.aicr.run for current coverage.
service=k0sis Preview, covering the singlek0s / h200 / ubuntu / trainingcoordinate. See k0s H200 Setup for its prerequisites and known gaps.
Examples:
POST /v1/recipe
Generate an optimized configuration recipe from a criteria body. This endpoint provides an alternative to query parameters.
Content Types:
application/json- JSON formatapplication/x-yaml- YAML format
An explicit, supported Content-Type is required; a missing, aliased, or unsupported media type is rejected.
GET and POST return identical documents for equivalent criteria, including the criteria object echoed in the response: unspecified dimensions are reported as any on both. HEAD is accepted on /v1/recipe, /v1/query, and /metrics (/health, /ready, and / allow GET only) and returns the same headers without a body — it resolves the recipe to produce them, so it costs what GET costs rather than serving as a cheap probe. Use /health or /ready for liveness.
Request Body:
The request body is a strict envelope. Unknown fields are rejected, so a typo fails loudly instead of being silently ignored:
profile is optional; omit it to take the composition’s declared default. If you also pass profile= as a query parameter, the two must agree.
The Kubernetes-style RecipeCriteria resource body that this endpoint accepted before v0.21 is no longer supported and returns 400.
Examples:
Error Responses:
400 Bad Request- No criteria provided: at least one ofservice,accelerator,intent,os,platform, ornodesmust be non-zero. An empty request returns"no criteria provided: specify at least one of service, accelerator, intent, os, platform, nodes". This guard applies toGET /v1/recipe,POST /v1/recipe,GET /v1/query, andPOST /v1/query.400 Bad Request- Invalid criteria format, missing required fields, or invalid enum values400 Bad Request- A stated criteria dimension is not honored by any applicable recipe overlay (uncovered dimension). This applies toGET /v1/recipe,POST /v1/recipe,GET /v1/query, andPOST /v1/query: every dimension you state (service,accelerator,intent,os,platform) must be matched by at least one applied overlay, or the request fails instead of silently returning a recipe that ignores it.nodesis exempt — it is advisory and never required to be covered. The response’sdetails.uncoveredarray names the offending dimension(s), the requested value, and anyvalidCompletions(additional criteria that would make the request coverable). Snapshot-driven resolution (CLI--snapshot/ Go SDK) may additionally attachexcludedOverlaysandconstraintWarningsto the error; the HTTP API resolves from criteria only and never emits those two fields.405 Method Not Allowed- Only GET, HEAD, and POST are supported; the response carriesAllow: GET, HEAD, POST
Uncovered-Dimension Error Example:
Response:
metadata.excludedOverlays is optional. When present, each entry includes the overlay name and a machine-readable reason such as constraint-failed or mixin-constraint-failed.
metadata.gpuDriverState is optional and appears only for snapshot-driven recipes. It records the NVIDIA kernel driver state observed on the sampled GPU node — preinstalled or absent — and is omitted when no snapshot was provided or the snapshot carried no usable driver-loaded reading. The bundle-time CheckDriverOwnershipCoherence validation consumes it: a recipe whose snapshot observed no driver (absent) is blocked from bundling with the preinstalled-driver assumption, since that would leave GPU nodes driverless.
metadata.mariaDBOperatorState is optional and appears when a snapshot supplies MariaDB Operator conflict evidence during resolution of AICR-provided Slurm accounting. It records absent, api-detected, crs-detected, or unknown; query-generated recipes and older snapshots without the collector subtype omit the field. Recipe generation remains observational: api-detected, crs-detected, and unknown emit warnings but still produce a recipe. At bundle time, crs-detected and unknown block AICR-provided installation, while api-detected or omitted evidence warns but proceeds; absent proceeds silently.
GET /v1/query
Query a specific value from a fully hydrated recipe. Resolves a recipe from criteria (same parameters as GET /v1/recipe), merges all base, overlay, and inline overrides, then returns the value at the given selector path.
Query Parameters:
All GET /v1/recipe parameters are supported, plus:
Response:
- Scalar values (string, number, bool) are returned as plain JSON values
- Complex values (maps, lists) are returned as JSON objects/arrays
Error Responses:
400 Bad Request- No criteria provided: at least one criteria dimension (service,accelerator,intent,os,platform, ornodes) must be non-zero. Returns"no criteria provided: specify at least one of service, accelerator, intent, os, platform, nodes". See the POST /v1/recipe error responses entry for full details.400 Bad Request- OmittingselectorreturnsINVALID_REQUEST. An explicitly emptyselector=remains valid and returns the entire hydrated recipe.400 Bad Request- A stated criteria dimension not honored by any applicable overlay (uncovered dimension) — samedetails.uncoveredshape as described in the POST /v1/recipe error responses above.
Examples:
POST /v1/query
Alternative to GET /v1/query that accepts the criteria and selector in the request body. The body is a strict envelope with a criteria object (the same dimensions accepted as query parameters on GET /v1/query), a required selector string, and an optional profile. Unknown fields are rejected.
Content Types:
application/json- JSON formatapplication/x-yaml- YAML format
Request Body:
Examples:
The response format matches GET /v1/query: scalar values are returned as plain JSON values; maps and lists are returned as JSON objects/arrays.
Error Responses:
Same as GET /v1/query — see the GET /v1/query error responses section above. The no-criteria and uncovered-dimension 400 cases apply. selector must be present on both GET and POST; omitting it is a 400. Passing it explicitly empty (selector= on GET, "selector": "" in a POST envelope) is the documented way to ask for the entire hydrated recipe.
Profile and Slurm-accounting endpoints
The v1 in the route and the apiVersion in a recipe document are
independent version axes. The route segment versions the HTTP contract;
aicr.run/v1 and aicr.run/v1beta2 are the recipe schemas emitted from v0.22.
/v1/bundle also still accepts the superseded aicr.run/v1alpha2 and
aicr.run/v1alpha3, retired in v1.0.0:
aicr.run/v1 for a default recipe and aicr.run/v1beta2 for a
profile/configuration recipe, plus versionless legacy artifacts. Selecting a
profile or resolving a Slurm accounting mode determines which schema track
applies.
There is a single route family. /v1/recipe, /v1/query, and /v1/bundle
serve every composition — profiled and unprofiled alike — with one contract.
The AKS, GKE, and OKE families are the embedded profile adopters (gpuStack) and
need no special routing.
GET /v1/recipe. Accepts the /v1/recipe criteria parameters plus
optional profile=name=value, slurmAccountingMode, and
gkeTcpxoInterfaces. Profile omission
applies the resolved declaration’s required default. Slurm accounting accepts
disabled, customer-managed, or aicr-provided; omission defaults a Slurm
recipe to disabled. The setting is recorded at
configuration.slurm.accounting.mode in an aicr.run/v1beta2 RecipeResult (the superseded aicr.run/v1alpha3 is still read).
gkeTcpxoInterfaces carries the ordered eth1=<network>,...,eth8=<network>
GPU-NIC Network mapping for the torch-distributed-tcpxo runtime; it is
required when the resolved recipe ships that runtime (h100 GKE kubeflow
training) and is recorded at configuration.gke.tcpxoInterfaces.
The route rejects unknown query parameters and conflicting repeated values.
POST /v1/recipe. Accepts a strict JSON or YAML envelope. criteria is
the plain criteria object, not a RecipeCriteria resource. Profile selection
may be supplied in the envelope, as the profile query parameter, or in both
places when the values agree. slurmAccountingMode is supplied as the same
query parameter used by GET, as is gkeTcpxoInterfaces (required for the GKE
h100 kubeflow training family — e.g.
gkeTcpxoInterfaces=eth1=gpu-nic-0,...,eth8=gpu-nic-7). Conflicting selections
are rejected:
Unknown fields, duplicate or trailing documents, malformed selections, and
selections against an unprofiled composition fail with 400 INVALID_REQUEST. POST envelopes require Content-Type: application/json or
Content-Type: application/x-yaml; missing, aliased, or unsupported media
types are rejected.
GET and POST /v1/query. GET accepts the recipe parameters, including
slurmAccountingMode and gkeTcpxoInterfaces, plus
selector. POST accepts the same strict envelope with a required selector.
POST profile selection follows the same query/envelope agreement rule as
/v1/recipe:
POST /v1/bundle. Uses the query parameters and ZIP response documented
under POST /v1/bundle below. It carries no
profile-selection field because its body is
an already-selected RecipeResult. It accepts legacy
aicr.run/v1alpha2 and aicr.run/v1 default recipes, including older
artifacts that omit apiVersion, and strictly decodes profiled or
accounting-configured aicr.run/v1alpha3 and aicr.run/v1beta2 recipes. The
request requires Content-Type: application/json or Content-Type: application/x-yaml; missing, aliased, or unsupported media types are
rejected.
Profile-bearing responses record metadata.selectedProfile; accounting-aware
responses record configuration.slurm.accounting. Both use recipe apiVersion
aicr.run/v1beta2 (the superseded aicr.run/v1alpha3 is still read). Their owned paths are immutable across AICR’s supported
override surfaces: divergent static values, intersecting dynamic paths,
owned-component removal, and argocd-helm install-time values fail closed before
output.
A recipe resolved without an explicit profile or slurmAccountingMode uses
the default-track response shape. That track is aicr.run/v1 from v0.22, and
the schema also still admits the superseded aicr.run/v1alpha2 so a client
generated from this spec reads artifacts captured earlier. Profile and
Slurm-accounting selection are available on every endpoint; no composition
needs special routing.
The AKS, GKE, and OKE families are the embedded profile adopters (gpuStack). Their
compositions resolve like any other: omit profile= to take the declared
default, or select one explicitly.
POST /v1/bundle
Generate deployment bundles from a recipe.
Query Parameters:
Request Body:
The request body is the recipe (RecipeResult) directly. No wrapper object is
needed. This release emits apiVersion: aicr.run/v1 or
aicr.run/v1beta2 and kind: RecipeResult; its bundle readers additionally
accept the superseded aicr.run/v1alpha2 and aicr.run/v1alpha3, respectively. The profile track identifies
recipes carrying metadata.selectedProfile, typed
configuration.slurm.accounting, or both; profile-bearing artifacts must use
/v1/bundle. New clients should preserve the version emitted by recipe
resolution.
For backward compatibility, the endpoint also accepts:
- Legacy artifacts that omit
apiVersionorkind, or carry them as empty strings after a decode/remarshal round trip. - The
kind: Recipevalue this contract published through v0.18.0.
All three shapes reach the bundler identically: the endpoint normalizes kind
on ingest, stamping kind: RecipeResult when the request carries an absent,
empty, or legacy Recipe kind. The generated bundle’s recipe.yaml
therefore always carries the canonical kind and reloads through
aicr bundle -r, aicr validate -r, and the tooling that reads a bundle’s
recipe.yaml (TestGrid publication, evidence synthesis). Only kind is
rewritten — a request that omits apiVersion still produces an artifact with
an empty apiVersion, which every reader accepts as the legacy shape.
Any other kind is rejected with a 400, so the endpoint never emits an artifact
it would refuse to read back. This matches the /v1/bundle decode path, and the
CLI file loader for the same values — aicr bundle -r accepts a
RecipeMetadata file as an overlay to hydrate, but as a hydrated
RecipeResult artifact it too accepts only RecipeResult or an absent kind.
apiVersion is validated separately, as described next.
The shared artifact gate rejects any apiVersion outside
aicr.run/v1alpha2, aicr.run/v1, aicr.run/v1alpha3, and
aicr.run/v1beta2 with a 400, on this endpoint as well as on the CLI file-load
path. An absent or empty apiVersion is still admitted as the legacy shape on
RecipeResult inputs through v0.22, and v1.0.0 stops admitting it along with the
alpha values. The tolerance is scoped to RecipeResult, which predates the
field: a RecipeMetadata overlay is a catalog document however it arrives, so
aicr bundle -r and aicr validate -r reject a headerless one exactly as a
--data catalog scan does. The reader and emitter clocks are separate: v0.21
and v0.22 both read the alpha values, the target values, and the empty header,
while generated recipes carried alpha headers through v0.21 and carry the target
values from v0.22 onward. On the CLI file-load path, reading an alpha or
headerless artifact logs a deprecation warning naming the file; these endpoints
take the artifact as a request body, so there is no file to name and no
equivalent signal. See
Catalog and binary compatibility
for the release-by-release table.
Components
These are the recipe components in recipes/registry.yaml — the names the bundlers query parameter accepts (a request may only name components the recipe declares). The registry is the authoritative source — see the component catalog for detailed descriptions, pinned versions, and per-component caveats.
Examples:
Note: The POST body must be a fully-hydrated
RecipeResult— the server adopts the body as-is and does not hydrate registry defaults, so a hand-authored partial body (missingnamespace,valuesFile,overrides,dependencyRefs) yields empty values and namespaces in the generated bundle. Obtain a complete body fromaicr recipe ... --format json --output -(the CLI defaults to YAML, butPOST /v1/bundleJSON-decodes its body) orGET /v1/recipeand pass it unchanged. The inline bodies below are elided for brevity (only a few component fields shown) — use a generatedRecipeResult, not these literals.To bundle a subset of the recipe’s components, use the
bundlersquery parameter (e.g.?bundlers=gpu-operator,network-operator) rather than hand-trimmingcomponentRefs— trimming the body silently drops required dependencies and breaks deployers like Helmfile on danglingdependencyRefs. The filter prunes those edges safely (a filtered-out dependency is assumed satisfied externally) and rejects unknown or disabled component names with HTTP 400. Slurm accounting adds required-component checks: customer-managed mode requiresslinky-slurm, while AICR-provided mode also requiresmariadb-operator-crds,mariadb-operator, andslurm-accounting-mariadb. Abundlersfilter that omits any required component is rejected with HTTP 400; required components are not automatically added to the selection.Enabled Helm refs must reference a deployable primary: an external chart (a
sourcerepository plus an effectiveversion— empty, whitespace-only, or a barevis rejected; the chart name falls back to the component name whenchartis unset, but achartwithout asourceis rejected) or local primarymanifestFiles.chart,source, andversionvalues carrying surrounding whitespace are rejected — deployers consume them verbatim. Incoherent refs are rejected with HTTP 400 naming the component. Component ref names must also be unique within a recipe (enabled or disabled refs) when non-empty; a duplicate non-empty name is rejected with HTTP 400 naming the conflicting positions. Refs with an empty name are exempt from the uniqueness check.
Response Headers:
Before writing the response, the server stages a private, revalidated
closed-world inventory. The ZIP contains only the inventory-derived
directories and regular files, including recipe.yaml when present;
unverified entries are rejected rather than archived. X-Bundle-Files and
X-Bundle-Size are derived from that same frozen inventory.
Bundle Structure
Checksums are root-level only; component folders carry install.sh at their
root (no scripts/ subdirectory), and no uninstall.sh/undeploy.sh is
generated. After extraction, aicr verify . performs full closed-world
verification: every manifest digest must match and every additional file or
directory, symlink, or other non-regular object is rejected, except the exact
allowed inventory metadata paths.
Server-Side Signing
Pass ?attest=true to POST /v1/bundle to receive a cryptographically signed
bundle. The server signs the bundle as itself using an operator-configured
signing identity. This is the trust boundary: no signing key, token, or identity
is ever taken from the request, so any client that can reach the endpoint gets
bundles signed under the server’s identity, never its own.
A signed bundle additionally carries, inside the returned zip:
attest=true requires a configured signing identity. If none is configured the
request is rejected with HTTP 400 (Server is not configured for attestation).
An unparseable attest value is also HTTP 400. Absent or false returns an
unsigned bundle, as before.
The server supports two mutually exclusive signing modes, selected by environment variables at startup. The configuration is validated fail-fast: a malformed or ambiguous setting stops the server from starting.
Mode A: KMS key. Set AICR_SIGNING_KEY to a cosign KMS URI. The bundle is
signed with a long-lived key held in the KMS; no OIDC identity is involved.
Mode B: keyless against a private Sigstore. Set AICR_FULCIO_URL to a
private Fulcio CA plus a token source. The server obtains a short-lived signing
certificate from Fulcio using its own OIDC identity. Operator setup for Mode B:
- Run a private Fulcio that trusts the cluster’s ServiceAccount token issuer.
- Mount a projected ServiceAccount token with audience
sigstoreinto the aicrd pod, and pointAICR_IDENTITY_TOKEN_FILEat it. The token is read fresh for every signed request because ServiceAccount tokens rotate. - Alternatively, when aicrd itself runs inside GitHub Actions, its ambient OIDC environment is used as the token source.
Setting both AICR_SIGNING_KEY and the keyless variables is ambiguous and the
server refuses to start.
Server signing also requires the aicrd binary attestation
(aicrd-attestation.sigstore.json, issued under the NVIDIA-CI identity and
bound to the aicrd binary digest) to be shipped inside the container image. The
server verifies it once at startup and embeds it as tool provenance
(attestation/aicr-attestation.sigstore.json) in every signed bundle. If
signing is enabled but that attestation is missing or invalid, the server fails
to start. Producing that attestation in the CI/release pipeline is a separate
dependency, tracked outside this feature.
By default the server discovers that attestation next to its own executable. Set
AICR_BINARY_ATTESTATION_FILE to point at an explicit path when the image stages
it elsewhere: a ko-built image places assets under KO_DATA_PATH
(/var/run/ko/aicrd-attestation.sigstore.json), not next to the binary. Only the
attestation file path changes; it is still verified against the aicrd binary’s
own digest.
GET /health
Service health check (liveness probe).
Response:
GET /ready
Service readiness check (readiness probe).
Response:
GET and HEAD /metrics
Prometheus metrics endpoint. HEAD returns the same headers with no body;
every other method is rejected with 405 and an Allow: GET, HEAD header.
Key Metrics:
Complete Workflow Example
Fetch a recipe and generate bundles in one workflow:
Error Handling
Error Response Format
Error Codes
INVALID_REQUESTis not always400:POST /v1/queryandPOST /v1/recipereturn it with HTTP 413 Request Entity Too Large when the request body exceeds the server’s body-size limit (MaxRecipePOSTBytes).
Handling Rate Limits
When rate limited (HTTP 429), use the Retry-After header:
Rate Limiting
- Limit: 100 requests per second (a single process-global token bucket shared across all clients, not per-IP)
- Burst: 200 requests
- Headers:
X-RateLimit-Limit,X-RateLimit-Remaining,X-RateLimit-Reset - 429 Response: Includes
Retry-Afterheader
Criteria Allowlists
The API server can be configured to restrict which criteria values are allowed. This enables operators to limit the API to specific accelerators, services, intents, or OS types.
Configuration
Allowlists are configured via environment variables when starting the server:
Behavior:
- If an environment variable is not set, all values for that criteria are allowed
- If an environment variable is set, only the specified values are permitted
- The
anyvalue is always allowed regardless of allowlist configuration - Allowlists apply to
/v1/recipe,/v1/query, and/v1/bundle
Example Configuration
Error Response
When a disallowed criteria value is requested:
Response (HTTP 400):
CLI Behavior
The CLI (aicr) is not affected by allowlists. Allowlists only apply to the API server, allowing operators to restrict API access while maintaining full CLI functionality for administrative tasks.
Programming Language Examples
Python
Go
JavaScript/Node.js
Shell Script (Batch Processing)
OpenAPI Specification
The full OpenAPI 3.1 specification is available at: api/aicr/v1/server.yaml
Generate client SDKs:
Troubleshooting
Common Issues
“Invalid accelerator type” error:
“Recipe is required” error:
Empty zip file:
Connection refused (local):
See Also
- CLI Reference - Command-line interface
- Agent Deployment - Kubernetes agent for snapshot capture
- Installation Guide - Setup instructions
- Data Flow - Understanding recipe data architecture
- Automation Guide - CI/CD integration patterns
- Kubernetes Deployment - Self-hosted API server deployment