Docs API reference
Markdown Get an API key

API referenceGeneration

Personal LoRAs

Import a LoRA file you have permission to use, then apply it to renders on compatible models. Your library is the same one shown in My LoRAs on Sogni Web. Importing checks and stores the file; it does not train a new model.

Only your account can list or use its library. To render, the file is downloaded to GPU workers on the Sogni Supernet, so do not import weights that must stay confidential.

Authenticate with Authorization: Bearer $SOGNI_API_KEY (the api-key header also works). Responses use { "status": "success", "data": ... } with Cache-Control: private, no-store.

#Endpoints

Method and path Result
GET /v1/loras/personal data.loras (your import records), data.models (model IDs you can import for), and data.limits
GET /v1/loras/personal/:id One import record and its current status
GET /v1/loras/personal/catalog Ready entries in data.loras, shaped like public catalog rows
POST /v1/loras/personal 202 with an import record
DELETE /v1/loras/personal/:id Removes the entry from your library; data is {}

#Access

Importing, reading /catalog, and rendering with a personal LoRA require an active Unlimited or Unlimited Pro plan. Free trials count, and so does a cancelled plan until its paid period ends. A renewal payment that is still being retried does not. Without access, these calls return 403 with errorCode: 179.

Listing, reading one record, and removing entries keep working after access ends. Entries still show their status, but cannot be used until access returns.

An ID that is not in personal-<uuid> form returns 400. An unknown ID, or one owned by another account, returns 404. If a DELETE races another change to the same entry, it returns 409; retry it.

#Import a LoRA

curl --fail-with-body https://api.sogni.ai/v1/loras/personal \
  -H "Authorization: Bearer $SOGNI_API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{"url":"https://huggingface.co/author/repository/resolve/main/style.safetensors","name":"My style","modelId":"krea2_turbo_fp8_scaled","rightsConfirmed":true}'
Field Rules
url Required. A supported Hugging Face or Civitai link (see below).
name Required. 1–80 characters, shown only in your library.
modelId Required. One of data.models from GET /v1/loras/personal.
rightsConfirmed Required. Must be the boolean true. Send it only after confirming you have permission to use the file on Sogni.

The body must be JSON (415 otherwise).

