# Batch Create Content Source: https://docs.uselamina.ai/api-reference/batch-create POST /v1/content/batch Create multiple content pieces in a single request. Brand context is resolved once and shared across all items for consistency and efficiency. Each item in the `items` array is an independent content creation request with its own brief and optional overrides. All items are executed in parallel. **Limits:** Maximum 10 items per batch. **Response:** Returns immediately with a `batchId` and per-item status. Items that could not be started are marked as `failed` with an error message. Successful items are `queued` with a run ID you can poll individually. **Agent usage pattern:** 1. Generate briefs with `POST /v1/content/brief` (count: 5) 2. Feed all briefs into this endpoint as a batch 3. Poll each `runId` from the response for results Create multiple pieces of content in a single request. Each item follows the same logic as the create-content endpoint — brief resolution, app selection, and execution — but all items run in parallel. Brand context is resolved once and shared across the batch. Returns an array of results, one per item. Each successful item includes a run ID you can track via the wait or get-run endpoints. Failed items include the error reason. Maximum 10 items per batch. # Generate Content Brief Source: https://docs.uselamina.ai/api-reference/create-brief POST /v1/content/brief Generate content briefs from a goal, enriched with brand context and trend signals. Returns multiple brief suggestions that can be fed directly into `POST /v1/content/create`. **Example workflow:** 1. Call this endpoint with a goal: "Increase Instagram engagement for our sneaker line" 2. Review the generated briefs (each is a self-contained content creation instruction) 3. Pass the best brief as the `brief` field to `POST /v1/content/create` Each generated brief incorporates: - Workspace brand DNA and voice attributes - Current trend signals for the target platform - Historical performance patterns Generate structured content briefs grounded in your brand context, trend signals, and performance data. Each brief includes a creative direction, suggested format, target platform, and recommended app. Use this as a planning step before content creation, or feed briefs directly into the create-content endpoint for fully automated production. Returns up to `count` briefs (default 3, max 10) per request. # Create Content Source: https://docs.uselamina.ai/api-reference/create-content POST /v1/content/create One-call creation endpoint. Provide a natural-language brief and Lamina handles everything: resolves brand context, selects the best workflow/app, injects guidance into prompts, and executes. **How it works:** 1. Resolves brand DNA and active guidance for the workspace 2. If no `appId` is provided, selects the best-matching app for the brief, platform, and modality 3. Translates the brief into app-specific inputs using brand context 4. Starts the execution and returns immediately (async) **After starting:** Use `GET /v1/runs/{runId}` to poll for results, `GET /v1/runs/{runId}/stream` for SSE streaming, or pass a `webhook` query parameter on the execution. **Optional overrides:** - `appId` -- Force a specific app instead of automatic selection - `inputs` -- Override specific input parameters on the selected app - `templateId` -- Apply a content template for structured guidance (from `GET /v1/templates`) - `brandProfileId` / `campaignId` -- Scope brand context to a specific profile or campaign One-call creation. Describe what you want, and the API handles app selection, input mapping, brand context injection, and execution. Instead of manually choosing an app and mapping inputs, pass a `brief` describing the content you need. The engine resolves brand context, selects the best-fit app, injects creative guidance, and starts the execution. The operation is asynchronous — use the returned run ID with the wait, stream, or get-run endpoints to retrieve results. Best for agent-driven workflows where you want a single call to go from intent to finished asset. # Get App Details Source: https://docs.uselamina.ai/api-reference/get-app GET /v1/apps/{appId} Returns app metadata and the full list of input parameters the app accepts. Use this to understand what inputs you need to provide when running the app. Each parameter has a `type` that determines what value to send: - **text**: A free-form string (prompts, descriptions, product names) - **options**: Pick one label from the `options` array (e.g. `"Caucasian"`, `"Male"`, `"Studio"`) - **url**: A publicly accessible URL pointing to an image or video Use the parameter `name` as the key when providing `inputs` to the Run App endpoint. Inspect an app's input schema before running it. The `parameters` array is the source of truth for: * which parameters have a `default` (safe to omit) and which must be supplied * which values are valid for `options` type parameters * which fields expect public URLs * which parameter `key` (or `name`) values to use as keys in the `inputs` object — prefer `key`, the stable snake\_case identifier Always call this before `POST /v1/apps/{appId}/runs` — never guess parameter names or option values. # Get App Workflow Source: https://docs.uselamina.ai/api-reference/get-app-workflow GET /v1/apps/{appId}/workflow Returns the underlying workflow graph for an app -- the processing nodes and the edges (connections) between them. Useful for understanding how an app transforms inputs into outputs. Each node represents a processing step (e.g. image generation, video creation, upscaling). Edges show the data flow between nodes. Inspect the DAG (nodes and edges) behind an app. Each node is a processing step — image generation, video creation, compositing, upscaling — and edges show the data flow between them. Useful for agents that need to reason about what an app does before running it, or for building tooling that classifies apps by pipeline structure. # Get Brand Context Source: https://docs.uselamina.ai/api-reference/get-brand-context GET /v1/intelligence/brand-context Retrieve the complete brand context package for your workspace: brand DNA, workflow guidance, and top-performing content patterns. This is the primary intelligence endpoint -- use it to inform content creation decisions. The response combines three data sources resolved in parallel: - **brandDna** -- Voice attributes, visual identity, content pillars, audience signals, guardrails, tone spectrum, and performance profile extracted from the workspace's brand profile. - **guidance** -- Prompt directives, negative prompts, recommended creative moves, test ideas, winning/weak patterns, supporting metrics, and creative structure. Resolved from the active workflow guidance package, optionally scoped by campaign, workflow, platform, or objective. - **topPatterns** -- The highest-performing content items and aggregated pattern analysis (winning patterns vs weak patterns with occurrence counts and average performance scores). All parameters are optional. Omit them to get the workspace default context. Provide `brandProfileId` to scope to a specific brand profile, `campaignId` or `workflowId` to narrow guidance, and `platform`/`objective`/`modality` to get platform-specific recommendations. Retrieve the intelligence layer's understanding of your brand: voice attributes, visual identity, content pillars, audience signals, guardrails, and performance patterns. The Creative Engine uses this context automatically when you call `POST /v1/content/create`. Use this endpoint when you want to inspect what the engine knows, feed brand data into your own pipelines, or build brand-aware agent prompts. # Get Run Status Source: https://docs.uselamina.ai/api-reference/get-execution GET /v1/runs/{runId} Poll this endpoint to check execution progress and retrieve results. **Execution status:** `queued` -> `running` -> `completed` or `failed` **Output status:** `pending` -> `completed` or `error` **Output type:** `pending` while processing, then `image`, `video`, or `text` once produced. Poll every 3-5 seconds. Stop when execution `status` is `completed` or `failed`. Check the current status and outputs of an execution. For agents, prefer `GET /v1/runs/{id}/wait` — it blocks until the execution finishes, so you don't need a polling loop. Use this endpoint when you need to check status without blocking, or as a fallback. Status progresses: `queued` → `running` → `completed` or `failed`. Outputs start as `pending` placeholders and resolve to `image`, `video`, or `text` with a URL or content value. # Get Publish History Source: https://docs.uselamina.ai/api-reference/get-publish-history GET /v1/publishing/history List past publish operations for the authenticated user. Returns publish records including platform, content URLs, captions, post URLs, and status. Use this to verify past publishes, track which content went to which platforms, and audit publish failures. Returns the publish history for your workspace, including status and destination details for each publish job. Use this to audit what has been published, track pending or failed publishes, and build dashboards showing content distribution activity. Results are paginated and ordered by publish time (newest first). # Get Recommendations Source: https://docs.uselamina.ai/api-reference/get-recommendations GET /v1/intelligence/recommendations List open content recommendations for the workspace. Recommendations are generated by the Content Intelligence Engine based on brand context, trend signals, and historical performance data. Each recommendation includes a type (e.g. `trend_opportunity`, `gap_analysis`, `optimization`), priority level, title, summary, and a `data` object with structured details. Use recommendations to surface actionable content ideas for agents or human creators. Combine with `POST /v1/content/create` to act on a recommendation by passing its details as the content brief. Returns actionable content recommendations grounded in your brand context and recent performance data. Recommendations cover what to create next, which formats to prioritize, and how to improve existing content. Use this to drive editorial calendars, feed agent-based content planners, or surface suggestions in creative tools. # Get Template Source: https://docs.uselamina.ai/api-reference/get-template GET /v1/templates/{id} Get full template details including guidance data and multi-asset composition. The `guidanceData` section contains the structured guidance that will be injected into the content creation process when this template is used: - `promptDirectives` -- Positive instructions for the AI generation prompt - `negativePrompts` -- Things to avoid in generation - `winningPatterns` -- Patterns known to perform well - `testIdeas` -- Suggested A/B test variations - `creativeStructure` -- Optional structured creative direction The `multiAssetComposition` section (if present) describes a multi-asset content piece with roles for each asset (e.g. hero image, supporting image, caption). Returns full details for a specific content template, including the recommended app, pre-filled inputs, and customizable parameters. Use this to inspect what a template provides before using it as the basis for a content creation request. The response includes enough context for an agent or UI to show the template's purpose, preview its defaults, and let the user override specific fields. # Get Trends Source: https://docs.uselamina.ai/api-reference/get-trends GET /v1/intelligence/trends Aggregate trending content signals for the workspace. Returns trend patterns detected across the workspace's content landscape over a configurable time window. Use this to identify what is gaining traction, discover emerging opportunities, and feed trend data into content briefs or creation decisions. Returns current trend signals relevant to your workspace's content categories and audience. Trends are sourced from cross-platform performance data and refreshed regularly. Use this to discover what topics and formats are gaining traction, inform content strategy, or feed trend data into automated brief generation. # Get Usage Source: https://docs.uselamina.ai/api-reference/get-usage GET /v1/account/usage Check remaining credits and rate limit status for the workspace. Use this before starting expensive operations (batch creates, video generation) to verify sufficient credit balance. The `rateLimit` section shows the current rate limit configuration (requests per window). **Credit model:** Each app execution consumes credits based on the processing nodes involved. Image generation, video generation, and AI text generation each have different credit costs. Returns the current credit balance, usage breakdown, and rate limit status for your workspace. Use this to monitor consumption, enforce budget limits in your integration, or display remaining credits in a dashboard. The response includes credits used in the current billing period, remaining balance, and per-endpoint rate limit counters. # Get Webhook Signing Key Source: https://docs.uselamina.ai/api-reference/get-webhook-signing-key GET /v1/webhooks/signing-key Returns the public key used to verify webhook signatures. When you receive a webhook callback, verify it's from Lamina by checking the ED25519 signature in the `X-Lamina-Webhook-Signature` header. **Verification steps:** 1. Get the public key from this endpoint (cache it -- it rarely changes) 2. Reconstruct the signed message: `.` 3. Verify the ED25519 signature against the message using the public key **Headers sent with each webhook:** - `X-Lamina-Webhook-Signature` -- ED25519 signature (hex-encoded) - `X-Lamina-Webhook-Timestamp` -- Unix timestamp (seconds) when signed - `X-Lamina-Webhook-Request-Id` -- Run ID (for idempotency) - `X-Lamina-Webhook-User-Id` -- User ID that triggered the run - `Content-Type: application/json` **Retry policy:** If your endpoint returns non-2xx or times out (15s), we retry 3 times with backoff: 5s, 30s, 2 minutes. Each retry has a fresh signature. **Replay protection:** Reject webhooks with timestamps older than 5 minutes. The signing key is returned as a JWK. Consume it directly -- the `x` field is a raw 32-byte Ed25519 public key (base64url), not a DER/SPKI blob. **Node.js verification example:** ```javascript const crypto = require('crypto'); // Fetch once at startup: const { keys } = await fetch('.../v1/webhooks/signing-key').then(r => r.json()); // const PUBLIC_JWK = keys[0]; function verifyLaminaWebhook(rawBody, signatureHex, timestamp, jwk) { const publicKey = crypto.createPublicKey({ key: jwk, format: 'jwk' }); const message = Buffer.from(timestamp + '.' + rawBody); const signature = Buffer.from(signatureHex, 'hex'); return crypto.verify(null, message, publicKey, signature); } ``` **Python verification example:** ```python import base64 from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PublicKey # jwk = requests.get('.../v1/webhooks/signing-key').json()['keys'][0] # raw = base64.urlsafe_b64decode(jwk['x'] + '==') # 32-byte Ed25519 public key # public_key = Ed25519PublicKey.from_public_bytes(raw) def verify_lamina_webhook(raw_body, signature_hex, timestamp, public_key): message = f"{timestamp}.{raw_body}".encode() signature = bytes.fromhex(signature_hex) public_key.verify(signature, message) # Raises on failure ``` Returns the ED25519 public key for verifying webhook signatures. Cache this key. It rarely changes, and your webhook handler should use it to verify that incoming callbacks are genuinely from Lamina and have not been tampered with. # Health Check Source: https://docs.uselamina.ai/api-reference/health-check GET /v1/health Lightweight health probe. No authentication required. Use this from load balancers, orchestrators, or agent startup routines to verify the API is reachable before sending real requests. Lightweight health probe for load balancers, orchestrators, and agent startup routines. No authentication required. Returns the API version and server timestamp. Use this to verify the API is reachable before sending authenticated requests. # List Apps Source: https://docs.uselamina.ai/api-reference/list-apps GET /v1/apps Search and discover apps you can run. Returns your own workspace apps and public apps from other workspaces. Use `search` to find apps by name or description. Discover what the Creative Engine can do. Returns apps your workspace can run, plus public apps from other workspaces. Use `search` to find apps by capability — `"catalog"`, `"try on"`, `"video"`. Each app has a stable input contract, so the same integration code works across all of them. If you're using `POST /v1/content/create`, app selection is automatic. Use this endpoint when you want to pick the app yourself. # List Assets Source: https://docs.uselamina.ai/api-reference/list-assets GET /v1/assets List generated assets (images, videos, audio, text) from completed executions in your workspace. Use this to browse all content your workspace has produced, filter by execution or output type, and retrieve CDN URLs for downstream use (publishing, further processing, etc.). Only assets from completed executions with status `completed` are returned. Results are ordered by creation time, newest first. Returns generated assets (images, videos, text) from past executions in your workspace. Use this endpoint when you need to browse outputs across multiple executions, build a media library view, or let users pick previously generated assets as inputs for new runs. Results are paginated and can be filtered by media type. # List Channels Source: https://docs.uselamina.ai/api-reference/list-channels GET /v1/publishing/channels List social accounts connected to the workspace. These are the channels you can publish content to via `POST /v1/publishing/publish`. Each channel includes the platform, account name, username, and whether an Instagram business account is linked (for Facebook pages with Instagram). Only valid (non-expired) connections are returned. Discover connected distribution destinations — Instagram, TikTok, YouTube, and other social accounts linked to your workspace. Call this before `POST /v1/publishing/publish` to get the `accountIds` you'll need. Each channel shows the platform, username, and account type. # List Runs Source: https://docs.uselamina.ai/api-reference/list-executions GET /v1/runs List past runs for the workspace with optional filters. Returns a paginated list of run summaries (without full output details). Use this to find runs to inspect, or to monitor recent activity. For full output details on a specific run, use `GET /v1/runs/{runId}`. Results are ordered by creation time, newest first. List past executions for your workspace, ordered by creation time (newest first). Use this to build execution history views, audit logs, or find a specific run you need to inspect. Supports filtering by status, app ID, and time range. Results are paginated with offset/limit. Default limit is 25, maximum 100. # List Templates Source: https://docs.uselamina.ai/api-reference/list-templates GET /v1/templates List available content templates (both built-in and custom workspace templates). Templates provide structured guidance for content creation -- prompt directives, negative prompts, winning patterns, test ideas, and multi-asset compositions. Use templates with `POST /v1/content/create` by passing the template `id` as the `templateId` field. The content endpoint will apply the template's guidance to the content creation process. **Template categories:** - `launch` -- New product or feature launches - `trend` -- Capitalizing on trending content patterns - `campaign` -- Campaign-specific content strategies - `optimization` -- Improving existing content performance - `testing` -- A/B testing and experimentation strategies - `library` -- General-purpose content patterns Returns content creation templates available to your workspace. Templates are pre-configured starting points for common content types -- product hero shots, social media posts, campaign banners, and more. Each template specifies a recommended app and pre-filled input values. Use this to offer quick-start options in your UI or to let agents pick the right template for a given content goal. # Predict Content Performance Source: https://docs.uselamina.ai/api-reference/predict-performance POST /v1/intelligence/predict Predict how a content concept would perform on a given platform before creating it. Returns a performance prediction based on the workspace's historical data and brand context. Use this to validate ideas before spending credits on content creation, or to compare multiple concepts and pick the strongest one. The `concept` field is a natural-language description of the content you are considering -- it does not need to be a polished prompt. For example: "A lifestyle photo of our new sneakers in an urban setting with warm golden-hour lighting." Predict how a content concept will perform on a given platform before you create or publish it. Send a text `concept` describing the content idea along with the target `platform` and `modality`. Returns a performance prediction grounded in your workspace's historical data and brand context. Use this in pre-creation decision flows, A/B content selection, or automated quality gates to prioritize high-performing concepts. # Publish Content Source: https://docs.uselamina.ai/api-reference/publish-content POST /v1/publishing/publish Publish content (image, video, or caption) to one or more connected social accounts. Provide at least one of `imageUrl`, `videoUrl`, or `caption`. Use `GET /v1/publishing/channels` to discover available channel IDs. **Important:** URLs must point to publicly accessible media. If you have a generated asset from an execution, you may need to transfer it to CDN first using `POST /v1/publishing/transfer-asset` to ensure it remains available. Publish content to one or more connected social channels. Provide the asset (image URL, video URL, or both), a caption, and the target `accountIds` from the list-channels endpoint. You must provide at least one of `imageUrl`, `videoUrl`, or `caption`. Use the publish-history endpoint to track the status of past publishes. # Refine Run Source: https://docs.uselamina.ai/api-reference/refine-run POST /v1/runs/{runId}/feedback Iterative refinement: provide natural-language feedback on a completed run's outputs. An LLM identifies which workflow nodes need prompt changes, re-executes them, and returns updated outputs with a summary of what changed. Only works on runs in `completed` status. The refinement happens in-place on the same run — no new run ID is created. **Example:** After generating a product image, send `"Make the background brighter and add more contrast"` to refine the output without re-running the entire workflow. Iterative refinement for completed runs. Provide natural-language feedback and the engine re-executes only the nodes that need to change. The feedback agent uses an LLM to analyze which workflow nodes (image generation, video editing, etc.) need prompt modifications to address the feedback, rewrites only those prompts, and re-executes the affected subgraph. Use this for quality feedback loops: generate content, review it, then refine without starting over. # Run App Source: https://docs.uselamina.ai/api-reference/run-app POST /v1/apps/{appId}/runs Start an asynchronous app execution. Returns immediately with an execution ID and pre-created output placeholders. **Providing inputs:** - Use the `inputs` object with parameter **names** as keys (from the Get App response) - For `options` parameters, send the **option label** (e.g. `"Caucasian"`, not an internal value) - For `url` parameters, send a publicly accessible URL - For `text` parameters, send a string value - Omit optional parameters to use their defaults **After starting:** You can poll `GET /v1/runs/{runId}` every 3-5 seconds, use a **webhook** to receive results automatically, stream via `GET /v1/runs/{runId}/stream`, or combine approaches -- they all work side by side. **Webhook (optional, recommended for agents):** Pass `?webhook=https://your-server.com/callback` as a query parameter. When the execution completes, we POST the results to your URL -- same structure as the polling response, signed with ED25519 for verification. The polling endpoint still works regardless, so you can use it as a fallback or for safety checks. See the Webhook Verification endpoint for details. Start an execution of a specific app. You choose the app and provide exact inputs. For automatic app selection, use `POST /v1/content/create` instead. Key each field in `inputs` by the parameter's `key` (stable snake\_case identifier) or `name` from `GET /v1/apps/{appId}`. Prefer `key` when present — it never changes, while `name` is a display label and matches case-sensitively. ## Getting results | Method | Best for | | ----------------------------------------- | --------------------------------------------------------- | | **Wait** — `GET /v1/runs/{id}/wait` | Agents. Blocks until done, no polling loop. | | **Webhook** — pass `?webhook=` | Production. Results POST to your URL on completion. | | **SSE** — `GET /v1/runs/{id}/stream` | Real-time UIs. Server-Sent Events with per-node progress. | | **Poll** — `GET /v1/runs/{id}` every 3-5s | Fallback when none of the above fit. | # Score Content Source: https://docs.uselamina.ai/api-reference/score-content POST /v1/content/score Evaluate workspace content across multiple quality dimensions. Scores are computed based on brand alignment, predicted engagement, visual quality, and platform fitness. You can either: - Provide specific `contentItemIds` to score particular items - Omit `contentItemIds` and use `platform`/`modality`/`limit` filters to score recent workspace content matching those criteria Use scores to identify your strongest content, find items that need improvement, and prioritize what to publish. Evaluate workspace content across multiple quality dimensions including brand alignment, visual quality, and engagement potential. Pass `contentItemIds` to score specific items, or use the `platform` and `modality` filters to score recent content matching those criteria. Results include per-dimension breakdowns useful for automated quality gates, ranking content variants, or building review dashboards. # Stream Run Source: https://docs.uselamina.ai/api-reference/stream-execution GET /v1/runs/{runId}/stream Opens a Server-Sent Events (SSE) stream for real-time execution progress. This is an alternative to polling `GET /v1/runs/{runId}` -- use whichever fits your architecture better. **Connection:** The stream uses standard SSE (`text/event-stream`). Most HTTP clients and all modern browsers support this natively. **Event types:** | Event | When | Data payload | |-------|------|-------------| | `progress` | Execution status changes (e.g. `queued` -> `running`) | Full `ExecutionStatus` object | | `complete` | All outputs are ready | Full `ExecutionStatus` with final values | | `error` | Execution failed or was cancelled | Full `ExecutionStatus` with error details | | `timeout` | Stream exceeded 10-minute limit | `{ "message": "Stream timeout after 10 minutes" }` | | `ping` | Keepalive every ~15 seconds | Unix timestamp | **Terminal behavior:** - If the execution is already in a terminal state (`completed`, `failed`, `cancelled`) when you connect, the stream sends a single `complete` or `error` event and closes. - Otherwise, the stream stays open and polls internally every 2 seconds. - The stream automatically closes after 10 minutes with a `timeout` event. **Agent usage pattern:** ``` const es = new EventSource('/v1/runs/{id}/stream', { headers: { 'x-api-key': 'lma_...' } }); es.addEventListener('complete', (e) => { const result = JSON.parse(e.data); // result.outputs contains final values es.close(); }); es.addEventListener('error', (e) => { const result = JSON.parse(e.data); console.error(result.errorMessage); es.close(); }); ``` Opens a Server-Sent Events (SSE) stream for real-time execution progress. Use this instead of polling when you need instant updates -- for example, to drive a progress bar, stream partial outputs to a UI, or feed status changes into an agent loop. The connection stays open until the execution reaches `completed` or `failed`. Each SSE event contains the current execution state, including per-node output status. The final event carries the full completed (or failed) execution payload, after which the server closes the stream. # Transfer Asset to CDN Source: https://docs.uselamina.ai/api-reference/transfer-asset POST /v1/publishing/transfer-asset Transfer an asset from an external URL to the Lamina CDN for reliable, permanent access. Use this when you need a stable URL for publishing or downstream consumption. Generated assets from executions are often stored on temporary provider URLs that may expire. Transfer them to CDN before publishing or sharing. Returns the permanent CDN URL that can be used with `POST /v1/publishing/publish` or any other system. Transfer a generated asset from an external URL to the Lamina CDN for reliable, long-term access. AI model providers often host generated assets on temporary URLs that expire. Use this endpoint to copy the asset to Lamina's CDN before the source URL goes away. Returns the permanent CDN URL. Supports `image`, `video`, and `audio` assets. # Wait For Run Source: https://docs.uselamina.ai/api-reference/wait-for-execution GET /v1/runs/{runId}/wait Block until the execution reaches a terminal state (`completed`, `failed`, or `cancelled`) or the timeout expires. This is a simpler alternative to SSE streaming for agents and HTTP clients that cannot consume event streams. The server polls internally and returns once the execution is done. If the timeout expires before the execution finishes, the response includes `timeout: true` alongside the current execution state. This is NOT an error — the execution is still running. You can call this endpoint again or switch to polling. **Recommended for agents:** Use `timeout=60` (default) for typical image workflows. Use `timeout=120` for video workflows. Long-poll endpoint that blocks until the execution finishes or the timeout expires. Simpler alternative to SSE streaming for agents and HTTP clients that cannot consume event streams. Use this when your agent runtime does not support `EventSource` or SSE. The server polls internally and returns once the execution reaches `completed`, `failed`, or `cancelled`. If the timeout expires first, the response includes `timeout: true` alongside the current execution state — this is not an error. ## Recommended timeouts * Image workflows: `timeout=60` (default) * Video workflows: `timeout=120` ## Compared to other result delivery methods | Method | When to use | | ------------------------ | ----------------------------------------------------------------------- | | **Wait (this endpoint)** | Agent can make HTTP calls but not consume SSE. Simplest integration. | | **SSE stream** | Need real-time progress events (node completions, intermediate status). | | **Webhook** | Production workloads where you don't want open connections. | | **Polling** | Fallback when none of the above fit. Poll every 3-5 seconds. | # Authentication Source: https://docs.uselamina.ai/authentication How authentication works, how workspace-scoped keys behave, and what to send on every request. ## Overview Lamina's public Apps API uses **workspace-scoped API keys**. The canonical public path is `/v1/...`. Send the key with either: ```http theme={null} x-api-key: lma_your_api_key ``` or: ```http theme={null} Authorization: Bearer lma_your_api_key ``` Use `x-api-key` unless you have a reason to standardize on bearer auth across your stack. ## What The Key Grants Access To An API key is bound to a Lamina workspace. That means the key determines: * which private apps can be discovered * which executions can be started * which execution records can be read * which workspace context is used for authorization When you call the Apps API with an API key, you do **not** need a JWT or cookie-based session. ## Workspace Scope Keys are workspace-scoped, not user-scoped. In practice this means: * your own workspace apps are available through the key * public apps from other workspaces may also be visible * requests are still checked against the key's workspace context If you send `x-workspace-id`, it must match the workspace associated with the API key. ## Example ```bash theme={null} curl -H "x-api-key: lma_your_api_key" \ https://app.uselamina.ai/v1/apps ``` ## Security Recommendations * Keep Lamina API keys on your server, not in browser code. * Rotate keys if they are exposed in logs, screenshots, or commits. * Create separate keys for staging and production. * Prefer one key per integration so usage is easier to audit. ### Using the CLI locally `lamina login` opens your browser for an OAuth approval flow — the same pattern used by `gh`, `supabase`, `vercel`, `firebase`. No copy-paste of secrets. ```bash theme={null} lamina login ``` For CI / scripted callers, pass an API key non-interactively: ```bash theme={null} lamina login --api-key lma_your_api_key ``` Either path persists credentials at `~/.lamina/config.json` (mode `0600`). OAuth access tokens auto-refresh in the background when they're near expiry; if the refresh token also expires (after 30 days), the CLI surfaces a clear "session expired" error pointing at `lamina login`. See [Use The CLI And SDK](/guides/use-the-cli-and-sdk) for the full command reference. ## Common Authentication Errors ### `401 Missing API key` No supported auth header was sent. ### `401 Invalid API key` The key is malformed, revoked, or unknown. ### `403 Workspace header does not match API key workspace scope` The request included an `x-workspace-id` value that does not match the key's workspace. ## Next Steps * Read [Quick Start](/quick-start) to make your first request * Read [Use The CLI And SDK](/guides/use-the-cli-and-sdk) to work from a terminal * Read [Apps and Executions](/concepts/apps-and-executions) to understand the lifecycle * Read [Integration Recipes](/guides/capability-recipes) for commerce, try-on, video, and media workflow patterns * Read [Run Your First App](/guides/run-your-first-app) for a concrete end-to-end execution example # Apps And Executions Source: https://docs.uselamina.ai/concepts/apps-and-executions The core Lamina mental model: discover an app, inspect its inputs, run it asynchronously, then fetch the execution result. ## The Core Lamina Model ### App An **app** is a packaged workflow with a stable input contract. Think of an app as the unit you expose in your product. One app might generate product imagery, another might run try-on, and another might turn still assets into video. The integration pattern stays the same. You use app endpoints to: * discover what is available * inspect the app's parameters * optionally inspect the underlying workflow graph * start an execution ### Execution An **execution** is one asynchronous run of an app. Executions are durable server-side jobs. They may complete quickly for lightweight image tasks, or take longer for multi-step catalog and video workflows. ## Standard Flow Call `GET /v1/apps` to find an app you can run. Call `GET /v1/apps/{appId}` to see which inputs the app accepts. Call `POST /v1/apps/{appId}/runs?webhook=` with your inputs. Receive results via webhook callback, or poll `GET /v1/runs/{runId}` until the status is terminal. The point of this model is stability: your backend always works with `appId`, `runId`, `inputs`, and `outputs`, even as the workflows behind those apps evolve. ## Execution Lifecycle Executions move through a small set of top-level states: * `queued` * `running` * `completed` * `failed` For long-running apps, especially video generation, the request that starts execution returns quickly. Your integration should either pass a `?webhook=` URL to receive results automatically, or treat the execution ID as a job handle and poll for status. ## What Comes Back When you first start an execution, Lamina may return placeholder outputs with status `pending`. When the execution finishes: * output `type` changes from `pending` to something like `image`, `video`, or `text` * `value` contains the final result * `status` becomes `completed` for successful outputs If execution fails, inspect: * the top-level `errorMessage` * each output's `error` field ## When To Use Workflow Inspection `GET /v1/apps/{appId}/workflow` returns the node graph behind an app. Most product integrations do not need this endpoint. It is useful when you want to: * reason about what the app does internally * build richer agent tooling * classify apps by pipeline structure For most backend and product teams, the core API surface is still: * list apps * get app * run app * get execution # The Creative Engine Source: https://docs.uselamina.ai/concepts/creative-engine Lamina's core mental model: apps as creative schemas, executions as content operations, intelligence as editorial brain, distribution as delivery layer. ## What Is The Creative Engine Lamina is an agentic creative API for generating videos, movies, and images for products, brands, social, and ads. Instead of calling models directly, you call **apps** — packaged multi-node workflows that may internally chain image generation, video generation, LLM reasoning, compositing, upscaling, and audio synthesis. Each app has a typed input schema. You send inputs, the API handles orchestration, brand context injection, and output delivery. The engine sits between your application and the models. Your code never needs to know which providers run behind an app, how nodes are wired, or where outputs are hosted. You work with five primitives, and the engine handles everything else. ## The Five Primitives | Primitive | What It Is | CMS Analogy | | ---------------- | ------------------------------------------------ | ---------------------------------- | | **App** | A packaged workflow with a typed input schema | Content type / document schema | | **Execution** | One async run of an app that produces outputs | Document creation / mutation | | **Intelligence** | Brand context, predictions, recommendations | Editorial strategy / content rules | | **Template** | Pre-configured starting point for a content type | Content template / blueprint | | **Channel** | A connected publishing destination | Frontend / delivery endpoint | **Apps** define what you can create. **Executions** are the act of creating. **Intelligence** makes the output smarter. **Templates** accelerate setup. **Channels** deliver the result. This is the full content lifecycle through one API: discover, create, evaluate, distribute. ## Two Ways To Create ### One call: let the engine decide ```bash theme={null} curl -X POST \ -H "x-api-key: lma_your_api_key" \ -H "Content-Type: application/json" \ -d '{ "brief": "A premium hero shot of a leather handbag on marble, warm studio lighting", "contentType": "product_image" }' \ https://app.uselamina.ai/v1/content/create ``` One call. The engine resolves brand context, selects the best-fit app, maps inputs, and starts the execution. You get back a run ID and collect results the same way as any other execution. This is the path for agents and automation — go from intent to asset without manual orchestration. ### Step by step: you pick the app ```bash theme={null} # 1. Discover curl -H "x-api-key: lma_your_api_key" \ https://app.uselamina.ai/v1/apps # 2. Inspect curl -H "x-api-key: lma_your_api_key" \ https://app.uselamina.ai/v1/apps/{appId} # 3. Execute curl -X POST \ -H "x-api-key: lma_your_api_key" \ -H "Content-Type: application/json" \ -d '{ "inputs": { "Front": "https://example.com/front.jpg", "Location": "Studio" } }' \ "https://app.uselamina.ai/v1/apps/{appId}/runs?webhook=https://your-server.com/callback" ``` You choose the app, inspect its schema, provide exact inputs. Full control over every parameter. Both paths produce the same execution object, the same output structure, and the same delivery options. The difference is who does the thinking — your code or the engine. ## The Execution Lifecycle Every creation in Lamina — whether started via `content/create` or `apps/{appId}/runs` — follows the same lifecycle: The execution is accepted and waiting for capacity. You receive a run ID immediately. The workflow nodes are executing. For multi-step apps, individual nodes complete in sequence or parallel. Terminal state. Outputs resolve from `pending` placeholders to concrete `image`, `video`, or `text` values. Four ways to get results: | Method | When to use | | ----------------- | ----------------------------------------------------------------------------------- | | **Wait endpoint** | Simplest. `GET /v1/runs/{id}/wait` blocks until done. Best for agents. | | **SSE stream** | Real-time progress. `GET /v1/runs/{id}/stream` sends events as nodes complete. | | **Webhook** | Production workloads. Pass `?webhook=` at execution time. Signed with ED25519. | | **Polling** | Fallback. Poll `GET /v1/runs/{id}` every 3-5 seconds. | See [Handle Long-Running Executions](/guides/handle-long-running-executions) for implementation patterns. ## Intelligence Layer The intelligence endpoints are the editorial brain of the engine. They make creation smarter over time. | Endpoint | What it provides | | -------------------------------------- | ------------------------------------------------------------------ | | `GET /v1/intelligence/brand-context` | Brand DNA, visual guidelines, tone, creative constraints | | `POST /v1/intelligence/predict` | Performance prediction before you publish | | `GET /v1/intelligence/recommendations` | Actionable content suggestions based on brand and performance data | | `GET /v1/intelligence/trends` | Trend signals for your categories | When you use `POST /v1/content/create`, the engine queries brand context automatically and injects it into the execution. When you use the step-by-step path, you can fetch brand context yourself and use it to inform your input choices. Intelligence is what separates a creative engine from a model proxy. The engine knows your brand, predicts what will perform, and recommends what to create next. ## Distribution Layer Channels are connected publishing destinations — social accounts, CDN endpoints, storage targets. | Endpoint | What it does | | ------------------------------------ | ----------------------------------------------- | | `GET /v1/publishing/channels` | List connected social accounts and destinations | | `POST /v1/publishing/publish` | Publish content to one or more channels | | `POST /v1/publishing/transfer-asset` | Transfer assets to external CDN or storage | | `GET /v1/publishing/history` | View publish history and job status | The pattern is the same as a headless CMS delivering to frontends: your content is created once, stored as structured data, and delivered to any channel from one API. No per-channel integration work on your side. ## What This Means For Agents The Creative Engine is designed for programmatic consumption. If you are building an agent — whether with Claude, GPT, LangChain, or your own orchestrator — the API gives you: * **Schema-first discovery.** Every app has a typed input contract you can inspect at runtime. No guessing. * **Async-native execution.** Executions return immediately. Results arrive via webhook, SSE, wait endpoint, or polling. * **Intelligence grounding.** Brand context and performance data are API-accessible. Your agent can make informed creative decisions, not blind ones. * **One-call creation.** `POST /v1/content/create` goes from a text brief to a finished asset without manual app selection or input mapping. * **Structured outputs.** Every result is typed (`image`, `video`, `text`) with a stable schema. Parse once, handle every app. If you are building an agent, start with [Create Content](/api-reference/create-content). If you are building a product integration, start with [Quick Start](/quick-start). # Inputs And Outputs Source: https://docs.uselamina.ai/concepts/inputs-and-outputs How Lamina parameter types map to request payloads, and how to interpret execution outputs. ## Building Requests Safely When starting an execution, send an `inputs` object keyed by parameter **name** from `GET /v1/apps/{appId}`. Parameter names are case-sensitive and must match the app metadata exactly. This is what lets Lamina support different apps without changing the request envelope. Your code keeps sending `inputs`; only the parameter schema changes from app to app. Example: ```json theme={null} { "inputs": { "Front": "https://example.com/front.jpg", "Back": "https://example.com/back.jpg", "Model Gender": "Female" } } ``` ## Public Parameter Types | Type | What you send | Notes | | --------- | --------------- | -------------------------------------------- | | `text` | a string | prompts, descriptions, product names | | `options` | an option label | send the displayed label, not an internal ID | | `url` | a public URL | typically an image or video URL | ## Important Rules ### `options` For option parameters, send the **label** shown in the app metadata. Example: ```json theme={null} { "inputs": { "Location": "Studio" } } ``` ### `url` URLs should be publicly accessible by Lamina at execution time. Good sources include: * your own CDN * cloud object storage with public access * signed URLs that will remain valid long enough for processing If you use signed URLs, make sure they stay valid for the full processing window. ### Defaults Every parameter is returned with `required: true`. If it has a `default`, omitting it is safe — the app uses the default value. If it has no `default`, you must supply a value or the request is rejected with a `missing_no_default` error. ## Reading Results Executions return an `outputs` array. Each output object contains: * `id` * `label` * `type` * `value` * `status` * `error` Example completed output: ```json theme={null} { "id": "node-1", "label": "Generated Video", "type": "video", "value": "https://cdn.example.com/result.mp4", "status": "completed", "error": null } ``` ## Output Types Common output types include: * `image` * `video` * `text` * `pending` **Branch on `status`, not `type`, to decide if an output is finished.** `type` stays `"pending"` on failed outputs — only `status` flips to `"error"`. At execution start, outputs appear with `type: "pending"` and `value: null`; treat those as placeholders until `status` reaches `completed`, `error`, or `cancelled`. ## Agent Artifacts Agent-facing status responses also include an `artifacts` array. `outputs` remains the backward-compatible raw result list, while `artifacts` adds reuse metadata so agents can chain creative work safely. Each artifact includes: * `id`, `label`, `type`, `status`, and `error` * `url` when the artifact is downloadable media * `mimeType`, `dimensions`, and `durationSeconds` when known or inferable * `provider`, `model`, `cost`, and `prompt` when available * `reusableAs` roles such as `image_reference` or `video_reference` * `provenance` with `runId`, `workflowId`, node identity, and output index Use `artifacts` when an agent needs to reuse, publish, inspect, or pass a result into another Lamina run. Use `outputs` for compatibility with existing integrations. ## Practical Integration Pattern For admin tooling, merchant tooling, and agent-driven clients: 1. fetch the app metadata 2. render input controls from the parameter list 3. submit `inputs` keyed by parameter name 4. handle output rendering based on output type # Errors Source: https://docs.uselamina.ai/errors Structured error responses, machine-readable codes, retry guidance, and recovery patterns. ## Error Envelope Every `/v1/` error response uses a structured envelope: ```json theme={null} { "error": "Human-readable error message", "code": "AUTH_INVALID_KEY", "retryable": false, "requestId": "550e8400-e29b-41d4-a716-446655440000" } ``` | Field | Type | Always present | Description | | ------------ | ------- | -------------------------- | --------------------------------------------- | | `error` | string | Yes | Human-readable error message | | `code` | string | Yes | Machine-readable error code (see table below) | | `retryable` | boolean | Yes | Whether the request can be retried | | `retryAfter` | integer | Only on `429` | Seconds to wait before retrying | | `details` | array | Only on `VALIDATION_ERROR` | Per-field validation errors | | `requestId` | string | Yes | Trace ID from `X-Request-Id` header | **For agents:** branch on `code` for error handling, use `retryable` to decide whether to retry, and include `requestId` in support requests. ## Error Codes | Code | HTTP | Retryable | When | | ------------------------ | ---- | --------- | --------------------------------------------------------- | | `AUTH_MISSING_KEY` | 401 | No | No API key in `x-api-key` or `Authorization` header | | `AUTH_INVALID_KEY` | 401 | No | Key hash not found or key is revoked | | `AUTH_INVALID_CONTEXT` | 401 | No | Key record is missing workspace or user context | | `AUTH_FAILED` | 500 | Yes | Database error during key validation | | `FORBIDDEN` | 403 | No | Workspace mismatch, app access denied, trigger inactive | | `NOT_FOUND` | 404 | No | App, execution, trigger, or template not found | | `VALIDATION_ERROR` | 400 | No | Invalid inputs, missing required fields, bad request body | | `RATE_LIMITED` | 429 | Yes | Request rate exceeded; check `retryAfter` | | `WEBHOOK_INVALID_URL` | 400 | No | Webhook URL malformed or unsupported protocol | | `WEBHOOK_INVALID_SECRET` | 401 | No | Webhook secret does not match | | `RESOURCE_UNAVAILABLE` | 503 | Yes | Service not configured or temporarily unavailable | | `INTERNAL_ERROR` | 500 | Yes | Unhandled server error | ## HTTP Status Codes | Status | Meaning | | ------ | -------------------------------------------- | | `200` | Successful read | | `202` | Execution accepted and queued asynchronously | | `400` | Invalid request body or inputs | | `401` | Missing or invalid API key | | `403` | Access denied for this workspace or app | | `404` | App or execution not found | | `429` | Rate limit exceeded | | `500` | Unexpected server-side failure | | `503` | Service temporarily unavailable | ## Interpreting `202 Accepted` `POST /v1/apps/{appId}/runs` returns `202` when Lamina has accepted the job. That does **not** mean outputs are ready yet. After a `202` response: * store the returned run ID * call `GET /v1/runs/{runId}/wait?timeout=60` (simplest) * or poll `GET /v1/runs/{runId}` every 3-5 seconds * or wait for your webhook callback ## Common Error Cases ### Authentication errors ```json theme={null} { "error": "Missing API key", "code": "AUTH_MISSING_KEY", "retryable": false, "requestId": "abc-123" } ``` Action: * `AUTH_MISSING_KEY`: send `x-api-key` header or `Authorization: Bearer ...` * `AUTH_INVALID_KEY`: verify the key value, check it has not been revoked, confirm correct environment * `AUTH_INVALID_CONTEXT`: the key exists but is misconfigured — contact support ### Invalid inputs Validation errors include a `details` array with one structured entry per problem: ```json theme={null} { "error": "Invalid inputs", "code": "VALIDATION_ERROR", "retryable": false, "requestId": "abc-123", "details": [ { "param": "Mention Key Ingredients", "code": "missing_no_default", "message": "\"Mention Key Ingredients\" is required: no default is configured for this parameter." }, { "param": "Location", "code": "invalid_option", "message": "\"Location\": invalid option \"Mars\". Must be one of: Studio, Urban, Park" } ] } ``` Each detail entry has: | Field | Type | Meaning | | --------- | -------------- | ------------------------------------------ | | `param` | string \| null | The offending parameter name | | `code` | string | A machine-readable detail code (see below) | | `message` | string | A human-readable explanation | **Detail codes:** | Code | When it fires | | -------------------- | ---------------------------------------------------------------------------- | | `missing_no_default` | A parameter has no default and you didn't supply a value for it | | `unknown_parameter` | The input key doesn't match any parameter on this app | | `invalid_option` | You sent an option value that isn't in the allowed list | | `invalid_media` | A `url` parameter failed media validation (bad URL, unreachable, wrong type) | | `invalid_type` | You sent the wrong JSON type (e.g. a number where a string was expected) | **About `required` and defaults:** every parameter in the schema is returned with `required: true`, so agents always know to supply a value. If a parameter has a `default`, omitting it is still safe -- the workflow will run with the default. If it has no `default`, omitting it triggers a `missing_no_default` error with `400`. **How to recover:** 1. Parse `details[]` and branch on `code`. 2. For `missing_no_default`: read the `param` field, look it up in your cached schema, and fill in a value. 3. For `unknown_parameter`: re-fetch `GET /v1/apps/{appId}` -- your cached schema may be stale. 4. For `invalid_option`: read the message for the allowed option list and pick one. 5. For `invalid_media` or `invalid_type`: fix the value and retry. ### Not found ```json theme={null} { "error": "App not found", "code": "NOT_FOUND", "retryable": false, "requestId": "abc-123" } ``` Action: * verify the `appId` or `runId` * confirm the resource is accessible to the workspace associated with your key ### Rate limit exceeded ```json theme={null} { "error": "Too many requests, please try again later", "code": "RATE_LIMITED", "retryable": true, "retryAfter": 60, "requestId": "abc-123" } ``` Action: * wait `retryAfter` seconds before retrying * read `RateLimit-Limit`, `RateLimit-Remaining`, and `RateLimit-Reset` headers * avoid tight polling loops; prefer webhooks or the wait endpoint for long-running executions ## Current Rate Limit All `/v1/*` endpoints are currently limited to **100 requests per minute per IP**. ## Retry Guidance Use the `retryable` field to decide whether to retry. As a general rule: **Safe to retry** (`retryable: true`): * `RATE_LIMITED` -- wait `retryAfter` seconds first * `INTERNAL_ERROR` -- transient server failure, use exponential backoff * `AUTH_FAILED` -- transient database error * `RESOURCE_UNAVAILABLE` -- service temporarily down * Network timeouts while fetching status * Idempotent reads (`GET /v1/apps`, `GET /v1/runs/{id}`) **Do not retry unchanged** (`retryable: false`): * `VALIDATION_ERROR` -- fix the inputs first * `AUTH_MISSING_KEY` / `AUTH_INVALID_KEY` -- fix credentials * `FORBIDDEN` -- access denied, won't change on retry * `NOT_FOUND` -- wrong ID ## Support Checklist If you need to debug an issue quickly, capture: * `requestId` from the error response * `code` from the error response * request path and method * app ID or execution ID * timestamp # Agent Integration Patterns Source: https://docs.uselamina.ai/guides/agent-integration-patterns How coding agents should use Lamina through MCP, discover apps safely, execute with webhooks, and consume results reliably. ## Use Hosted MCP When Your Agent Supports Remote Install The preferred agent integration is Lamina's hosted remote MCP server. It lets MCP clients install Lamina through OAuth, bind access to a Lamina workspace, and call the five high-level creative tools without placing an API key in every tool call. Install page: ```text theme={null} https://app.uselamina.ai/mcp/install ``` Hosted endpoint: ```text theme={null} https://app.uselamina.ai/mcp/agent ``` The endpoint supports OAuth discovery: * protected resource metadata: `/.well-known/oauth-protected-resource/mcp/agent` * authorization server metadata: `/.well-known/oauth-authorization-server` * authorization endpoint: `/mcp/oauth/authorize` * token endpoint: `/mcp/oauth/token` * dynamic client registration: `/mcp/oauth/register` Read [MCP OAuth Install](/guides/mcp-oauth-install) for the full install and verification flow. Read [Agent Recipes](/guides/agent-recipes) for end-to-end create, wait, clarification, brand context, and artifact reuse examples. Use [Agent Runtime Benchmarks](/guides/agent-runtime-benchmarks) to measure install success, auth reliability, first generation time, clarification loops, and output usability across MCP clients. ## Use Local MCP When Your Agent Can Spawn Tools If your agent platform only supports local stdio MCP, run the Lamina MCP server locally and let the agent call tools instead of hand-rolling HTTP requests. Today, the MCP server lives in this repository and can be started with: ```bash theme={null} lamina mcp serve ``` Or directly: ```bash theme={null} npx -y @uselamina/mcp ``` The local stdio MCP server uses the same Lamina API key resolution order as the CLI: 1. explicit `--api-key` 2. `LAMINA_API_KEY` 3. saved CLI login from `~/.lamina/config.json` ## MCP Tool Surface The MCP server intentionally exposes five high-level tools. This keeps agent tool selection simple while preserving the full `/v1` REST API for developer integrations and power users. * `lamina_create` - create brand-aware content from a natural-language brief * `lamina_status` - check a run or wait for generated assets * `lamina_discover` - find what Lamina can make for a creative goal * `lamina_brand` - load brand memory, guidance, and winning patterns * `lamina_batch` - queue up to 10 related content creations Lower-level operations such as direct app inspection, direct app execution, publishing, asset transfer, usage, templates, and webhook management remain available through `/v1`. ## Example MCP Client Configuration For hosted remote MCP clients, add the remote server URL and complete OAuth in the client: ```json theme={null} { "mcpServers": { "lamina": { "url": "https://app.uselamina.ai/mcp/agent" } } } ``` For local agent clients that support a spawned process: ```json theme={null} { "mcpServers": { "lamina": { "command": "npx", "args": ["-y", "@uselamina/mcp"], "env": { "LAMINA_API_KEY": "lma_your_api_key" } } } } ``` If the agent runs under the same user account as your CLI login, it can also reuse the saved key from `~/.lamina/config.json`. ## Build Agents Around Outcomes, Not Raw APIs Lamina apps are not single-shot utility functions. They are packaged creative workflows with asynchronous execution. Agents should usually describe the desired outcome to `lamina_create` instead of chaining low-level calls themselves. An agent should treat Lamina as a system with: * discoverable app capabilities * brand-aware prompt and input mapping * asynchronous execution * polling-based result delivery through `lamina_status` * typed outputs The best integrations do **not** guess raw workflow inputs from app names alone. They let Lamina select and run the workflow, and only pass `appId` or advanced `inputs` when a developer already knows the exact workflow contract. ## Recommended Agent Flow Call `lamina_create` with the user's creative request, target platform, and modality when known. Call `lamina_status` with the returned `runId`. Set `wait=true` when the agent should block for final assets. Call `lamina_discover` when the user asks what Lamina can make, or when the agent needs workflow suggestions before creating. Call `lamina_brand` when the agent needs explicit voice, visual, guardrail, or pattern context for planning. Call `lamina_batch` for catalog shots, campaign variants, or content series. ## Webhooks For REST Integrations The MCP surface uses `lamina_status` for result polling. If you are building a server-side integration against `/v1`, you can use REST webhooks instead: 1. expose a public HTTPS callback URL from your integration 2. start a run with `POST /v1/apps/{appId}/runs?webhook=` 3. verify the signed callback when Lamina sends completion data This lets your application react to the Lamina callback directly instead of polling. ## Direct REST App Runs When using direct REST app execution instead of `lamina_create`, fetch app metadata unless you already have fresh metadata cached for that exact `appId`. This is important because the app metadata is the source of truth for: * parameter names * whether fields are required * valid `options` labels * whether a field expects a media URL ## Use Parameter Names Exactly When calling `POST /v1/apps/{appId}/runs`, `inputs` should be keyed by parameter name. Example: ```json theme={null} { "inputs": { "Front": "https://example.com/front.jpg", "Location": "Studio" } } ``` Do not invent aliases or normalize parameter names unless your integration keeps a reliable mapping layer. ## Handle `options` Carefully For parameters of type `options`, send one of the labels returned by the app metadata. If the app says the valid options are: ```json theme={null} ["Male", "Female"] ``` then the execution request should send one of those exact labels. ## Prefer Webhooks In Production For production agent integrations, always use webhooks instead of polling: ``` POST /v1/apps/{appId}/runs?webhook=https://your-agent.com/lamina-callback ``` Benefits over polling: * No polling loop to manage * Results arrive immediately when ready * Signed with ED25519 - verify authenticity * Automatic retries if your endpoint is temporarily down For MCP clients, prefer `lamina_create` followed by `lamina_status` unless your integration needs direct REST webhook control. ## Webhook Payload Structure The webhook payload matches the polling response exactly: ```json theme={null} { "data": { "runId": "...", "workflowId": "...", "status": "completed", "outputs": [ { "id": "...", "label": "Generated Image", "type": "image", "value": "https://...", "status": "completed", "error": null } ], "errorMessage": null, "startedAt": "...", "completedAt": "...", "createdAt": "..." } } ``` `data.executionId` may also be present as a deprecated compatibility alias for `data.runId`. New integrations should store and compare `runId`. ## Read Output Type Before Consuming Value Do not assume every output is an image URL. Check: * `type` * `status` * `value` Examples of output types include: * `image` * `video` * `text` * `pending` ## Handle Failures Explicitly If execution fails: * inspect top-level `errorMessage` * inspect each output's `error` * avoid blindly retrying the same invalid inputs If the failure is caused by invalid inputs, fetch the app metadata again and rebuild the request payload. ## Good Agent Behaviors * use `lamina_create` for brief-to-result work * cache app metadata briefly, but refresh when uncertain * use `lamina_status` for MCP result delivery * preserve `runId` for later retrieval * branch logic on output `type` * verify webhook signatures * surface execution failure with the returned error details ## Poor Agent Behaviors * guessing parameter names from natural language * sending internal option IDs that were never returned by the API * treating execution as synchronous * resubmitting the same long-running request repeatedly because output is not immediate * ignoring webhook signatures # Agent Recipes Source: https://docs.uselamina.ai/guides/agent-recipes End-to-end recipes for agents that create, monitor, clarify, and reuse creative work with Lamina. ## What Agents Should Prefer For interactive agents, prefer hosted MCP: ```text theme={null} https://app.uselamina.ai/mcp/agent ``` For server-side agents, use the SDK or REST API with `LAMINA_API_KEY`. Hosted MCP and REST share the same runtime model: create work, preserve `runId`, wait or subscribe to completion, inspect typed outputs, and reuse final artifacts explicitly. ## Recipe 1: Hosted MCP Creative Run After installing Lamina MCP in Claude Code, Codex, Cursor, or another MCP client, use this flow: Call `lamina_create` with `brief`, `platform`, and `modality`. If the response status is `needs_input`, ask the user for the listed missing fields and call `lamina_create` again with those values in `inputs`. Call `lamina_status` with `runId` and `wait=true`. Inspect each output's `type`, `status`, and `value`. Reuse completed asset URLs as explicit references in the next Lamina run. Example MCP call: ```json theme={null} { "tool": "lamina_create", "arguments": { "brief": "Create a cinematic launch image for a premium running shoe.", "platform": "instagram", "modality": "image" } } ``` Expected started response: ```json theme={null} { "runId": "99ebc7c6-24f3-4fc2-9d53-2e3f49f3bec1", "status": "queued", "workflowName": "Product Hero Image", "selectedApp": { "name": "Product Hero Image", "whyMatched": "Matches image launch creative intent" } } ``` Expected clarification response: ```json theme={null} { "runId": null, "status": "needs_input", "needsInput": { "message": "This workflow needs additional inputs before Lamina can start the run.", "missing": [ { "name": "Product Image", "type": "url", "accept": ["image"] } ], "suggestedPrompt": "Ask the user for Product Image." } } ``` ## Recipe 2: Local Stdio MCP Variant Use local stdio MCP when the agent can spawn local tools but cannot complete remote OAuth. ```json theme={null} { "mcpServers": { "lamina": { "command": "npx", "args": ["-y", "@uselamina/mcp"], "env": { "LAMINA_API_KEY": "lma_your_api_key" } } } } ``` The tool flow is the same as hosted MCP: call `lamina_create`, handle `needs_input`, then call `lamina_status` with the returned `runId`. ## Recipe 3: TypeScript Agent With SDK Use this for Node.js agents, backend workers, and agent runtimes that own their own API key. ```ts theme={null} import { LaminaClient } from '@uselamina/sdk'; const client = LaminaClient.fromEnv(); const created = await client.content.create({ brief: 'Create a premium Instagram launch visual for a running shoe.', platform: 'instagram', modality: 'image', }); if (created.data.needsInput || !created.data.runId) { console.log(created.data.needsInput?.missing); console.log(created.data.needsInput?.suggestedPrompt); process.exit(0); } const result = await client.runs.wait(created.data.runId, { timeoutMs: 120_000, intervalMs: 4_000, }); for (const output of result.data.outputs) { if (output.status === 'completed') { console.log(output.type, output.value); } } ``` Full example: ```text theme={null} examples/agents/typescript/creative-agent-recipe.ts ``` ## Recipe 4: Python Agent With REST Use this when the agent is written in Python or runs in an environment without the TypeScript SDK. ```python theme={null} created = request( "/v1/content/create", method="POST", body={ "brief": "Create a cinematic launch image for a premium running shoe.", "platform": "instagram", "modality": "image", }, ) if created["data"].get("needsInput") or not created["data"].get("runId"): print(created["data"].get("needsInput", {}).get("missing", [])) raise SystemExit(0) status = wait_for_run(created["data"]["runId"]) ``` Full example: ```text theme={null} examples/agents/python/creative_agent_recipe.py ``` ## Recipe 5: Brand-Aware Planning Agents should call brand context before generating when the user asks for brand-safe, campaign-safe, or performance-aware content. ```ts theme={null} const brand = await client.intelligence.getBrandContext({ platform: 'instagram', modality: 'image', objective: 'launch campaign', }); console.log(brand.data.brandDna?.guardrails ?? []); console.log(brand.data.guidance?.promptDirectives ?? []); ``` For MCP clients, use `lamina_brand` before `lamina_create` when the agent needs explicit voice, visual, guardrail, or winning-pattern context. ## Recipe 6: Artifact Reuse Agents should not scrape provider-specific payloads. Treat completed outputs with string `value` fields as reusable artifact URLs. ```ts theme={null} const artifacts = result.data.outputs .filter((output) => output.status === 'completed' && typeof output.value === 'string') .map((output) => ({ type: output.type, url: output.value })); if (artifacts[0]) { await client.content.create({ brief: 'Create a matching story variant using this reference.', platform: 'instagram-story', modality: artifacts[0].type, inputs: { Reference: artifacts[0].url, }, }); } ``` ## Smoke Test Checklist Run this checklist for every supported MCP client before broad distribution: 1. Install `https://app.uselamina.ai/mcp/agent`. 2. Complete OAuth and choose a workspace. 3. Confirm the client lists exactly five Lamina tools. 4. Call `lamina_discover` for an image or video launch brief. 5. Call `lamina_create` with a simple brief. 6. If `needs_input` is returned, provide the missing input and retry. 7. Call `lamina_status` with `wait=true`. 8. Confirm completed outputs include typed values the agent can reuse. 9. Remove or revoke the client after testing if it used a temporary workspace. ## Troubleshooting * `authorization_required`: start OAuth from the MCP client or reinstall the hosted server. * `insufficient_scope`: reconnect and approve the requested scope. * `needs_input`: ask the user for the listed missing field; do not start a run with guessed data. * `failed`: surface `errorMessage` and output-level errors; do not blindly retry invalid inputs. * Timeout while waiting: call `lamina_status` again with the same `runId`. # Agent Runtime Benchmarks Source: https://docs.uselamina.ai/guides/agent-runtime-benchmarks Metrics, scenarios, and pass criteria for measuring Lamina as an agent-native creative runtime. Use this guide to measure whether an AI agent can install Lamina, authenticate, create assets, and retrieve outputs without custom recovery code. ## What To Measure Track every agent run against five stages: | Stage | Success Signal | Failure Stage | | ------- | ---------------------------------------------------------------------------------- | ------------- | | Install | Client discovers `/mcp/agent` and completes dynamic registration or manual install | `install` | | Auth | OAuth authorization and token exchange succeed for the selected workspace | `auth` | | Input | The agent supplies enough brief, brand, and asset context to start a run | `input` | | Runtime | Lamina queues and completes the selected creative workflow | `runtime` | | Output | The agent receives usable final assets or structured outputs | `output` | The hosted MCP runtime emits benchmark-oriented telemetry events when server telemetry is enabled with `POSTHOG_SERVER_API_KEY`. | Event | When It Fires | | -------------------------------------------- | --------------------------------------------------------------- | | `agent_runtime.install.discovery_challenged` | A remote MCP client discovers that `/mcp/agent` requires OAuth | | `agent_runtime.install.client_registered` | Dynamic MCP OAuth registration succeeds | | `agent_runtime.install.failed` | Dynamic registration fails | | `agent_runtime.auth.authorize_redirected` | The OAuth authorize request validates and redirects to consent | | `agent_runtime.auth.approved` | A signed-in user approves workspace access | | `agent_runtime.auth.token_issued` | Authorization code or refresh-token exchange succeeds | | `agent_runtime.auth.succeeded` | A bearer token is accepted for `/mcp/agent` | | `agent_runtime.auth.failed` | OAuth authorization, token exchange, or bearer validation fails | | `agent_runtime.tool_call.completed` | A five-tool MCP call completes and is classified | Every `agent_runtime.tool_call.completed` event includes: ```json theme={null} { "tool_name": "lamina_create", "success": true, "outcome": "success", "failure_stage": null, "failure_category": null, "duration_ms": 1420, "auth_mode": "oauth", "run_status": "completed", "needs_input": false, "output_count": 1 } ``` ## Benchmark Scenarios Run these scenarios for each supported MCP client before calling the distribution ready. | Scenario | Required Proof | | -------------------- | ------------------------------------------------------------------------------------------------------------------------------------ | | Hosted OAuth install | The client discovers metadata, registers or uses its configured client, shows consent, receives tokens, and lists exactly five tools | | First image run | The agent calls `lamina_create` from a short prompt and obtains at least one image output through `lamina_status` | | First video run | The agent calls `lamina_create` for a video task and can wait or poll until a terminal result | | Brand-aware planning | The agent calls `lamina_brand`, uses the returned guidance, and starts a run with the same workspace context | | Batch creative | The agent calls `lamina_batch` with 3 to 10 related briefs and receives per-item `runId` values or actionable item errors | | Clarification loop | An intentionally underspecified request returns `needsInput` with missing fields, examples, and a follow-up prompt | | Auth recovery | An expired or insufficient-scope token yields a clear OAuth error and the client can reauthorize | ## Pass Criteria Use these thresholds for the preferred-runtime scorecard: | Metric | Target | | ----------------------------------- | ----------------------------------------------------- | | Install success rate | 95% or higher per supported client | | First successful generation time | Under 5 minutes from clean client install | | Tool call auth failure rate | Under 1% after successful install | | Clarification-loop rate | Tracked separately from hard failures | | Run completion rate | 90% or higher for benchmark workflows | | Webhook or polling delivery success | 99% for terminal run visibility | | Output usability rate | 95% of completed runs have at least one usable output | Do not count `needs_input` as a runtime failure. It is an input-stage clarification outcome and should be optimized by improving discovery, examples, and prompt mapping. ## Suggested Benchmark Record Store one record per client, scenario, and run: ```json theme={null} { "client": "claude-code", "scenario": "first-image-run", "startedAt": "2026-04-23T07:00:00.000Z", "completedAt": "2026-04-23T07:02:11.000Z", "installSucceeded": true, "authSucceeded": true, "toolCalls": [ { "name": "lamina_create", "outcome": "success", "durationMs": 812 }, { "name": "lamina_status", "outcome": "success", "durationMs": 1304 } ], "runId": "00000000-0000-0000-0000-000000000000", "finalStatus": "completed", "outputCount": 1, "failureStage": null, "notes": "Clean install, OAuth consent, one image output." } ``` ## Dashboard Breakdown At minimum, build dashboard cards for: * Install starts, successful registrations, and failed registrations by MCP client * OAuth approvals, token issues, bearer-token failures, and insufficient-scope failures * Tool-call success rate by `tool_name` * `needs_input` rate by `tool_name` and requested modality * Runtime failure rate by workflow/app when available * Empty-output and failed-output rate after terminal `completed` status * Time from first install event to first completed output ## Related Guides * [MCP OAuth Install](/guides/mcp-oauth-install) * [Agent Integration Patterns](/guides/agent-integration-patterns) * [Handle Long-Running Executions](/guides/handle-long-running-executions) # Integration Recipes Source: https://docs.uselamina.ai/guides/capability-recipes Map common generative media use cases to the real Lamina `/v1` API for image, catalog, try-on, and video workflows. Use this guide when you want to turn Lamina into a product feature, not just call a raw model. Each recipe below uses the same execution pattern: 1. discover the right app 2. inspect its schema 3. start an execution 4. receive results by webhook or polling That makes the API predictable for application developers even when the underlying workflow changes. ## The Real API Behind Every Recipe The `/v1` API has 24 endpoints across 9 groups. The core execution flow uses: * `GET /v1/apps` — discover apps * `GET /v1/apps/{appId}` — inspect inputs * `POST /v1/apps/{appId}/runs` — run * `GET /v1/runs/{runId}` — poll results Additional groups — Intelligence, Publishing, Content, Templates, Assets, Account — extend what you can do after execution. See the full [API Reference](/api-reference/list-apps). ## Core Contract | Capability need | Current executable Lamina endpoint | | --------------------------------------------- | ------------------------------------ | | Start image, catalog, try-on, or video work | `POST /v1/apps/{appId}/runs` | | Check job status | `GET /v1/runs/{runId}` | | Stream job progress in real time | `GET /v1/runs/{runId}/stream` | | Discover which app to call | `GET /v1/apps` | | Verify the exact input schema | `GET /v1/apps/{appId}` | | Intelligent content creation (agent-friendly) | `POST /v1/content/create` | | Batch multiple creations | `POST /v1/content/batch` | | Get brand context for grounded prompts | `GET /v1/intelligence/brand-context` | | Publish to social channels | `POST /v1/publishing/publish` | | Check credit balance | `GET /v1/account/usage` | ### Authentication ```http theme={null} x-api-key: lma_your_api_key Authorization: Bearer lma_your_api_key ``` ### Rate Limits All `/v1/*` endpoints are currently limited to **100 requests per minute per IP**. On `429 Too Many Requests`, read these headers before retrying: * `RateLimit-Limit` * `RateLimit-Remaining` * `RateLimit-Reset` * `Retry-After` ### Common Status Codes | Status | Meaning | | ------ | -------------------------------------------- | | `200` | Successful read | | `202` | Execution accepted and queued | | `400` | Invalid request body, webhook URL, or inputs | | `401` | Missing or invalid API key | | `403` | Workspace or app access denied | | `404` | App or execution not found | | `429` | Rate limit exceeded | | `500` | Unexpected server-side failure | ## Example Public Apps By Capability These are example public apps observed on **April 11, 2026**. Use them as capability anchors in docs or demos, but pin the exact `appId` you want to support in production and re-check its schema with `GET /v1/apps/{appId}` before you ship against it. | Capability | Example public app | Example appId | | ------------------------ | ----------------------------------------- | -------------------------------------- | | Single image generation | `Product Shots with Mood Board` | `ec7ec3ce-69b4-43c9-8eea-fe9752d679a4` | | Batch catalog generation | `Premium Catalog 1.0` | `bbb17293-5fe7-4645-8c6e-0745bc28254d` | | Batch catalog generation | `Swift Catalog Generation` | `de5cca6b-73aa-4e27-a714-3339024db15d` | | Virtual try-on | `Product Try on` | `a76e85ec-20b4-4fbc-99ad-055423a868a2` | | Video generation | `Eyewear Shoot (Multi-Shot 21s Video)` | `1afc70fd-cb66-4f44-847d-bdaf7e237be4` | | Video generation | `Performance Marketing Video (V3) Sample` | `ec9b3525-4b72-4083-a6cb-00371672e128` | ## Ecommerce Image Generation Use this pattern for: * product shots * background swaps * lifestyle scenes * hero images **Recommended backing app example:** `Product Shots with Mood Board` ### Typical integration flow ```bash theme={null} curl -H "Authorization: Bearer $LAMINA_API_KEY" \ "https://app.uselamina.ai/v1/apps?search=product" ``` Then inspect the chosen app and use the returned parameter names exactly: ```bash theme={null} curl -H "Authorization: Bearer $LAMINA_API_KEY" \ https://app.uselamina.ai/v1/apps/ ``` ```bash theme={null} curl -X POST \ -H "Authorization: Bearer $LAMINA_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "inputs": { "": "" } }' \ https://app.uselamina.ai/v1/apps//runs ``` ### What developers usually send * a product image URL or a set of source images * optional reference imagery * a prompt or creative brief * styling or environment choices exposed as `options` ### What to expect back * one or more generated image outputs * occasional text outputs for summaries or metadata * `queued` or `running` before final media is ready ## Catalog And Merchandising Automation Catalog workflows are usually run by commerce backends, merchant tooling, or creative ops teams that need consistent outputs across many products. **Recommended backing app examples:** * `Premium Catalog 1.0` * `Swift Catalog Generation` For a concrete catalog payload, the existing Quick Start example uses: * front image URL * back image URL * model or style options such as gender, ethnicity, body type, and location Example execution body: ```json theme={null} { "inputs": { "Front": "https://example.com/front.jpg", "Back": "https://example.com/back.jpg", "Model Gender": "Female", "Location": "Studio" } } ``` Send it to: ```http theme={null} POST /v1/apps/{catalogAppId}/runs ``` ### Good fit for * marketplace seller onboarding * catalog enrichment pipelines * merchandising teams refreshing seasonal collections * internal creative production systems ## Virtual Try-On Try-on integrations usually combine shopper-uploaded imagery, mannequin imagery, or model imagery with garment assets supplied by a commerce system. **Recommended backing app example:** `Product Try on` Suggested discovery query: ```bash theme={null} curl -H "Authorization: Bearer $LAMINA_API_KEY" \ "https://app.uselamina.ai/v1/apps?search=try on" ``` Expected input pattern: * one or more person or selfie image URLs * one or more garment image URLs * optional fit, pose, or styling fields depending on the app Execution call: ```bash theme={null} curl -X POST \ -H "Authorization: Bearer $LAMINA_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "inputs": { "": "https://example.com/person.jpg", "": "https://example.com/garment.jpg" } }' \ https://app.uselamina.ai/v1/apps//runs ``` ### Common platform pattern * fetch the app schema at startup or cache it server-side * render the form dynamically for internal tooling or merchant portals * pass the resulting execution ID into your job UI * use webhooks for completion updates instead of aggressive polling ## Video Generation Video generation follows the same app-execution pattern, but jobs are more likely to be long-running and media-heavy. This is a good fit for product marketing tools, seller studios, and content platforms generating motion assets from still inputs. **Recommended backing app examples:** * `Eyewear Shoot (Multi-Shot 21s Video)` * `Performance Marketing Video (V3) Sample` Suggested discovery query: ```bash theme={null} curl -H "Authorization: Bearer $LAMINA_API_KEY" \ "https://app.uselamina.ai/v1/apps?search=video" ``` Common input pattern: * one or more source image URLs * optional product or motion brief text * optional aspect ratio, duration, or style fields from app metadata Execution call: ```bash theme={null} curl -X POST \ -H "Authorization: Bearer $LAMINA_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "inputs": { "": "https://example.com/product.jpg", "": "Create a premium ecommerce product reel" } }' \ "https://app.uselamina.ai/v1/apps//runs?webhook=https://your-server.com/callback" ``` Use webhooks in production for video jobs. They are often long-running enough that polling is only a fallback. ## Job Status And Result Retrieval Every capability above resolves to the same status check: ```http theme={null} GET /v1/runs/{runId} ``` Use the run ID returned from `POST /v1/apps/{appId}/runs`. Poll every 3-5 seconds until the run reaches `completed` or `failed`. If you are using webhooks, verify the callback signature with: ```http theme={null} GET /v1/webhooks/signing-key ``` ## Production Guidelines If you are building a customer-facing integration or partner API on top of Lamina: 1. pin the `appId` values you support instead of relying on search at runtime 2. re-fetch app metadata when you deploy changes to a workflow or form 3. validate inputs against the latest parameter schema before execution 4. prefer webhooks for long-running image and video jobs 5. design your UI around execution states, not immediate outputs The underlying API stays small on purpose. Most of the product-specific behavior lives in the apps you choose to expose. # Handle Long-Running Executions Source: https://docs.uselamina.ai/guides/handle-long-running-executions Recommended patterns for apps that take minutes to complete, especially image-heavy and video-heavy workflows. ## Why Lamina Uses Async Jobs Some Lamina apps complete in seconds. Others, especially multi-step image/video pipelines, may take much longer. Because of that, `POST /v1/apps/{appId}/runs` starts work and returns an execution handle immediately instead of blocking until the final output is ready. If you are integrating Lamina into a backend, queue system, merchant tool, or content platform, design around the execution lifecycle from the start. ## Two Delivery Patterns ### Option 1: Webhook (Recommended) Pass a webhook URL as a query parameter. When the execution completes, we POST the results to your URL. ```bash theme={null} POST /v1/apps/{appId}/runs?webhook=https://your-server.com/callback ``` Benefits: * No polling loop needed * Results arrive as soon as they're ready * Signed with ED25519 for security * Automatic retries (3 attempts with backoff) ### Option 2: Polling Poll `GET /v1/runs/{runId}` on an interval until `status` reaches `completed` or `failed`. Use different polling intervals depending on the workflow: * Short image jobs: every 3-5 seconds * Heavier multi-step jobs: every 5-10 seconds * Long-running video jobs: every 10-15 seconds Avoid polling every second in production. ## Recommended Backend Flow Call `POST /v1/apps/{appId}/runs?webhook=` and store the returned run ID. Save it in your job table, queue, or request state so you can resume later. Your webhook endpoint receives the completed results, or poll `GET /v1/runs/{runId}` as a fallback. ## What To Persist For every execution you start, store at least: * `runId` * `appId` * input payload * current status * started timestamp This makes retries, dashboards, and support much easier. ## Failure Handling If an execution fails: * inspect the top-level `errorMessage` * inspect each output's `error` * keep the original inputs for debugging or replay If your product has end users, show a user-friendly status while keeping the raw error for logs and support tooling. ## Webhook Verification When receiving webhook callbacks, verify the signature to ensure it's from Lamina: 1. Fetch the public key from `GET /v1/webhooks/signing-key` 2. Verify the ED25519 signature: `verify(signature, ".", publicKey)` 3. Reject timestamps older than 5 minutes (replay protection) See the [Webhook Signing Key](/api-reference/get-webhook-signing-key) reference for verification code examples. # MCP OAuth Install Source: https://docs.uselamina.ai/guides/mcp-oauth-install Install Lamina as a hosted remote MCP server with OAuth and workspace-scoped access. ## What This Enables Hosted MCP lets an AI client connect to Lamina without asking you to paste an API key into tool arguments. The client discovers Lamina's OAuth metadata, sends you to Lamina to approve access for a workspace, receives an access token, and then calls the remote MCP endpoint with `Authorization: Bearer `. Remote MCP endpoint: ```text theme={null} https://app.uselamina.ai/mcp/agent ``` Hosted install surface: ```text theme={null} https://app.uselamina.ai/mcp/install ``` Use the install page when you want copyable endpoint values, OAuth metadata links, and setup patterns for remote OAuth-capable MCP clients or local stdio fallback clients. ## Install in Popular MCP Clients Prefer the hosted endpoint whenever the client supports remote Streamable HTTP MCP with OAuth. Use the local stdio fallback when a client only supports local servers or cannot complete OAuth. ### Supported Clients The list of popular AI tools that can connect to Lamina MCP: * [Claude Code](#claude-code) * [Claude.ai and Claude Desktop](#claudeai-and-claude-desktop) * [ChatGPT](#chatgpt) * [Codex CLI](#codex-cli) * [Cursor](#cursor) * [VS Code with Copilot](#vs-code-with-copilot) * [Devin](#devin) * [Raycast](#raycast) * [Goose](#goose) * [Windsurf](#windsurf) * [Gemini Code Assist](#gemini-code-assist) * [Gemini CLI](#gemini-cli) * [Continue](#continue) * [Cline](#cline) * [Zed](#zed) Additional clients can use the same hosted endpoint if they support remote Streamable HTTP MCP with OAuth, or the local stdio fallback if they only support local MCP servers. ### Quick Compatibility Matrix | Client | Recommended Lamina setup | Notes | | ---------------------------- | --------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------- | | ChatGPT | Hosted remote MCP | Remote MCP only. Full write-tool support depends on plan, workspace settings, and developer mode availability. | | Codex CLI | Hosted remote MCP | Works through the Codex MCP CLI/config flow. | | VS Code with Copilot | Hosted remote MCP | Use `type: "http"` in `mcp.json`; VS Code handles authentication-capable MCP servers. | | Claude Code | Hosted remote MCP | Add HTTP transport, then use `/mcp` to authenticate. | | Claude.ai and Claude Desktop | Hosted remote MCP when available; local stdio fallback otherwise | Use hosted custom connectors when the Claude plan/client supports them. | | Cursor | Hosted remote MCP | Cursor supports Streamable HTTP and OAuth-capable MCP servers. | | Windsurf | Hosted remote MCP | Add a Streamable HTTP server and refresh Cascade tools. | | Continue | Local stdio fallback first | Use remote `streamable-http` only if your Continue build can complete OAuth for remote MCP. | | Cline | Hosted remote MCP when OAuth is available; local stdio otherwise | Cline supports HTTP and stdio MCP configuration. | | Zed | Hosted remote MCP on recent builds; local stdio fallback | Recent Zed builds support OAuth for remote MCP servers. | | Devin | Hosted remote MCP | Add Lamina as a custom HTTP MCP server in Devin's MCP Marketplace. | | Raycast | Hosted remote MCP | Add Lamina through Raycast's MCP server install flow. | | Goose | Hosted remote MCP when OAuth is available; local command-line extension otherwise | Add Lamina as a remote Streamable HTTP extension or a command-line extension. | | Gemini Code Assist | Hosted remote MCP through Gemini settings | Use native HTTP support when available or `mcp-remote` fallback. | | Gemini CLI | Hosted remote MCP when OAuth is available; local stdio otherwise | Gemini CLI supports HTTP, SSE, and stdio MCP servers. | ### Setup Connect your AI client to Lamina MCP and authorize access to your workspace. #### Install with add-mcp Install Lamina MCP for supported coding agents with the community `add-mcp` installer: ```bash theme={null} npx add-mcp https://app.uselamina.ai/mcp/agent ``` Add `-y` to skip confirmation prompts. Add `-g` to install globally across projects: ```bash theme={null} npx add-mcp -g https://app.uselamina.ai/mcp/agent ``` If `add-mcp` does not detect your client or cannot complete OAuth, use the manual client-specific instructions below. #### ChatGPT ChatGPT custom MCP connectors are remote-only. They are controlled by ChatGPT plan and workspace settings, and full MCP write actions may require developer mode or workspace admin approval. 1. In ChatGPT, open **Settings**. 2. Open **Apps** or **Connectors**, then enable **Developer mode** if your plan/workspace requires it. 3. Create a custom app/connector for a remote MCP server. 4. Use this server URL: ```text theme={null} https://app.uselamina.ai/mcp/agent ``` 5. Complete Lamina OAuth, choose the workspace, and test with `lamina_discover`. If ChatGPT reports that the connector is unsupported, verify that the workspace supports full MCP connectors and write tools. Some ChatGPT connector surfaces are read/fetch-oriented and may not expose Lamina's creative write tools. #### Codex CLI Add Lamina as a remote MCP server: ```bash theme={null} codex mcp add lamina --url https://app.uselamina.ai/mcp/agent codex mcp list ``` Codex shares MCP configuration between the CLI and IDE extension. If your Codex build prompts for browser authentication, complete Lamina OAuth and choose the workspace. You can also configure the hosted server directly in `~/.codex/config.toml`: ```toml theme={null} [mcp_servers.lamina] url = "https://app.uselamina.ai/mcp/agent" ``` #### VS Code with Copilot Create `.vscode/mcp.json` for a workspace install: ```json theme={null} { "servers": { "lamina": { "type": "http", "url": "https://app.uselamina.ai/mcp/agent" } } } ``` Then restart the MCP server from the MCP server list or reload VS Code. When prompted, trust the server and complete OAuth. In Copilot Chat Agent mode, use the tools picker to enable Lamina tools. For a local fallback, use stdio with an input variable: ```json theme={null} { "inputs": [ { "id": "lamina-api-key", "type": "promptString", "description": "Lamina API key", "password": true } ], "servers": { "lamina-local": { "type": "stdio", "command": "npx", "args": ["-y", "@uselamina/mcp"], "env": { "LAMINA_API_KEY": "${input:lamina-api-key}" } } } } ``` #### Devin Devin supports custom MCP servers through the MCP Marketplace. 1. Open Devin **Settings > MCP Marketplace**. 2. Select **Add Your Own**. 3. Choose **HTTP** as the transport. 4. Set the server URL: ```text theme={null} https://app.uselamina.ai/mcp/agent ``` 5. Save the server, then complete Lamina OAuth when Devin prompts for authorization. If Devin asks for headers, leave them empty for the hosted OAuth flow. Lamina issues workspace-scoped tokens after OAuth consent. #### Raycast Raycast supports installing MCP servers from its MCP server management commands. 1. Open Raycast and run the MCP **Install Server** command. 2. Enter these details: ```text theme={null} Name: Lamina Transport: HTTP URL: https://app.uselamina.ai/mcp/agent ``` 3. Click **Install** and authenticate with Lamina if prompted. If your Raycast build only supports local stdio MCP servers, use: ```text theme={null} Name: Lamina Command: npx Arguments: -y @uselamina/mcp Environment: LAMINA_API_KEY=YOUR_LAMINA_API_KEY ``` #### Claude Code Add the hosted HTTP server: ```bash theme={null} claude mcp add --transport http lamina --scope user https://app.uselamina.ai/mcp/agent ``` Inside Claude Code, run: ```text theme={null} /mcp ``` Choose Lamina and authenticate in the browser. Verify the install: ```bash theme={null} claude mcp list claude mcp get lamina ``` For local stdio fallback: ```bash theme={null} claude mcp add --transport stdio lamina-local \ --env LAMINA_API_KEY=YOUR_LAMINA_API_KEY \ -- npx -y @uselamina/mcp ``` #### Claude.ai and Claude Desktop Claude Desktop primarily uses local MCP servers and Desktop Extensions. Until Lamina ships a DXT package, configure the local stdio package in `claude_desktop_config.json`: ```json theme={null} { "mcpServers": { "lamina": { "type": "stdio", "command": "npx", "args": ["-y", "@uselamina/mcp"], "env": { "LAMINA_API_KEY": "YOUR_LAMINA_API_KEY" } } } } ``` Restart Claude Desktop after saving the file. If your Claude client exposes a remote MCP connector surface, use the hosted endpoint instead: ```text theme={null} https://app.uselamina.ai/mcp/agent ``` #### Cursor Create a project config at `.cursor/mcp.json` or a global config at `~/.cursor/mcp.json`: ```json theme={null} { "mcpServers": { "lamina": { "url": "https://app.uselamina.ai/mcp/agent" } } } ``` In Cursor, open MCP settings, refresh servers, and authenticate if prompted. For Cursor CLI: ```bash theme={null} cursor-agent mcp list cursor-agent mcp login lamina cursor-agent mcp list-tools lamina ``` For local stdio fallback: ```json theme={null} { "mcpServers": { "lamina-local": { "command": "npx", "args": ["-y", "@uselamina/mcp"], "env": { "LAMINA_API_KEY": "YOUR_LAMINA_API_KEY" } } } } ``` #### Windsurf In Windsurf, open **Settings > Tools > Windsurf Settings > Add Server**, or edit the raw `~/.codeium/windsurf/mcp_config.json` file: ```json theme={null} { "mcpServers": { "lamina": { "serverUrl": "https://app.uselamina.ai/mcp/agent" } } } ``` Use `url` instead of `serverUrl` if your Windsurf build expects that key. Press refresh in Cascade after adding the server, then authenticate if prompted. Local fallback: ```json theme={null} { "mcpServers": { "lamina-local": { "command": "npx", "args": ["-y", "@uselamina/mcp"], "env": { "LAMINA_API_KEY": "YOUR_LAMINA_API_KEY" } } } } ``` #### Continue Continue MCP servers run in agent mode. Create `.continue/mcpServers/lamina.yaml`: ```yaml theme={null} name: Lamina MCP version: 0.0.1 schema: v1 mcpServers: - name: Lamina type: streamable-http url: https://app.uselamina.ai/mcp/agent ``` Use this hosted config only if your Continue build supports OAuth for remote MCP. Otherwise, use the local stdio fallback: ```yaml theme={null} name: Lamina MCP version: 0.0.1 schema: v1 mcpServers: - name: Lamina type: stdio command: npx args: - "-y" - "@uselamina/mcp" env: LAMINA_API_KEY: YOUR_LAMINA_API_KEY ``` Restart Continue and switch to agent mode before testing. #### Cline For hosted HTTP MCP: ```bash theme={null} cline mcp add lamina https://app.uselamina.ai/mcp/agent --type http ``` If your Cline build cannot complete remote OAuth, edit `~/.cline/data/settings/cline_mcp_settings.json` for local stdio: ```json theme={null} { "mcpServers": { "lamina": { "command": "npx", "args": ["-y", "@uselamina/mcp"], "env": { "LAMINA_API_KEY": "YOUR_LAMINA_API_KEY" }, "disabled": false } } } ``` #### Zed In Zed, open settings and add a custom context server: ```json theme={null} { "context_servers": { "lamina": { "url": "https://app.uselamina.ai/mcp/agent" } } } ``` Recent Zed builds show an authentication action when a remote MCP server requires OAuth. If your build does not, use the local stdio fallback: ```json theme={null} { "context_servers": { "lamina-local": { "command": "npx", "args": ["-y", "@uselamina/mcp"], "env": { "LAMINA_API_KEY": "YOUR_LAMINA_API_KEY" } } } } ``` Open the Agent Panel settings and verify the Lamina server status indicator is active. #### Goose In Goose Desktop, open **Extensions**, choose **Add custom extension**, and add a **Remote Extension (Streamable HTTP)**: ```text theme={null} Name: Lamina Endpoint URL: https://app.uselamina.ai/mcp/agent ``` In Goose CLI: ```bash theme={null} goose configure ``` Choose **Add Extension**, then **Remote Extension (Streamable HTTP)**, and enter the Lamina endpoint. If your Goose build cannot complete OAuth for remote MCP, add a command-line extension instead: ```text theme={null} Name: Lamina Command: npx Arguments: -y @uselamina/mcp Environment: LAMINA_API_KEY=YOUR_LAMINA_API_KEY ``` #### Gemini Code Assist Gemini Code Assist shares MCP configuration with Gemini's MCP settings. Use native HTTP support when your IDE build supports OAuth-capable remote MCP servers: ```json theme={null} { "mcpServers": { "lamina": { "httpUrl": "https://app.uselamina.ai/mcp/agent" } } } ``` If your build does not complete OAuth for remote HTTP MCP, use `mcp-remote`: ```json theme={null} { "mcpServers": { "lamina": { "command": "npx", "args": ["mcp-remote", "https://app.uselamina.ai/mcp/agent"] } } } ``` Restart the IDE after updating `~/.gemini/settings.json`, then authenticate when prompted. #### Gemini CLI For hosted HTTP MCP: ```bash theme={null} gemini mcp add --transport http --scope user lamina https://app.uselamina.ai/mcp/agent gemini mcp list ``` You can also configure `~/.gemini/settings.json`: ```json theme={null} { "mcpServers": { "lamina": { "httpUrl": "https://app.uselamina.ai/mcp/agent" } } } ``` If your Gemini CLI build cannot complete OAuth for remote HTTP MCP, use local stdio: ```bash theme={null} gemini mcp add --scope user \ --env LAMINA_API_KEY=YOUR_LAMINA_API_KEY \ lamina-local npx -y @uselamina/mcp ``` Avoid underscores in Gemini MCP server aliases because Gemini's policy engine parses tool names with underscores. ## OAuth Discovery Lamina exposes standard MCP OAuth metadata for HTTP clients: ```text theme={null} GET /.well-known/oauth-protected-resource/mcp/agent GET /.well-known/oauth-authorization-server ``` The protected-resource response identifies the MCP resource: ```json theme={null} { "resource": "https://app.uselamina.ai/mcp/agent", "authorization_servers": ["https://app.uselamina.ai"], "bearer_methods_supported": ["header"], "scopes_supported": [ "lamina:creative:read", "lamina:creative:write", "lamina:brand:read" ] } ``` ## Install Flow Clients that support dynamic registration call `POST /mcp/oauth/register` with `client_name` and `redirect_uris`. Public clients use `token_endpoint_auth_method: "none"`. The client opens `/mcp/oauth/authorize` with `response_type=code`, `client_id`, `redirect_uri`, `scope`, `state`, `resource`, `code_challenge`, and `code_challenge_method=S256`. Lamina shows a protected consent page. Choose the workspace the agent should use, then approve the requested scopes. The client posts to `/mcp/oauth/token` with `grant_type=authorization_code`, the authorization `code`, the same `redirect_uri`, `client_id`, `resource`, and the PKCE `code_verifier`. The client calls `/mcp/agent` with `Authorization: Bearer `. The client posts to `/mcp/oauth/revoke` with `client_id`, `token`, and optional `token_type_hint` when the user disconnects Lamina. ## Scopes * `lamina:creative:read` allows discovery and run status. * `lamina:creative:write` allows creating and batching creative runs. * `lamina:brand:read` allows reading brand memory and guidance. If a token lacks a required scope, Lamina returns an insufficient-scope challenge so the client can request step-up authorization. ## Revoke Access OAuth clients should revoke tokens when a user removes Lamina or rotates client state: ```http theme={null} POST /mcp/oauth/revoke Content-Type: application/json { "client_id": "lamina_mcp_client_...", "token": "lma_mcp_at_or_rt_...", "token_type_hint": "refresh_token" } ``` Revoking either an access token or refresh token revokes the token pair stored for that grant. A later call to `/mcp/agent` with that bearer token returns `invalid_token`. ## First Creative Run After install, the agent runs through discover → describe → create → poll. Find an app for the user's goal: ```json theme={null} { "tool": "lamina_discover", "arguments": { "keywords": ["product", "hero", "running shoe", "cinematic"] } } ``` Read the chosen app's parameter contract: ```json theme={null} { "tool": "lamina_describe", "arguments": { "appId": "" } } ``` Dispatch the run with the required inputs (keyed by parameter `key` from `lamina_describe`): ```json theme={null} { "tool": "lamina_create", "arguments": { "appId": "", "inputs": { "": "" } } } ``` The response includes a `runId`. Poll for completion: ```json theme={null} { "tool": "lamina_status", "arguments": { "runId": "", "wait": true } } ``` ## Local Alternative If your MCP client cannot install remote OAuth MCP servers, use the local stdio package: ```bash theme={null} npx -y @uselamina/mcp ``` Set `LAMINA_API_KEY` or sign in through the Lamina CLI. Local stdio MCP remains useful for coding agents, CI sandboxes, and environments that do not support remote MCP OAuth yet. # Run Your First App Source: https://docs.uselamina.ai/guides/run-your-first-app A practical end-to-end flow: discover an app, inspect its inputs, execute it, and get the result. This is the simplest complete Lamina flow. It uses only the existing `/v1` endpoints: * `GET /v1/apps` * `GET /v1/apps/{appId}` * `POST /v1/apps/{appId}/runs` * `GET /v1/runs/{runId}` ## Step 1: List Available Apps ```bash theme={null} curl -H "x-api-key: lma_your_api_key" \ https://app.uselamina.ai/v1/apps ``` Look through the response and choose an `appId`. ## Step 2: Inspect The App ```bash theme={null} curl -H "x-api-key: lma_your_api_key" \ https://app.uselamina.ai/v1/apps/{appId} ``` Use this response to determine: * which fields are required * which parameters are `text`, `options`, or `url` * which default values are already defined ## Step 3: Start Execution ### With webhook ```bash theme={null} curl -X POST \ -H "x-api-key: lma_your_api_key" \ -H "Content-Type: application/json" \ -d '{ "inputs": { "Prompt": "A cinematic ecommerce hero image", "Aspect Ratio": "16:9" } }' \ "https://app.uselamina.ai/v1/apps/{appId}/runs?webhook=https://your-server.com/callback" ``` Use this in production when you want Lamina to notify your backend as soon as the job finishes. ### Without webhook ```bash theme={null} curl -X POST \ -H "x-api-key: lma_your_api_key" \ -H "Content-Type: application/json" \ -d '{ "inputs": { "Prompt": "A cinematic ecommerce hero image", "Aspect Ratio": "16:9" } }' \ https://app.uselamina.ai/v1/apps/{appId}/runs ``` The response includes a run ID. ## Step 4: Get Results ### Via webhook If you passed `?webhook=`, your callback URL receives a POST when the execution completes: ```json theme={null} { "data": { "runId": "fc32ae7d-...", "status": "completed", "outputs": [ { "id": "aiDesignerNode-...", "label": "Hero Image", "type": "image", "value": "https://storage.example.com/generated.png", "status": "completed", "error": null } ] } } ``` Headers include `X-Lamina-Webhook-Signature` for verification. See [Webhook Signing Key](/api-reference/get-webhook-signing-key) for details. ### Via polling If not using webhooks, poll until the execution reaches a terminal state: ```bash theme={null} curl -H "x-api-key: lma_your_api_key" \ https://app.uselamina.ai/v1/runs/{runId} ``` Poll every 3-5 seconds. Stop when `status` is `completed` or `failed`. ## JavaScript Example ```js theme={null} const baseUrl = 'https://app.uselamina.ai'; const apiKey = process.env.LAMINA_API_KEY; async function laminaFetch(path, init = {}) { const response = await fetch(`${baseUrl}${path}`, { ...init, headers: { 'Content-Type': 'application/json', 'x-api-key': apiKey, ...(init.headers || {}), }, }); if (!response.ok) { const body = await response.text(); throw new Error(`Lamina request failed: ${response.status} ${body}`); } return response.json(); } // With webhook — start and forget (results arrive at your webhook) async function runAppWithWebhook(appId, inputs, webhookUrl) { const started = await laminaFetch( `/v1/apps/${appId}/runs?webhook=${encodeURIComponent(webhookUrl)}`, { method: 'POST', body: JSON.stringify({ inputs }) } ); return started.data.runId; } // With polling — start and wait async function runAppWithPolling(appId, inputs) { const started = await laminaFetch(`/v1/apps/${appId}/runs`, { method: 'POST', body: JSON.stringify({ inputs }), }); const runId = started.data.runId; for (;;) { const status = await laminaFetch(`/v1/runs/${runId}`); const state = status.data.status; if (state === 'completed' || state === 'failed') { return status.data; } await new Promise((resolve) => setTimeout(resolve, 5000)); } } ``` ## Python Example ```python theme={null} import os import time import requests from urllib.parse import urlencode BASE_URL = "https://app.uselamina.ai" API_KEY = os.environ["LAMINA_API_KEY"] headers = { "x-api-key": API_KEY, "Content-Type": "application/json", } # With webhook — start and forget def run_app_with_webhook(app_id, inputs, webhook_url): r = requests.post( f"{BASE_URL}/v1/apps/{app_id}/runs?webhook={webhook_url}", headers=headers, json={"inputs": inputs}, timeout=60, ) r.raise_for_status() return r.json()["data"]["runId"] # With polling — start and wait def run_app_with_polling(app_id, inputs): started = requests.post( f"{BASE_URL}/v1/apps/{app_id}/runs", headers=headers, json={"inputs": inputs}, timeout=60, ) started.raise_for_status() run_id = started.json()["data"]["runId"] while True: status = requests.get( f"{BASE_URL}/v1/runs/{run_id}", headers=headers, timeout=60, ) status.raise_for_status() payload = status.json()["data"] if payload["status"] in ("completed", "failed"): return payload time.sleep(5) ``` # Test Webhooks Locally Source: https://docs.uselamina.ai/guides/test-webhooks-locally Receive real Lamina execution callbacks on your machine, verify signatures, and reuse the same flow from the CLI or an agent. ## Why This Matters Most production Lamina integrations should use webhooks instead of tight polling loops. The local CLI can now do the same thing: * start a local webhook receiver * verify Lamina's ED25519 signature * expose the listener through ngrok or another public tunnel * run a real app against that callback URL ## Lamina Webhook Contract When you start an execution with: ```http theme={null} POST /v1/apps/{appId}/runs?webhook=https://your-server.com/callback ``` Lamina sends a signed POST when the execution finishes. Headers: * `X-Lamina-Webhook-Signature` * `X-Lamina-Webhook-Timestamp` * `X-Lamina-Webhook-Request-Id` Message to verify: ```text theme={null} . ``` Verification key source: ```http theme={null} GET /v1/webhooks/signing-key ``` The CLI listener and MCP listener both verify this contract directly. ## Start A Local Listener From the repository root: ```bash theme={null} lamina webhook listen --port 8788 ``` That starts a local receiver at: ```text theme={null} http://127.0.0.1:8788/lamina/webhook ``` ## Expose It Publicly Use ngrok, cloudflared, or your preferred tunnel. Example with ngrok: ```bash theme={null} ngrok http 8788 ``` If the public URL is `https://example.ngrok.dev`, save it as the default Lamina webhook URL: ```bash theme={null} lamina webhook listen \ --port 8788 \ --public-url https://example.ngrok.dev \ --save-default ``` The CLI normalizes that to: ```text theme={null} https://example.ngrok.dev/lamina/webhook ``` Check what is saved: ```bash theme={null} lamina webhook status ``` Clear it if needed: ```bash theme={null} lamina webhook clear ``` ## Run An App Against The Saved Webhook URL ```bash theme={null} lamina run --file inputs.json --webhook default ``` You can also pass the public URL directly: ```bash theme={null} lamina run \ --file inputs.json \ --webhook https://example.ngrok.dev/lamina/webhook ``` ## Expected Listener Output For a successful callback, the listener prints a verified execution message like: ```text theme={null} Verified webhook 1 for execution 99ebc7c6-24f3-4fc2-9d53-2e3f49f3bec1 (completed) ``` If verification fails, the listener rejects the callback and prints the verification error instead. ## Real End-To-End Flow Run `lamina login` (browser OAuth) or `lamina login --api-key lma_...` for CI. Run `lamina webhook listen --port 8788`. Start ngrok or another tunnel and get a public HTTPS URL. Start the listener with `--public-url ... --save-default`, or pass the full webhook URL directly when you run the app. Execute `lamina run --file inputs.json --webhook default`. The local listener validates the Lamina signature and prints the completed execution. ## MCP Uses Polling Instead The hosted and local MCP servers intentionally expose only five high-level creative tools. They do not expose webhook listener management tools. For MCP clients, start work with `lamina_create` and retrieve results with `lamina_status`. Use REST webhooks when you are building a server-side integration that owns a public callback URL. ## Fallback If you do not want to expose a callback during local development, omit `?webhook=` and use polling with: ```bash theme={null} lamina run --file inputs.json --wait ``` # Use The CLI And SDK Source: https://docs.uselamina.ai/guides/use-the-cli-and-sdk How to run Lamina from a terminal, save an API key, inspect apps, execute jobs, and understand where the SDK fits today. ## Choose The Right Integration Surface Lamina supports three developer entry points: | Surface | Best for | What it does | | ---------- | ------------------------------------------------------------------ | ------------------------------------------------------------------------ | | HTTP API | Backends and production services | Call `/v1` directly from your own code | | CLI | Terminal workflows and manual testing | Save an API key, inspect apps, run executions, receive webhook callbacks | | MCP server | Coding agents such as Claude Code, Cursor, and desktop MCP clients | Expose Lamina as tools an agent can call | The HTTP API is the canonical contract. The CLI and MCP server are thin wrappers around that same `/v1` surface. ## Install From npm ```bash theme={null} npm install @uselamina/sdk npm install -g @uselamina/cli ``` If you want to run the MCP server without a global install: ```bash theme={null} npx -y @uselamina/mcp ``` If you prefer a global MCP binary: ```bash theme={null} npm install -g @uselamina/mcp lamina-mcp ``` ## Authenticate Once `lamina login` opens your browser for an OAuth approval flow — pick a workspace, click **Approve**, and the CLI receives the token on a loopback callback. ```bash theme={null} lamina login ``` For CI / scripted callers, pass a workspace API key non-interactively: ```bash theme={null} lamina login --api-key lma_your_api_key ``` Either path stores credentials at `~/.lamina/config.json` (mode `0600`). OAuth tokens auto-refresh near expiry — you stay logged in for up to 30 days without re-prompting. Inspect the active identity at any time: ```bash theme={null} lamina whoami ``` Or skip local storage entirely and use the environment: ```bash theme={null} export LAMINA_API_KEY=lma_your_api_key ``` The CLI and MCP server resolve auth in this order: 1. `LAMINA_API_KEY` environment variable 2. saved CLI credentials at `~/.lamina/config.json` (OAuth tokens or API key) Sign out: ```bash theme={null} lamina logout ``` ## Inspect Apps List available apps: ```bash theme={null} lamina apps list --search catalog ``` Inspect one app's parameter contract: ```bash theme={null} lamina apps get ``` ## Upload A Local File Pass a path; the CLI streams the bytes to Lamina's CDN via a pre-signed URL and prints the resulting URL you can pass to `lamina run --input ...`: ```bash theme={null} URL=$(lamina assets upload ./me.jpg --json | jq -r '.data.url') ``` ## Run An App You can run with a JSON file: ```bash theme={null} lamina run --file inputs.json --wait ``` Or inline values: ```bash theme={null} lamina run \ --input "Your photo=https://example.com/selfie.png" \ --input "Celebrity Name=Anne Hathaway" \ --input "Aspect Ratio=1:1" \ --wait ``` The CLI fetches the app schema first and validates: * unknown parameter names * missing required inputs * invalid option labels * invalid `url` values ## Where The SDK Fits The shared Lamina client is published as `@uselamina/sdk` and powers both the CLI and MCP server. If you are integrating from your own backend, use `@uselamina/sdk` or call the `/v1` HTTP API directly. The client wraps: * `GET /v1/apps` * `GET /v1/apps/{appId}` * `GET /v1/apps/{appId}/workflow` * `POST /v1/apps/{appId}/runs` * `GET /v1/runs/{runId}` * `GET /v1/webhooks/signing-key` ## Next Steps * Read [Test Webhooks Locally](/guides/test-webhooks-locally) to receive signed Lamina callbacks on your machine * Read [Agent Integration Patterns](/guides/agent-integration-patterns) to run Lamina through MCP in coding agents * Read [Quick Start](/quick-start) if you want to stay on the raw HTTP API # Lamina Source: https://docs.uselamina.ai/introduction The generative media platform for AI agents. Create product images, videos, virtual try-on, and more through MCP, REST API, or CLI. The generative media platform for AI agents. You call apps — not models. Lamina handles orchestration, brand context, quality scoring, and multi-channel publishing.