API docs

Manage keys
Quickstart

The basic flow

1

Create an API key with the scopes your integration needs.

2

List recent jobs when the user wants to continue existing workspace work.

3

Attach reference images directly when creating or iterating a CAD job.

4

Create or continue a CAD job, poll until completed, review the result, then download STEP artifacts.

Environment
export ORVILLE_API_KEY="gcad_live_..."
export ORVILLE_BASE_URL="https://www.ballistalabs.ai"
Authentication

Bearer API keys

Pass workspace API keys in the Authorization header. Keys are scoped, revocable, and only shown once when created.

cad:readList jobs, read status/history/artifacts, review completed jobs, and download STEP files
cad:writeCreate jobs, send follow-up iterations, and write review messages
billing:readRead public API billing status
Stability

Versioning

Documented `/api/v1` endpoints, request fields, response fields, status meanings, and error codes are stable. Breaking changes ship under a new version such as `/api/v2`.

We may add nullable fields or new endpoints in v1. Data-bearing API responses include `request_id`, return `X-Request-Id`, and use `Cache-Control: no-store`.

Rate limits

Default tier

Public API keys currently use the standard tier. Limits are scoped by workspace and API key, with a per-job status polling limit to prevent hot polling.

CAD writes
10 requests per minute per workspace for creates and follow-ups.
Review writes
20 review assistant messages per minute per workspace.
Status polling
3 requests per 10 seconds per API key per job, plus 600 read requests per minute per workspace.
Artifacts
120 artifact list or download requests per minute per API key.
Billing
60 balance requests per minute per API key.

Rate-limited responses return HTTP 429 with `Retry-After`. Job responses include `poll_after_seconds`; integrations should wait at least that long before polling again.

Endpoints

CAD workflow endpoints

GET/api/v1/cad/jobs

List recent jobs

List recent non-hidden CAD jobs in the workspace so an integration can find and continue existing work.

  • Includes jobs created in the webapp and jobs created through the public API.
  • Use limit to request up to 100 jobs. Default is 20.
  • Use next_cursor from the response to request the next page.
  • Use q for lightweight search across project titles and original prompts.
  • Each job summary can be continued with GET /cad/jobs/{job_id}, GET /cad/jobs/{job_id}/messages, POST /cad/jobs/{job_id}/messages, and POST /cad/jobs/{job_id}/review.
Required scopecad:read
Request
curl "https://www.ballistalabs.ai/api/v1/cad/jobs?limit=20&q=mounting%20plate" \
  -H "Authorization: Bearer $ORVILLE_API_KEY"
Response
{
  "jobs": [
    {
      "id": "cadjob_385f3130-1ebc-40b7-85d9-6ded5ecc8a0f",
      "status": "completed",
      "source": "webapp",
      "title": "Mounting plate",
      "prompt_preview": "Create a mounting plate with rounded corners.",
      "last_message": "Increase the thickness to 8 mm.",
      "latest_revision_id": "rev_4FIoIk2zfzNGakNvYMXDAA",
      "created_at": "2026-05-20T14:00:00.000Z",
      "updated_at": "2026-05-20T14:04:12.000Z"
    }
  ],
  "next_cursor": null,
  "request_id": "req_8f3c2d0e98ab4e53981f481d661d44ab"
}
Next page
curl "https://www.ballistalabs.ai/api/v1/cad/jobs?cursor=$NEXT_CURSOR" \
  -H "Authorization: Bearer $ORVILLE_API_KEY"
POST/api/v1/cad/jobs

Create a CAD job

Queue a new CAD request with a prompt and optional image attachments.

  • Required field: prompt. Maximum length: 60,000 characters.
  • Optional field: images. Attach up to 5 images as repeated multipart form fields named images.
  • Accepted images: JPEG, PNG, WebP, GIF, HEIC, and HEIF. Maximum size: 10MB each.
  • Use application/json with only prompt when the request has no images.
  • Idempotency-Key is required, max 160 characters, and should only be reused for exact retries.
