Disciplines · Infrastructure

Oshun RunPod Integration

RunPod provides serverless GPU infrastructure for running AI inference and generation tasks.

11sections8 minread

On this page

This document describes how Oshun integrates with RunPod for GPU-accelerated AI workloads.

Overview#

RunPod provides serverless GPU infrastructure for running AI inference and generation tasks. Oshun uses RunPod for:

  • Image generation (Stable Diffusion, SDXL, Flux)
  • Video generation
  • Audio processing
  • AI model inference
text
┌─────────────────────────────────────────────────────────────────────────────┐
│                           Oshun Architecture                                 │
│                                                                              │
│  ┌─────────────────────────────────────────────────────────────────────┐    │
│  │                         AWS (ECS)                                    │    │
│  │  ┌─────────────┐     ┌─────────────┐     ┌─────────────┐           │    │
│  │  │   API       │────▶│   Worker    │────▶│   Queue     │           │    │
│  │  │   Service   │     │   Service   │     │   (SQS)     │           │    │
│  │  └─────────────┘     └──────┬──────┘     └─────────────┘           │    │
│  └─────────────────────────────│────────────────────────────────────────┘    │
│                                │                                             │
│                                │ HTTPS                                       │
│                                ▼                                             │
│  ┌─────────────────────────────────────────────────────────────────────┐    │
│  │                         RunPod                                       │    │
│  │  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐  ┌────────────┐ │    │
│  │  │  ComfyUI    │  │   Flux      │  │   SD        │  │ Inference  │ │    │
│  │  │  Endpoint   │  │  Endpoint   │  │  Endpoint   │  │  Endpoint  │ │    │
│  │  │  (A100/A40) │  │   (A100)    │  │  (A40/4090) │  │  (Various) │ │    │
│  │  └─────────────┘  └─────────────┘  └─────────────┘  └────────────┘ │    │
│  └─────────────────────────────────────────────────────────────────────┘    │
└─────────────────────────────────────────────────────────────────────────────┘

Endpoints#

Endpoint Configuration#

Endpoints are configured via the RunPod dashboard and updated via the CI/CD pipeline.

Endpoint GPU Type Min Workers Max Workers Idle Timeout
ComfyUI SD1.5 A40 0 5 60s
ComfyUI SDXL A100 0 3 120s
Character Consistency A6000 0 2 60s
Cinematic Video A100 80GB 0 2 90s
Three-D A100 80GB 0 2 90s
Texture A6000 0 3 60s
Flux A100 0 3 120s
SD (Basic) 4090 0 10 30s
Inference Various 0 5 60s

Endpoint Class Strategy (WS09-001)#

Isis classifies RunPod serverless lanes by three dimensions:

  • Asset complexity: low / medium / high / extreme
  • Control complexity: basic / guided / consistency-heavy / orchestration-heavy
  • Runtime profile: interactive-short / interactive-medium / batch-long / sequence-long
Class ID Lifecycle Primary Family Asset Complexity Control Complexity Runtime Profile Primary Workloads
comfyui-general active comfyui medium guided interactive-medium general multi-modal generation
sd-fast-image active sd low basic interactive-short fast image generation/editing
flux-high-fidelity-image active flux high consistency-heavy interactive-medium high-fidelity image consistency
audio-sfx active audio-sfx medium guided batch-long text-to-audio SFX
audio-voice active audio-voice medium consistency-heavy batch-long voice synthesis
audio-music active audio-music high orchestration-heavy batch-long music generation / multi-track cues
character-consistency planned comfyui high consistency-heavy interactive-medium identity-retained character workflows
cinematic-video planned comfyui extreme orchestration-heavy sequence-long shot/sequence video generation
three-d-asset planned comfyui extreme orchestration-heavy batch-long 3D and geometry-heavy workflows
texture-optimization planned flux high guided interactive-medium texture upscaling and material polish

Warm/Cold Policy Optimization (WS09-004)#

Desired-state worker warmness is now governed by workload class + SLA tier policy:

  • Policy source: infra/runpod/endpoints/warm-cold-policy.json
  • Policy applicator/checker: scripts/isis/apply_runpod_warm_cold_policy.mjs

The policy maps each endpoint family to:

  • Workload class (interactive-short, interactive-medium, batch-long, sequence-long)
  • SLA tier (critical, standard, economy)
  • Warmness profile (warm, balanced, cold)

