API Pro: saldo, limiti e personaggi addestrati
Due route di sola lettura, sempre consentite qualunque sia il permesso della chiave, più il registro attività conservato nell'app.
Saldo e limiti
GET /credits
{ "balance": 1830, "licenseId": "lic_...", "key": "k_1a2b3c4d",
"limits": { "concurrent": 2, "perMinute": 60, "perDay": 0 }, "running": 1,
"buy_url": "https://tendre.ai/credits?license=lic_..." }| Campo | Significato |
|---|---|
balance | Crediti rimanenti. null quando il saldo è sconosciuto (ambiente di test). |
licenseId | La licenza a cui appartiene questa chiave. |
key | L'identificatore pubblico della chiave, sicuro da registrare nei log. Mai il segreto. |
limits.concurrent | Render in esecuzione contemporaneamente, 2 di default. |
limits.perMinute | Richieste al minuto, 60 di default. |
limits.perDay | Limite giornaliero di crediti impostato sulla chiave. 0 significa nessun limite. |
running | Render di questa chiave ancora in attesa di essere recuperati. |
buy_url | La pagina di acquisto dei crediti della tua licenza, con carta o crypto. |
È la chiamata meno costosa dell'API e quella giusta per verificare che una chiave funzioni.
Acquistare crediti con la carta salvata
POST /credits/purchase
{ "pack": "m" }pack è un id di pacchetto (s, m, l, xl, xxl o xxxl), oppure "c25000" per una quantità libera oltre il pacchetto più grande. Il pagamento avviene sulla carta salvata nella ricarica automatica della tua pagina account, solo se lì hai autorizzato il tuo agente ad acquistare, entro il limite mensile che hai fissato (ricariche automatiche comprese). Un'e-mail conferma ogni acquisto. Un acquisto alla volta.
La risposta è 200 { credits, eur, balance }. Ogni rifiuto contiene buy_url, il link di acquisto normale, da usare al suo posto: 402 confirmation_required (la tua banca chiede la tua conferma) o payment_declined; 409 agent_not_allowed, no_saved_card, monthly_cap_reached, topup_in_progress o purchase_in_progress; 400 unknown_pack. Con 503 payment_unavailable non sappiamo se l'addebito è avvenuto: non riprovare subito, un addebito avvenuto viene accreditato.
Personaggi addestrati
Questi sono i personaggi addestrati come LoRA, per immagini e video ordinari. I personaggi di Multi-Ref Video, sette viste ricavate da una foto, sono un'altra cosa: vedi Characters.
GET /loras
{ "image": [ { "name": "megan_krea2", "trigger": "megan", "base": "krea2", "size": 0 } ],
"video": [ { "name": "megan_ltx", "trigger": "megan", "size": 0 } ] }Passa name come lora, prendendolo dalla lista image per le immagini e dalla lista video per
i video, e inserisci il suo trigger nel prompt, altrimenti il personaggio non apparirà. Un
personaggio che non appartiene all'account risponde 403 unknown_lora.
Carica la tua LoRA
POST /loras/upload
Un file .safetensors che hai già, addestrato altrove o che si trova sul tuo server, viene
inserito nello spazio dell'account e diventa utilizzabile tramite lora, esattamente come un personaggio addestrato
nell'app. Ci sono due modalità: in un colpo solo fino a 90 MB, oppure a parti oltre tale soglia. Una LoRA
di solito pesa dai 200 ai 500 MB, quindi la seconda è il caso ordinario. Il limite massimo è 800 MB
in entrambi i casi.
In un colpo solo: un body grezzo (application/octet-stream, Content-Length obbligatorio), i parametri nell'URL.
Oltre i 100 MB la richiesta non raggiunge mai l'API. L'infrastruttura a monte la blocca, quindi
ciò che si riceve è un semplice 413 senza body JSON: un client che analizza ogni errore come JSON
si bloccherà invece di segnalarlo. Quella dimensione va inviata a parti.
| Parametro | Ruolo |
|---|---|
name | Nome del file, .safetensors, lettere, cifre, . _ -, massimo 80 caratteri. Un nome già esistente viene sostituito. |
kind | image (il default) o video. Richiede rispettivamente il permesso images o videos. |
trigger | La parola trigger da inserire nel prompt, massimo 60 caratteri. |
curl -s -X POST "$B/loras/upload?name=my_character.safetensors&kind=image&trigger=ohwx" -H "Authorization: Bearer $KEY" -H "Content-Type: application/octet-stream" --data-binary @my_character.safetensorsLa risposta è { "ok": true, "name": "my_character.safetensors", "kind": "image", "size": 314572800, "trigger": "ohwx", "architecture": "krea2", "storage": { ... } }.
La compatibilità viene verificata al momento del caricamento. Le immagini sono renderizzate da Krea 2, che ha un'architettura propria: si applica solo una LoRA Krea 2, sia che sia stata addestrata in tendre.AI sia per Krea 2 altrove. Tieni presente che una LoRA FLUX.1 viene rifiutata, il che non è la prima cosa che si pensa. I video sono renderizzati da LTX 2.5, quindi serve una LoRA LTX 2.5 o 2.3.
L'API legge l'header del file prima di archiviare qualsiasi cosa. Una LoRA riconosciuta come SDXL, SD 1.5, SD3,
FLUX.1, Wan o HunyuanVideo viene respinta con 415 incompatible_lora, che indica l'architecture rilevata,
e un file che non è un safetensors con 415 invalid_safetensors.
Nulla viene archiviato e nessuna quota viene consumata in nessuno dei due casi.
Un caso riesce comunque a passare: un'architettura che l'header non identifica viene accettata, con
architecture: "inconnue", e viene valutata solo al momento del render. Questo è l'unico errore che ti costa
un render per scoprirlo.
Invia una LoRA grande a parti
Tre chiamate, più una per annullare.
1. Apri la sessione. POST /loras/upload/start con { "name", "kind", "size", "trigger" },
dove size è l'intero file in byte. Verifica il nome, la dimensione e la quota, poi risponde:
{ "uploadId": "...", "part_size": 20971520, "max_part_size": 104857600, "parts_expected": 15 }2. Invia ogni parte. POST /loras/upload/part?uploadId=...&part=N, body grezzo, parti numerate
a partire da 1, part_size byte ciascuna tranne l'ultima, mai più di max_part_size. Ogni risposta
contiene un etag, e ne hai bisogno di tutti per il passaggio successivo, quindi conservali man mano.
La parte 1 è speciale: contiene l'header del file, quindi è lì che viene valutata l'architettura. Una LoRA
incompatibile viene intercettata lì con 415 e la sessione viene annullata. Lo scopri dopo
20 MB invece che dopo 400.
3. Chiudi la sessione. POST /loras/upload/finish con { "uploadId", "parts": [ { "part": 1, "etag": "..." }, ... ] }.
Assembla il file e lo registra, rispondendo esattamente come l'upload in un colpo solo.
Per annullare, POST /loras/upload/abort con { "uploadId" }. Una sessione lasciata senza finish viene
eliminata dopo sei ore. Nulla appare in GET /loras e nulla viene contato contro la quota
finché finish non riesce, quindi un upload interrotto non ti costa storage.
import os, requests
B, H = "https://api.tendre.ai/pro/v1", {"Authorization": "Bearer " + KEY}
path = name = "my_character.safetensors"
s = requests.post(f"{B}/loras/upload/start", headers=H,
json={"name": name, "kind": "image", "size": os.path.getsize(path), "trigger": "ohwx"}).json()
parts = []
with open(path, "rb") as f:
n = 1
while chunk := f.read(s["part_size"]):
r = requests.post(f"{B}/loras/upload/part",
headers={**H, "Content-Type": "application/octet-stream"},
params={"uploadId": s["uploadId"], "part": n}, data=chunk)
r.raise_for_status()
parts.append({"part": n, "etag": r.json()["etag"]})
n += 1
done = requests.post(f"{B}/loras/upload/finish", headers=H,
json={"uploadId": s["uploadId"], "parts": parts}).json()
print(done["name"], done["architecture"], done["storage"])Con l'app viene fornito uno script PowerShell già pronto, disponibile in scripts/api-pro-lora.ps1.
Elimina una LoRA
DELETE /loras/{name}?kind=image|video rimuove il file e libera la quota. Risponde con
{ "ok": true, "name": ..., "storage": { ... } }, oppure 404 unknown_lora quando il nome non
esiste sull'account.
Quota di storage
Ogni licenza dispone di un massimo di 20 file e 10 GB, immagini e video insieme, e i personaggi
addestrati nell'app vi contribuiscono. GET /loras riporta lo stato in storage:
{ "count": 3, "max_count": 20, "bytes": 943718400, "max_bytes": 10737418240 }Superato uno dei due limiti, un upload risponde 413 lora_quota insieme a quanto rimane. Elimina un file per fare spazio.
L'addestramento di un personaggio non è ancora esposto dall'API. Avviene nell'app, e il risultato appare in questo elenco.
Attività
L'app conserva il registro di ogni render effettuato dalle tue chiavi (data, chiave, tipo, costo, stato) e il consumo per chiave, in Impostazioni > API > Attività.
È anche lì che puoi revocare una chiave che è stata compromessa, senza toccare le altre.