Required scopecad:write
Request
curl -X POST "https://www.ballistalabs.ai/api/v1/cad/jobs" \
  -H "Authorization: Bearer $ORVILLE_API_KEY" \
  -H "Idempotency-Key: 87b5972d-05b9-4f0a-9b68-97c53fd4f840" \
  -F 'prompt=Create a mounting plate with rounded corners and two through holes.' \
  -F 'images=@mounting-plate-sketch.png'
Response
{
  "id": "cadjob_385f3130-1ebc-40b7-85d9-6ded5ecc8a0f",
  "status": "queued",
  "poll_after_seconds": 30,
  "request_id": "req_8f3c2d0e98ab4e53981f481d661d44ab"
}
JSON request without images
curl -X POST "https://www.ballistalabs.ai/api/v1/cad/jobs" \
  -H "Authorization: Bearer $ORVILLE_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 9e16de21-3919-48f8-9df3-73a2b993fa1f" \
  -d '{
    "prompt": "Create a simple flat rectangular bracket with two through holes."
  }'
GET/api/v1/cad/jobs/{job_id}

Poll job status

Poll until status is completed. The completed response includes the explanation, latest revision ID, assembly tree, and artifacts.

  • Status values: queued, running, completed, failed.
  • Poll every 1-2 minutes and stop when the status is completed or failed.
  • poll_after_seconds is the recommended minimum delay before the next status request.
  • created_at and updated_at are ISO timestamps when available.
  • cost is populated on completed jobs when the billing record is available. charged_cents is null until then.
  • explanation, latest_revision_id, assembly_tree, and artifacts can be null or empty before completion.
  • assembly_tree exposes artifact_id values instead of storage paths.
  • Artifacts include download_url values for STEP downloads.
Required scopecad:read
Request
curl "https://www.ballistalabs.ai/api/v1/cad/jobs/cadjob_385f3130-1ebc-40b7-85d9-6ded5ecc8a0f" \
  -H "Authorization: Bearer $ORVILLE_API_KEY"
Response
{
  "id": "cadjob_385f3130-1ebc-40b7-85d9-6ded5ecc8a0f",
  "status": "completed",
  "created_at": "2026-05-20T14:00:00.000Z",
  "updated_at": "2026-05-20T14:04:12.000Z",
  "poll_after_seconds": null,
  "explanation": "The mounting plate features a flat rectangular profile...",
  "cost": {
    "currency": "usd",
    "charged_cents": 200
  },
  "latest_revision_id": "rev_4FIoIk2zfzNGakNvYMXDAA",
  "assembly_tree": {
    "label": "mounting_plate",
    "kind": "step",
    "filename": "449_mounting_plate.step",
    "artifact_id": "art_pW89wqwr67T7O00Ri_8ThA",
    "children": []
  },
  "artifacts": [
    {
      "id": "art_pW89wqwr67T7O00Ri_8ThA",
      "label": "mounting_plate",
      "filename": "449_mounting_plate.step",
      "kind": "step",
      "download_url": "/api/v1/artifacts/art_pW89wqwr67T7O00Ri_8ThA/download"
    }
  ],
  "request_id": "req_8f3c2d0e98ab4e53981f481d661d44ab"
}
POST/api/v1/cad/jobs/{job_id}/messages

Send a follow-up iteration

Send a follow-up prompt, with optional new image attachments, against the same job.

  • Supports the same request fields as create job: prompt and optional multipart images.
  • The API rejects follow-ups while the job already has an active run.
  • Idempotency-Key is required, max 160 characters, and should only be reused for exact retries.
Required scopecad:write
Request
curl -X POST "https://www.ballistalabs.ai/api/v1/cad/jobs/cadjob_385f3130-1ebc-40b7-85d9-6ded5ecc8a0f/messages" \
  -H "Authorization: Bearer $ORVILLE_API_KEY" \
  -H "Idempotency-Key: cfb418cc-f8dc-471d-a418-84f612f71449" \
  -F 'prompt=Increase the thickness to 8 mm and move the holes farther apart.' \
  -F 'images=@markup.png'