Verification command:

bash
node scripts/isis/apply_runpod_warm_cold_policy.mjs --environment all --check

Pay-Per-Second Cost Attribution (WS09-005)#

RunPod runtime billing telemetry now includes deterministic attribution buckets for:

  • workflow
  • project
  • tenant
  • endpoint

Attribution data is built from completed job runtime (billableSeconds), GPU tier pricing (USD/second), and metadata context propagated from submit routes (workflowId, projectId, tenantId/organizationId).

GET /api/v1/jobs/runpod/operations now returns cost snapshots with:

  • attribution.workflow.{credits,usd,seconds}
  • attribution.project.{credits,usd,seconds}
  • attribution.tenant.{credits,usd,seconds}
  • attribution.endpoint.{credits,usd,seconds}

Unscoped jobs are explicitly tracked in fallback buckets:

  • __unscoped_project__
  • __unscoped_tenant__
  • __unknown_endpoint__

Per-Endpoint SLO Telemetry (WS09-006)#

RunPod operations telemetry now tracks endpoint-level SLO health using rolling runtime samples captured from completed, failed, and timeout outcomes.

SLO objective defaults:

  • latency p95 <= 180000ms
  • success rate >= 99%
  • timeout rate <= 1%
  • queue wait p95 <= 30000ms
  • minimum sample size 5 before breach evaluation

GET /api/v1/jobs/runpod/operations now includes runpod.slos with:

  • objective thresholds under runpod.slos.objectives
  • timestamped snapshot metadata (generatedAt)
  • endpoint entries containing:
    • endpointId, family, channel, sampleSize
    • latencyP95Ms, successRatePct, timeoutRatePct, queueWaitP95Ms
    • breach flags for each SLO dimension

RunPod Anomaly Detection (WS09-007)#

RunPod operations now include explicit anomaly detection snapshots for:

  • cost spikes
  • timeout storms
  • model cache thrashing

Detection runs over configurable sliding windows and emits active alerts when thresholds are breached.

GET /api/v1/jobs/runpod/operations now returns runpod.anomalies with:

  • detection objectives/thresholds
  • cost-spike snapshot (sampleSize, baseline, zScore, spike)
  • timeout-storm entries per endpoint
  • model-cache-thrashing entries per endpoint
  • active alerts with type/severity/endpoint context

Automated Canary Strategy and Rollback Triggers (WS09-008)#

RunPod operations now computes canary rollout strategy decisions per endpoint family using endpoint SLOs and anomaly telemetry.

GET /api/v1/jobs/runpod/operations now includes runpod.canaryStrategy with:

  • current and target canary percentages per family
  • action (promote, hold, rollback, steady)
  • explicit rollback trigger flag
  • machine-readable reason list (SLO breach, endpoint anomaly alert, insufficient samples, global cost spike)

Operational execution script:

bash
OPERATIONS_FILE=/tmp/runpod-operations.json \
FAIL_ON_ROLLBACK=true \
PROMOTE_CALLBACK='echo promote "$FAMILY" "$TARGET_PERCENTAGE"' \
ROLLBACK_CALLBACK='echo rollback "$FAMILY"' \
scripts/isis/execute_runpod_endpoint_canary_strategy.sh

The script executes promote/rollback callbacks and exits non-zero when rollback is triggered (unless FAIL_ON_ROLLBACK=false).

Fleet Topology and Economics Console (WS09-009)#

RunPod operations now includes a fleet topology and economics snapshot that combines desired-state endpoint definitions with runtime attribution telemetry.

GET /api/v1/jobs/runpod/operations now includes runpod.fleet with:

  • source metadata (desiredStateFile, load state, schema/update metadata)
  • topology inventory by environment and endpoint key:
    • endpoint family mapping
    • configured stable/canary endpoint IDs
    • worker min/max, GPU IDs, timeout envelopes
    • mapped endpoint class IDs
  • projected economics from configured workers and GPU hourly catalog:
    • totals ($ / hour, $ / month)
    • breakdowns by environment, family, and endpoint
  • observed economics rollups from runtime endpoint attribution:
    • by-endpoint USD/seconds
    • by-family USD/seconds

The RunPod operator page now surfaces this topology/economics data as first-class tables so operators can inspect configured fleet shape and projected/observed cost posture in one place.

