> For the complete documentation index, see [llms.txt](https://docs.flora.ai/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.flora.ai/api/guides/loras.md).

# Train and use LoRA Styles

Train a LoRA Style from your own images with the FLORA API, recover safely from a lost response, and generate with the trained Style.

`POST /loras/train` trains a LoRA and saves it as a Style, through the same workflow as the **Train** dialog in FLORA: the same image preparation, captioning defaults, model parameters, permissions and run lifecycle. When the Style is ready, you pass its `sty_` ID to a compatible model as `params.lora_id`.

Training spends workspace balance even if you never poll the result. Canvas-only promotions don't apply to API or MCP training.

## 1. Pick a trainer

List the trainers available to the workspace:

```ts
import { FloraClient } from "@flora-ai/flora"

const client = new FloraClient()
const workspaceId = "ws_abc123"

const { trainers } = await client.loras.listTrainers({ workspace_id: workspaceId })
```

Each trainer lists its family key (for example `flux-2` or `krea-2`), its training parameters with their effective defaults, image-count and format limits, and the inference model IDs it's compatible with, with their strength defaults and ranges. Use these fields instead of guessing a trainer endpoint or parameter name.

## 2. Upload the training images

Upload your images through the [Assets API](/api/guides/assets.md) first and collect their FLORA URLs. Don't pass a ZIP file or a provider's training URL; the trainer enforces its own image count and format limits.

## 3. Start training

Pass the image URLs, an existing project and its workspace, a name, and the trainer's family key as `base_model`:

```ts
// Generate the token once and save it with your dataset before the first request.
const clientToken = crypto.randomUUID()

const training = await client.loras.train(
  {
    workspace_id: workspaceId,
    project_id: "prj_abc123",
    name: "Ink study",
    base_model: "krea-2",
    image_urls: uploadedImageUrls,
    client_token: clientToken,
  },
  { maxRetries: 0, timeoutInSeconds: 300 },
)

console.log(training.run_id, training.style_id)
```

Training is asynchronous. Poll the returned `run_id` with `client.generations.retrieve({ runId: training.run_id })` (`GET /runs/{runId}`) until it completes or fails, waiting about 15 seconds between polls. If training fails, the pending Style may be removed; the run still shows the failure and the final charged cost.

### Recover a lost response

If a response is lost, repeat the request with the **same `client_token` and the same inputs**. A new token means a new, paid training run. A replay can return a null `style_id` if someone has since deleted the Style; don't treat that as a reason to retrain automatically.

The optional `Idempotency-Key` header is a separate, two-hour guard against duplicate HTTP requests. It doesn't replace `client_token`. See [Idempotency](/api/platform/idempotency.md).

ZIP-based trainers keep the original dataset archive and resolved settings for recovery. URL-list trainers, such as Recraft, keep the source URLs, so leave those images available and unchanged until training finishes.

### Training parameters and captions

Omit `trainer_params` to keep the **Train** dialog's defaults. To change something deliberately, pass supported parameters such as `steps`, `learning_rate`, the trigger field listed for that trainer, or its captioning mode.

`captions` holds one entry per image, in the same order as `image_urls`; `null` and blank entries use the shared fallback. Automatic and manual captioning are model-specific, as in the dialog. On Krea, omitting the mode and the captions uses automatic Style captions; supplying non-blank captions selects manual (`"Off"`) mode. You can also choose manual mode explicitly with `trainer_params.auto_captioning: "Off"`. Combining manual captions with an explicit automatic mode is rejected. The trigger is added to each caption once for you, so send the caption itself without the trigger.

Keep the effective settings with your dataset so you can compare runs.

## 4. Generate with the Style

Read the Style, check that it's ready, then pick a compatible inference model and put the Style's ID in **`params.lora_id`**:

```ts
if (!training.style_id) throw new Error("The trained Style was deleted")

const style = await client.loras.retrieve({
  styleId: training.style_id,
  workspace_id: workspaceId,
})
if (style.status !== "ready") throw new Error(`The Style is ${style.status}`)

const image = await client.generations.create({
  workspace_id: workspaceId,
  project_id: "prj_abc123",
  type: "image",
  model: compatibleModelId, // from the trainer's compatible inference models
  prompt: "A lighthouse above a calm sea",
  params: { lora_id: style.style_id },
})
```

`style_id` is the response field; `lora_id` is the inference parameter. Passing `params.style_id` doesn't attach the LoRA. A Style's status is `training`, `ready` or `unavailable`. Saved trigger words are applied for you, and the weights stay subject to workspace and owner permissions.

## From an AI client

With [FLORA MCP](/mcp.md), the same flow uses `flora_list_lora_trainers`, `flora_train_lora`, `flora_list_generations` with `run_ids: [run_id]`, and `flora_get_lora`, then passes the ready Style to `flora_create_generations`. Calling `flora_create_generations` with a trainer model doesn't run the Style preparation; `flora_train_lora` is the supported entry point. See [Model and LoRA tools](/mcp/tools/models-and-loras.md).

## Common endpoints

| Method | Path                                           | Description                                                                                 |
| ------ | ---------------------------------------------- | ------------------------------------------------------------------------------------------- |
| GET    | `/loras/trainers?workspace_id={workspace_id}`  | [List trainers, parameters and compatible models](/api/reference/loras/listloratrainers.md) |
| POST   | `/loras/train`                                 | [Start training](/api/reference/loras/trainlora.md)                                         |
| GET    | `/runs/{runId}`                                | [Poll the training run](/api/reference/runs/getrun.md)                                      |
| GET    | `/loras/{styleId}?workspace_id={workspace_id}` | [Read a Style's readiness](/api/reference/loras/getlora.md)                                 |


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://docs.flora.ai/api/guides/loras.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