Response
{
  "id": "cadjob_385f3130-1ebc-40b7-85d9-6ded5ecc8a0f",
  "status": "queued",
  "message_id": "3b6b1e3a-5a40-475b-bef0-6d52e2fdd16c",
  "poll_after_seconds": 30,
  "request_id": "req_8f3c2d0e98ab4e53981f481d661d44ab"
}
JSON follow-up without images
curl -X POST "https://www.ballistalabs.ai/api/v1/cad/jobs/cadjob_385f3130-1ebc-40b7-85d9-6ded5ecc8a0f/messages" \
  -H "Authorization: Bearer $ORVILLE_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 2e640777-35f6-4fd3-8921-0dd59616c2aa" \
  -d '{
    "prompt": "Make the plate 8 mm thick."
  }'
POST/api/v1/cad/jobs/{job_id}/review

Ask the review assistant

Ask the review assistant about a completed CAD job without starting a CAD iteration.

  • Required field: prompt. Maximum length: 60,000 characters.
  • Optional field: revision_id. Defaults to the current CAD revision.
  • Optional field: viewer_state. Use this for host-viewer camera, selection, section, annotation, or snapshot context.
  • The response is synchronous and includes the assistant message, optional viewer actions, and any iteration proposal.
  • The request is persisted to the job conversation, so it appears in message history and in the webapp.
  • Idempotency-Key is required, max 160 characters, and should only be reused for exact retries.
Required scopecad:read + cad:write
Request
curl -X POST "https://www.ballistalabs.ai/api/v1/cad/jobs/cadjob_385f3130-1ebc-40b7-85d9-6ded5ecc8a0f/review" \
  -H "Authorization: Bearer $ORVILLE_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: f0fb3637-75e8-44f9-b2c9-5e3dce39c631" \
  -d '{
    "prompt": "Review the current model for manufacturability and assembly risks.",
    "revision_id": "rev_4FIoIk2zfzNGakNvYMXDAA",
    "viewer_state": {
      "camera": {
        "position": [120, 80, 90],
        "target": [0, 0, 0],
        "up": [0, 0, 1]
      }
    }
  }'
Response
{
  "job_id": "cadjob_385f3130-1ebc-40b7-85d9-6ded5ecc8a0f",
  "revision_id": "rev_4FIoIk2zfzNGakNvYMXDAA",
  "message_id": "2df6c964-bf0b-4e9a-998b-1c2ad907fcb6",
  "assistant_message_id": "77f52fe8-d731-48b8-85fb-1748ef7409ea",
  "review": {
    "mode": "review",
    "message": "The bracket is generally straightforward to manufacture...",
    "model": "review-agent",
    "iteration_proposal": null,
    "viewer_actions": []
  },
  "request_id": "req_8f3c2d0e98ab4e53981f481d661d44ab"
}
Snapshot context
curl -X POST "https://www.ballistalabs.ai/api/v1/cad/jobs/cadjob_385f3130-1ebc-40b7-85d9-6ded5ecc8a0f/review" \
  -H "Authorization: Bearer $ORVILLE_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 18f7f767-c776-41de-9a34-59b2bb564873" \
  -d '{
    "prompt": "Review this marked-up area.",
    "viewer_state": {
      "snapshots": [
        {
          "label": "Marked area",
          "mime_type": "image/png",
          "data_base64": "...",
          "source": "viewer_snapshot"
        }
      ]
    }
  }'
GET/api/v1/cad/jobs/{job_id}/messages

Read message history

Fetch the sanitized conversation for a CAD job, including user prompts, image attachment metadata, assistant replies, and revision artifacts.

  • Returns user and assistant messages only. System messages and hidden text are not exposed.
  • User messages include image metadata such as filename, content type, and size. Raw storage paths and signed image URLs are not exposed.
  • Assistant messages include revision_id, revision, and artifacts when that message produced a CAD revision.
  • Review assistant messages include a review object with mode, message, optional viewer actions, and optional iteration proposal.
  • Artifacts use the same public artifact shape and download_url values as the job and artifact endpoints.