Disaster Recovery Runbooks and Game-Day Exercises (WS09-010)#

RunPod disaster-recovery readiness now includes dedicated drills for both provider and endpoint-region failure classes:

  • Provider outage drill:
    • automation: scripts/isis/run_provider_outage_drill.sh
    • runbook: docs/domains/isis/runbooks/provider-outage-drill.md
  • Endpoint region failover drill:
    • automation: scripts/isis/run_endpoint_region_failover_drill.sh
    • runbook: docs/domains/isis/runbooks/endpoint-region-failover-drill.md
  • Composite game-day harness:
    • automation: scripts/isis/run_runpod_disaster_recovery_gameday.sh
    • runbook: docs/domains/isis/runbooks/runpod-dr-gameday.md

The endpoint-region drill simulates regional loss by promoting a family canary endpoint to stable routing and validating failover plus restore invariants.

Endpoint IDs#

Endpoint IDs are stored as GitHub repository variables:

  • RUNPOD_ENDPOINT_COMFYUI_SD15 - ComfyUI SD1.5 endpoint
  • RUNPOD_ENDPOINT_COMFYUI_SDXL - ComfyUI SDXL endpoint
  • RUNPOD_ENDPOINT_CHARACTER_CONSISTENCY - Character consistency endpoint
  • RUNPOD_ENDPOINT_CINEMATIC_VIDEO - Cinematic video endpoint
  • RUNPOD_ENDPOINT_THREE_D - 3D endpoint
  • RUNPOD_ENDPOINT_TEXTURE - Texture endpoint
  • RUNPOD_ENDPOINT_FLUX - Flux endpoint
  • RUNPOD_ENDPOINT_SD - Stable Diffusion endpoint
  • RUNPOD_ENDPOINT_INFERENCE - General inference endpoint

Docker Images#

Image Structure#

text
docker/runpod/
├── base/                    # Base image with common dependencies
│   ├── Dockerfile
│   └── requirements.txt
├── comfyui/                 # ComfyUI worker
│   ├── Dockerfile.sd15      # SD1.5 variant
│   ├── Dockerfile.sdxl      # SDXL variant
│   └── handler.py
├── sd/                      # Stable Diffusion worker
│   ├── Dockerfile
│   └── handler.py
├── flux/                    # Flux worker
│   ├── Dockerfile
│   └── handler.py
└── inference/               # Generic inference worker
    ├── Dockerfile
    └── handler.py

Base Image#

The base image includes:

  • CUDA 12.1 runtime
  • Python 3.11
  • PyTorch 2.1
  • Common ML libraries (transformers, diffusers, accelerate)
  • RunPod SDK

Building Images#

Images are built automatically by the CI/CD pipeline on changes to docker/runpod/, workflow runtime contracts, and endpoint manifest configuration.

