# maichine public API

Base URL: `https://<your-maichine-origin>`

Thin integration surface: authentication, projects, versions, color
mapping, quotes, and production. Everything an agent needs for a normal
task is available; nothing prescribes how the agent creates the model.

## Authentication

Bearer API key from the **Integrations** panel:

```
Authorization: Bearer mk_live_...
```

Keys are shown once. Store as `MAICHINE_KEY`; never commit or print it.

`/api/public/v1/capabilities` is public (no auth). Every other endpoint
requires a key.

## Errors

JSON with an HTTP status code, a stable machine `code`, and — for uploads
— structured `findings[]`:

```json
{ "error": "…", "code": "TOO_LARGE", "findings": [{ "code": "TOO_LARGE", "message": "…" }] }
```

Common statuses: `400` invalid input · `401` missing/invalid key · `403`
missing scope · `404` project not found · `413` upload too large · `415`
extension not allowed · `429` rate or daily-quota exceeded.

---

## GET `/api/public/v1/bootstrap`

Authenticated. One request returns account info, capabilities version,
upload formats and limits, the live production color palette, and every
endpoint URL. Cache for the current task.

```json
{
  "authenticated": true,
  "account_id": "…",
  "capabilities_version": "…",
  "formats": { "upload": ["stl","3mf"], "multicolor_preferred": "3mf" },
  "limits": { "max_upload_bytes": 26214400 },
  "production_colors": [ { "id": "10101", "name": "Black", "hex": "#000000" }, … ],
  "endpoints": {
    "projects": "https://…/api/public/v1/projects",
    "versions": "https://…/api/public/v1/projects/{id}/versions",
    "version_status": "https://…/api/public/v1/projects/{id}/versions/{versionId}",
    "update_colors": "https://…/api/public/v1/projects/{id}/color-mapping",
    "quotes": "https://…/api/public/v1/quotes",
    "production_requests": "https://…/api/public/v1/production-requests",
    "credits_balance": "https://…/api/public/v1/credits/balance"
  },
  "production_api": { "status": "available", "message": "…" }
}
```

## GET `/api/public/v1/capabilities`

Public. Full machine-readable capabilities feed. Cacheable for 5 minutes;
honors `If-None-Match` via the `ETag` header.

## POST `/api/public/v1/projects`

Create a project.

```json
{ "name": "Desk lamp base", "description": "optional", "agent": "optional" }
```

Response `201`:

```json
{
  "project_id": "…",
  "slug": "desk-lamp-base",
  "name": "Desk lamp base",
  "preview_url": "https://…/preview/<preview_slug>",
  "upload_url": "https://…/api/public/v1/projects/<id>/versions"
}
```

## POST `/api/public/v1/projects/{id}/versions`

Upload a new model version as `multipart/form-data`.

Fields:

- `file` — required. `.stl` or `.3mf`, up to 25 MB. Other formats are
  rejected with `FORMAT_NOT_PRODUCTION_SUPPORTED`.
- `notes`, `agent`, `prompt` — optional context strings.

Response `201` (`200` if the byte hash duplicates an existing version):

```json
{
  "version": { "id": "…", "version_number": 3, "print_eligible": true, "security_status": "passed" },
  "preview_url": "https://…/preview/<preview_slug>",
  "status_url": "https://…/api/public/v1/projects/<id>/versions/<version_id>",
  "duplicate": false,
  "color_mapping_complete": true,
  "parts": [
    { "part_key": "base", "name": "Keychain Base", "design_color": "#000000", "production_color_id": "10101" },
    { "part_key": "logo", "name": "Gemini Spark",  "design_color": "#0086D6", "production_color_id": "10603" }
  ]
}
```

Use `parts[].part_key` for later color changes — do not re-upload.

## GET `/api/public/v1/projects/{id}/versions`

Newest first. Each entry includes `status`, `security_status`,
`print_eligible`, and a `status_url` for polling.

## GET `/api/public/v1/projects/{id}/versions/{versionId}`

One version's metadata.

## PATCH `/api/public/v1/projects/{id}/color-mapping`

Change production colors on an existing version without re-uploading.
Requires scope `versions:write`.

```json
{
  "version_id": "ver_…",
  "updates": [
    { "part_key": "logo", "production_color_id": "10200" }
  ]
}
```

Response:

```json
{
  "project_id": "…",
  "version_id": "…",
  "color_mapping_complete": true,
  "parts": [ { "part_key": "logo", "production_color_id": "10200", "name": "Gemini Spark", "design_color": "#0086D6" } ],
  "preview_url": "https://…/preview/<preview_slug>"
}
```

Error codes: `UNKNOWN_PART_KEY` (returns `available_part_keys`),
`PRODUCTION_COLOR_UNAVAILABLE`. Any non-terminal quote on the version is
invalidated (declined) — the human must re-confirm before production.

---

## Production API

Requires matching scopes on the API key. Every quote requires explicit
human approval via `confirmation_url` before it can be submitted.

- `GET /api/public/v1/credits/balance` — `credits:read`.
- `GET /api/public/v1/production/options` — `production:read`.
- `POST /api/public/v1/quotes` — `production:read`. Returns
  `{ quote_id, confirmation_url, ... }`.
- `POST /api/public/v1/production-requests` — `production:write`.
  Requires a confirmed `quote_id` and a unique `Idempotency-Key` header.