Required scopecad:read
Request
curl "https://www.ballistalabs.ai/api/v1/cad/jobs/cadjob_385f3130-1ebc-40b7-85d9-6ded5ecc8a0f/messages" \
  -H "Authorization: Bearer $ORVILLE_API_KEY"
Response
{
  "job_id": "cadjob_385f3130-1ebc-40b7-85d9-6ded5ecc8a0f",
  "messages": [
    {
      "id": "3b6b1e3a-5a40-475b-bef0-6d52e2fdd16c",
      "role": "user",
      "content": "Create a mounting plate from this sketch.",
      "created_at": "2026-05-20T14:00:00.000Z",
      "images": [
        {
          "id": "file_6FZkCpLcH7s3VRYB0a8quA",
          "filename": "mounting-plate-sketch.png",
          "content_type": "image/png",
          "size_bytes": 184221
        }
      ],
      "revision_id": null,
      "revision": null,
      "artifacts": [],
      "review": null
    },
    {
      "id": "8c7cfd22-c8ed-4247-9907-4d469c0c7ad3",
      "role": "assistant",
      "content": "The mounting plate features a flat rectangular profile...",
      "created_at": "2026-05-20T14:04:12.000Z",
      "images": [],
      "revision_id": "rev_4FIoIk2zfzNGakNvYMXDAA",
      "revision": "A",
      "artifacts": [
        {
          "id": "art_pW89wqwr67T7O00Ri_8ThA",
          "label": "mounting_plate",
          "filename": "449_mounting_plate.step",
          "kind": "step",
          "download_url": "/api/v1/artifacts/art_pW89wqwr67T7O00Ri_8ThA/download"
        }
      ],
      "review": null
    },
    {
      "id": "77f52fe8-d731-48b8-85fb-1748ef7409ea",
      "role": "assistant",
      "content": "The bracket is generally straightforward to manufacture...",
      "created_at": "2026-05-20T14:07:12.000Z",
      "images": [],
      "revision_id": null,
      "revision": null,
      "artifacts": [],
      "review": {
        "mode": "review",
        "message": "The bracket is generally straightforward to manufacture...",
        "model": "review-agent",
        "iteration_proposal": null,
        "viewer_actions": []
      }
    }
  ],
  "request_id": "req_8f3c2d0e98ab4e53981f481d661d44ab"
}
GET/api/v1/cad/jobs/{job_id}/artifacts

List latest artifacts

List the latest revision artifacts for a CAD job without reading the full job response.

  • Returns job_id, revision_id, assembly_tree, and artifacts.
  • Artifacts are available after a job has completed successfully.
Required scopecad:read
Request
curl "https://www.ballistalabs.ai/api/v1/cad/jobs/cadjob_385f3130-1ebc-40b7-85d9-6ded5ecc8a0f/artifacts" \
  -H "Authorization: Bearer $ORVILLE_API_KEY"
Response
{
  "job_id": "cadjob_385f3130-1ebc-40b7-85d9-6ded5ecc8a0f",
  "revision_id": "rev_4FIoIk2zfzNGakNvYMXDAA",
  "assembly_tree": {
    "label": "mounting_plate",
    "kind": "step",
    "filename": "449_mounting_plate.step",
    "artifact_id": "art_pW89wqwr67T7O00Ri_8ThA",
    "children": []
  },
  "artifacts": [
    {
      "id": "art_pW89wqwr67T7O00Ri_8ThA",
      "label": "mounting_plate",
      "filename": "449_mounting_plate.step",
      "kind": "step",
      "download_url": "/api/v1/artifacts/art_pW89wqwr67T7O00Ri_8ThA/download"
    }
  ],
  "request_id": "req_8f3c2d0e98ab4e53981f481d661d44ab"
}
GET/api/v1/artifacts/{artifact_id}/download

Download a STEP file

