Cloud Functions API
All Cloud Functions are deployed at us-central1 in Firebase project yugmastudio-addcd. Callable functions require Firebase Authentication.
Invocation
import { getFunctions, httpsCallable } from 'firebase/functions'
const functions = getFunctions()
const fn = httpsCallable(functions, 'functionName')
const result = await fn({ ...params })
AI Functions
aiCompose
The AI brain. Delegates to composeCore and runs an agentic tool-use loop (default 3 iterations, configurable 0–5 via AI_MAX_ITERATIONS). After each iteration a pure-math scene audit appends a SCENE AUDIT: line to the tool results (heavy overlaps, floating/sunken/oversized objects) so the model self-corrects on its next turn. aiComposeStream is the SSE variant — same composeCore, timeoutSeconds: 300.
Timeout: 180s | Secrets: ANTHROPIC_API_KEY, CEREBRAS_API_KEY, GEMINI_API_KEY
| Field | Type | Required | Description |
|---|---|---|---|
messages | {role, content}[] | Yes | Conversation history |
sceneContext | object | No | YSL scene text, objects, environment, styleHint |
mode | 'creative' | 'precise' | No | Temperature toggle (0.7 / 0.2) |
sessionId | string | No | Session ID for cross-session persistence |
Returns:
{
"text": "I've created a sci-fi hangar with...",
"toolCalls": [{ "name": "add_object", "input": {...} }, ...],
"iterations": 3,
"totalToolCalls": 7
}
generateMaterial
Generate PBR material values from a text description.
| Field | Type | Required | Description |
|---|---|---|---|
description | string | Yes | e.g. "weathered copper with patina" |
Returns: { color, roughness, metalness, emissive, emissiveIntensity }
inspectMaterial
Analyze a material and suggest improvements.
| Field | Type | Required | Description |
|---|---|---|---|
material | object | Yes | Current material properties |
Returns: { analysis, suggestions[] }
rankAssetCandidates
Vision-ranks Sketchfab search candidates for the client-side import_from_sketchfab flow. The client sends candidate thumbnail URLs (Sketchfab hosts only); the server fetches the images and asks Gemini which candidate is the single clean object matching the intent — keeping GEMINI_API_KEY server-side. Kill-switch: ASSET_VISION_RANK=off.
Secrets: GEMINI_API_KEY
| Field | Type | Required | Description |
|---|---|---|---|
query | string | Yes | The import search query, e.g. "office chair" |
candidates | { thumbnailUrl, name?, license? }[] | Yes | 2–6 candidates; thumbnails must be https URLs on sketchfab.com |
Returns: { ok: true, verdict } when a verdict was reached; { ok: false } on any soft failure (feature disabled, no key, vision error, nothing parseable). The client treats ok: false as "use the first result" — this call never blocks an import.
Generation Functions
generateAsset
Start a Meshy.ai text-to-3D generation job.
| Field | Type | Required | Description |
|---|---|---|---|
prompt | string | Yes | Description of the 3D model |
Returns: { jobId, status: 'pending' }
sweepGenerationJobs (scheduled)
Replaces the deleted checkGenerationStatus callable. Runs every minute,
polls Meshy for all processing jobs, and updates each
generationJobs/{jobId} document. Clients observe progress via a Firestore
onSnapshot listener on their job document (owner-readable) instead of
polling a callable.
Project Functions
deleteProject
Delete a project and cascade-remove associated assets.
| Field | Type | Required | Description |
|---|---|---|---|
projectId | string | Yes | Project to delete |
Authorization: Owner only.
exportScene
Convert a Yugma scene to glTF 2.0 JSON.
| Field | Type | Required | Description |
|---|---|---|---|
scene | object | Yes | Scene JSON |
Storage Functions
generateSignedUploadUrl
| Field | Type | Required |
|---|---|---|
filename | string | Yes |
Returns: { url, path } — PUT to the URL with appropriate Content-Type.
generateSignedDownloadUrl
| Field | Type | Required |
|---|---|---|
path | string | Yes |
Returns: { url } — expires after 1 hour.
Billing
stripeWebhook
HTTP endpoint (not a callable). Stripe posts subscription events here. It verifies the Stripe-Signature header using Stripe's v1 scheme (HMAC-SHA256 over <timestamp>.<rawBody>, constant-time compare, 5-minute tolerance, keyed by STRIPE_WEBHOOK_SECRET) and handles three events: checkout.session.completed, customer.subscription.updated, and customer.subscription.deleted.
Processing is level-triggered, not edge-triggered: every subscription is mirrored into users/{uid}.billingSubs, and on each event the effective tier (premium / ultimate) is recomputed as the highest tier among still-active subscriptions — so out-of-order or duplicate deliveries converge to the customer's true entitlement. Idempotency is enforced via stripeEvents/{event.id}; test-mode (livemode !== true) events never grant a tier. The buyer's UID arrives as client_reference_id on the Payment Link checkout, and the tier is read from metadata.tier on the session / price.
The retired
lemonsqueezyWebhookendpoint is still exported but dormant —stripeWebhookis the live billing path.
The former Phase 12/14/16 stub functions (
startVideoReconstruction,octopartProxy,renderStoryboard) were removed from the codebase in the 2026-07 cleanup and are not deployed. See the feature docs for their planned designs.
Auth Triggers
onUserCreate (v1)
Seeds a user profile document in Firestore on signup.
onUserDelete (v1)
Cascade-deletes user profile and projects on account deletion.
Error Codes
| Code | Meaning |
|---|---|
unauthenticated | No valid auth token |
invalid-argument | Missing or invalid parameters |
not-found | Resource doesn't exist |
permission-denied | User lacks access |
resource-exhausted | Rate limit exceeded (30/hr for AI) |
Secrets
| Secret | Used by | Status |
|---|---|---|
GEMINI_API_KEY | aiCompose, aiComposeStream, rankAssetCandidates | Set via firebase secrets |
ANTHROPIC_API_KEY | aiCompose, aiComposeStream, generateMaterial, inspectMaterial | Set via firebase secrets |
CEREBRAS_API_KEY | aiCompose, aiComposeStream | Set via firebase secrets |
MESHY_API_KEY | generateAsset, sweepGenerationJobs | Set via firebase secrets |
STRIPE_WEBHOOK_SECRET | stripeWebhook | Set via firebase secrets |