> ## Documentation Index
> Fetch the complete documentation index at: https://docs.uselamina.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# The Agent Creation Loop

> The full in-assistant loop — discover, top up, generate, see, score, refine within budget, and deliver — and the guarantees it gives you.

This is the canonical end-to-end loop for an agent creating on-brand media with Lamina **without leaving the assistant**. Every step is available over both MCP (tool names below) and the REST `/v1` API, so the same loop works whether you're a remote MCP client or calling HTTP directly.

The loop closes on itself: a result that isn't good enough feeds back into a bounded refine pass, and a workspace that runs out of credits gets a one-tap top-up — so the agent never dead-ends.

```text theme={null}
discover → estimate → (top up if needed) → generate → see inline
    → score → approve / reject → refine within budget → deliver
                                        ↑__________________|
```

## The Guarantees

Two hard rules hold across the whole loop. Rely on them:

<CardGroup cols={2}>
  <Card title="No card entry in chat" icon="lock">
    Credit top-ups always return a **Stripe-hosted checkout link**. The agent never asks for, collects, or handles card details — the user pays on Stripe's page and the credits land on the workspace.
  </Card>

  <Card title="Loops are cost-capped" icon="gauge">
    The refine loop **never spends past `maxCredits`**. It stops the instant the score clears the bar, the iteration cap is hit, or the next attempt wouldn't fit the budget — whichever comes first — and reports exactly what it cost.
  </Card>
</CardGroup>

## The Loop, Step by Step

<Steps>
  <Step title="Discover an app">
    Find an app that fits the intent. `lamina_discover` (MCP) or `GET /v1/apps`. Each app has a typed, discoverable input contract — inspect it with `lamina_describe` / `GET /v1/apps/{appId}` before calling.

    Prefer one-call creation (`POST /v1/content/create`) when you want the engine to pick the app and map inputs for you.
  </Step>

  <Step title="Estimate the cost, and top up if needed">
    Check the balance and what a run will cost before committing. `lamina_credits` returns `{ balance, packages[] }`; `POST /v1/apps/{appId}/estimate` returns a per-node cost breakdown.

    If the balance is short, **don't dead-end** — start a top-up:

    ```text theme={null}
    lamina_credits   → see balance + packages
    lamina_topup     → get a Stripe checkout link for a package
    ```

    Hand the user the `checkoutUrl`. Once they pay, the credits land on the workspace and you retry the run. **Never ask for card details** — that's what the Stripe page is for.

    If a generate/run *does* fail for credits, the error comes back structured — the exact `shortfall`, a `suggestedPackage`, and a `topUp` hint — so you can say "you need N more credits, top up here" and continue.
  </Step>

  <Step title="Generate">
    Run the app: `lamina_run` (app workflow), `lamina_generate_image` / `lamina_generate_video` (atomic), or `POST /v1/apps/{appId}/runs`. Runs are async and return a `runId` immediately.

    Poll with `lamina_status` / `GET /v1/runs/{runId}`, or use `wait: true` to block server-side until terminal. Most apps finish in 1–5 minutes.
  </Step>

  <Step title="See the result inline">
    `lamina_status` returns generated **images inline** as MCP image blocks, so the assistant renders them in-chat — the full-resolution URL is always in the JSON too.

    For **video**, the status carries `output.poster` — a still preview frame the assistant shows inline — alongside the durable, playable video link. Output URLs are long-lived public links you can hand the user to open later.
  </Step>

  <Step title="Score and take the approve / reject turn">
    Before showing the result as final, check how on-brand it is:

    * `lamina_brand_compliance` — read the Brand Guard result the run recorded (per-node status + score), when Brand Guard was enabled on the app.
    * `lamina_brand_score` — score **any** completed run's image on demand (Brand Guard on or off), returning a `brandFit` (0–100), the specific `deviations`, and `suggestions`.

    Show the output alongside the score, then ask the user to **approve or reject**. On a reject, call `lamina_brand_feedback` with the reason — it becomes a **brand guardrail** the next generation avoids, closing the loop.
  </Step>

  <Step title="Refine within budget">
    If it's close but not there, let Lamina improve it automatically — capped:

    ```text theme={null}
    lamina_refine_to_brand { runId, minScore?, maxIterations?, maxCredits? }
    ```

    This repeats **score → refine** (re-rolling with the deviations fed back in) and stops the instant `brandFit >= minScore`, `iterations >= maxIterations`, or the credit budget would be exceeded — whichever comes first. It returns the **best** result plus a plain-English summary:

    ```text theme={null}
    "Spent 20 of 200 credits across 2 refine attempts; best brand-fit 85 (met_score)."
    ```

    Defaults: `minScore` 80, `maxIterations` 3, `maxCredits` 200. Per-attempt and cumulative cost are in `attempts[]`, so you can always tell the user exactly what a refine cost.
  </Step>

  <Step title="Deliver the artifact">
    Hand over the best result's durable URL (and, for video, the poster). The link is a long-lived public URL the user can open any time.

    Want it published to a channel instead? `POST /v1/publishing/publish`.
  </Step>
</Steps>

## Tools in the Loop

| Step                 | MCP tool                                                       | REST                                                 |
| -------------------- | -------------------------------------------------------------- | ---------------------------------------------------- |
| Discover             | `lamina_discover`, `lamina_describe`                           | `GET /v1/apps`, `GET /v1/apps/{id}`                  |
| Estimate / balance   | `lamina_credits`                                               | `POST /v1/apps/{id}/estimate`                        |
| Top up               | `lamina_topup`                                                 | `POST /v1/billing/checkout`                          |
| Generate             | `lamina_run`, `lamina_generate_image`, `lamina_generate_video` | `POST /v1/apps/{id}/runs`, `POST /v1/content/create` |
| See / poll           | `lamina_status`                                                | `GET /v1/runs/{id}`                                  |
| Read compliance      | `lamina_brand_compliance`                                      | `GET /v1/runs/{id}/brand-compliance`                 |
| Score on demand      | `lamina_brand_score`                                           | `POST /v1/runs/{id}/brand-score`                     |
| Approve / reject     | `lamina_brand_feedback`                                        | `POST /v1/runs/{id}/brand-feedback`                  |
| Refine within budget | `lamina_refine_to_brand`                                       | `POST /v1/runs/{id}/refine-to-brand`                 |

## A Minimal Run of the Loop

```text theme={null}
1. lamina_discover "instagram product photo"        → appId
2. lamina_credits                                    → balance 40, need ~60
3. lamina_topup { packageId }                        → checkoutUrl → user pays
4. lamina_run { appId, inputs }                      → runId
5. lamina_status { runId, wait: true }               → image shown inline
6. lamina_brand_score { runId }                      → brandFit 72, deviations
7. lamina_refine_to_brand { runId, minScore: 85 }    → best brandFit 88, spent 30
8. deliver best.imageUrl                             → done
```

Every arrow stays inside the assistant. See [Agent Integration Patterns](/guides/agent-integration-patterns) for how to wire the MCP server, and [Capability Recipes](/guides/capability-recipes) for task-specific flows.