Accepted links. Links must be public HTTPS URLs without a port, credentials, fragment, or access token, up to 2,048 characters.

  • Hugging Face: a single file, https://huggingface.co/<owner>/<repo>/resolve/<revision>/<path>.safetensors. blob links are accepted too. Repository pages are not.
  • Civitai: a model page (https://civitai.com/models/<id>, which imports its newest published version), a specific version (/models/<id>?modelVersionId=<version>), or a version download link (/api/download/models/<version>). The model must be a LoRA.

The import is pinned to an exact revision or version, so the stored source can differ from the link you sent.

File requirements. Standard low-rank LoRA weights in .safetensors format, stored as FP16, BF16, or FP32, with rank 128 or lower. LoKr, LoHa, DoRA, OFT, GLoRA, LoCon/Tucker, and full-weight files are refused. The maximum file size is data.limits.fileBytes, which is null when that value is briefly unavailable.

#What a 202 returns

A new import returns a queued record. If you already imported the same link for a model in the same family, the API returns that existing entry, whatever its status, instead of creating another one. If that existing entry was rejected, it is queued again.

#Import errors

Status Cause
400 rightsConfirmed is not true, the link is not supported (the message says why), modelId is not importable, name is invalid, or the source cannot be downloaded.
403 No active Unlimited access (errorCode: 179).
409 The file is already a Sogni catalog LoRA for that model (the message names it), or your library is full.
429 Too many requests (wait for Retry-After), or today's import allowance is used.
503 The source could not be checked right now, or the import queue is full. Retry later.

#Limits

Limit Value
Library entries 20 (limits.entries). Entries in every status count; remove one to free a slot.
New imports 5 per UTC day (limits.importsPerDay), resetting at 00:00 UTC (5 PM PDT or 4 PM PST).
LoRAs per render 8 (limits.perGeneration), personal and catalog LoRAs combined.

An accepted import uses one of the day's imports even if it is later rejected or removed. Refused links, catalog duplicates, and returning an existing entry do not. Re-queuing a rejected link does. Reads and import attempts are also rate-limited per account, and refused links count toward that limit.

#Track an import

Poll GET /v1/loras/personal/:id about every 10 seconds until the status is ready, rejected, or revoked. Most imports finish within a few minutes.

{
  "status": "success",
  "data": {
    "id": "personal-3f0c…",
    "name": "My style",
    "modelId": "krea2_turbo_fp8_scaled",
    "modelIds": ["krea2_turbo_fp8_scaled", "krea2_identity_edit_v1_2", "…"],
    "source": "https://huggingface.co/author/repository/resolve/main/style.safetensors",
    "status": "ready",
    "createdAt": 1789600000000,
    "updatedAt": 1789600090000,
    "bytes": 228480000,
    "requirements": []
  }
}

createdAt and updatedAt are Unix milliseconds. bytes, reason, and failureCode appear when known. requirements lists plain-language usage rules, and is non-empty only for some ready entries.

Status Meaning
queued Waiting to be imported.
validating Downloading and checking the file.
review Not usable right now, usually because the model it was imported for changed or was retired. If it does not return to ready, remove it and import for a current model.
ready Usable on every model in modelIds.
rejected Not imported. Show reason to the user.
revoked Withdrawn by Sogni. Remove the entry.

A status can change after an import finishes, so check it before rendering. Some rejections are retried automatically after Sogni improves import support.

failureCode What to do
source_access The file could not be downloaded. It may have been removed, restricted to its owner, or require an agreement.
subscription_required Unlimited access ended while the import was queued. Import again after renewing.
catalog_duplicate The file is already in the Sogni catalog; use that LoRA instead.
invalid_artifact The format, precision, rank, or size is not supported, or the download kept failing.

A rejection found while rendering can have a reason but no failureCode.

#Render with a ready LoRA

GET /v1/loras/personal/catalog returns one row per ready entry:

{
  "loraId": "personal-3f0c…",
  "slug": "personal-3f0c…",
  "name": "My style",
  "description": "Your imported LoRA.",
  "relatedLoraIds": [],
  "modelIds": ["krea2_turbo_fp8_scaled", "…"],
  "ui": {
    "category": "personal", "label": "My style",
    "min": 0, "max": 1, "default": 1, "step": 0.05,
    "recommendedMin": 0.5, "recommendedMax": 1,
    "nsfw": true, "sexual": false,
    "creator": "Imported by you",
    "sourceUrl": "https://huggingface.co/…"
  }
}

Other fields can be present; ignore them. loraId is the import id.

  • Model. Render only on a model listed in the entry's modelIds. Never infer compatibility from the LoRA's name.
  • Strength. Pass loras and a matching loraStrengths array in the same order. Personal LoRA strength must be greater than 0 and at most 1. Use ui.default when the user has not chosen one. If strengths are omitted, each LoRA is applied at 1.
  • Trigger words. Sogni does not add the author's trigger words; include them in your prompt. If the entry lists requirements (such as a required phrase or a narrower strength range), follow them, or the render is refused.
  • Content filter. Every personal entry has ui.nsfw: true, because imports are not reviewed for content. Sogni Web shows imported LoRAs only while the Sensitive Content Filter is off; apply the same rule in your interface.

#Compatible models

An import works on every model in its family, not only the model you chose when importing it. Use data.models and each entry's modelIds as the authoritative lists; they change as models are added. Families include:

Family Models
Krea 2 Krea 2 Turbo, Krea 2 Identity Edit, Dark Beast Krea 2, and Dark Beast Krea 2 Identity Edit
Qwen Image Edit 2511 Qwen Image Edit 2511 (not Lightning)
MiniMax H3 Text-to-video, image-to-video, and first/last-frame modes across Standard, Balanced, Turbo, FastH3 Turbo, and FastH3 Two-Stage
MiniMax H3 Reference-to-Video Its own family, separate from the other H3 modes

Audio-guided H3 modes (image + audio, first/last frame + audio, and audio only) do not accept LoRAs.

#Hosted tools and workflows

Creative workflow steps and hosted chat tools accept loras and loraStrengths (lora_strengths also works) on generate_image, edit_image, generate_video, and animate_photo, and use at most 8. Choose a model argument in the entry's family:

Tool Model argument
generate_image model: "krea-2-turbo" or "dark-beast-krea2"
edit_image model: "krea-identity-edit", "dark-beast-krea2-identity-edit", or "qwen"
generate_video videoModel: "minimax-h3-t2v", "minimax-h3-t2v-turbo", "minimax-h3-t2v-balanced", "minimax-h3-fasth3-t2v-turbo", "minimax-h3-fasth3-t2v-turbo-2stage", "minimax-h3-r2v", "minimax-h3-r2v-turbo", "minimax-h3-r2v-balanced", "minimax-h3-r2v-2stage", or "minimax-h3-r2v-balanced-2stage"
animate_photo videoModel: "minimax-h3-i2v", "minimax-h3-i2v-turbo", "minimax-h3-i2v-balanced", "minimax-h3-fasth3-i2v-turbo", "minimax-h3-fasth3-i2v-turbo-2stage", or the matching minimax-h3-flf2v selectors

Sending a personal-… ID with a model that cannot use personal LoRAs returns 400.

#SDK

const library = await sogni.projects.personalLoras.list(); // choose modelId from library.models
const imported = await sogni.projects.personalLoras.import({
  url: 'https://huggingface.co/author/repository/resolve/main/style.safetensors',
  name: 'My style',
  modelId: 'krea2_turbo_fp8_scaled',
  rightsConfirmed: true,
});
const current = await sogni.projects.personalLoras.get(imported.id); // poll until ready, rejected, or revoked

const { loras } = await sogni.projects.personalLoras.catalog({ modelId: 'krea2_turbo_fp8_scaled' });
const entry = loras.find((row) => row.loraId === imported.id);
if (entry) {
  await sogni.projects.create({
    type: 'image',
    modelId: 'krea2_turbo_fp8_scaled',
    numberOfMedia: 1,
    positivePrompt: 'portrait in my style',
    loras: [entry.loraId],
    loraStrengths: [entry.ui.default],
  });
}
// Only on an explicit removal request:
// await sogni.projects.personalLoras.remove(imported.id);

sogni.projects.availableLoras({ modelId, includePersonal: true }) merges your ready entries into the public catalog. It requires authentication and active Unlimited access, and it throws without them rather than returning only public LoRAs. Personal rows are never stored in the shared public catalog cache. The Python SDK exposes the same library as sogni.projects.personal_loras (list, get, import_lora, remove, catalog) and available_loras(include_personal=True).

The public LoRA catalog remains GET /v1/loras/comfy?modelId=... and needs no authentication. Each row's modelIds lists the models that offer the LoRA. A model that can load a LoRA but where tests showed it does not work is listed under that row's restrictedModelIds instead, never in modelIds, and a render of that pairing is not offered: for example vh5tape Worn VHS on the FastH3 and Standard MiniMax H3 text- and image-to-video modes. ?modelId= returns only the LoRAs a model offers. The field is omitted when a LoRA has no restrictions.

#Render errors and charges

  • A render that names an unavailable personal LoRA (not ready, not yours, the wrong model, or no active access) is refused at submission.
  • If you remove an entry or lose access, queued jobs that have not started are cancelled.
  • If a worker finds the file does not match the model, the job fails without a charge, and an entry used on its own is marked rejected.
  • The first render with a LoRA a worker has not cached starts more slowly.

Completed project records list each personal LoRA used with its public source link and version, never your library name. That record remains after you remove the entry.