Developer Documentation

Programmatic access to Text-to-CAD generation, file import/export, and part management.

Authentication

All API requests require authentication via an API key. Include your key in the X-API-Key header:

X-API-Key: pk_live_your_api_key_here

Getting an API Key

  1. Log in to PartWork Studio
  2. Open Settings (gear icon)
  3. Click "Manage" next to Developer API
  4. Click "Create Key" and save your key securely
Important: Your API key is shown only once when created. Store it securely - you cannot retrieve it later.

Permissions

API keys can be scoped with specific permissions:

Permission Description
generate Create CAD models from text prompts
export Export models to STEP, STL, 3MF, GLB formats
import Upload CAD files (STEP, IGES, DXF, BREP, OBJ)
parts:read List and retrieve conversations and parts

Quick Start

Generate a CAD model with a single API call:

curl -X POST https://api.partwork.ai/v1/generate \
  -H "X-API-Key: pk_live_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "prompt": "Create a 2 inch cube with rounded edges",
    "conversation_id": null
  }'

Response:

{
  "job_id": "job_abc123xyz",
  "status": "pending",
  "conversation_id": "conv_def456"
}

Poll for completion:

curl https://api.partwork.ai/v1/job/job_abc123xyz \
  -H "X-API-Key: pk_live_your_api_key"

When complete:

{
  "job_id": "job_abc123xyz",
  "status": "completed",
  "result": {
    "model_url": "https://...",
    "thumbnail_url": "https://...",
    "part_id": "part_789"
  }
}

Endpoints

Base URL: https://api.partwork.ai/v1

POST /generate

Generate a CAD model from a text prompt. Returns a job ID for polling.

Request Body

prompt required
Text description of the part to generate
conversation_id optional
Continue an existing conversation, or null for new

Response

{
  "job_id": "job_abc123xyz",
  "status": "pending",
  "conversation_id": "conv_def456"
}
GET /job/{job_id}

Get the status of a generation job. Poll until status is completed or failed.

Response - Pending

{
  "job_id": "job_abc123xyz",
  "status": "pending",
  "progress": 45
}

Response - Completed

{
  "job_id": "job_abc123xyz",
  "status": "completed",
  "result": {
    "model_url": "https://...",
    "thumbnail_url": "https://...",
    "part_id": "part_789"
  }
}

Response - Failed

{
  "job_id": "job_abc123xyz",
  "status": "failed",
  "error": "Could not generate model from prompt"
}
POST /export

Export a part to STEP, STL, 3MF, or GLB format.

Request Body

part_id required
The part ID to export
format required
Export format: step, stl, 3mf, or glb

Response

{
  "download_url": "https://...",
  "expires_at": "2025-01-15T12:00:00Z"
}
POST /upload/presign

Get a presigned URL for uploading a CAD file. Supported formats: STEP, IGES, DXF, BREP, OBJ.

Request Body

filename required
Name of the file to upload
content_type required
MIME type of the file

Response

{
  "upload_url": "https://s3.amazonaws.com/...",
  "upload_id": "upload_xyz789",
  "expires_at": "2025-01-15T12:00:00Z"
}
POST /upload/process

Process an uploaded CAD file after uploading to the presigned URL.

Request Body

upload_id required
The upload ID from presign response

Response

{
  "job_id": "job_process123",
  "status": "pending"
}
GET /conversations

List your conversations (projects).

Query Parameters

limit optional
Number of results (default: 20, max: 100)
cursor optional
Pagination cursor from previous response

Response

{
  "conversations": [
    {
      "id": "conv_abc123",
      "name": "Mounting Bracket",
      "created_at": "2025-01-15T10:30:00Z",
      "updated_at": "2025-01-15T14:22:00Z"
    }
  ],
  "next_cursor": "eyJsYXN0..."
}
GET /conversations/{id}

Get details of a specific conversation including its parts.

Response

{
  "id": "conv_abc123",
  "name": "Mounting Bracket",
  "parts": [
    {
      "id": "part_xyz789",
      "name": "Base Plate",
      "model_url": "https://...",
      "thumbnail_url": "https://..."
    }
  ],
  "created_at": "2025-01-15T10:30:00Z"
}
POST /download

Get a presigned download URL for a file (STEP, STL, 3MF, GLB, or thumbnail). Use this to download generated models or exported files.

Request Body

s3_key required
The S3 key of the file to download (from job result or export response)

Response

{
  "download_url": "https://s3.amazonaws.com/...",
  "expires_in": 3600
}
The s3_key can be found in the job result (e.g., result.step_key) or extracted from file URLs. The presigned URL expires after 1 hour.

