# רכיבים

> הרצת כל רכיב ב-REST API של Nodaro עם POST /v1/<node-type>‎, גילוי רכיבים, מודלים וערכי בוררים, ושליטה בפרומפט עם ייחוסים ומזהי הכוונה.

Source: https://nodaro.ai/he/docs/developers/api/nodes

**הרצת רכיב** קוראת לרכיב אחד של Nodaro ישירות, בלי לבנות תהליך: שולחים `POST /v1/<node-type>` עם ההגדרות של הרכיב בגוף הבקשה. כל רכיבי היצירה בנויים כך, מ-`generate-image` ו-`generate-video` ועד `text-to-speech`, ונקודות הקצה של הגילוי מראות אילו רכיבים, מודלים והגדרות קיימים. רוב הרצות הרכיבים הן אסינכרוניות: התגובה היא `jobId`, ובעזרתו בודקים את הסטטוס מדי כמה שניות עד שהתוצאה מוכנה.

## הרצת רכיב
הנתיב של סוג רכיב הוא `POST /v1/` ואחריו הסוג, וגוף הבקשה הוא ההגדרות של הרכיב ב-JSON:

```http
POST /v1/generate-image
Authorization: Bearer ndr_…
Content-Type: application/json

{ "prompt": "a lighthouse in a storm, oil painting", "provider": "nano-banana-pro", "aspectRatio": "3:4" }
```

מה שחוזר נקבע לפי הרכיב:

- **רכיבי יצירה** מחזירים `200` עם `{ "jobId": "…" }`. העבודה רצה ב-worker, ובודקים את הסטטוס של המשימה עם `GET /v1/jobs/:id/status` מדי כמה שניות, עד שהסטטוס שלה הוא `completed`. ראו את דף ה-API [משימות](https://nodaro.ai/docs/developers/api/jobs).
- **יצירות של תמונה ווידאו** עשויות להוסיף `adjustments`, רשימה של ההגדרות שהשרת תיקן כדי שיתאימו למודל שנבחר. הנתיבים `generate-video` ו-`generate-image` עשויים להוסיף גם `warnings`. ראו בדף ה-API [תיקוני פרמטרים](https://nodaro.ai/docs/developers/api/workflows#parameter-corrections).
- **רכיבים שרצים מיד**, כגון `combine-text`, מחזירים את התוצאה המלאה שלהם מיד, בלי `jobId`.
- **רכיבי גריפה**, כגון `web-scrape`, גם הם עונים מיד. התגובה שלהם כוללת גם `jobId`, בשביל ההיסטוריה שלכם, וגם את הנתונים עצמם.

כשיצירה מתחילה, משוריינים לה קרדיטים. כשאין בחשבון מספיק קרדיטים להרצה, הקריאה נדחית עם `402 insufficient_credits`.

### רכיבים עם נתיב ארוך יותר
רוב רכיבי הטקסט שקוראים למודל שפה פועלים לפי אותו כלל של `POST /v1/<node-type>`: `generate-script`, `image-critic`, `qa-check` ו-`describe-to-picker`. כמה אחרים רשומים תחת נתיב ארוך יותר:

| סוג הרכיב | נתיב |
| --- | --- |
| `llm-chat` | `POST /v1/llm-chat/generate` |
| `after-effects` | `POST /v1/after-effects/generate` |
| `motion-graphics` | `POST /v1/motion-graphics/generate` |
| `lottie-overlay` | `POST /v1/lottie-overlay/generate` |
| `3d-title` | `POST /v1/3d-title/generate` |
| `image-to-text` | `POST /v1/image-to-text/describe` |
| `video-composer` | `POST /v1/scene-graph/generate` |

המתודה `client.nodes.run(type, params)` של ה-SDK והפקודה `nodaro nodes run` של ה-CLI שולחות בשבילכם כל אחד מסוגי הרכיבים האלה לנתיב שלו. ראו את הדף [הרצת רכיבים ב-SDK](https://nodaro.ai/docs/developers/sdk/nodes#runtype-params-options).

נתיבים של מודלי שפה מקבלים שני שדות אופציונליים:

- **`reasoningEffort`**: `none`, `low`, `medium`, `high`, `xhigh` או `max`, לפי המודל. השמיטו את השדה, או בחרו רמה שהמודל לא תומך בה, כדי לקבל את ברירת המחדל של המודל עצמו. לרמה אין מחיר משלה. הקריאה מחויבת לפי הטוקנים שהיא השתמשה בהם, ולכן רמה גבוהה יותר עולה יותר רק בגלל החשיבה שהמודל כותב. במודל Claude, בחירה של רמה כלשהי מריצה את הקריאה ב-API הישיר של Anthropic, בתעריף של ה-API הזה.
- **`advancedMode: true`**: מודלי Gemini ו-Claude. הבקשה רצה ב-API של יצרן המודל עצמו, השירות היחיד שבו `temperature`, `maxTokens` וכל טווח החשיבה נכנסים לתוקף. הקריאה מחויבת בתעריף לטוקן של השירות הזה. בקשה עם מודל שאין לו שירות כזה נדחית עם `400 advanced_mode_unsupported`. כל עוד הספק של המודל עצמו לא יכול לקבל בקשות, הבקשה נדחית, במקום לרוץ בשירות ברירת המחדל, שבו ההגדרות האלה לא היו נכנסות לתוקף. התגובה היא `400 advanced_mode_unavailable` לפני שנגבים קרדיטים, או `502 advanced_mode_unavailable` באמצע הקריאה. על דחייה באמצע הקריאה הקרדיטים מוחזרים, אלא אם הספק חייב על הניסיון. כבו את המצב המתקדם כדי להריץ את המודל בשירות ברירת המחדל שלו.

הרכיב `describe-to-picker` מחזיר את התוצאה שלו באותה בקשה, והוא יכול גם להזרים את התשובה כאירועי שרת (SSE). ראו את הדף [תיאור לבורר](https://nodaro.ai/docs/nodes/image/describe-to-picker#api). השדות של `generate-script` מופיעים בדף [יצירת תסריט](https://nodaro.ai/docs/nodes/video/generate-script#api).

## דוגמה: יצירת תמונה
### התחלת המשימה
**curl**

```bash
curl -s -X POST https://app.nodaro.ai/v1/generate-image \
  -H "Authorization: Bearer $NODARO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
"prompt": "a knight on a hill at dawn, cinematic",
"provider": "nano-banana-pro",
"aspectRatio": "16:9",
"resolution": "2K"
}'
```

```json
{ "jobId": "0f1a9c2e-5b7d-4e8a-9c31-6d2f8b4a7e10" }
```

**TypeScript SDK**

```ts

const client = createClient({
baseUrl: 'https://app.nodaro.ai',
auth: new StaticTokenAuth(process.env.NODARO_API_KEY!),
})

const result = await client.nodes.run('generate-image', {
prompt: 'a knight on a hill at dawn, cinematic',
provider: 'nano-banana-pro',
aspectRatio: '16:9',
resolution: '2K',
})
```

**CLI**

```bash
nodaro nodes run generate-image \
  --param prompt="a knight on a hill at dawn, cinematic" \
  --param provider=nano-banana-pro \
  --param aspectRatio=16:9 \
  --param resolution=2K
```

### בדיקת הסטטוס של המשימה
בקשו את הסטטוס של המשימה כל 2–5 שניות, עד שהוא `completed` או `failed`:

```bash
curl -s https://app.nodaro.ai/v1/jobs/0f1a9c2e-5b7d-4e8a-9c31-6d2f8b4a7e10/status \
  -H "Authorization: Bearer $NODARO_API_KEY" | jq -r .data.status
```

### קריאת התוצאה
משימת תמונה שהסתיימה כוללת את הכתובת של התמונה ב-`output_data.imageUrl`. משימת וידאו משתמשת ב-`videoUrl`, משימת אודיו ב-`audioUrl`, ובמשימות רבות יש גם `thumbnailUrl`.

```json
{
"data": {
"id": "0f1a9c2e-5b7d-4e8a-9c31-6d2f8b4a7e10",
"status": "completed",
"progress": 100,
"output_data": { "imageUrl": "https://…/0f1a9c2e.png" },
"error_message": null
}
}
```

ה-SDK וה-CLI יכולים לבדוק את הסטטוס בשבילכם:

**TypeScript SDK**

```ts
const output = await client.nodes.runAndWait('generate-image', {
prompt: 'a knight on a hill at dawn, cinematic',
provider: 'nano-banana-pro',
})
console.log(output.imageUrl)

// Several candidates at once, in input order:
const results = await client.nodes.runMany('generate-image', [
{ prompt: 'a knight on a hill, sunrise' },
{ prompt: 'a knight on a hill, golden hour' },
{ prompt: 'a knight on a hill, blue hour' },
])
for (const { jobId, output } of results) console.log(jobId, output.imageUrl)
```

**CLI**

```bash
nodaro nodes run generate-image \
  --param prompt="a knight on a hill at dawn, cinematic" \
  --param provider=nano-banana-pro \
  --watch --json | jq -r '.output_data.imageUrl'
```

כברירת מחדל, המתודה `runAndWait` בודקת את הסטטוס כל 2,000 אלפיות שנייה, עד 15 דקות. אפשר לשנות את זה עם `pollMs` ו-`maxMs`, לעצור את ההמתנה עם `AbortSignal` ב-`signal`, ולקבל עדכוני התקדמות עם `onProgress`. המתודה זורקת שגיאות עם טיפוס, שאפשר לתפוס עם `instanceof`:

| שגיאה | מתי |
| --- | --- |
| `InsufficientCreditsError`, `StorageExceededError`, `JobBlockedError` | ההרצה נדחתה לפני שמשימה כלשהי התחילה. |
| `ModelUnavailableError` | הפריסה הזו לא מציעה את המודל, או שמנהל כיבה אותו. בחרו מודל אחר. |
| `JobFailedError` | המשימה הסתיימה בסטטוס `failed` או `cancelled`. השגיאה כוללת את `jobId` ואת הודעת השגיאה. |
| `JobTimeoutError` | עבר הזמן שהוגדר ב-`maxMs`. המשימה לא מבוטלת, ובדרך כלל היא עדיין מסתיימת: קבלו אותה מאוחר יותר עם `client.jobs.get(jobId)`. |
| `JobAbortedError` | ה-`signal` שלכם הופעל. |
| `JobHeldError` | המשימה נכנסה לסטטוס `pending_review` בפריסה שבודקת תוצאות. המשימה לא מבוטלת: בדקו שוב מאוחר יותר. |

ב-CLI, העבירו גוף בקשה מורכב, כגון מערכים או אובייקטים מקוננים, בקובץ, עם `--params-file body.json`. ערכי הדגלים גוברים על הקובץ באותו מפתח, והערכים `true`, `false`, `null` ומספרים מומרים מטקסט.

### הגדרות של רכיב יצירת תמונה
השדות שלמטה הם ההגדרות הנפוצות ביותר של `POST /v1/generate-image`. מה כל הגדרה עושה בעורך מוסבר בדף [יצירת תמונה](https://nodaro.ai/docs/nodes/image/generate-image), ובמה כל מודל תומך מופיע בדפי [מודלי התמונה](https://nodaro.ai/docs/models/image).

<TypeTable
type={{
prompt: { type: 'string', description: 'התיאור של התמונה. עד 30,000 תווים בגוף הבקשה; לכל מודל יש מגבלה נמוכה יותר משלו, ופרומפט ארוך יותר מקוצר כדי להיכנס בה.', required: true },
provider: { type: 'string', description: 'מזהה המודל, כגון nano-banana-pro או gpt-image-2-5-flare. הנתיב GET /v1/nodes/generate-image מחזיר את כל המזהים ב-providers.' },
aspectRatio: { type: 'string', description: 'הערכים auto, 1:1, 16:9, 9:16, 4:3, 3:4, 3:2, 2:3, 5:4, 4:5, 21:9 ועוד. ערך שהמודל לא תומך בו מתוקן.' },
resolution: { type: 'string', description: 'אחד הערכים 1K, 2K או 4K, או 0.5 MP, 1 MP, 2 MP או 4 MP, במודלים שתומכים בכך. משפיע על מחיר הקרדיטים.' },
quality: { type: 'string', description: 'אחד הערכים basic, medium או high, במודלים שיש להם רמות איכות. משפיע על מחיר הקרדיטים.' },
negativePrompt: { type: 'string', description: 'ממה להימנע, עד 5,000 תווים.' },
seed: { type: 'integer', description: 'מספר קבוע שמאפשר לחזור על הרצה, במודלים שתומכים בכך.' },
referenceImageUrls: { type: 'string[]', description: 'עד 14 כתובות של תמונות ייחוס, החשובה ביותר ראשונה.' },
connectedReferences: { type: 'object[]', description: 'ייחוסים מתויגים שהנתיב משלב בפרומפט. ראו את הסעיף שימוש בייחוסים.' },
describedReferences: { type: 'object[]', description: 'עד 10 נושאים עם שם, שאין לכם תמונה שלהם.' },
referenceOrder: { type: 'string[]', description: 'מזהי ייחוסים, בסדר שבו הייחוסים צריכים להיות ממוספרים.' },
referenceLock: { type: 'string', description: 'אחד הערכים standard או multi-person. מוסיף ניסוח בדוק לנאמנות לייחוסים.' },
direction: { type: 'object', description: 'מזהי בוררים למצלמה, לתאורה ולמראה. ראו את הסעיף הכוונה קולנועית לפי מזהה.' },
subject: { type: 'object', description: 'מזהי בוררים לאדם, לסטיילינג ולאביזרים שבשוט.' },
baseImageUrl: { type: 'string', description: 'תמונה לעריכה, במקום יצירה מאפס.' },
maskUrl: { type: 'string', description: 'עם baseImageUrl: מסכה שבה לבן מסמן מה לשנות ושחור מה להשאיר. רק האזור הלבן נוצר מחדש.' },
strength: { type: 'number', description: 'מ-0 עד 1. עד כמה עידון יכול להתרחק מ-baseImageUrl, במודלים שתומכים בכך.' },
guidanceScale: { type: 'number', description: 'מ-0 עד 20, במודלים שתומכים בכך.' },
presetId: { type: 'string', description: 'ערכה מובנית או מותאמת אישית להחלה, כמו שהעורך מחיל אותה. ראו את הסעיף החלת ערכה.' },
presetVariables: { type: 'object', description: 'ערכים למשתנים של הערכה שאין להם ברירת מחדל, כגון ‎{ "BRAND": "Acme" }‎.' },
count: { type: 'integer', description: 'מ-1 עד 4. מספר התמונות, וכל תמונה היא משימה משלה. ראו את הסעיף כמה תמונות בקריאה אחת.' },
models: { type: 'string[]', description: 'מ-2 עד 4 מזהים שונים של מודלי תמונה, במקום provider. אותה בקשה בכל מודל.' },
promptPrefix: { type: 'string', description: 'טקסט שמתווסף לפני הפרומפט, כמו ברכיב AI שמקבל פרומפט.' },
promptSuffix: { type: 'string', description: 'טקסט שמתווסף אחרי הפרומפט. יחד עם promptPrefix והפרומפט, הוא לא יכול לחרוג ממגבלת הפרומפט.' },
userPrompt: { type: 'string', description: 'הטקסט כפי שהמשתמש שלכם הקליד אותו, כשהשדה prompt הוא גרסה שהאפליקציה שלכם הרחיבה. נשמר במשימה ואף פעם לא נשלח למודל. ברירת המחדל: prompt.' },
sessionId: { type: 'string', description: 'אחד מסשני היצירה שלכם. המשימה נשמרת בו. ראו את הסעיף סשני יצירה.' },
origin: { type: 'string', description: 'ה-slug של האפליקציה שלכם: אותיות לטיניות קטנות, ספרות ומקפים, עד 40 תווים. נשמר במשימה, והנתיב GET /v1/jobs?origin=‎ מסנן לפיו.' },
}}
/>

עריכה עם מסכה או עידון עולה כמו יצירה חדשה באותו מודל.

הנתיב `/v1/generate-image` יכול להחזיר `warnings`, רשימה של `{ code, message }`, על משהו שביקשתם ולא נשלח. כרגע הקוד היחיד הוא `moodboard_images_not_sent`: חלק מהתמונות של [מודבורד](#palettes-and-moodboards) לא נכנסו במגבלת הייחוסים של המודל. ההרצה ממשיכה, והמשימה שומרת את אותן אזהרות ב-`input_data.warnings`.

### החלת ערכה
הנתיב `POST /v1/generate-image` מקבל `presetId`: מזהה מובנה, כגון `generate-image/cinematic-still`, או ה-uuid של אחת הערכות שלכם. את המזהים המובנים מחזיר הנתיב `GET /v1/node-presets/factory?nodeType=generate-image`, שלא צריך אסימון. הנתיב מחיל את הערכה כמו שהעורך מחיל אותה על רכיב יצירת תמונה, ולכן המשימה זהה לזו שהכפתור **הרצה** בעורך היה שולח:

- הפרומפט של הערכה, והטקסט לפני ואחרי שלה (`promptPrefix` ו-`promptSuffix`).
- משתני `{name || default}`, עם ערכי ברירת המחדל שלהם.
- ה-`style` של הערכה, שמשולב בפרומפט.
- הפרומפט השלילי של הערכה: במודל שמקבל פרומפט שלילי הוא נשלח בשדה המיועד לכך, ובכל מודל אחר הוא מתווסף לפרומפט כשורת `Avoid:` (להימנע מ:).
- ההגדרות של הערכה, וברירות המחדל של העורך להגדרות שהיא לא קובעת: המודל `gpt-image-2` והיחס `16:9`. המודל והמחיר זהים לאלה שבעורך.

שדות ששולחים גוברים על הערכה, כמו עריכה ברכיב שהערכה הוחלה עליו. ה-`prompt` שלכם מחליף את הפרומפט של הערכה, והטקסט לפני ואחרי של הערכה עוטף אותו. השדות `provider`, `aspectRatio`, `resolution`, `quality`, `negativePrompt`, `style` ושאר ההגדרות מחליפים את הערכים של הערכה. הייחוסים, `sessionId` ו-`origin` נשמרים.

```bash
curl -s -X POST https://app.nodaro.ai/v1/generate-image \
  -H "Authorization: Bearer $NODARO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"presetId": "generate-image/logo-wordmark", "presetVariables": {"BRAND": "Acme"}}'
```

- **משתנה בלי ברירת מחדל**, כגון `{BRAND}`, צריך ערך ב-`presetVariables`. בלי ערך, הנתיב מחזיר `400` עם `code: "preset_variables_required"` ועם `tokens: ["BRAND"]`.
- **`presetId` לא מוכר** נדחה עם `404 preset_not_found`.
- **בדיקת הקרדיטים מתמחרת את הבקשה אחרי שהערכה מוחלת.** השדות `presetId` ו-`presetVariables` לא נשמרים במשימה.
- **בערכה שעובדת מתמונות**, הערך של `minReferences` גדול מ-0. שלחו את מספר התמונות הזה ב-`referenceImageUrls`. ראו את דף ה-API [ערכות](https://nodaro.ai/docs/developers/api/presets).
- **ערכה משתלבת עם `count` ועם `models`.** ראו את הסעיף הבא.
- **ערכה לא יכולה להשתמש בפלטות או במודבורדים שלכם.** כשה-`direction` שלה מציין `palette:<id>` או `moodboard:<id>`, בין שאתם שלחתם אותו ובין שהוא שמור בערכה, הנתיב מחזיר `400 validation_error`, והשדה `field` מציין את המפתח. התגובה מגיעה לפני שמשהו נוצר ולפני שנגבים קרדיטים.

### כמה תמונות בקריאה אחת
הנתיב `POST /v1/generate-image` יכול ליצור כמה תמונות בבת אחת:

- **`count`** (מ-1 עד 4) יוצר את מספר התמונות הזה ממודל אחד. כל תמונה היא משימה משלה.
- **`models`** (מ-2 עד 4 מזהים שונים של מודלי תמונה, אותם מזהים ש-`provider` מקבל) מחליף את `provider` ושולח את אותה בקשה לכל מודל. ההגדרות של כל מודל מותאמות אליו כמו בקריאה יחידה, והתיקונים מדווחים לכל מודל בנפרד.
- **עם שניהם**, השדה `count` חל על כל מודל: הערך `models: ["gpt-image-2", "flux-2-max"]` עם `count: 2` יוצר ארבע תמונות. בקשה אחת יוצרת לכל היותר 8 תמונות.

```bash
curl -s -X POST https://app.nodaro.ai/v1/generate-image \
  -H "Authorization: Bearer $NODARO_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 0f8e6c1a-try-1" \
  -d '{"prompt": "a lighthouse at dusk", "models": ["gpt-image-2", "flux-2-max"], "count": 2, "seed": 41}'
```

```text
{
"jobId": "<first job>",
"jobIds": ["<job 1>", "<job 2>", "<job 3>", "<job 4>"],
"batchId": "6b1f7e2a-0c4d-5e8f-9a1b-2c3d4e5f6a7b",
"groups": [
{ "model": "gpt-image-2", "jobIds": ["<job 1>", "<job 2>"], "credits": <credits> },
{ "model": "flux-2-max", "jobIds": ["<job 3>", "<job 4>"], "credits": <credits>,
"adjustments": [{ "field": "resolution", "from": "1K", "to": "2 MP", "reason": "..." }] }
],
"totalCredits": <credits>
}
```

על כל תמונה משלמים כמו על קריאה יחידה שמבקשת אותה. בלי `count` ו-`models`, או עם `count: 1`, התגובה היא ה-`{ "jobId" }` הרגיל. השדה `jobId` הוא תמיד המשימה הראשונה, ולכן קוד שקורא אותו ממשיך לעבוד.

- **קרדיטים: על כל האצווה, או כלום.** היתרה נבדקת מול הסכום הכולל של האצווה לפני שמשהו נוצר. כשאין מספיק, הנתיב מחזיר `402 insufficient_credits`, עם הסכום הכולל ב-`required`, ושום דבר לא נוצר. כשחיוב נכשל באמצע, הקרדיטים על המשימות שכבר נוצרו מוחזרים, והמשימות נמחקות לפני התגובה.
- **אידמפוטנטיות, לכל תמונה.** עם `Idempotency-Key: K`, המפתח של תמונה *i* הוא `K:i`. בקשה חוזרת עם אותו מפתח מחזירה את אותם מזהי משימות, ואף פעם לא גורמת לחיוב כפול של משימה. אצווה שנוצרה רק בחלקה נוצרת עד הסוף. בקשה שנייה עם אותו מפתח, באותו זמן, ממתינה לראשונה, או נדחית עם `409 batch_in_progress` אחרי 15 שניות; שלחו אותה שוב. שימוש חוזר במפתח עם גוף בקשה אחר נדחה עם `422 idempotency_key_reused`.
- **זרעים.** עם `seed`, תמונה *i* של כל מודל מקבלת `seed + i`, כך שהתמונות שונות זו מזו.
- **עם ערכה**, השדה `count` יוצר N תמונות מהבקשה של הערכה. עם `models`, הערכה מוחלת פעם אחת לכל מודל, כך שכל מודל מקבל את הבקשה שהעורך היה שולח אליו.
- **מציאת אצווה מאוחר יותר.** בכל משימה של האצווה מופיעים אותו `input_data.batchId` והמיקום שלה ב-`input_data.batchIndex`. הנתיב `GET /v1/jobs?batchId=<id>` מחזיר את כל המשימות האלה.
- **דחייה לפני שמשהו נוצר.** הנתיב מחזיר `400 validation_error` על `count` מחוץ לטווח 1–4, על פחות מ-2 או יותר מ-4 מודלים ב-`models`, על מודל שחוזר פעמיים או מודל לא מוכר, על `models` יחד עם `provider`, או על יותר מ-8 תמונות. על מודל שהפריסה לא מציעה התגובה היא `403 model_not_available`.

## יצירת מסכה
הנתיב `POST /v1/generate-mask` מסמן בתמונה נושא שמתואר בטקסט, ומחזיר תמונת מסכה בשחור־לבן. הלבן הוא הנושא. גוף הבקשה הוא `{ imageUrl, prompt, threshold? }`:

- השדה `prompt` מקבל מ-1 עד 500 תווים, למשל `"the person"` (האדם).
- השדה `threshold` הוא ערך בין 0 ל-1, וברירת המחדל היא 0.3. ערך נמוך יותר מסמן יותר.

גוף בקשה פגום נדחה עם `400 validation_error`. התגובה היא `{ jobId }`. הפלט של משימה שהסתיימה הוא `{ maskUrl, imageUrl }`: השדה `maskUrl` הוא קובץ ה-PNG של המסכה, והשדה `imageUrl` הוא תמונת המקור, שמועברת בלי שינוי. העבירו את שניהם לעריכה עם השלמה. הרצה עולה 55 קרדיטים, והנתיב `GET /v1/credits/model-cost?model=generate-mask` מחזיר את אותו מחיר. ראו את הדף [יצירת מסכה](https://nodaro.ai/docs/nodes/image/generate-mask).

## יצירת וידאו
שני נתיבים יוצרים וידאו. הנתיב `POST /v1/generate-video` מנפיש מתמונות: פריים פתיחה ב-`imageUrl`, פריים סיום אופציונלי ב-`endFrameUrl`, או ייחוסים בלבד במודלים שמקבלים אותם. הנתיב `POST /v1/text-to-video` יוצר קליפ מפרומפט בלבד, ולכן `prompt` הוא שדה נדרש בו. כשמשמיטים את `provider`, הפלטפורמה משתמשת במודל הווידאו שמוגדר אצלה כברירת מחדל.

הנתיב `POST /v1/text-to-video` מקבל גם ערך בוליאני אופציונלי, `contentPolicyRewrite`. כשהוא חסר או `true`, אחרי דחייה בגלל זכויות יוצרים יש ניסיון חוזר אחד עם פרומפט בניסוח אחר, ואז הפלט של המשימה כולל `contentPolicyRewrite: { original, rewritten }`. עם `false`, דחייה בגלל זכויות יוצרים מכשילה את המשימה.

```json
{
"imageUrl": "https://…/frame.png",
"provider": "seedance-2",
"prompt": "she turns toward the window",
"duration": 8,
"resolution": "720p",
"direction": { "cameraMotion": "dolly-in", "timeOfDay": "dawn" }
}
```

התגובה היא `{ "jobId": "…" }`, עם `warnings` ו-`adjustments` כשהם רלוונטיים. במשימה שהסתיימה מופיע `output_data.videoUrl`.

<TypeTable
type={{
imageUrl: { type: 'string', description: 'פריים הפתיחה. נדרש, אלא אם המודל יכול לרוץ מייחוסים בלבד.' },
endFrameUrl: { type: 'string', description: 'פריים הסיום, במודלים שתומכים בווידאו מפריים ראשון ואחרון.' },
prompt: { type: 'string', description: 'מה קורה בקליפ, עד 30,000 תווים בגוף הבקשה. למודלי וידאו יש מגבלות משלהם, נמוכות בהרבה.' },
provider: { type: 'string', description: 'מזהה המודל, כגון seedance-2, kling-3.0 או veo3.1.' },
duration: { type: 'number', description: 'שניות, מ-1 עד 60, לפי המודל. הערך ‎-1 פירושו אוטומטי במשפחת Seedance 2.' },
resolution: { type: 'string', description: 'למשל 480p, 720p, 1080p או 4k. ערך שהמודל לא תומך בו מותאם לרמה הקרובה ביותר שיש למודל.' },
aspectRatio: { type: 'string', description: 'אחד הערכים 16:9, 9:16, 1:1, 4:3, 3:4, 4:5, 5:4, 21:9, 9:21 או adaptive, לפי המודל.' },
generateAudio: { type: 'boolean', description: 'בקשה לסאונד עם הווידאו, במודלים שיכולים ליצור אותו.' },
negativePrompt: { type: 'string', description: 'ממה להימנע.' },
referenceImageUrls: { type: 'string[]', description: 'תמונות ייחוס, עד 30 בגוף הבקשה. לכל מודל יש מקסימום משלו.' },
referenceVideoUrls: { type: 'string[]', description: 'קליפי ייחוס, עד 10, במודלים שמקבלים אותם.' },
referenceAudioUrls: { type: 'string[]', description: 'אודיו ייחוס, עד 10, במודלים שמקבלים אותו.' },
connectedReferences: { type: 'object[]', description: 'תמונות ייחוס מתויגות. ראו את הסעיף שימוש בייחוסים.' },
direction: { type: 'object', description: 'מזהי בוררים לתנועת מצלמה, למסגור, לתאורה ולמראה.' },
subject: { type: 'object', description: 'מזהי בוררים לאדם, לסטיילינג ולאביזרים.' },
seed: { type: 'integer', description: 'מספר קבוע להרצות שאפשר לחזור עליהן, במודלים שתומכים בכך.' },
characterReferences: { type: 'object[]', description: 'עד 3 אנשים שהקליפ שומר על הפנים שלהם, ב-Gemini Omni וב-Gemini Omni Flash, או עד 7 נושאים עם שם ב-Kling 4.0. גם הנתיב POST /v1/text-to-video מקבל את השדה. ראו את הכללים שלמטה.' },
keepOriginalSound: { type: 'boolean', description: 'Kling 4.0: שמירה על הסאונד של סרטוני הייחוס. בלי סרטון ייחוס, אין לשדה השפעה.' },
audioSoundtrack: { type: 'boolean', description: 'Kling 4.0: הקליפ הראשון ב-referenceAudioUrls מתנגן כפסקול (ברירת המחדל: true). הערך false משאיר כל קליפ כדגימת קול.' },
audioUrl: { type: 'string', description: 'מיושן. מוסיף רצועת אודיו מעל התוצאה. במקום זאת, שלחו את הקליפ ואת האודיו לרכיב מיזוג וידאו ואודיו.' },
}}
/>

כמה כללים משתנים ממודל למודל:

- **במשפחת Seedance 2, `resolution` ו-`aspectRatio` מועברים כמו שהם**: המודל מתעלם מערך שהוא לא תומך בו, ואף פעם לא דוחה אותו. [Seedance 2](https://nodaro.ai/docs/models/video/seedance-2) הוא היחיד עם `4k` ו-`adaptive`. [Seedance 2.5](https://nodaro.ai/docs/models/video/seedance-2-5) יוצר עד 30 שניות בקריאה אחת, ועם פריים פתיחה הוא תמיד מרנדר ביחס הגובה־רוחב של אותו פריים.
- **[MiniMax H3](https://nodaro.ai/docs/models/video/minimax-h3) מקבל ב-`resolution` את `2K` או את `768P`**, בדיוק בכתיב הזה. הנתיב `GET /v1/nodes/:type` מציג ב-`providerResolutionWire` את הערך שצריך לשלוח.
- **אפשר לשלב פריימים וייחוסים** ב-Seedance 2 וב-MiniMax H3. כששולחים ייחוס לצד פריים פתיחה או פריים סיום, הפריימים הופכים לייחוסים ממוספרים בפרומפט, במקום נקודות התחלה וסיום קבועות. [Wan 3.0](https://nodaro.ai/docs/models/video/wan-3-0) לא יכול לשלוח את שניהם, ולכן הפריים מתווסף במקום זאת לסוף רשימת הייחוסים, והקריאה עדיין מצליחה.
- **סרטוני ייחוס עולים יותר.** במודלים שגובים תשלום על סרטוני ייחוס, קליפ ייחוס מתומחר לפי האורך שלו ועוד אורך הפלט, ולכן על קליפ מקור ארוך יותר משוריינים יותר קרדיטים. Wan 3.0 גובה רק לפי שניות הפלט. Kling 4.0 גובה לפי הפלט ועוד הארוך מבין הפלט והקליפים, ולכן קליפ קצר מהפלט נחשב באורך הפלט.
- **`characterReferences` שומר על הפנים של אדם אמיתי** ב-[Gemini Omni](https://nodaro.ai/docs/models/video/gemini-omni) וב-[Gemini Omni Flash](https://nodaro.ai/docs/models/video/gemini-omni-flash). כל רשומה היא `{ imageUrl, description, bodyImageUrl?, name?, voice? }`: פורטרט, תיאור של האדם עד 2,000 תווים, תמונת גוף מלא אופציונלית ושם אופציונלי עד 100 תווים. השדה `referenceImageUrls` הוא הקשר כללי בשביל המודל, ואילו הקלט הזה שומר על הפנים. בכל מודל אחר הבקשה נדחית עם `400 character_references_unsupported`. פריים פתיחה או פריים סיום באותה בקשה גורמים לדחייה עם `400 character_references_with_start_frame` או `400 character_references_with_end_frame`. הבקשה חייבת להיכנס בתקציב הקלטים של המודל, 7 יחידות: כל תמונה נחשבת 1, כל סרטון 2, וכל דמות 1, או 2 עם `bodyImageUrl`. בקשה שחורגת מהתקציב נדחית עם `400 character_references_quota`. על ייחוסי דמויות אין תוספת מחיר.
- **[Kling 4.0](https://nodaro.ai/docs/nodes/video/generate-video#kling-40-references-characters-and-keyframes)** (`kling-4`) רץ מטקסט בלבד, או מחומרי ייחוס, עד 15 חומרים בסך הכול. הוא מקבל `referenceImageUrls` (עד 10), `referenceVideoUrls` (עד 5, כל אחד באורך 2–30 שניות), `characterReferences` (עד 7) ו-`referenceAudioUrls` (עד 3). בקשה עם יותר מזה נדחית עם `400` עוד לפני שמשוריינים קרדיטים: הקוד `character_references_quota` כשהבקשה כוללת `characterReferences`, והקוד `validation_error` בכל מקרה אחר. השדות `imageUrl` או `endFrameUrl` נחשבים לתמונה: הם מצטרפים לתמונות הייחוס אחרי התמונות שלכם, והפרומפט מציין אותם כפריים הפותח או כפריים הסוגר. כל ייחוס דמות הוא נושא עם שם: השדות `imageUrl`, `bodyImageUrl` ועד 3 `extraImageUrls` הם 2–4 התמונות שלו, והפרומפט קורא לו `@<name>`. רשומה עם תמונה אחת נשלחת כתמונת ייחוס רגילה. מותר לשלב עם ייחוסי דמויות פריים פתיחה או פריים סיום, אבל רשומה עם `voice` נדחית עם `400 character_voice_unsupported`; שלחו קליפי קול ב-`referenceAudioUrls`. בפרומפט, קראו לחומרים `@image_N`, `@video_N`, `@<name>` ו-`@audio_N`. כשהפרומפט מזכיר חומר שהבקשה לא כוללת, ה-`@` שלו מוסר, כך שההרצה עדיין מתחילה. הרצה עם סרטון ייחוס מחויבת לפי כל שנייה של הפלט ועוד הארוך מבין הפלט וסרטוני הייחוס, בתעריף של הרצה עם ייחוסים.
- **פסקול אודיו ב-Kling 4.0 (מופעל כברירת מחדל).** הקליפ הראשון ב-`referenceAudioUrls` מתנגן כפסקול של התוצאה. הוא נשלח כעוד סרטון ייחוס: פריים סטטי עם האודיו, באורך האודיו, 2 שניות לפחות ולכל היותר כאורך הפלט. הסאונד שלו נשמר, ולכן האנשים בסצנה משחקים בדיוק לפי הרצועה. שאר הקליפים נשארים קולות עם שם, והאסימון `@audio_2` הופך ל-`@Voice1`. הפסקול תופס אחד מ-5 המקומות של סרטוני הייחוס, וההרצה מחויבת כהרצה עם סרטון ייחוס. עם `audioSoundtrack: false`, כל קליפ נשאר דגימת קול. עם הפסקול, Kling שומר גם על הסאונד של סרטוני הייחוס ששולחים.
- **Kling 4.0 Keyframes** (`kling-4-keyframes`) מקבל פריים פתיחה ב-`imageUrl`, עד 8 פריימי ביניים ב-`referenceImageUrls`, לפי הסדר, ופריים סיום ב-`endFrameUrl`. מותר לשלוח פריים סיום לבדו, ובלי אף פריים ההרצה היא טקסט לווידאו. השדה `duration` מגביל את מספר הפריימים הכולל: 4 ב-3 שניות, עוד פריים אחד על כל שנייה נוספת, ו-10 החל מ-9 שניות. בקשה עם יותר פריימים, או עם יותר מ-8 פריימי ביניים, נדחית עם `400 validation_error` עוד לפני שמשוריינים קרדיטים. בפרומפט, הפריימים הם `@first_frame`, `@keyframe_N` ו-`@last_frame`, והאסימון `@image_N` משוכתב ל-`@keyframe_N`. השדה `aspectRatio` חל רק כשאין אף פריים. המודל לא מקבל סרטוני ייחוס, אודיו או דמויות.
- **שני מודלי Kling 4.0** מקבלים `duration` של 3–30 שניות שלמות (ברירת המחדל 5) ו-`resolution` של `720p` (ברירת המחדל) או `1080p`; כל רזולוציה אחרת מרונדרת ב-720p ומשלמים עליה כמו על 720p. השדה `aspectRatio` הוא `auto` (ברירת המחדל: הוא מתאים את עצמו לחומרים, ו-16:9 כשאין חומרים), `16:9`, `9:16`, `1:1` או `21:9`. הסאונד תמיד מופעל (`sound` לא נלקח בחשבון), אין `seed`, והפרומפט יכול להכיל עד 8,000 תווים. המחיר שלהם הוא לפי שנייה, והם לא זמינים במסלול החינמי.
- **`voice` מצמיד קול אחד לייחוס דמות** לאורך כמה קליפים: `{ preset, description?, exampleLine? }`, כשהשדה `preset` הוא אחד ממזהי הקולות של Gemini. בקשה יכולה להצמיד עד 3 קולות שונים; אחרת היא נדחית עם `400 character_voices_over_limit`. ראו את הדף [יצירת וידאו](https://nodaro.ai/docs/nodes/video/generate-video) לרשימת הקולות.

האורכים, הרזולוציות ומחירי הקרדיטים של כל מודל מופיעים בדפי [מודלי הווידאו](https://nodaro.ai/docs/models/video), והנתיב `GET /v1/models` מחזיר אותם.

## שימוש בייחוסים
ייחוסים הם תמונות, ובווידאו גם קליפים ואודיו, שהמודל צריך להתאים אליהם. אפשר לשלוח אותם כרשימה שטוחה של כתובות, או כ**ייחוסים בפורמט מובנה**, שהנתיב ממספר, מתייג וכותב לפרומפט בשבילכם, בדיוק כמו שהעורך עושה עם רכיבים מחוברים.

### ייחוסים בפורמט מובנה
הנתיבים `POST /v1/generate-image`, `POST /v1/generate-video`, `POST /v1/text-to-video` ו-`POST /v1/extend-video` מקבלים `connectedReferences`. כל רשומה מתארת תמונה אחת:

<TypeTable
type={{
id: { type: 'string', description: 'מזהה יציב משלכם לייחוס. המזהה יכול לכלול נקודתיים ולוכסנים, ולכן גם כתובת של תמונה היא מזהה תקין.', required: true },
defaultName: { type: 'string', description: 'השם של הייחוס, למשל Maya או Old Town.', required: true },
source: { type: 'string', description: 'אחד הערכים manual, wired-image, wired-character, wired-face, wired-object, wired-creature או wired-location.', required: true },
url: { type: 'string', description: 'הכתובת של התמונה. כתובת ברשת פרטית, או כתובת עם פרוטוקול שהוא לא http או https, נדחית.', required: true },
description: { type: 'string', description: 'התווית שהפרומפט משתמש בה לייחוס הזה.' },
defaultRole: { type: 'string', description: 'מה לקחת מהתמונה כשאזכור לא מציין תפקיד, למשל background.' },
identityLock: { type: '{ enabled: boolean; text?: string }', description: 'כבוי כברירת מחדל. כשהוא מופעל, הוא מוסיף שורה קצרה שמקבעת בדיוק את הזהות של הייחוס הזה. השדה text מחליף את הניסוח המובנה, והמשתנה ‎{ref}‎ בתוכו מייצג את הביטוי שמפנה לייחוס.' },
descriptionOverride: { type: 'string', description: 'עד 2,000 תווים. מה הייחוס הזה, בהרצה הזו בלבד. התיאור הזה גובר על התיאור השמור של הדמות או של האובייקט.' },
}}
/>

בנתיבי הווידאו, הנתיב הופך את הרשומות האלה לייחוסים ממוספרים:

- **כל ייחוס שלא מזכירים מצורף.** הכתובת שלו מצטרפת לרשימת הייחוסים, בלי כפילויות, והוא מקבל שורה כמו `@image_1 (reference): <label>`. רשומה של `wired-character` הופכת במקום זאת לחלק מההוראה "Use these characters:" (השתמש בדמויות האלה:).
- **הרשימה נחתכת במגבלה של המודל** לפני המספור, כך שמספר ייחוס בפרומפט אף פעם לא מפנה לתמונה שלא נשלחה.
- **`{image:N:label}` בפרומפט** הופך ל-"the label from `@image_N`" (התווית מ-`@image_N`), וממוספר לפי הייחוסים המצורפים.
- **`{ref:<id>}` ו-`{ref:<id>:label}`** מפנים לייחוס לפי ה-`id` שנתתם לו. הפלטפורמה מחליפה את האסימון במספר של הייחוס אחרי שהיא ממספרת את הרשימה, כך שאף פעם לא צריך לחשב מספר בעצמכם. סדר המספור: קודם `referenceImageUrls` השטוחה, אחר כך דמויות שלא הזכרתם, ואחר כך שאר הרשומות, בסדר שלכם. אסימון שהייחוס שלו לא צורף, כי הוא חרג מהמגבלה או כי המודל לא מקבל ייחוסים, מוחלף בתווית שלו; אם אין תווית, ב-`defaultName`; ואם גם הוא חסר, האסימון מוסר בלי תחליף. האסימון אף פעם לא מגיע למודל כטקסט גולמי.
- **`referenceOrder`**, רשימה של מזהי ייחוסים, משנה את סדר הייחוסים וממספר אותם מחדש בהתאם. גם הנתיב `POST /v1/generate-image` מקבל אותו.

**עותקי תצוגה מוחלפים בקבצים המקוריים.** זה חל על כל קלט תמונה ב-`POST /v1/generate-video` וב-`POST /v1/text-to-video`: `imageUrl`, `endFrameUrl`, `referenceImageUrls`, ייחוסים מחוברים וייחוסי דמויות. קלט שהוא עותק תצוגה בגודל מותאם של תמונה ב-CDN של Nodaro, `https://cdn.nodaro.ai/cdn-cgi/image/<options>/<path>`, נשמר ונשלח כקובץ המקורי, `https://cdn.nodaro.ai/<path>`. אותו דבר חל על התמונות של הרצת טרנד ושל הרצת Nodaro Motion. בכתובות בשרתים אחרים משתמשים כמו שנשלחו.

**מטא־דאטה מוטמע מוסר לפני שמודל רואה תמונה.** בחלק מתמונות הקלט יש נתונים מוטמעים, כגון Content Credentials, EXIF, XMP, IPTC או מקטעי טקסט של PNG. תמונה כזו מקודדת מחדש בלי הנתונים האלה, בפורמט שלה, לפני שהיא נשלחת למודל. תמונה נקייה נשלחת כמו שהיא. הקובץ השמור שלכם לא משתנה.

השדה `connectedReferences` מעביר תמונות בלבד. השדות `referenceVideoUrls` ו-`referenceAudioUrls` נשארים רשימות שטוחות. בלי `connectedReferences`, הנתיבים מתנהגים כמו קודם: ה-`prompt` וה-`referenceImageUrls` שלכם נשלחים כמו שהם.

ב-`POST /v1/generate-image` ובכלי ה-MCP `generate_image`, אסימון `{image:N}` או `{image:N:label}` ב-`prompt` מפנה לתמונת הייחוס ה-N:

- **בלי ייחוסים מחוברים**, N סופר את `referenceImageUrls` השטוחה לפי הסדר, כך שהאסימון `{image:1}` הוא הכתובת הראשונה ששלחתם. זה כולל בקשה שהייחוסים האחרים היחידים בה הם `describedReferences`.
- **עם רשומה אחת לפחות ב-`connectedReferences`**, N סופר את הרשומות של `connectedReferences` לפי הסדר. כתובות `referenceImageUrls` שטוחות באותה בקשה עדיין מצורפות, לפני המחוברות, אבל אף אסימון לא מפנה אליהן. כדי להפנות לתמונה בפרומפט, שלחו אותה כרשומה ב-`connectedReferences`.
- **אסימון שאין תמונה במיקום שלו** הופך לתווית שלו: מ-`{image:2:person}` נשאר `person`. האסימון `{image:2}` בלי תווית מושמט.

העורך והרצת תהליך ממספרים את האסימונים באותו אופן. גם תמונה שמגיעה לרכיב יצירת תמונה דרך רכיב ביניים, כגון רכיב טקסט, נשלחת וממוספרת.

### אילו מודלי וידאו מקבלים תמונות ייחוס
| משפחת מודלים | תמונות ייחוס |
| --- | --- |
| משפחת [Seedance 2](https://nodaro.ai/docs/models/video/seedance-2) | עד 9 |
| [HappyHorse Ref2V](https://nodaro.ai/docs/models/video/happyhorse-1-1-ref2v) | עד 9 |
| [Gemini Omni](https://nodaro.ai/docs/models/video/gemini-omni), [Kling 3 Omni](https://nodaro.ai/docs/models/video/kling-3-omni), [Grok Imagine מתמונה לווידאו](https://nodaro.ai/docs/models/video/grok-imagine-i2v) | עד 7 |
| [VEO 3.1 Fast](https://nodaro.ai/docs/models/video/veo-3-1-fast) ו-[VEO 3.1 Lite](https://nodaro.ai/docs/models/video/veo-3-1-lite) | עד 3 |

בכל מודל אחר, אסימוני `{image:N}` מצטמצמים לתוויות שלהם, ושום דבר לא מצורף. [VEO 3.1 Quality](https://nodaro.ai/docs/models/video/veo-3-1-quality) לא נמצא ברשימה: המודל מתעלם מייחוסים שנשלחים עם `veo3`, וההרצה משתמשת בפריימים שלה.

### וידאו מייחוסים בלבד
ב-`POST /v1/generate-video`, פריים הפתיחה הוא אופציונלי כשהמודל תומך לפחות בסוג אחד של הייחוסים ששולחים, למשל Kling 3 Omni עם `referenceImageUrls` בלבד. הרצה מייחוסים בלבד ב-VEO 3.1 Fast או ב-Lite עוברת למצב ייחוסים מעצמה, כך שאין צורך ב-`generationType`. סוג ייחוס שהמודל לא יכול להשתמש בו לא נחשב: ייחוסי אודיו בלבד, במודל שמקבל רק תמונות, נדחים עם `400`.

**פריים סיום לבדו** מתקבל במודלים שמצרפים אותו לייחוסים שלהם: משפחת Seedance 2, MiniMax H3 ו-Wan 3.0. שלחו את `endFrameUrl` פעם אחת, בלי לחזור על התמונה ב-`referenceImageUrls`. החבילה `@nodaro/shared` מייצאת את `videoProviderFoldsLoneEndFrame(provider)`, כדי שהממשק שלכם יוכל להשתמש באותו כלל.

כשבבקשה אין פריים פתיחה, אין מצב ייחוסים ואין ייחוס שהמודל יכול להשתמש בו, התגובה נקבעת לפי המודל:

- במודל שלא יכול ליצור וידאו מטקסט בלבד, כגון Kling 3 Omni, HappyHorse Ref2V או Hailuo 2.3, הבקשה נדחית עם `400 image_required`. ההודעה מציינת אם ייחוסים יעבדו במקום זאת. הנתיב `GET /v1/models` הוא המקור הקובע לשאלה אילו מודלים אלה.
- בכל מודל אחר הבקשה נדחית עם `400 validation_error`, וההודעה מפנה אתכם ל-`POST /v1/text-to-video` ליצירת קליפ מפרומפט בלבד.

### הרחבת וידאו
הנתיב `POST /v1/extend-video` מקבל `connectedReferences` ו-`referenceImageUrls` רק עם `provider: "seedance-2-extend"`. כל מודל הרחבה אחר דוחה אותם עם `400`. המגבלה היא 8 תמונות שלכם, כי הפריים האחרון של קליפ המקור תופס מקום אחד ברשימת הייחוסים, אחרי התמונות שלכם, כך שהמספרים שלכם אף פעם לא זזים. 2 השניות האחרונות של המקור נשלחות כ-`@video_1`, והמחיר שלהן כבר כלול בתעריף ההרחבה: תמונות ייחוס לא מוסיפות קרדיטים. לרכיב הרחבת וידאו אין `direction` ואין `subject`, כי הפרומפט שלו ממשיך קליפ שכבר יש לו מראה.

### ייחוסים מתוארים
השדה `describedReferences` נותן שם לנושא שאפשר לתאר אבל עדיין אין לכם תמונה שלו, כגון תפקיד בתסריט. הנתיבים `POST /v1/generate-image`, `/v1/generate-video`, `/v1/text-to-video` ו-`/v1/extend-video` מקבלים עד 10 רשומות של `{ name, description }`. השם יכול להכיל עד 80 תווים, והתיאור עד 2,000.

- **שום דבר לא מצורף.** ייחוס מתואר לא תופס מקום ברשימת הייחוסים, והמספור של שאר הייחוסים לא משתנה.
- **הוא מגיע למודל כשורת טקסט:** `<Name> — <description>.` השאירו את השם בפרומפט, למשל `Natalie walks down the pier.` (Natalie הולכת לאורך המזח), והשורה מסבירה למודל מי זו Natalie. אל תכתבו בשבילו אזכור עם `@`: התחביר הזה מפנה לתמונה מצורפת.
- **הוא עובד לבד.** שלחו `describedReferences` בלי שום `connectedReferences`, כמו במקרה הרגיל של סיפור שנכתב לפני שקיימת דמות כלשהי.
- **רשומות בלי שם או בלי תיאור נשמטות**, ושם שחוזר נכתב פעם אחת. כל מודלי ההרחבה מקבלים ייחוסים מתוארים, כי אין בהם כתובת.

### תיאורים להרצה אחת וכיתובים
- **`descriptionOverride`** ברשומה של `connectedReferences` אומר מה הייחוס בהרצה הזו בלבד, עד 2,000 תווים. הוא נכנס למקום התיאור שכבר יש בהוראה של הייחוס, וגובר על התיאור השמור. כשאין להוראה תיאור, הוא מוסיף שורה אחת, כך שהמודל מקבל את המידע פעם אחת, אף פעם לא פעמיים.
- **`referenceVideoCaptions` ו-`referenceAudioCaptions`** ב-`POST /v1/generate-video` וב-`/v1/text-to-video` מתארים את קליפי הייחוס ואת האודיו, עד 500 תווים כל אחד. הם מיושרים לפי אינדקס: הרשומה `referenceVideoCaptions[0]` מתארת את `referenceVideoUrls[0]`. כל כיתוב הופך לשורה כמו `@video_1: <caption>.`, ורשומה ריקה מדלגת על קליפ אחד בלי לשבור את היישור.

### אזכור ייחוס בפרומפט
ב-`POST /v1/generate-image` אפשר למקם ייחוס בתוך המשפט, במקום להשאיר אותו ברשימה שבסוף. כתבו `@<name-slug>:<index>`, או `@<name-slug>:<index>:<role>` כדי לציין מה לקחת ממנו:

```json
{
"prompt": "a wide shot of @nessie:1 rising beside @dock:2:material",
"connectedReferences": [
{ "id": "cr-1", "defaultName": "Nessie", "source": "wired-creature", "url": "https://…/nessie.png" },
{ "id": "ob-1", "defaultName": "Dock", "source": "wired-object", "url": "https://…/dock.png" }
]
}
```

המודל מקבל "a wide shot of the creature from reference image A rising beside the material from reference image B" (שוט רחב של היצור מתמונת הייחוס A, שעולה לצד החומר מתמונת הייחוס B).

- **ה-slug נגזר מ-`defaultName`**, באותיות קטנות, וכל רצף של תווים אחרים הופך ל-`-` אחד: השם `Old Town` הופך ל-`old-town`. אין שדה slug שצריך להגדיר.
- **האינדקס רק מתאים את האזכור לייחוס.** הוא אף פעם לא נכתב לפרומפט, והפלטפורמה ממספרת את הייחוסים בעצמה.
- **התפקידים** של תמונות (`manual` ו-`wired-image`) הם `object`, `person`, `face`, `clothes`, `background`, `style`, `pose` ו-`texture`. יצורים מקבלים `creature`, `anatomy`, `markings`, `pose`, `color` ו-`style`. אובייקטים מקבלים `object`, `shape`, `material`, `color`, `texture` ו-`style`. כל מילה בודדת אחרת עוברת כמו שנכתבה. בלי תפקיד, חל ה-`defaultRole` של הרשומה.
- **`~lock` ו-`~nolock`** אחרי אזכור, כמו ב-`@town:1:background~lock`, מפעילים או מכבים את נעילת הזהות של הייחוס הזה, רק באזכור הזה.
- **השמות מותאמים לייחוסים בסדר הזה:** דמות, מיקום, תמונה, יצור, אובייקט. שם שמשותף לדמות ולתמונה מתייחס לדמות, ובתוך סוג אחד ההתאמה הראשונה גוברת.
- **אי אפשר להזכיר שם שה-slug שלו מתחיל בספרה**, למשל `3D Render`, שהופך ל-`3d-render`. כדי להזכיר ייחוס כזה, שנו את השם שלו. אזכור של ייחוס שחרג מהמגבלה של המודל נשאר טקסט מילולי.
- **אזכור מזיז את הייחוס** מהרשימה שבסוף למקום שבו הקלדתם אותו, והאותיות של הייחוסים שאחריו משתנות לפי הסדר במשפט. ביצור או באובייקט, אזכור גם מחליף את השורה שהייחוס היה מוסיף אחרת בסוף.

### נעילת ייחוסים
השדה `referenceLock` ב-`POST /v1/generate-image` מוסיף לפני תיאור הסצנה את הניסוח הבדוק של Nodaro לנאמנות לייחוסים:

| ערך | מה הוא מוסיף | למה הוא מתאים |
| --- | --- | --- |
| `standard` | הוראות להשתמש רק במה שהייחוסים מראים, לשמור על הדמיון ולשלב את הייחוסים יחד | קומפוזיציות מכמה ייחוסים |
| `multi-person` | אותו דבר, ועוד כללים שאוסרים לשנות פנים או לערבב ביניהן | שני פרצופים או יותר בשוט אחד |

שולחים את המזהה, לא טקסט: הניסוח שייך לפלטפורמה ומשתפר בלי לעדכן את הלקוח. בלי השדה, לא מתווספת נעילה.

## הכוונה קולנועית לפי מזהה
השדה `direction` מתאר את המצלמה, את התאורה ואת המראה בעזרת **מזהי בוררים**, במקום טקסט חופשי. הנתיבים `POST /v1/generate-image`, `POST /v1/generate-video` ו-`POST /v1/text-to-video` מקבלים אותו. Nodaro כותב לפרומפט את הניסוח הבדוק שלו לכל מזהה, כך שבקשה שמורה מקבלת עם הזמן ניסוח משופר, במקום להקפיא את הטקסט שהלקוח שלכם כתב.

```json
{
"prompt": "a knight on a hill",
"provider": "nano-banana-pro",
"direction": {
"shotSize": "wide-shot",
"lens": "wide-24mm",
"lightingStyle": "rembrandt",
"style": "anime",
"mood": ["happy", "joyful"]
}
}
```

### המפתחות ומאיפה מגיעים המזהים שלהם
כל מפתח הוא שדה של אחד הבוררים בבקרות יצירתיות. את המזהים התקפים מקבלים מ-`GET /v1/picker-catalogs/<picker>`, אותם קטלוגים שהבוררים בעורך משתמשים בהם.

| מפתחות | בורר | תמונה | וידאו |
| --- | --- | --- | --- |
| `shotSize`, `angle`, `coverage`, `composition`, `vantage` | [מסגור](https://nodaro.ai/docs/nodes/creative-controls/framing) | כן | כן |
| `pose` | [תנוחה](https://nodaro.ai/docs/nodes/creative-controls/pose) | כן | כן |
| `compositionEffect` | [אפקטי קומפוזיציה](https://nodaro.ai/docs/nodes/creative-controls/composition-effects) | כן | כן |
| `cameraFormat` | [מצלמה / סרט צילום](https://nodaro.ai/docs/nodes/creative-controls/camera-format) | כן | כן |
| `lens` | [עדשה](https://nodaro.ai/docs/nodes/creative-controls/lens) | כן | כן |
| `aperture`, `shutterSpeed`, `isoValue` | [הגדרות חשיפה](https://nodaro.ai/docs/nodes/creative-controls/exposure-settings) | כן | לא |
| `timeOfDay`, `lightingStyle`, `lightingDirection`, `lightingRatio`, `colorTemperature` | [תאורה](https://nodaro.ai/docs/nodes/creative-controls/lighting) | כן | כן |
| `colorLook` | [צבע / מראה](https://nodaro.ai/docs/nodes/creative-controls/color-look) | כן | כן |
| `atmosphere` | [אווירה](https://nodaro.ai/docs/nodes/creative-controls/atmosphere) | כן | כן |
| `postProcess` | [אפקטי פוסט־עיבוד](https://nodaro.ai/docs/nodes/creative-controls/post-process-effects) | כן | לא |
| `style` | [סגנון](https://nodaro.ai/docs/nodes/creative-controls/style) | כן | כן |
| `mood` | [מצב־רוח](https://nodaro.ai/docs/nodes/creative-controls/mood) | כן | כן |
| `aesthetic` | [אסתטיקה](https://nodaro.ai/docs/nodes/creative-controls/aesthetic) | כן | כן |
| `photoGenre` | [ז׳אנר צילום](https://nodaro.ai/docs/nodes/creative-controls/photo-genre) | כן | לא |
| `photographer` | [צלם](https://nodaro.ai/docs/nodes/creative-controls/photographer) | כן | לא |
| `renderQuality` | [איכות רינדור](https://nodaro.ai/docs/nodes/creative-controls/render-quality) | כן | לא |
| `setting` | [סביבה](https://nodaro.ai/docs/nodes/creative-controls/setting) | כן | כן |
| `era` | [תקופה](https://nodaro.ai/docs/nodes/creative-controls/era) | כן | כן |
| `backdrop` | [רקע](https://nodaro.ai/docs/nodes/creative-controls/backdrop) | כן | כן |
| `cameraMotion` | [תנועת מצלמה](https://nodaro.ai/docs/nodes/creative-controls/camera-motion) | לא | כן |
| `actionFx` | [אפקטי פעולה](https://nodaro.ai/docs/nodes/creative-controls/action-fx) | לא | כן |
| `temporalSpeed`, `temporalFreeze`, `temporalDirection`, `temporalShutter` | [זמן](https://nodaro.ai/docs/nodes/creative-controls/temporal) | לא | כן |
| `transition` | [מעבר](https://nodaro.ai/docs/nodes/creative-controls/transition) | לא | כן |
| `loopSubject` | [נושא לולאה](https://nodaro.ai/docs/nodes/creative-controls/loop-subject) | לא | כן |

מפתח שלא רלוונטי לנתיב מתקבל ופשוט לא מוסיף כלום, כך שאפשר לשלוח את אותה מפה של מזהי מראה גם לנתיבי התמונה וגם לנתיבי הווידאו, בלי שינוי.

### ערכים ומגבלות
- **מזהה אחד או רשימה.** המפתחות עם בחירה מרובה, `mood`, `aesthetic`, `photographer`, `atmosphere`, `postProcess`, `composition` ו-`lightingStyle`, מקבלים מזהים עד המגבלה של כל מפתח, ומזהים עודפים נשמטים. מפתח של בחירה יחידה שמקבל רשימה משתמש ברשומה הראשונה.
- **שני מקרים נדחים** עם `400 validation_error`: יותר מ-8 רשומות למפתח אחד, ומזהה ארוך מ-100 תווים.
- **מפתח חסר הוא לא מפתח ריק.** מפתח חסר פירושו שאין רמז, ואף פעם לא ברירת מחדל. מחרוזת ריקה או רשימה ריקה לא מוסיפות כלום.
- **הפלטות והמודבורדים שלכם אף פעם לא מדולגים.** ב-`/v1/generate-image`, השדה `colorLook` מקבל גם `"palette:<id>"`, והשדה `style` מקבל `"moodboard:<id>"`. ראו את הסעיף [הפלטות והמודבורדים שלכם](#palettes-and-moodboards).
- **מפתחות לא מוכרים ומזהים לא מוכרים מדולגים**, ולא נדחים. לקוח חדש יותר מול שרת ישן יותר מקבל פחות רמזים, ולא שגיאה, ולכן עדכנו את השרת לפני לקוח ששולח מפתחות חדשים.
- **חבילות קטלוג מותאמות אישית.** בפריסה שרושמת חבילות קטלוג משלה, הנתיב `GET /v1/picker-catalogs` מחזיר את המזהים שחבילה מוסיפה. המזהים האלה מתקבלים, אבל לא מוסיפים ניסוח ל-`direction`.

### לאן המילים נכנסות
הביטויים מתווספים אחרי הפרומפט שלכם, במקטע `[style]`. שורת הצילום כוללת את `cameraFormat`, `colorLook`, `style` ו-`era`, ושורת הסצנה כוללת את שאר מפתחות המראה:

```text
a knight on a hill

[style]:
<film line>
<scene line>
```

- הסדר בתוך שורה הוא הסדר הקבוע של הפלטפורמה, לא הסדר של המפתחות שלכם. ביטוי ששני מפתחות מוסיפים נכתב פעם אחת.
- שורה ריקה מושמטת. כשאף מפתח לא מוסיף כלום, אין מקטע בכלל, וה-`prompt` שלכם מגיע למודל בלי שינוי.
- בנתיבי הווידאו, מפתחות התנועה עובדים אחרת. הם מוסיפים מונח מקצועי קצר, כגון `cross-dissolve`, ונשארים בגוף הפרומפט, אחרי הטקסט שלכם, כי התנועה היא חלק מהשוט. המפתח `cameraMotion` בא ראשון. רק מפתחות המראה נכנסים למקטע `[style]`.
- בנתיבי הווידאו, המפתח `style` מוסיף את הניסוח של הסגנון לווידאו במקום הניסוח שלו לתמונה, באותו מקטע `[style]`. הניסוח לווידאו נכתב לשוטים בתנועה: הוא מציין קודם את המדיום ואחר כך את הטכניקות שלו, וסגנון צילומי מציין מדיום של צילום חי, כך שהתוצאה נשארת מצולמת. לכן אותו `"style": "anime"` מוסיף ניסוח שונה בנתיב וידאו וב-generate-image. השדה `jobs.input_data.prompt` שומר את הטקסט שהמודל קיבל. נקודות הקצה של הקטלוג מחזירות רק את הניסוח לתמונה. על הקנבס זה עובד באותו אופן: ראו את הסעיף [סגנונות לתמונות ולווידאו](https://nodaro.ai/docs/nodes/creative-controls/style#images-and-video) בדף סגנון.

### כשהפרומפט ארוך מדי
כל מודל מקבל פרומפט עד אורך משלו. אובייקט `direction` מלא יכול לחרוג לבדו ממגבלה קטנה, למשל 3,000 תווים ב-Seedream בצד התמונה, ו-1,000 תווים ב-Kling בצד הווידאו. במקרה כזה, Nodaro מסיר את ביטויי ההכוונה אחד אחד, מסוף הסדר הקבוע שלו, עד שהפרומפט נכנס במגבלה. שום דבר אחר לא מוסר לפניהם:

- ביטויי הנושא מוסרים רק אחרי כל ביטויי ההכוונה.
- הטקסט שלכם, הייחוסים שלכם והביטויים שקושרים אותם, אזכורי `@` והשורות `Style:` ו-`Avoid:` תמיד נשארים.
- רק כשהפרומפט עדיין לא נכנס, גם בלי שום רמז, סוף הטקסט נחתך ומתווסף `...`.

בנתיבי הווידאו, התקציב סופר גם את הוראות הייחוס שהנתיב מוסיף, ואלה אף פעם לא מוסרות. במודל שאין לו הגדרה של פרומפט שלילי, ה-`negativePrompt` שלכם מתווסף כשורת `Avoid:` שהמקום שלה נשמר קודם, כך שפרומפט שלילי ארוך בא על חשבון ביטויי הרמזים, ולא על חשבון הטקסט שכתבתם. הטקסט האופציונלי `injectCharacterContext` מתווסף אחרי השלב הזה ולא נכלל בתקציב.

המשימה מתעדת מה קרה. השדה `input_data.prompt` הוא מה שהמודל קיבל, השדה `input_data.userPrompt` הוא הטקסט ששלחתם (מחרוזת ריקה כששלחתם רק `direction`), והשדה `input_data.direction` הוא המזהים שלכם כפי ששלחתם אותם.

### הכוונה ששמורה ברכיב
רכיב [יצירת תמונה](https://nodaro.ai/docs/nodes/image/generate-image) בתהליך שמור יכול לשמור בנתונים שלו את אותו אובייקט `direction`. את האובייקט כותבים ה-API, MCP או אפליקציה שיוצרת תהליכים. העורך מתחשב בו בכל הרצה ובתצוגת הפרומפט הסופי. מזהים שמורים מתווספים לכל בורר מסגור, תאורה או סגנון שמחובר לרכיב: הרמז מהבורר המחובר בא קודם, ואחריו המזהים השמורים. ערכות וייצואים של תהליכים שומרים את המזהים יחד עם שאר הרכיב.

## הפלטות והמודבורדים שלכם
**פלטה** היא סט משלכם של 2–12 צבעים, ו**מודבורד** הוא סט משלכם של 1–12 תמונות ייחוס עם תיאור. שומרים אותם פעם אחת, ואז מכוונים בעזרתם כל הרצה של `POST /v1/generate-image`, כמו שבוחרים מראה צבע מובנה או סגנון מובנה. הם עובדים בכל מהדורה. מה הם עושים בעורך מוסבר במדריך [פלטות ומודבורדים](https://nodaro.ai/docs/guides/palettes-and-moodboards).

| | פלטה | מודבורד |
| --- | --- | --- |
| תוכן | שם באורך 1–80 תווים, 2–12 צבעים בפורמט `#RRGGBB` והערה אופציונלית של עד 500 תווים | שם, תיאור אופציונלי של עד 1,000 תווים ובין 1 ל-12 תמונות `{ url, caption? }` |
| אופן השימוש | `direction.colorLook: "palette:<id>"` | `direction.style: "moodboard:<id>"` |
| מה ההרצה מקבלת | כל צבע בפרומפט, בשם יומיומי ובקוד ההקסדצימלי שלו | התיאור בפרומפט, והתמונות כייחוסי סגנון |

```json
{
"prompt": "a lighthouse on a cliff at dusk",
"provider": "nano-banana-pro",
"referenceImageUrls": ["https://example.com/my-lighthouse.png"],
"direction": {
"colorLook": "palette:0f9e8d7c-6b5a-4c3d-9e2f-1a2b3c4d5e6f",
"style": "moodboard:1a2b3c4d-5e6f-4a7b-8c9d-0e1f2a3b4c5d",
"lightingStyle": "rembrandt"
}
}
```

### ניהול פלטות ומודבורדים
| שיטה | נתיב | גוף הבקשה או פרמטרי השאילתה | מה היא עושה |
| --- | --- | --- | --- |
| `GET` | `/v1/palettes` | `limit` (1–100, ברירת המחדל 50), `cursor` | הפלטות שלכם, מהחדש לישן: `{ data, nextCursor }`. |
| `POST` | `/v1/palettes` | `{ name, colors, description? }` | יצירת פלטה. מחזירה `201`. הצבעים נשמרים באותיות גדולות. |
| `GET` | `/v1/palettes/:id` | אין | פלטה אחת. |
| `PATCH` | `/v1/palettes/:id` | כל שילוב של `name`, `colors` ו-`description` | רק השדות ששולחים נכתבים. הערך `description: null` מנקה את ההערה. |
| `DELETE` | `/v1/palettes/:id` | אין | מחזירה `{ success: true }`. |
| `GET` | `/v1/moodboards` | `limit`, `cursor` | המודבורדים שלכם, מהחדש לישן: `{ data, nextCursor }`. |
| `POST` | `/v1/moodboards` | `{ name, description?, images }` | יצירת מודבורד. מחזירה `201`. |
| `GET` | `/v1/moodboards/:id` | אין | מודבורד אחד. |
| `PATCH` | `/v1/moodboards/:id` | כל שילוב של `name`, `description` ו-`images` | רק השדות ששולחים נכתבים. השדה `images` מחליף את כל התמונות. |
| `DELETE` | `/v1/moodboards/:id` | אין | מחזירה `{ success: true }`. התמונות המקושרות לא משתנות. |

- **התמונות של מודבורד הן קישורים, אף פעם לא עותקים.** כל `url` הוא כתובת `http` או `https` שהיא לא `localhost` ולא כתובת ברשת פרטית, עד 2,048 תווים. כיתוב יכול להכיל עד 200 תווים. תמונה שמחקתם, או תמונה שנמחקה לפי מדיניות שמירת המדיה של התוכנית שלכם, נעלמת גם מהמודבורד.
- **`nextCursor`** הוא ערך אטום. העבירו אותו בחזרה כ-`cursor` כדי לקבל את העמוד הבא. בעמוד האחרון הוא `null`.
- **הם שלכם בלבד.** על פלטה או מודבורד של אדם אחר התגובה היא `404`, כמו על פלטה או מודבורד שלא קיימים.
- **הרשאות:** אסימון של אפליקציית OAuth צריך `assets:read` לקריאות ו-`assets:write` לכתיבות. מגבלת הכתיבות היא 30 לדקה.
- **אותם נתיבים** זמינים ב-SDK (`client.palettes`, `client.moodboards`), ב-CLI (`nodaro palettes`, `nodaro moodboards`) ובכלי ה-MCP `list_palettes`, `save_palette`, `list_moodboards` ו-`save_moodboard`. כלי ה-MCP `generate_image` מקבל `palette_id` ו-`moodboard_id`. דרך MCP אין מחיקה.

### מה ההרצה מקבלת
- **פלטה** מוסיפה `color palette: <name> <hex>, …` לשורת הצילום במקטע `[style]`, במקום שבו היה נכנס מראה הצבע המובנה. כל צבע מקבל את השם היומיומי הקרוב אליו, כי מודל נצמד בצורה אמינה יותר לצבע שיש לו שם, ואת קוד ההקסדצימלי המדויק שלו. ההערה של הפלטה לא מתווספת לפרומפט.
- **מודבורד** מוסיף את התיאור שלו, בשורה אחת, במקום שבו היה נכנס הסגנון המובנה. כשאין לו תיאור, נשלחות רק התמונות שלו.
- **כמו כל ביטוי הכוונה**, הביטויים האלה מוסרים ראשונים כשהפרומפט חורג מהמגבלה של המודל.
- **התמונות של המודבורד נשלחות למודל כתמונות ייחוס**, כל אחת עם שורה קצרה שאומרת למודל לקחת ממנה את הצבעים, את האור, את המרקם ואת האווירה, ולא את הנושא או את הקומפוזיציה שלה. כשיש לתמונה כיתוב, הוא מתווסף.
- **הייחוסים שלכם באים קודם.** תמונות ששולחים בעצמכם שומרות על המקומות ועל המספור שלהן. התמונות של המודבורד ממלאות רק את המקומות שנשארים אחריהן.
- **אף פעם לא יותר ממה שהמודל מקבל.** התמונות נכנסות לפי הסדר במודבורד, ומה שלא נכנס לא נשלח. במקרה כזה התגובה כוללת את האזהרה `moodboard_images_not_sent`. מודל שלא מקבל תמונות ייחוס לא מקבל אף אחת מהן, והתיאור עדיין נכנס.
- **מחיר.** התמונות נחשבות לייחוסים, ולכן ההרצה מתומחרת כמו כל הרצה עם אותו מספר ייחוסים. המחיר שנבדק לפני ההרצה והמחיר שמשוריין סופרים את אותן תמונות: אלה שנשלחו בפועל.
- **`aspectRatio: "auto"`** שומר על הצורה של התמונה הראשונה שההרצה שולחת. כשלא שולחים תמונה משלכם, זו התמונה הראשונה של המודבורד.
- **המשימה שומרת את המזהים, לא עותקים.** הרצה חוזרת של המשימה משתמשת בפלטה או במודבורד כפי שהם באותו רגע.

### שגיאות ומגבלות
| סטטוס | קוד | מתי |
| --- | --- | --- |
| `400` | `validation_error` | קידומת `palette:` או `moodboard:` בלי uuid אחריה, פלטה במפתח שהוא לא `colorLook`, מודבורד במפתח שהוא לא `style`, פלטה או מודבורד ששולבו עם ערך אחר באותו מפתח, או בקשה ששולחת גם `presetId`. השדה `field` מציין את המפתח. |
| `404` | `direction_not_found` | הפלטה או המודבורד לא שייכים לכם, או שהם כבר לא קיימים. השדה `field` הוא `direction.colorLook` או `direction.style`. |

שתי התגובות מגיעות לפני שמשהו נוצר ולפני שנגבים קרדיטים. פלטות ומודבורדים עובדים רק ב-`generate-image`: הנתיבים `POST /v1/generate-video` ו-`POST /v1/text-to-video` מחזירים עליהם `400 validation_error`. בתהליך שמור, כשה-`direction` של רכיב יצירת תמונה מציין פלטה או מודבורד, הם נטענים מחדש בכל פעם שהתהליך רץ.

## תיאור הנושא לפי מזהה
השדה `subject` הוא אותו רעיון, בשביל **מי שמופיע בשוט**: האדם, הסטיילינג שלו והאביזרים בפריים. הנתיבים `POST /v1/generate-image`, `POST /v1/generate-video`, `POST /v1/text-to-video`, `POST /v1/generate-character` ו-`POST /v1/generate-creature` מקבלים אותו:

```json
{
"prompt": "on the seawall at dusk",
"provider": "nano-banana-pro",
"subject": {
"type": "woman",
"age": "age-30s",
"ethnicity": "east-asian",
"hairBase": "base-short-straight",
"makeup": "makeup-smoky",
"outerwear": "outerwear-trench",
"heldProp": "smartphone"
}
}
```

- **המפתחות הם השדות של הבוררים [אדם](https://nodaro.ai/docs/nodes/creative-controls/person) ו[סטיילינג](https://nodaro.ai/docs/nodes/creative-controls/styling)**, כגון `type`, `age`, `ethnicity`, `faceShape`, `hairColor`, `skinTone`, `makeup`, `outfit`, `outerwear` ו-`footwear`, ועוד שלושה מפתחות של אביזרים: `heldProp` ([אביזר יד](https://nodaro.ai/docs/nodes/creative-controls/held-prop)), `material` ([חומר](https://nodaro.ai/docs/nodes/creative-controls/material)) ו-`animal` ([חיה](https://nodaro.ai/docs/nodes/creative-controls/animal)). הנתיבים `GET /v1/picker-catalogs/person` ו-`/styling` מחזירים כל שדה וכל מזהה.
- **`subject` ו-`direction` אף פעם לא חופפים.** המפתחות שלהם נפרדים, כך שבחירה אחת אף פעם לא מוסיפה שני ביטויים. המפתח `pose` שייך ל-`direction`.
- **`customAge` הוא המספר היחיד.** שלחו `"age": "age-custom"` עם `"customAge": 34` כדי לקבל גיל מדויק בשנים. הערך מעוגל ונשמר בטווח שבין 0 ל-120.
- **לרשימות יש מגבלות לכל מפתח.** המפתחות `jewelry`, `wardrobeState` ו-`distinctiveFeature` מקבלים 3 מזהים. המפתחות `ethnicity`, `regionalAesthetic`, `hairColor`, `eyeColor`, `lipState`, `eyeState`, `skinTexture`, `hairState`, `heldProp` ו-`material` מקבלים 2. כל מפתח אחר, כולל `animal`, מקבל 1. מזהים עודפים נשמטים.
- **חריגות שנדחות:** יותר מ-8 רשומות למפתח אחד, מזהה ארוך מ-100 תווים, יותר מ-128 מפתחות או מפתח ארוך מ-64 תווים. התגובה היא `400 validation_error`.
- **מפתחות לא מוכרים מוסרים, ומזהים לא מוכרים מדולגים.** השדה `input_data.subject` במשימה מתעד בדיוק את המזהים שהיו בשימוש.
- **ביטויי הנושא הם חלק מהטקסט שלכם**, לפני ביטויי ההכוונה, ואף פעם לא במקטע `[style]`. הבורר אדם הופך לביטוי אחד, והבורר סטיילינג לביטוי אחר, ובחירות חופפות נכתבות פעם אחת. בנתיב התמונה, כל בחירה מוסיפה את הביטוי המלא שלה; נתיבי הווידאו מוסיפים את המונח הקצר, כי פריים הפתיחה כבר מראה מי הנושא.
- **ב-`POST /v1/generate-character` וב-`POST /v1/generate-creature`**, השדה `subject` מתאר מי מופיע בפורטרט. הניסוח נכנס מיד אחרי `seedPrompt` או `description`, ואחרי בחירות האדם והמלתחה של הדמות או של היצור עצמם, ובחירה שמופיעה בשניהם באותו ניסוח נכתבת פעם אחת. שני הנתיבים האלה משתמשים בניסוח של תמונת סטילס, ואף פעם לא מקצרים את הביטויים. ראו בדף ה-API [דמויות](https://nodaro.ai/docs/developers/api/characters#subject).

## סשני יצירה
כלי יצירה שבנוי על ה-API, כגון כלי ליצירת תמונות, וידאו או מוזיקה, יכול לשמור את העבודה שלו ב**סשנים** בתוך ה**פרויקטים** שלכם: בפרויקט נמצאים סשנים, ובכל סשן נמצאים פריטים. אותם סשנים מוצגים בכל מכשיר, והאפליקציה שלכם לא צריכה לשמור שום דבר בעצמה.

```bash
# 1. Open a session, optionally inside one of your projects.
SESSION=$(curl -s -X POST "https://app.nodaro.ai/v1/sessions" \
  -H "Authorization: Bearer $NODARO_API_KEY" -H "Content-Type: application/json" \
  -d '{"projectId": "<project-id>", "tool": "create-image", "title": "Beach"}' | jq -r .data.id)

# 2. Generate inside it: send sessionId beside the usual settings.
curl -s -X POST "https://app.nodaro.ai/v1/generate-image" \
  -H "Authorization: Bearer $NODARO_API_KEY" -H "Content-Type: application/json" \
  -d "{\"prompt\": \"a red car\", \"sessionId\": \"$SESSION\", \"origin\": \"create-image\"}"

# 3. Read the session's feed, newest first.
curl -s "https://app.nodaro.ai/v1/sessions/$SESSION/items" -H "Authorization: Bearer $NODARO_API_KEY"
```

| שיטה | נתיב | מה היא עושה |
| --- | --- | --- |
| `POST` | `/v1/sessions` | יצירת סשן. גוף הבקשה: `tool` (נדרש: אותיות לטיניות קטנות, ספרות ומקפים, עד 40 תווים), `projectId` (אופציונלי, אחד הפרויקטים שלכם) ו-`title` (אופציונלי, עד 200 תווים). |
| `GET` | `/v1/sessions` | הסשנים שלכם, מהחדש לישן. פרמטרי שאילתה: `projectId` (`none` לסשנים שלא שייכים לאף פרויקט), `tool`, `includeArchived=true`, `limit` (1–100, ברירת המחדל 50) ו-`cursor`. בכל סשן מופיעים `itemCount` (הפריטים הפעילים שלו), `removedCount` (הפריטים שלו בפח), `coverJobId` ועד ארבע כתובות ב-`previewUrls`: קודם התמונה של תמונת השער, כשהיא מוגדרת, ואחר כך התמונות של הפריטים החדשים ביותר שהסתיימו. |
| `GET` | `/v1/sessions/:id` | סשן אחד. |
| `PATCH` | `/v1/sessions/:id` | שינוי של `title` (`null` פירושו ללא שם), של `archived` ושל `coverJobId`: אחד הפריטים של הסשן, שיופיע ראשון בתצוגות המקדימות שלו (`404` כשהוא לא פריט של הסשן הזה), או `null` כדי לחזור לבחירה אוטומטית. הפרויקט של סשן נקבע כשהסשן נוצר. |
| `DELETE` | `/v1/sessions/:id` | מחיקת הסשן והקישורים שלו. המשימות שלו נשמרות, ונשארות בכל סשן אחר שהן נמצאות בו. |
| `GET` | `/v1/sessions/:id/items` | הפיד: כל פריט פעיל, שנוצר בסשן או שיובא אליו, מהחדש לישן. עם `removed=true`, במקום זאת, הפריטים שהוסרו ממנו, שהם החלק שלו בפח, מההסרה האחרונה לראשונה. לכל פריט יש `jobId`, `addedAt`, `removedAt` (`null` כשהפריט פעיל), `imported`, `importedFrom { sessionId, title }` ו-`job` במבנה ש-`GET /v1/jobs/:id` מחזיר. פריט שמחקתם את המשימה שלו לא מופיע באף אחת מהרשימות. עוברים בין העמודים עם `limit` ו-`cursor`. |
| `POST` | `/v1/sessions/:id/items` | ייבוא של `{ "jobIds": [...] }`, 1–100 משימות, כקישורים, אף פעם לא כעותקים. כשמזהה כלשהו הוא לא אחת המשימות שלכם, או שהמשימה נמחקה, שום דבר לא מיובא (`404`). פריט שהוסר מהסשן חוזר למקום שבו היה. מחזירה `{ imported, requested }`. |
| `DELETE` | `/v1/sessions/:id/items/:jobId` | הסרה של פריט אחד מהסשן הזה. הפריט עובר לפח ונשאר שם עד שמשחזרים אותו או מוחקים אותו לצמיתות. שום דבר לא מתנקה אוטומטית. רק הקישור זז: המשימה, וכל סשן אחר שהיא נמצאת בו, נשארים. הפרמטר `?forever=true` מוחק לצמיתות פריט שכבר הוסר, והמשימה עדיין נשארת. בפריט פעיל, הנתיב מחזיר `409 not_in_trash`. |
| `POST` | `/v1/sessions/:id/items/:jobId/restore` | החזרה של פריט שהוסר, למקום שבו היה. |
| `POST` | `/v1/sessions/:id/items/move` | העברה של `{ "jobIds": [...], "fromSessionId": "…" }`, 1–100 פריטים, לסשן הזה, בטרנזקציה אחת. הסשן הזה מקבל אותם, וסשן המקור שומר אותם כפריטים שהוסרו, כך שאפשר לבטל את ההעברה מהפח של סשן המקור. כשמזהה כלשהו הוא לא פריט פעיל של `fromSessionId`, שום דבר לא זז (`404`). מחזירה `{ moved }`. |

- **כל נתיב שיוצר משימה מקבל `sessionId`**, כגון `generate-image`, `generate-video`, `text-to-video`, `text-to-speech` ו-`generate-music`. הערך חייב להיות אחד הסשנים שלכם, באותו צד של הגבול בין סביבות העבודה כמו הבקשה. אחרת התגובה היא `404`, ושום דבר לא נוצר ולא נגבים קרדיטים. הרצות של תהליך אף פעם לא נשמרות בסשן.
- **ה-`session_id` של המשימה** נקבע פעם אחת, כשהמשימה נוצרת. הוא מופיע ב-`GET /v1/jobs/:id` וב-`GET /v1/jobs`.
- **כל נתיב שיוצר משימה מקבל גם `origin`**, ה-slug של האפליקציה שלכם. המשימה שומרת אותו ב-`input_data.origin`, והנתיב `GET /v1/jobs?origin=` מסנן לפיו. ערך שהוא לא slug נדחה עם `400 validation_error`, ושום דבר לא נוצר ולא נגבים קרדיטים.
- **כדי להעביר עבודה לפרויקט אחר**, העבירו את הפריטים לסשן של אותו פרויקט עם `POST /v1/sessions/:id/items/move`. הפרויקט של סשן קבוע, ולכן כך פריט עובר לפרויקט אחר. כדי להשאיר פריט בשני הפרויקטים, ייבאו אותו במקום להעביר אותו.
- **איפה משימה נמצאת עכשיו.** הנתיב `GET /v1/jobs` מקבל `projectId=<id>`, המשימות שנמצאות עכשיו בסשן של אותו פרויקט, ו-`projectId=none`, המשימות שנמצאות בסשן שלא שייך לאף פרויקט. הוא מקבל גם `inSession=false`, המשימות שלא נמצאות עכשיו באף סשן (**כללי**), ו-`inSession=true`. כל משימה מופיעה פעם אחת, לא משנה בכמה סשנים היא נמצאת. הפרמטר `withSessions=true` מוסיף לכל משימה `sessions: [{ id, title, projectId }]`, לתצוגה של ״כל מה שבפרויקט הזה״. אי אפשר לשלב את `projectId` עם `inSession=false` (`400`). המסננים האלה משתלבים עם `type`, עם `status` ועם שאר המסננים.
- **הפח.** פריטים שהוסרו ומשימות שנמחקו עוברים לפח. ראו בדף ה-API [הפח](https://nodaro.ai/docs/developers/api/jobs#the-trash).
- **הרשאות:** עם אסימון של אפליקציית OAuth, קריאת סשנים דורשת `jobs:read`. יצירה, עריכה, ייבוא והסרה דורשים `assets:write`, וכך גם שליחת `sessionId` עם יצירה. אסימון API אישי שמוגבל לתהליכים מסוימים לא יכול להגיע לנתיבים האלה.

## אומדני מחיר להרצה
הנתיב `POST /v1/quote` מחזיר כמה קרדיטים ישוריינו להרצת רכיב, לפני שמתחילים אותה. שלחו את סוג הרכיב ובדיוק את גוף הבקשה שהייתם שולחים לנתיב ההרצה שלו. השרת מתמחר אותו עם הקוד של נתיב ההרצה עצמו: אותה בדיקה של גוף הבקשה, אותה מדידה של קליפי ייחוס, אותו מזהה קרדיטים ואותו חישוב. לכן אומדן המחיר הוא בדיוק הסכום שמשוריין להרצה. שום דבר לא מתחיל, ולא נגבים קרדיטים.

- **איפה:** רק ב-Nodaro Cloud. הנתיב דורש אימות ומאפשר 30 בקשות לדקה.
- **אילו סוגים:** `text-to-video`, `generate-video`, `video-to-video` ו-`video-upscale`. ההשלמה ב-1080p של טיוטת Seedance 2.5 ב-`video-upscale` מתומחרת לפי הטיוטה ולפי הקליפים שהיא נוצרה מהם. בקשה עם כל סוג אחר נדחית עם `400 quote_unsupported`, עם רשימת הסוגים הנתמכים.
- **עריכות Seedance:** עריכת Seedance ב-`video-to-video` מתומחרת עם כל קליפ שנשלח איתה, ב-`videoUrl` וב-`referenceVideoUrls`. ראו את הדף [וידאו לווידאו](https://nodaro.ai/docs/nodes/video/video-to-video#api) לרשימות הייחוסים.
- **SDK:** `client.nodes.quote(type, params)`.

```json
{
"type": "text-to-video",
"params": {
"prompt": "edit {video:1} as follows: make it black and white",
"provider": "seedance-2-5",
"duration": -1,
"aspectRatio": "adaptive",
"referenceVideoUrls": ["https://…/clip.mp4"]
}
}
```

התגובה היא `{ "data": { identifier, reserve, expected?, adjustments, warnings } }`:

| שדה | משמעות |
| --- | --- |
| `identifier` | מזהה הקרדיטים שההרצה מחויבת לפיו. |
| `reserve` | הקרדיטים שמשוריינים להרצה כשהיא מתחילה. |
| `expected` | הקרדיטים שההרצה אמורה לעלות בפועל, כשהקלטים שנמדדו מאפשרים לחשב את זה. הרצת Seedance עם סרטון ייחוס משוריינת לפי המקרה הגרוע ביותר: שניות הקלט ועוד הארוך מבין האורך המבוקש והקליפ הארוך ביותר. עם משך אוטומטי, כשכל הקליפים נמדדו, השריון הוא שניות הקלט ועוד האורך של הקליפ הראשון, מעוגל כלפי מעלה לשנייה שלמה, ועוד שנייה אחת. השדה `expected` הוא הקלט ועוד האורך שההרצה כנראה תרנדר. זה האורך של קליפ המקור בעריכה עם משך אוטומטי, והשניות המבוקשות בכל מקרה אחר. הערך אף פעם לא גבוה מ-`reserve`. |
| `adjustments` | רשימה של `{ code, message, credits? }` שמסבירה למה המחיר הוא מה שהוא: `ref_video_length` (תמחור לפי האורכים של הקליפים), `extra_reference_images` (תמונות ב-MiniMax H3 מעבר ל-5 הראשונות), `auto_duration`, `loop_trim_addon` ו-`voiced_audio_addon` (עם הקרדיטים שלהם), ו-`params_adjusted` (רזולוציה או יחס שהותאמו לאלה שהמודל מציע). |
| `warnings` | רשימה של `{ code, message }`: `settles_to_actual` (ההרצה מחויבת לפי מה שנוצר, והקרדיטים מ-`reserve` שלא נדרשו מוחזרים) ו-`ref_video_unmeasured` (לא היה אפשר למדוד קליפ, ולכן הוא מתומחר כקליפ הארוך ביותר שהמודל מקבל). |

- גוף בקשה לא תקין נדחה עם ה-`400` של נתיב ההרצה עצמו. קליפ שההרצה הייתה דוחה נדחה עם ה-`400 video_too_long` שלה. מודל שהפריסה הזו לא מציעה נדחה עם ה-`403 model_not_available` של ההרצה.
- המדידה של קליפ שמאוחסן ב-Nodaro, כגון העלאה או תוצאה שנוצרה, נשמרת ל-15 דקות. כשמבקשים אומדן מחיר ואז מריצים את אותו גוף בקשה, הקליפ נמדד פעם אחת.
- אומדן מחיר בודק את גוף הבקשה ואת המחיר, לא את היתרה או את התוכנית שלכם. ההרצה עדיין יכולה להידחות עם `402`. כמה בדיקות שרק ההרצה עושה עדיין יכולות לדחות גוף בקשה שקיבל אומדן, למשל מודל שצריך פריים פתיחה.

## גילוי רכיבים
הנתיב `GET /v1/nodes` מחזיר את כל סוגי הרכיבים שהשרת מכיר, והנתיב `GET /v1/nodes/:type` מחזיר סוג אחד. שני הנתיבים ציבוריים, לא צריכים אסימון ונשמרים במטמון ל-5 דקות. על סוג לא מוכר התגובה היא `404 not_found`.

**curl**

```bash
curl -s https://app.nodaro.ai/v1/nodes/generate-image | jq .data
```

**TypeScript SDK**

```ts
const { data: nodes } = await client.nodes.list()
const imageNodes = nodes.filter((n) => n.category === 'ai-image')

const { data: generateImage } = await client.nodes.get('generate-image')
console.log(generateImage.providers)
```

**CLI**

```bash
nodaro nodes list --category ai-image
nodaro nodes get generate-image
```

```json
{
"data": {
"type": "generate-image",
"label": "Generate Image",
"category": "ai-image",
"description": "Generate an image from a text prompt using an AI provider.",
"outputType": "image",
"creditCost": "3-682",
"providers": ["nano-banana-pro", "gpt-image-2", "gpt-image-2-5-flare", "seedream-5-pro", "z-image"],
"capabilities": ["supports-reference-image", "supports-aspect-ratio"],
"inputSchema": {
"fields": [
{ "key": "prompt", "type": "text", "required": true },
{ "key": "provider", "type": "select", "options": ["nano-banana-pro", "gpt-image-2"] },
{ "key": "aspectRatio", "type": "select" },
{ "key": "promptPrefix", "type": "text" },
{ "key": "promptSuffix", "type": "text" }
]
}
}
}
```

| שדה | משמעות |
| --- | --- |
| `type` | סוג הרכיב. ברוב סוגי הרכיבים הוא גם הנתיב: `POST /v1/<type>`. |
| `label`, `category`, `description` | איך העורך קורא לרכיב ובאיזו קבוצה הוא מציג אותו. |
| `outputType` | `text`, `image`, `video`, `audio`, `data` או `none`. |
| `creditCost` | עלות הקרדיטים של הרכיב, או הטווח שלה. זה המחיר שהרצה עולה, אותו מספר שמופיע בכפתור **הרצה** של הרכיב. ברכיבים קיצוץ וידאו, לולאת וידאו ושילוב סרטונים, זה במקום זאת המחיר של מדרגה אחת של 5 שניות, והרצה עולה כמה מדרגות. רק ב-Nodaro Cloud: מהדורות בלי קרדיטים משמיטות את השדה. |
| `estimate` | הערך `true` כש-`creditCost` הוא אומדן: הקריאה הטיפוסית של רכיב LLM עם חיוב לפי השימוש, במודל ברירת המחדל שלו. ההרצה מחויבת לפי השימוש בפועל, ולכן הציגו את המספר כ-`≈N`. רכיב בחירת המיטב מציג טווח `"0-N"` שמסומן `estimate`: מהאסטרטגיות החינמיות שלו ועד הקריאה הטיפוסית של שופט ה-AI שלו. השדה חסר כשהמחיר קבוע. רכיב LLM עם חיוב לפי השימוש, שלמודל ברירת המחדל שלו עדיין אין מחיר, מציג `creditCost: "unpriced"`, והרצה במודל הזה נדחית. ראו בדף ה-API [שגיאות](https://nodaro.ai/docs/developers/api/errors#llm-model-unpriced). |
| `creditRate` | לרכיב שהמחיר שלו נקבע לפי יחידה של מדד משלו, וה-`creditCost` שלו הוא מילה ולא מספר. כרגע זה רכיב [מסגרות דוברים](https://nodaro.ai/docs/nodes/video/speaker-frames), עם `creditCost` שערכו `"per-1000-frames"`. האובייקט הוא `{ identifier, unit, creditsPerUnit, minUnits, rounding, note }`. השדה `creditsPerUnit` הוא המחיר המדויק של יחידה אחת, בלי עיגול. עם `rounding: "ceil-run-total"`, הרצה של N יחידות (לפחות `minUnits`) עולה `ceil(N × creditsPerUnit)`, עם עיגול כלפי מעלה פעם אחת. אל תכפילו את המחיר של יחידה אחת מ-`/v1/credits/model-cost`. |
| `providers` | מזהי המודלים שהרכיב מקבל ב-`provider`. |
| `capabilities` | דגלי יכולות, כגון `supports-reference-image`. |
| `inputSchema.fields` | ההגדרות של הרכיב, עם הסוג שלהן, אם הן נדרשות, והאפשרויות שלהן. |

כל רכיב שמקבל פרומפט מציג גם `promptPrefix` ו-`promptSuffix`: טקסט שמתווסף לפני הפרומפט ואחריו. ראו את הדף [טקסט לפני ואחרי הפרומפט](https://nodaro.ai/docs/concepts/prompt-pre-post-text). ברכיבים עם מגבלות לפי מודל יש שדות נוספים:

| שדה | משמעות |
| --- | --- |
| `maxDurationSec` | משך הזמן הארוך ביותר שהרכיב מקבל. |
| `sparseProviders` | מודלים עם מעט אורכי מקטע בלבד. ערך שנמצא ביניהם מותאם לקרוב ביותר. |
| `providerResolutions` | הרזולוציות של כל מודל, למשל `{ "minimax-h3": ["2K", "768P"] }`. |
| `providerResolutionWire` | הערך המדויק שצריך לשלוח לכל רזולוציה, למשל `768P` ולא `768p`, לרמה הזולה יותר של MiniMax H3. |
| `soundtrack` | ברכיב יצירת וידאו Pro: השרת מקבל את הקלט `soundtrack` של האודיו המקורי. |

אובייקט התיאור של הרכיב מתרחב עם הזמן, ולכן התעלמו משדות שאתם לא מכירים. אותם נתונים מזינים את [מדריך הרכיבים](https://nodaro.ai/docs/nodes).

## גילוי מודלים
הנתיב `GET /v1/models` מחזיר את קטלוג המודלים, מקובץ לפי סוג ולפי יצרן. הנתיב ציבורי ונשמר במטמון ל-5 דקות, והנתונים שלו זהים לאלה שכלי ה-MCP `list_models` מחזיר.

מודל שהפריסה הזו לא מציעה לא מופיע ברשימה, כולל מודל שמנהל כיבה. אותו מתג דוחה הרצה של המודל הזה עם `403 model_not_available`. לקוח ששומר את הרשימה במשך 5 הדקות עדיין עשוי לבקש מודל כזה.

**curl**

```bash
curl -s "https://app.nodaro.ai/v1/models?kind=video&mode=i2v" | jq '.totalModels'
```

**TypeScript SDK**

```ts
const catalog = await client.models.list({ kind: 'video', mode: 'i2v' })
for (const section of catalog.sections)
for (const family of section.families)
for (const m of family.models) console.log(m.id)
```

**CLI**

```bash
nodaro models list --kind video --mode i2v
```

התגובה היא `{ sections, recommendations, totalModels }`. כל מודל מציין את ה**יצרן** שלו וכולל את הסמל של היצרן:

| שדה | סוג | משמעות |
| --- | --- | --- |
| `maker` | `{ id, name }` | החברה שיצרה את המודל. השדה `id` יציב, אחד לכל חברה, באותיות קטנות עם מקפים, כגון `google`, `black-forest-labs` או `bytedance`. השתמשו בו כמפתח לקבצים ולמשאבים שלכם, וטפלו גם במזהה שאתם לא מכירים, כי שרת חדש יותר יכול להוסיף יצרנים. הוא לא תמיד ה-`family` שהמודל מקובץ תחתיה. |
| `logoUrl` | string או `null` | הסמל הרשמי של היצרן כקובץ SVG ב-CDN המדיה של השרת הזה, או `null` כשהשרת לא מפרסם סמל. במקרה כזה הציגו חלופה, כגון שם היצרן או האות הראשונה שלו. השתמשו בכתובת כמו שהיא: היא כוללת את הגרסה של הקובץ, כך שסמל שהוחלף מגיע בכתובת חדשה. |

הנתיב `GET /v1/model-makers` מחזיר רק את היצרנים, לאפליקציה שמציגה סמלים ולא צריכה את הקטלוג. התגובה היא `{ makers: [{ id, name, logoUrl }] }`, עם כל היצרנים שהפלטפורמה מכירה. השדות זהים לאלה שלמעלה, כך ששני הנתיבים אף פעם לא סותרים זה את זה. הנתיב ציבורי ונשמר במטמון ל-5 דקות, כמו `GET /v1/models`. הוא מציג יצרן גם כשאף אחד מהמודלים שלו לא מוצע.

כלי ה-MCP `list_models` כולל את `maker`, אבל לא את `logoUrl`.

כל מודל כולל גם את היכולות שלו (`modes`, `features`, `aspectRatios`, `resolutions`, `durations`), את מחירי הקרדיטים שלו לכל וריאנט ב-Nodaro Cloud ב-`pricing`, טיפים קצרים ב-`promptTips`, ואת `doctrineCovered`, שערכו `true` רק כשיש מדריך פרומפטים מבוסס מקורות למשפחת המודל. הקרדיטים ב-`pricing` הם המחיר שהרצה עולה, אותו מספר שמופיע בכפתור **הרצה** בעורך.

מודל דיבור, כגון מודל של טקסט לדיבור או מודל דיאלוג, מציג שורת `pricing` שנייה, `<model>:per-100-chars`, כשהדיבור מתומחר לפי אורך. השורה הזו היא המחיר של כל 100 תווים, או חלק מהם, מהטקסט שנשלח, עם מינימום של 8 יחידות להרצה. בקשה מתומחרת כמכלול ומעוגלת פעם אחת, ולכן `N × the row's credits`, כש-`N = max(8, ceil(characters / 100))`, הוא רק חסם עליון. המחיר המדויק הוא המזהה `<model>:per-100-chars:<N>u`, ש-`POST /v1/credits/model-costs` ו-`GET /v1/credits/model-cost` מחזירים. השתמשו במספר הזה כאומדן המחיר, ולא במכפלה. כשהשורה חסרה, גם כל המזהים מסוג `:<N>u` חסרים: השתמשו בשורה הקבועה של המודל. ראו בדף ה-API [מחירים מדויקים להרצות שמתומחרות לפי יחידה](https://nodaro.ai/docs/developers/api/credits#exact-prices-of-runs-priced-per-unit).

| פרמטר שאילתה | ערכים | מה נשאר ברשימה |
| --- | --- | --- |
| `kind` | `image`, `video` או `audio` | סוג מדיה אחד |
| `mode` | למשל `t2i`, `i2v`, `t2v`, `tts`, `video-analysis` | פעולה אחת |
| `family` | יצרן, למשל `Google` או `Bytedance` | יצרן אחד |
| `featuredOnly` | `true` | מודלים נבחרים |

[דפי המודלים](https://nodaro.ai/docs/models) מציגים את אותו קטלוג.

## גילוי ערכי בוררים
לבוררים של בקרות יצירתיות יש קטלוגים ציבוריים של מזהים תקפים, הערכים ש-`direction` ו-`subject` מקבלים:

| שיטה | נתיב | מה היא מחזירה |
| --- | --- | --- |
| `GET` | `/v1/picker-catalogs` | כל בורר: `nodeType`, `label`, `kind`, השדה או השדות שלו, `optionCount` ו-`imageCount`. |
| `GET` | `/v1/picker-catalogs/:nodeType` | האפשרויות של בורר אחד. הפרמטר `?detail=full` מוסיף לכל אפשרות את `description` ואת `promptHint` (באפשרות של `style`, הניסוח שלה לתמונה), הפרמטר `?category=` מסנן בורר עם שדה יחיד, והפרמטר `?field=` מחזיר שדה אחד של בורר עם כמה שדות. |
| `GET` | `/v1/catalogs` | כל הקטלוגים בקריאה אחת, לפי הבחירה של הפריסה. השדה `data` מופיע רק כשהפריסה רשמה חבילות קטלוג. |
| `POST` | `/v1/text-to-picker` | ״מילוי ב-AI״: בחירת מזהים לבוררים רבים מתוך תיאור חופשי של סצנה. בלי `llmModel`, הנתיב מריץ את Gemini 3.8 Flash. החיוב הוא לפי הטוקנים שהקריאות שלו משתמשות בהם, בתעריף של המודל. ראו את הדף [קטלוגי הבוררים](https://nodaro.ai/docs/developers/picker-catalogs#fill-pickers-from-a-description). |

בכל אפשרות יש `id`, `label` ו-`term`, הביטוי הקצר לשימוש בפרומפט, ועוד `imageUrl` כשיש לאפשרות תמונה. הקטלוגים מגיעים גם כנתונים בחבילת ה-npm `@nodaro/prompts`. המבנים המלאים מופיעים בדף [קטלוגי הבוררים](https://nodaro.ai/docs/developers/picker-catalogs). מהטרמינל: הפקודות `nodaro pickers list`, `nodaro pickers get mood --full` ו-`nodaro pickers analyze "<text>"`.

## פלט מובנה מ-LLM
הנתיב `POST /v1/llm/structured` מריץ קריאה אחת למודל שפה, שהתשובה שלה נכפית לתוך JSON Schema שאתם מספקים, עוברת בדיקת תקינות ומוחזרת כאובייקט. הקריאה מחויבת לפי הטוקנים שהיא השתמשה בהם בפועל, בתעריף של המודל בשירות שהריץ אותה. הטוקנים כוללים את פרומפט המערכת, הקלט והסכמה שלכם, כל בקשה חוזרת אחרי בדיקת תקינות שנכשלה וכל מעבר לשירות גיבוי, ואת התשובה. ראו את הסעיף [איך מחויבות קריאות LLM](https://nodaro.ai/docs/concepts/credits#llm-calls) בדף קרדיטים.

- **לפני הקריאה**, Nodaro משריין קרדיטים: המחיר של הניסיון הראשון לפי תקרת הפלט של המודל. כשהיתרה נמוכה מהשריון, הבקשה נדחית עם `402` וההודעה "Needs up to N credits" (נדרשים עד N קרדיטים).
- **אחרי הקריאה**, החיוב הוא לפי השימוש בפועל. בדרך כלל זה פחות מהשריון.
- **אומדן המחיר** לקריאה טיפוסית מגיע מ-`POST /v1/credits/model-costs` עם המפתח `llm-structured:<model>`. זה אומדן, ולכן הציגו אותו כ-`≈N`. ראו בדף ה-API [קרדיטים](https://nodaro.ai/docs/developers/api/credits#llm-calls).
- **כשלים**: על כשל שהספק חייב עליו משלמים לפי השימוש הזה, והשגיאה מציינת כמה. דוגמאות: תשובה שלא עברה בדיקת תקינות גם אחרי הניסיונות החוזרים, סירוב או תשובה שנקטעה בתקרה. על כשל שהספק לא חייב עליו, הקרדיטים מוחזרים.

Claude Fable 5 זמין בנתיב הזה.

```json
{
"system": "You write production plans.",
"input": "A rainy chase through Rome.",
"jsonSchema": {
"type": "object",
"properties": { "title": { "type": "string" } },
"required": ["title"]
}
}
```

התגובה היא `{ jobId, output, usage: { inputTokens, outputTokens } }`, ו-`output` בנוי לפי הסכמה שלכם.

- **שדות:** `system`, `input`, `jsonSchema`, ובאופן אופציונלי `schemaName` (עד 64 תווים), `llmModel`, `reasoningEffort`, `maxRetries`, `origin`, `advancedMode`, `temperature` ו-`maxTokens`. השדות `system` ו-`input` מקבלים עד 100,000 תווים כל אחד, והשדה `input` צריך תו אחד לפחות.
- **מודל:** בלי `llmModel`, הקריאה רצה ב-Gemini 3.6 Flash.
- **סכמה:** השורש חייב להיות סכמה של אובייקט פשוט, עד 64 KB ועד 20 רמות עומק. מילות המפתח המותרות: `properties`, `required`, `additionalProperties`, `items`, הטיפוסים הבסיסיים, `enum`, `const`, הגבולות המספריים וגבולות האורך, `multipleOf`, `exclusiveMinimum` ו-`description`. מילות המפתח `anyOf` ו-`oneOf` מתקבלות רק מתחת לשורש. מילות המפתח `not`, `if`, `then` ו-`else`, מילות המפתח מסוג `dependent`, `$ref` חיצוני ו-combinators בשורש נדחים עם `400`. מבנה `anyOf` של ענפי `required` מתחת לשורש מתקבל אבל לא נאכף, ולכן בדקו בעצמכם כללים שחוצים כמה שדות.
- **ניסיונות חוזרים:** השדה `maxRetries`, מ-0 עד 3, עם ברירת מחדל של 2, הוא מספר הפעמים שתשובה לא תקינה חוזרת למודל עם שגיאת הבדיקה שלה.
- **פרמטרי דגימה:** השדה `maxTokens` חל על כל קריאה, ואסור שיחרוג מהמגבלה של המודל עצמו. השדה `temperature` לא נלקח בחשבון, אלא אם שולחים גם `advancedMode: true`, שמריץ את הקריאה ב-API של יצרן המודל עצמו, בתעריף של אותו שירות.
- **משך:** הקריאה סינכרונית ויכולה לרוץ כמה דקות. כל ניסיון יכול להימשך עד 240 שניות בכל אחד משני שירותים, ולכן המקרה הגרוע ביותר הוא 24 דקות עם ברירת המחדל של `maxRetries`, ו-32 דקות עם המקסימום. הגדילו את פסק הזמן של לקוח ה-HTTP שלכם, או השתמשו בצורת המשימה שלמטה. ברירת המחדל של ה-SDK ל-`timeoutMs`, 60 שניות, קצרה מדי.
- **שגיאות:** `400 validation_error`, `400 advanced_mode_unavailable`, `401`, `402`, `500 internal_error`, `502 llm_error` אחרי שהניסיונות החוזרים נגמרו, `502 advanced_mode_unavailable` ו-`503 provider_unavailable`. על הדחייה `advanced_mode_unavailable`, ראו את התיאור של `advancedMode` למעלה. שני קודים נוספים, `409 job_cancelled` ו-`503 job_start_failed`, קשורים לשריון. ראו בדף ה-API [שגיאות](https://nodaro.ai/docs/developers/api/errors#metered-llm-requests).

ב-SDK, קראו ל-`client.llm.structured(body)`.

### כמשימה
הנתיב `POST /v1/llm/structured/jobs` מקבל את אותו גוף בקשה ומחזיר מיד `{ jobId }`. בדקו את הסטטוס עם `GET /v1/jobs/:id/status` מדי כמה שניות: ב-`completed`, השדה `output_data` הוא `{ output, inputTokens, outputTokens }`, וב-`failed`, השדה `error_message` מציין למה. את הטיוטות שלכם תמצאו שוב עם `GET /v1/jobs?type=llm-structured&origin=<your app>`. הנתיב מקבל שלושה שדות נוספים:

- `label`: שם תצוגה למשימה, עד 120 תווים.
- `videoUrl`: טיוטה מסרטון. הסרטון מנותח קודם, כמשימה נפרדת שגם היא שלכם, במחיר של ניתוח וידאו, והניתוח מתווסף ל-`input` שלכם. בזמן שהניתוח רץ, השדה `output_data.stage` הוא `analyzing`, ואחר כך `drafting`.
- `videoAnalysis`: האובייקט `{ llmModel?, selectionMode? }` לניתוח הזה.

הקרדיטים לטיוטה משוריינים כשהמשימה נוצרת, תחת `llm-structured:<model>` (עם `:direct` ב-API של הספק עצמו). אחר כך המשימה מחויבת לפי מה שהטיוטה השתמשה בו. ביטול הטיוטה עם `POST /v1/jobs/:id/cancel` מבטל גם ניתוח שרץ. כשהניתוח נדחה, כל הקרדיטים ששוריינו לטיוטה מוחזרים. בהתקנה ששולחת את הקריאות שלה למודלי שפה ל-nodaro.ai, צורת המשימה מחזירה `503 provider_unavailable`: בהתקנה כזו, השתמשו בקריאה הסינכרונית. ב-SDK, קראו ל-`client.llm.structuredJob(body)`.

## שאלות נפוצות

### איך יוצרים תמונה עם ה-API של Nodaro?

שלחו POST /v1/generate-image עם השדות prompt ו-provider. התגובה כוללת jobId. בדקו את הסטטוס עם GET /v1/jobs/:id/status מדי כמה שניות עד שהוא completed, ואז קבלו את התוצאה מ-output_data.imageUrl. ב-SDK, המתודה client.nodes.runAndWait עושה את שני השלבים.

### אילו רכיבים אפשר להריץ מה-API?

כל סוג רכיב שהנתיב GET /v1/nodes מחזיר. הנתיב הוא POST /v1/‎ ואחריו סוג הרכיב, וגוף הבקשה הוא ההגדרות של הרכיב ב-JSON. כמה רכיבי טקסט משתמשים בנתיב ארוך יותר, כגון ‎/v1/llm-chat/generate.

### מהו האובייקט direction?

מפה שטוחה של מזהי בוררים, כגון ‎{ "shotSize": "wide-shot", "timeOfDay": "golden-hour" }‎, ששולחים עם יצירה של תמונה או וידאו. Nodaro כותב לפרומפט את הניסוח הבדוק שלו לכל מזהה. המזהים התקפים מגיעים מ-GET /v1/picker-catalogs.

### איך שומרים על דמות עקבית דרך ה-API?

שלחו את התמונה של הדמות ב-connectedReferences, עם השדה source שערכו wired-character ועם שם הדמות ב-defaultName. הנתיב מצרף את התמונה וכותב לפרומפט את ההוראות לשמירה על הזהות, כמו שהעורך עושה.

### האם נקודות הקצה של הגילוי צריכות אסימון?

לא. הנתיבים GET /v1/nodes, GET /v1/models, GET /v1/picker-catalogs ו-GET /v1/catalogs ציבוריים ונשמרים במטמון ל-5 דקות. הם חושפים רק נתוני קטלוג, ואף פעם לא מידע על החשבון שלכם.

### מה קורה כשהפרומפט, יחד עם מזהי ההכוונה, ארוך מדי בשביל המודל?

Nodaro משמיט קודם את ביטויי ההכוונה, אחד אחד מהסוף, עד שהפרומפט נכנס במגבלה. ביטויי הנושא נשמטים אחרונים, והטקסט שכתבתם והייחוסים שלכם אף פעם לא נשמטים כדי לפנות להם מקום.