Manifest artifacts:

  • Build config source: infra/runpod/endpoints/image-build-config.json
  • Generated aggregate manifest: infra/runpod/endpoints/image-build-manifest.json
  • Generated per-endpoint inventories: infra/runpod/endpoints/inventory/*.json

Generation and validation commands:

bash
node scripts/isis/generate_runpod_endpoint_image_manifests.mjs
node scripts/isis/validate_runpod_endpoint_image_manifests.mjs

Manual build:

bash
# Build base image
docker build -t oshunai/runpod-base:latest docker/runpod/base/

# Build ComfyUI image
docker build -t oshunai/runpod-comfyui-sdxl:latest \
  -f docker/runpod/comfyui/Dockerfile.sdxl \
  docker/runpod/comfyui/

# Push to Docker Hub
docker push oshunai/runpod-comfyui-sdxl:latest

Handler Code#

Handler Structure#

Each handler follows the RunPod serverless handler pattern:

python
import runpod

def handler(event):
    """
    Main handler function for RunPod serverless.

    Args:
        event: Dict containing:
            - input: The request payload
            - id: Unique job ID

    Returns:
        Dict with output data or error
    """
    try:
        input_data = event["input"]

        # Validate input
        if "prompt" not in input_data:
            return {"error": "Missing required field: prompt"}

        # Process the request
        result = process_generation(input_data)

        return {"output": result}

    except Exception as e:
        return {"error": str(e)}


runpod.serverless.start({"handler": handler})

ComfyUI Handler#

The ComfyUI handler supports workflow-based generation:

python
def handler(event):
    input_data = event["input"]

    # Load workflow
    workflow = input_data.get("workflow")
    if workflow:
        # Use custom workflow
        workflow_data = json.loads(workflow)
    else:
        # Use default workflow based on type
        workflow_type = input_data.get("type", "txt2img")
        workflow_data = load_default_workflow(workflow_type)

    # Inject parameters into workflow
    inject_parameters(workflow_data, input_data)

    # Execute workflow
    result = execute_workflow(workflow_data)

    # Upload output to S3
    output_url = upload_to_s3(result["images"])

    return {
        "images": output_url,
        "seed": result["seed"],
        "execution_time": result["execution_time"]
    }

Input Schemas#

Text-to-Image:

json
{
  "prompt": "A beautiful sunset over mountains",
  "negative_prompt": "blurry, low quality",
  "width": 1024,
  "height": 1024,
  "steps": 30,
  "cfg_scale": 7.5,
  "sampler": "euler_ancestral",
  "seed": -1,
  "batch_size": 1
}

Image-to-Image:

json
{
  "prompt": "A beautiful sunset over mountains",
  "image": "https://example.com/input.png",
  "strength": 0.75,
  "width": 1024,
  "height": 1024,
  "steps": 30
}

ComfyUI Workflow:

json
{
  "workflow": "<base64 encoded workflow JSON>",
  "parameters": {
    "positive_prompt": "...",
    "negative_prompt": "...",
    "seed": 12345
  }
}

API Integration#

Making Requests#

From the Oshun worker service:

typescript
import { RunPodClient } from '@oshun/runpod-client';

const runpod = new RunPodClient({
  apiKey: process.env.RUNPOD_API_KEY,
  endpoints: {
    comfyui: process.env.RUNPOD_ENDPOINT_COMFYUI_SDXL,
    flux: process.env.RUNPOD_ENDPOINT_FLUX,
    sd: process.env.RUNPOD_ENDPOINT_SD,
  },
});

// Synchronous request (wait for result)
const result = await runpod.run('comfyui', {
  prompt: 'A beautiful sunset',
  width: 1024,
  height: 1024,
});

// Asynchronous request (get job ID, poll for result)
const job = await runpod.runAsync('comfyui', {
  prompt: 'A beautiful sunset',
  width: 1024,
  height: 1024,
});

// Poll for result
const result = await runpod.waitForJob(job.id, {
  timeout: 120000,
  pollInterval: 2000,
});

Webhook Integration#

For long-running jobs, use webhooks:

typescript
const job = await runpod.runAsync('flux', {
  prompt: 'A detailed landscape',
  webhook: 'https://api.oshun.ai/webhooks/runpod',
  webhook_events: ['completed', 'failed'],
});

Webhook payload:

json
{
  "id": "job-123",
  "status": "completed",
  "output": {
    "images": ["https://s3.../output.png"]
  },
  "execution_time": 45.2
}

Model Management#

Model Storage#

Models are stored in RunPod network storage volumes:

text
/runpod-volume/
├── models/
│   ├── checkpoints/         # SD checkpoints
│   │   ├── sd15/
│   │   └── sdxl/
│   ├── loras/               # LoRA models
│   ├── controlnet/          # ControlNet models
│   └── embeddings/          # Textual inversions
├── outputs/                  # Generated outputs (temporary)
└── cache/                    # Model cache

Adding New Models#

  1. Upload to network volume:
bash
runpodctl send models/checkpoints/sdxl/new_model.safetensors
  1. Update model registry in application:
typescript
// libs/isis/models/registry.ts
export const MODEL_REGISTRY = {
  sdxl: {
    'new-model': {
      path: '/runpod-volume/models/checkpoints/sdxl/new_model.safetensors',
      vae: 'sdxl-vae',
      type: 'checkpoint',
    },
  },
};
  1. Update handler to use new model:
python
# In handler.py
MODEL_PATHS = {
    "new-model": "/runpod-volume/models/checkpoints/sdxl/new_model.safetensors",
}

Model Preloading#

To reduce cold start times, models can be preloaded:

python
# In handler.py
def preload_models():
    """Preload frequently used models on worker startup."""
    global PRELOADED_MODELS

    models_to_preload = ["sdxl-base", "sdxl-refiner"]

    for model_name in models_to_preload:
        model_path = MODEL_PATHS[model_name]
        PRELOADED_MODELS[model_name] = load_model(model_path)

# Called on worker startup
if __name__ == "__main__":
    preload_models()
    runpod.serverless.start({"handler": handler})

Monitoring#

Endpoint Metrics#

Available via RunPod dashboard and API:

  • Job metrics:

    • Jobs completed
    • Jobs failed
    • Average execution time
    • Queue depth
  • Worker metrics:

    • Active workers
    • Idle workers
    • GPU utilization
    • Memory utilization

CloudWatch Integration#

Metrics are pushed to CloudWatch for unified monitoring:

typescript
// In worker service
async function trackRunPodMetrics(job: RunPodJob) {
  await cloudwatch.putMetricData({
    Namespace: 'Oshun/RunPod',
    MetricData: [
      {
        MetricName: 'JobExecutionTime',
        Value: job.executionTime,
        Unit: 'Seconds',
        Dimensions: [
          { Name: 'Endpoint', Value: job.endpoint },
          { Name: 'Environment', Value: process.env.ENVIRONMENT },
        ],
      },
      {
        MetricName: 'JobStatus',
        Value: job.status === 'completed' ? 1 : 0,
        Unit: 'Count',
        Dimensions: [
          { Name: 'Endpoint', Value: job.endpoint },
          { Name: 'Status', Value: job.status },
        ],
      },
    ],
  });
}

Alerts#

Alert Condition Action
High Error Rate > 5% jobs failing Slack + Ops
Long Queue Time > 2 min average wait Slack
Worker Scaling Issue Max workers reached Slack + Ops
Cold Start Spikes > 30s cold start avg Slack

Cost Management#

Pricing Model#

RunPod charges per second of GPU time:

GPU Type Price/Second Price/Hour
RTX 4090 $0.00014 $0.50
A40 $0.00024 $0.86
A100 40GB $0.00044 $1.58
A100 80GB $0.00056 $2.00

Cost Optimization#

  1. Idle timeout configuration:

    • Short timeout (30s) for infrequent endpoints
    • Longer timeout (120s) for high-traffic endpoints
  2. GPU selection:

    • Use cheaper GPUs (4090, A40) for SD1.5
    • Reserve A100s for SDXL/Flux
  3. Batching:

    • Batch similar requests together
    • Use batch_size parameter when possible
  4. Model selection:

    • Use quantized models (FP16) when quality permits
    • Consider smaller models for preview generation

Cost Tracking#

typescript
// In worker service
async function trackCost(job: RunPodJob) {
  const gpuPricePerSecond = GPU_PRICING[job.gpuType];
  const cost = job.executionTime * gpuPricePerSecond;

  await costTracker.record({
    jobId: job.id,
    userId: job.userId,
    projectId: job.projectId,
    endpoint: job.endpoint,
    gpuType: job.gpuType,
    executionTime: job.executionTime,
    cost: cost,
  });
}

CI/CD Pipeline#

Workflow#

The GitHub Actions workflow (.github/workflows/runpod-endpoint-image-build.yml) handles:

  1. Manifest generation: Build endpoint image and inventory manifests from desired state + workflow catalog.
  2. Warm/cold policy gate: Validate desired-state worker sizing against workload-class + SLA-tier policy before any build starts.
  3. Manifest validation: Enforce endpoint coverage, docker image parity, and per-endpoint inventory artifacts.
  4. Base image build: Build/push runpod-base used by endpoint images.
  5. Endpoint matrix build: Build/push endpoint-tagged images for all managed endpoint families.

Manual lifecycle operations (create/update/delete/batch) are exposed via .github/workflows/runpod-endpoint-lifecycle.yml, which invokes scripts/isis/runpod_endpoint_lifecycle.py against the RunPod GraphQL API. Desired endpoint state is versioned in infra/runpod/endpoints/desired-state.json (GPU class, worker bounds, idle timeout, execution timeout, canary endpoint IDs, canary traffic percentages). Drift detection runs via .github/workflows/runpod-endpoint-drift-detection.yml using scripts/isis/detect_runpod_endpoint_drift.py. Drift reconciliation is exposed through .github/workflows/runpod-endpoint-reconcile.yml and scripts/isis/reconcile_runpod_endpoint_drift.sh.

Canary Rollout by Endpoint#

RunPod staged rollout is controlled per endpoint type using endpoint-specific canary IDs and traffic percentages:

  • RUNPOD_COMFYUI_CANARY_ENDPOINT_ID + RUNPOD_COMFYUI_CANARY_PERCENTAGE
  • RUNPOD_SD_CANARY_ENDPOINT_ID + RUNPOD_SD_CANARY_PERCENTAGE
  • RUNPOD_FLUX_CANARY_ENDPOINT_ID + RUNPOD_FLUX_CANARY_PERCENTAGE
  • RUNPOD_AUDIO_SFX_CANARY_ENDPOINT_ID + RUNPOD_AUDIO_SFX_CANARY_PERCENTAGE
  • RUNPOD_AUDIO_VOICE_CANARY_ENDPOINT_ID + RUNPOD_AUDIO_VOICE_CANARY_PERCENTAGE
  • RUNPOD_AUDIO_MUSIC_CANARY_ENDPOINT_ID + RUNPOD_AUDIO_MUSIC_CANARY_PERCENTAGE

The generation API routes requests deterministically (job/correlation/user metadata hash) so repeated retries for the same job stay on the same canary or stable endpoint during rollout.

Pipeline Triggers#

  • Push to main: Build endpoint image matrix and push tags
  • Pull request: Validate + build matrix without pushing
  • Manual dispatch: Build selected endpoint key subset, optionally push

Updating Endpoints#

After image push, endpoints are updated via RunPod GraphQL API:

python
mutation = """
mutation UpdateEndpoint($input: UpdateEndpointInput!) {
    updateEndpoint(input: $input) {
        id
        name
        templateId
    }
}
"""

variables = {
    "input": {
        "id": endpoint_id,
        "templateId": new_template_id,
    }
}

response = requests.post(
    "https://api.runpod.io/graphql",
    headers={"Authorization": f"Bearer {api_key}"},
    json={"query": mutation, "variables": variables}
)

Lifecycle Automation Script#

The lifecycle script supports create, update, delete, and batch operations, plus apply-desired-state:

bash
# Dry-run update
python scripts/isis/runpod_endpoint_lifecycle.py \
  --operation update \
  --endpoint-spec-file /tmp/endpoint.json \
  --dry-run

# Batch create/update/delete
python scripts/isis/runpod_endpoint_lifecycle.py \
  --operation batch \
  --operations-file /tmp/operations.json

# Apply desired-state for production endpoints
python scripts/isis/runpod_endpoint_lifecycle.py \
  --operation apply-desired-state \
  --desired-state-file infra/runpod/endpoints/desired-state.json \
  --environment production

# Detect + reconcile drift end-to-end
bash scripts/isis/reconcile_runpod_endpoint_drift.sh

Troubleshooting#

Common Issues#

Job stuck in queue:

bash
# Check endpoint status
curl -X POST https://api.runpod.io/graphql \
  -H "Authorization: Bearer $RUNPOD_API_KEY" \
  -d '{"query": "{ myself { endpoints { id name workersMax workersMin } } }"}'

# Possible causes:
# - No workers available (increase max workers)
# - Workers failing to start (check image/volume)
# - GPU type unavailable (check region)

Worker crashes:

  1. Check worker logs in RunPod dashboard
  2. Common causes:
    • OOM (increase GPU memory or reduce batch size)
    • Model loading failure (check volume mount)
    • Handler exception (check handler code)

Slow cold starts:

  • Optimize Docker image (smaller layers)
  • Preload critical models
  • Increase idle timeout
  • Consider reserved workers

Image pull failures:

bash
# Verify image exists
docker pull oshunai/runpod-comfyui-sdxl:latest

# Check Docker Hub credentials in RunPod
# Template > Docker Credentials

Useful Commands#

bash
# Get job status
curl https://api.runpod.ai/v2/$ENDPOINT_ID/status/$JOB_ID \
  -H "Authorization: Bearer $RUNPOD_API_KEY"

# Cancel job
curl -X POST https://api.runpod.ai/v2/$ENDPOINT_ID/cancel/$JOB_ID \
  -H "Authorization: Bearer $RUNPOD_API_KEY"

# Purge queue
curl -X POST https://api.runpod.ai/v2/$ENDPOINT_ID/purge-queue \
  -H "Authorization: Bearer $RUNPOD_API_KEY"