Rate Limits

API requests are rate limited based on your tier. Limits reset at the start of each period.

Tier Requests/Min Requests/Day Generations/Month
Free 60 1,000 100
Standard 120 10,000 1,000
Pro 300 50,000 5,000

Rate Limit Headers

Every response includes rate limit information:

Header Description
X-RateLimit-Limit Maximum requests per minute for your tier
X-RateLimit-Remaining Requests remaining in current window
X-RateLimit-Reset Unix timestamp when the window resets
When rate limited, the API returns 429 Too Many Requests. Use the X-RateLimit-Reset header to know when to retry.

Errors

The API uses standard HTTP status codes and returns error details in the response body.

Status Description
400 Bad Request - Invalid parameters or request body
401 Unauthorized - Missing or invalid API key
403 Forbidden - API key lacks required permission
404 Not Found - Resource does not exist
429 Too Many Requests - Rate limit exceeded
500 Internal Server Error - Something went wrong

Error Response Format

{
  "error": "Invalid API key",
  "code": "invalid_api_key",
  "details": null
}

Common Error Codes

Code Description
invalid_api_key API key is missing, malformed, or revoked
permission_denied API key lacks the required permission
rate_limit_exceeded Too many requests - slow down
generation_limit_exceeded Monthly generation quota reached
invalid_prompt Prompt is empty or too long
job_not_found Job ID does not exist or has expired

Code Examples

Python

import requests
import time

API_KEY = "pk_live_your_api_key"
BASE_URL = "https://api.partwork.ai/v1"

headers = {
    "X-API-Key": API_KEY,
    "Content-Type": "application/json"
}

# Generate a part
response = requests.post(
    f"{BASE_URL}/generate",
    headers=headers,
    json={"prompt": "Create a 50mm diameter gear with 20 teeth"}
)
job = response.json()
print(f"Job started: {job['job_id']}")

# Poll for completion
while True:
    status = requests.get(
        f"{BASE_URL}/job/{job['job_id']}",
        headers=headers
    ).json()

    if status["status"] == "completed":
        print(f"Model URL: {status['result']['model_url']}")
        break
    elif status["status"] == "failed":
        print(f"Error: {status['error']}")
        break

    time.sleep(2)

# Download STEP file (if step_key is in result)
if "step_key" in status["result"]:
    download = requests.post(
        f"{BASE_URL}/download",
        headers=headers,
        json={"s3_key": status["result"]["step_key"]}
    ).json()
    print(f"STEP download: {download['download_url']}")

# Or export to a different format
export = requests.post(
    f"{BASE_URL}/export",
    headers=headers,
    json={
        "part_id": status["result"]["part_id"],
        "format": "stl"
    }
).json()
print(f"STL download: {export['download_url']}")

Node.js

const API_KEY = 'pk_live_your_api_key';
const BASE_URL = 'https://api.partwork.ai/v1';

const headers = {
  'X-API-Key': API_KEY,
  'Content-Type': 'application/json'
};

async function generatePart(prompt) {
  // Start generation
  const response = await fetch(`${BASE_URL}/generate`, {
    method: 'POST',
    headers,
    body: JSON.stringify({ prompt })
  });
  const job = await response.json();
  console.log(`Job started: ${job.job_id}`);

  // Poll for completion
  while (true) {
    const statusRes = await fetch(`${BASE_URL}/job/${job.job_id}`, { headers });
    const status = await statusRes.json();

    if (status.status === 'completed') {
      return status.result;
    } else if (status.status === 'failed') {
      throw new Error(status.error);
    }

    await new Promise(r => setTimeout(r, 2000));
  }
}

// Usage
generatePart('Create a 2 inch cube with rounded edges')
  .then(result => console.log('Model URL:', result.model_url))
  .catch(err => console.error('Error:', err));

cURL

# Generate a part
curl -X POST https://api.partwork.ai/v1/generate \
  -H "X-API-Key: pk_live_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{"prompt": "Create a mounting bracket with 4 holes"}'

# Check job status
curl https://api.partwork.ai/v1/job/job_abc123 \
  -H "X-API-Key: pk_live_your_api_key"

# Download a STEP file (using s3_key from job result)
curl -X POST https://api.partwork.ai/v1/download \
  -H "X-API-Key: pk_live_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{"s3_key": "parts/abc123/model.step"}'

# Export to STL
curl -X POST https://api.partwork.ai/v1/export \
  -H "X-API-Key: pk_live_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{"part_id": "part_xyz789", "format": "stl"}'

# List conversations
curl https://api.partwork.ai/v1/conversations \
  -H "X-API-Key: pk_live_your_api_key"