Download artifacts from the latest result. By default the endpoint redirects to a short-lived signed URL.

  • Default response is a 302 redirect to a signed STEP URL.
  • Pass redirect=false to receive JSON with url, filename, artifact_id, and expires_in_seconds.
  • Redirect responses include X-Request-Id and no-store headers.
Required scopecad:read
Request
curl -L "https://www.ballistalabs.ai/api/v1/artifacts/art_pW89wqwr67T7O00Ri_8ThA/download" \
  -H "Authorization: Bearer $ORVILLE_API_KEY" \
  -o mounting_plate.step
Response
ISO-10303-21;
HEADER;
FILE_DESCRIPTION(('Orville CAD Model'),'2;1');
...
Signed URL JSON request
curl "https://www.ballistalabs.ai/api/v1/artifacts/art_pW89wqwr67T7O00Ri_8ThA/download?redirect=false" \
  -H "Authorization: Bearer $ORVILLE_API_KEY"
Signed URL JSON response
{
  "artifact_id": "art_pW89wqwr67T7O00Ri_8ThA",
  "filename": "449_mounting_plate.step",
  "url": "https://storage.example.com/signed-step-url",
  "expires_in_seconds": 900,
  "request_id": "req_8f3c2d0e98ab4e53981f481d661d44ab"
}
GET/api/v1/billing/balance

Read billing status

Check the workspace balance and the current hold amount for starting a CAD job.

  • available_balance_cents is the workspace balance available to public API jobs.
  • cad_job_hold_cents is the current hold amount required to start a CAD job.
  • Completed job cost is returned on the CAD job response as cost.charged_cents.
Required scopebilling:read
Request
curl "https://www.ballistalabs.ai/api/v1/billing/balance" \
  -H "Authorization: Bearer $ORVILLE_API_KEY"
Response
{
  "currency": "usd",
  "available_balance_cents": 2500,
  "cad_job_hold_cents": 200,
  "request_id": "req_8f3c2d0e98ab4e53981f481d661d44ab"
}
Errors

Error shape

API errors use a stable JSON object with a machine-readable code and a human-readable message.

Error response
{
  "error": {
    "code": "job_already_running",
    "message": "CAD job already has an active run"
  },
  "request_id": "req_8f3c2d0e98ab4e53981f481d661d44ab"
}

Common statuses

400
Invalid input or missing Idempotency-Key.
401
Missing, invalid, revoked, or expired API key.
402
The workspace cannot start another CAD job.
403
The API key does not include the required scope.
409
Idempotency conflict or an active job run already exists.
429
Rate limit exceeded, or the workspace balance is too low for the active CAD job load.
503
A billing or background service check is temporarily unavailable.

Common error codes

missing_prompt
prompt is required.
prompt_too_long
prompt is longer than 60,000 characters.
invalid_json
The request body is not valid JSON.
invalid_form_data
The multipart request body is invalid.
json_images_unsupported
Images were provided in JSON instead of multipart form data.
unsupported_image_field
Image files were attached with a field name other than images.
invalid_file_type
The image type is not JPEG, PNG, WebP, GIF, HEIC, or HEIF.
file_too_large
An image is larger than 10MB.
too_many_images
More than 5 images were attached.
idempotency_key_required
A CAD write request is missing Idempotency-Key.
idempotency_key_conflict
The same Idempotency-Key was reused with a different request body.
job_already_running
A follow-up was sent while the job already had an active run.
job_not_completed
A review request was sent before the CAD job completed.
cad_revision_required
A review request was sent before a CAD revision was available.
review_agent_failed
The review assistant could not complete the request.
review_agent_unavailable
The review assistant is temporarily unavailable.
billing_required
The workspace needs prepaid billing before starting CAD jobs.
billing_needs_review
Workspace billing needs review before more CAD jobs can start.
insufficient_balance
The workspace balance is empty.
projected_balance_exceeded
The workspace balance is too low for the active job load.
rate_limited
The API key or workspace exceeded a rate limit. Use the Retry-After header before retrying.