# סביבות עבודה וארגונים

> פעולה בסביבת עבודה של ארגון דרך ה-API של Nodaro עם X-Nodaro-Workspace, שיוך אסימונים לסביבת עבודה וניהול של חברים, הזמנות, שיתוף ושימוש.

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

**סביבות עבודה** הן המקום שבו חברי ארגון עובדים יחד: כיתה בבית ספר, צוות בחברה. בקשה ל-API פועלת בסביבת עבודה אחת כששולחים את המזהה שלה בכותרת `X-Nodaro-Workspace`, ונקודות הקצה של הארגונים מנהלות את כל מה שמסביב: ארגונים, חברים, הזמנות, קודי הצטרפות, שיתוף, תקציבים ושימוש.

ארגונים הם יכולת של Nodaro Cloud, ומפעילים אותם בנפרד בכל מופע. במופע שבו הם לא מופעלים, נקודות הקצה שלמטה לא קיימות, והשרת מתעלם מהכותרת. התקנות באירוח עצמי לא כוללות ארגונים. ראו את הדף [סביבות עבודה](https://nodaro.ai/docs/concepts/workspaces) להסבר על המושגים.

## פעולה בסביבת עבודה
```http
X-Nodaro-Workspace: 6f1e6b4c-6a4e-4b7b-9d2a-2f0f0a1d9c34
```

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

בלי הכותרת אתם עובדים במרחב האישי שלכם, כמו שעובד תמיד חשבון שלא שייך לארגון.

**curl**

```bash
curl -s https://app.nodaro.ai/v1/workflows \
  -H "Authorization: Bearer $NODARO_API_KEY" \
  -H "X-Nodaro-Workspace: 6f1e6b4c-6a4e-4b7b-9d2a-2f0f0a1d9c34"
```

**TypeScript SDK**

```ts
// Every request of this client acts in the workspace:
const classClient = createClient({
baseUrl: 'https://app.nodaro.ai',
auth: new StaticTokenAuth(process.env.NODARO_API_KEY!),
workspaceId: '6f1e6b4c-6a4e-4b7b-9d2a-2f0f0a1d9c34',
})

// Or derive a second client from an existing one:
const classroom = client.withWorkspace(workspaceId)
await classroom.workflows.run(workflowId) // lands in the class
await client.workflows.run(workflowId)    // lands in the personal space
```

**CLI**

```bash
nodaro --workspace 6f1e6b4c-6a4e-4b7b-9d2a-2f0f0a1d9c34 workflows list   # this command only
export NODARO_WORKSPACE=6f1e6b4c-6a4e-4b7b-9d2a-2f0f0a1d9c34            # this shell or CI job
nodaro workspace use 6f1e6b4c-6a4e-4b7b-9d2a-2f0f0a1d9c34                # saved on the profile
nodaro workspace current                                                # which one applies, and why
```

המתודה `client.withWorkspace` מחזירה לקוח **חדש** במקום לשנות את הקיים, כך ששתי פעולות שרצות במקביל אף פעם לא מתנגשות בשאלה באיזו סביבת עבודה הן פועלות. כדי לעבוד במרחב האישי, העבירו `null`. ב-CLI, כל אחת משלוש הדרכים גוברת על זו שמתחתיה, והפקודה `nodaro workspace use` בודקת את סביבת העבודה לפני שהיא שומרת אותה.

שלחו את הכותרת רק עם סביבת עבודה שאתם חברים בה:

| סטטוס | קוד | מתי |
| --- | --- | --- |
| 400 | `validation_error` | הערך הוא לא UUID. |
| 403 | `not_a_member` | אתם לא חברים בסביבת העבודה הזו, היא לא קיימת או שהארגון שלה לא פעיל. |
| 403 | `member_suspended` | החברות שלכם מושעית. |

הבקשות הבאות אף פעם לא דוחות בחירה שכבר לא תקפה, ולכן בחירה כזו לא יכולה לנעול אתכם בחוץ: `GET /v1/me`, `GET /v1/workspaces`, קריאת הזמנה, אישור הזמנה והצטרפות לסביבת עבודה עם קוד. בבקשות האלה, סביבת עבודה שאתם כבר לא יכולים לבחור נחשבת כאילו לא שלחתם כותרת. הנתיבים `GET /v1/me` ו-`GET /v1/workspaces` מציינים אילו סביבות עבודה מותר לכם לבחור, ולכן מחקו בחירה ששמרתם במטמון ברגע שהיא כבר לא מופיעה בתשובות שלהם.

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

```bash
# Bind
curl -X PATCH https://app.nodaro.ai/v1/api-tokens/$TOKEN_ID \
  -H "Authorization: Bearer $SESSION_JWT" \
  -H "Content-Type: application/json" \
  -d '{"workspaceId": "6f1e6b4c-6a4e-4b7b-9d2a-2f0f0a1d9c34"}'

# Unbind
curl -X PATCH https://app.nodaro.ai/v1/api-tokens/$TOKEN_ID \
  -H "Authorization: Bearer $SESSION_JWT" \
  -H "Content-Type: application/json" \
  -d '{"workspaceId": null}'
```

ניהול אסימונים דורש את ה-JWT של הסשן המחובר שלכם: בקשה עם אסימון API או עם אסימון OAuth נדחית עם `403 forbidden`. אפשר לשייך אסימון רק לסביבת עבודה שהייתם יכולים לבחור עם הכותרת, וביטול השיוך מותר תמיד. רשימת האסימונים שלכם מחזירה את השיוך בשדה `workspaceId`.

## לאן נכנס מה שיוצרים
- **בתוך סביבת עבודה, מה שיוצרים נכנס לסביבת העבודה**, אף פעם לא למרחב האישי שלכם. בקשת יצירה שלא מציינת פרויקט נכנסת לפרויקט של סביבת העבודה. כשעדיין אין לסביבת העבודה פרויקט, בקשת היצירה נדחית עם `409 workspace_has_no_default_project`: ציינו פרויקט, והבקשה תצליח.
- **פרויקט שמציינים חייב להיות שייך לסביבת העבודה שפועלים בה.** בקשה עם פרויקט של סביבת עבודה אחרת, או של המרחב האישי שלכם, נדחית עם `404 Project not found` (הפרויקט לא נמצא). זו אותה תשובה כמו לפרויקט שלא קיים, ולכן אי אפשר להשתמש בכותרת כדי לגלות מה קיים.
- **ייתכן שיצירת פרויקטים מוגבלת למנהלים.** במקרה כזה, בקשה של חבר נדחית עם `403 project_create_not_allowed`.
- **ארגון יכול לסגור את המרחב האישי.** החברים שלו יוצרים אז רק בתוך סביבת עבודה, ובקשת יצירה בלי הכותרת נדחית עם `403 personal_space_disabled`. חשבונות שלא שייכים לארגון אף פעם לא מושפעים מזה.

## סביבות עבודה בארכיון הן לקריאה בלבד
העברה של סביבת עבודה לארכיון משאירה את כל מה שבה זמין לקריאה, ומונעת הוספה של עבודה חדשה. רשימות מתנהגות כמו קודם. כל בקשת יצירה — של פרויקט, תהליך, ייבוא או תת־תהליך — נדחית עם `409 workspace_archived`, וכך גם העברה של עבודה לתוך סביבת העבודה. העברה של עבודה החוצה נשארת מותרת, כי הצלת העבודה היא הסיבה לפתוח סביבת עבודה שבארכיון. פעולות כתיבה אחרות בסביבת העבודה, כגון שיתוף תהליך, נדחות עם `403 workspace_archived`. בדקו את הקוד, לא את הסטטוס.

## מי אתם: GET /v1/me
הנתיב `GET /v1/me` מחזיר את הפרופיל שלכם. במופע עם ארגונים, התשובה כוללת גם את `organizations`, `workspaces` ו-`lastWorkspaceId`, וכל רשומה בהם מציינת את התפקיד ואת הסטטוס שלכם. רשומות הארגונים כוללות את אוצר המילים הסופי של הארגון, `vocabulary`, למשל המילה שהארגון משתמש בה לסביבת עבודה, כך שלקוח אף פעם לא צריך לקבע בקוד "Class" (כיתה) או "Team" (צוות). הנתיב `GET /v1/workspaces` מחזיר את אותה רשימה של סביבות עבודה בפני עצמה.

הבחינו בין שלושת המצבים של שדות הארגונים:

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

## נקודות הקצה של הארגונים
כל נקודות הקצה דורשות אימות, חוץ מנקודת הקצה `GET /v1/invitations/by-token/:token`. גוף הבקשה הוא JSON, התגובות הן `{ "data": … }`, רשימות הן `{ "data": [ … ], "nextCursor": … }` והמזהים הם UUID.

### ארגונים
| שיטה | נתיב | מי | גוף או שאילתה |
| --- | --- | --- | --- |
| `POST` | `/v1/orgs` | כל מי שמחובר | `{ name, kind, slug?, acceptTerms?, settings? }`, כשהשדה `kind` הוא `school` או `team`. מחזירה `201`. |
| `GET` | `/v1/orgs` | כל אחד | הארגונים שלכם, עם ה-`role` וה-`memberStatus` שלכם. |
| `GET` | `/v1/orgs/:id` | חבר | |
| `PATCH` | `/v1/orgs/:id` | בעלים, מנהל | `{ name?, settings? }`. ההגדרות מתמזגות מפתח אחר מפתח, ומפתחות לא מוכרים לא נשמרים. |
| `POST` | `/v1/orgs/:id/transfer-ownership` | בעלים | `{ userId }`. הבעלים החדש חייב להיות מנהל פעיל, והבעלים הקודם הופך למנהל באותו שלב. |
| `DELETE` | `/v1/orgs/:id` | בעלים | הבקשה נדחית עם `409 has_active_workspaces` כל עוד יש סביבת עבודה שלא הועברה לארכיון. הארגון נמחק מחיקה רכה (soft delete), וה-slug שלו מתפנה. |
| `POST` | `/v1/orgs/:id/leave` | חבר | הבעלים לא יכול לעזוב (`409 owner_cannot_leave`): העבירו קודם את הבעלות. עזיבה מוציאה אתכם מכל סביבות העבודה של הארגון. |

- **אישור.** ארגון חדש מתחיל בדרך כלל בסטטוס `pending`, ועובר ל-`active` אחרי שהפלטפורמה מאשרת אותו. בזמן ההמתנה רק הבעלים שלו רואה אותו, ושום דבר בו לא יכול להשתנות. עדכנו את מי שיצר אותו, אחרת ההמתנה תיראה לו כמו כשל.
- **בית ספר דורש אישור תנאים.** יצירת בית ספר דורשת `acceptTerms: true`, ובערך הזה הבעלים מאשר שיש לו סמכות לרשום תלמידים. בלי הערך הזה, בקשת היצירה נדחית עם `400 terms_required`.
- **ה-slug** מורכב מ-1 עד 50 אותיות לטיניות קטנות, ספרות ומקפים. אם שולחים slug, הוא חייב להיות פנוי (`409 name_taken`). בלי slug, הוא נגזר מהשם: "Sunrise School" (בית ספר הזריחה) הופך ל-`sunrise-school`, ואחר כך ל-`sunrise-school-2`.
- יצירת ארגונים מוגבלת לכמה ארגונים בשעה לכל משתמש.

### חברים
| שיטה | נתיב | מי | גוף או שאילתה |
| --- | --- | --- | --- |
| `GET` | `/v1/orgs/:id/members` | בעלים, מנהל | `?limit=50&cursor=…`, עד 200. השדות בכל רשומה: `userId`, `role`, `status`, `joinedAt`, `email`, `displayName`, `avatarUrl`. |
| `PATCH` | `/v1/orgs/:id/members/:userId` | בעלים, מנהל | `{ role?, status? }`, עם `admin` או `member` ב-`role` ו-`active` או `suspended` ב-`status`. אי אפשר לשנות את הרשומה של הבעלים. |
| `DELETE` | `/v1/orgs/:id/members/:userId` | בעלים, מנהל | אי אפשר להסיר את הבעלים. ההסרה מוציאה את האדם גם מכל סביבות העבודה של הארגון. |

### סביבות עבודה
| שיטה | נתיב | מי | גוף או שאילתה |
| --- | --- | --- | --- |
| `POST` | `/v1/orgs/:id/workspaces` | בעלים, מנהל | `{ name, slug?, description?, settings? }`. מחזירה `201`. ה-slug ייחודי בתוך הארגון. |
| `GET` | `/v1/orgs/:id/workspaces` | חבר | חברים רואים את סביבות העבודה שלהם, ובעלים ומנהלים רואים את כולן. הפרמטר `?includeArchived=true` מוסיף את אלה שבארכיון. |
| `GET` | `/v1/workspaces` | כל אחד | כל סביבות העבודה שאתם חברים בהן, בכל הארגונים. |
| `GET` | `/v1/workspaces/:id` | חבר בסביבת העבודה | |
| `PATCH` | `/v1/workspaces/:id` | מנהל סביבת העבודה | `{ name?, description?, settings? }` |
| `POST` | `/v1/workspaces/:id/archive` | בעלים, מנהל | העברה לארכיון. אפשר לבטל אותה, ושום דבר לא נמחק. |
| `POST` | `/v1/workspaces/:id/unarchive` | בעלים, מנהל | הוצאה מהארכיון. |

### חברים בסביבת עבודה
| שיטה | נתיב | מי | גוף או שאילתה |
| --- | --- | --- | --- |
| `GET` | `/v1/workspaces/:id/members` | חבר בסביבת העבודה | `?limit&cursor`. חברים רואים את `userId`, `role`, `displayName`, `avatarUrl` ו-`addedAt`. מנהלים רואים גם את `status` ואת `creditCap`. ברשימת החברים אין כתובות אימייל. |
| `POST` | `/v1/workspaces/:id/members` | מנהל סביבת העבודה | `{ userId, role }`, כשהשדה `role` הוא `admin` או `member`. האדם חייב כבר להיות חבר פעיל בארגון (`400 not_org_member`): כדי לצרף מישהו חדש, הזמינו אותו. |
| `PATCH` | `/v1/workspaces/:id/members/:userId` | מנהל סביבת העבודה | `{ role?, status?, creditCap? }`. השדה `creditCap` הוא תקרת ההוצאה של החבר או `null` כשאין תקרה. |
| `DELETE` | `/v1/workspaces/:id/members/:userId` | מנהל סביבת העבודה | מסיר את האדם מסביבת העבודה בלבד. הוא נשאר בארגון. |

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

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

| שיטה | נתיב | מי | גוף או שאילתה |
| --- | --- | --- | --- |
| `POST` | `/v1/orgs/:id/invitations` | בעלים ומנהל, וגם מנהלי סביבות עבודה לסביבת העבודה שלהם כשהארגון מאפשר זאת | `{ emails, orgRole?, workspaceId?, workspaceRole? }`. עד 200 כתובות, שמומרות לאותיות קטנות ומנוקות מכפילויות. מחזירה `201` עם רשומה אחת לכל כתובת: `{ email, status, link? }`. |
| `GET` | `/v1/orgs/:id/invitations` | אותם אנשים | `?status=` (`open`, `accepted`, `revoked` או `expired`), `workspaceId`, `limit` ו-`cursor`. אף פעם לא מחזירה אסימון. |
| `DELETE` | `/v1/invitations/:id` | אותם אנשים | ביטול ההזמנה. הקישור מפסיק לעבוד. הבקשה נדחית על הזמנה שכבר אושרה. |
| `POST` | `/v1/invitations/:id/resend` | אותם אנשים | מנפיק אסימון ותאריך תפוגה חדשים ושולח את ההזמנה שוב. הקישור הקודם מפסיק לעבוד. |
| `GET` | `/v1/invitations/by-token/:token` | ציבורי | מה שהמוזמן צריך כדי להחליט: `orgName`, `kind`, `vocabulary`, `inviterName`, `workspaceName`, כתובת האימייל כשחלק ממנה מוסתר, `expiresAt` ו-`state`. עם מגבלת קצב לכל כתובת IP. |
| `POST` | `/v1/invitations/:token/accept` | המוזמן, כשהוא מחובר | האימייל של החשבון חייב להתאים להזמנה (`400 email_mismatch`). אישור שני נדחה. |

- **לכל כתובת יש רשומה משלה.** השדה `status` הוא `sent`, `link_only` או `failed`, והשדה `link` מופיע בכל פעם שלא נשלח אימייל לכתובת. בהתקנה בלי שירות אימייל, כל הכתובות חוזרות עם `link_only`. הציגו את הקישורים האלה: ההזמנה קיימת בכל מקרה, ובלי הקישור אף אחד לא יכול להגיע אליה.
- **תוקף ההזמנות פג אחרי 14 ימים**, וארגון יכול לשלוח עד 500 הזמנות ביום (`429 bulk_invite_cap_exceeded`).
- **הזמנה חוזרת של אותה כתובת** מעבירה את ההזמנה הפתוחה שלה לאסימון חדש, במקום ליצור הזמנה שנייה.
- **הזמנה של חבר קיים** מסמנת את ההזמנה כמנוצלת, אבל לא משנה את התפקיד שלו ולא מבטלת השעיה.

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

| שיטה | נתיב | מי | גוף או שאילתה |
| --- | --- | --- | --- |
| `GET` | `/v1/workspaces/:id/join-code` | מנהל סביבת העבודה | `{ code, enabled, rotatedAt, rotatedBy }` או `null` כשעדיין לא נוצר קוד. |
| `POST` | `/v1/workspaces/:id/join-code` | מנהל סביבת העבודה | `{ action }`: `rotate`, `enable` או `disable`. הפעולה `enable` בסביבת עבודה שמעולם לא היה לה קוד יוצרת קוד. הפעולה `rotate` מחליפה את הקוד, והקוד הישן מפסיק לעבוד מיד. |
| `POST` | `/v1/workspaces/join` | כל מי שמחובר | `{ code }`. גם הצורות שבהן מקריאים קוד עובדות: `BCDF-GHJK`, אותיות קטנות ואותיות שקל לשמוע לא נכון, כגון `O` במקום אפס. מוגבלת ל-10 ניסיונות בדקה לכל חשבון ול-30 לכל כתובת IP. |

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

| שיטה | נתיב | מי | גוף או שאילתה |
| --- | --- | --- | --- |
| `GET` | `/v1/workflows/:id/collaborators` | כל מי שרשאי לצפות בתהליך | רשומות של `{ userId, name, avatarUrl, role, createdAt }`, אף פעם לא כתובת אימייל. |
| `POST` | `/v1/workflows/:id/collaborators` | ראו למטה | `{ userId }` או `{ email }`, בדיוק אחד מהם, ועוד `role`, עם הערך `viewer` או `editor`. מחזירה `201`. מוגבלת ל-20 הוספות בדקה. |
| `PATCH` | `/v1/workflows/:id/collaborators/:userId` | כמו ב-`POST` | `{ role }` |
| `DELETE` | `/v1/workflows/:id/collaborators/:userId` | כמו ב-`POST`, או אתם עצמכם | כל אחד יכול להסיר את הגישה של עצמו. |
| `GET` | `/v1/workflows/shared-with-me` | כל מי שמחובר | תהליכים שיש לכם הרשאת גישה אליהם, מחוץ לסביבות העבודה שלכם, כל אחד עם ה-`grantedRole` שלו. מהחדש לישן, עד 200. |
| `GET` | `/v1/workflows/:id/access` | כל מי שרשאי לצפות בו | `{ access, workspaceId, visibility, canChangeVisibility, canShare, canRun }`, אף פעם לא הגרף של התהליך. |

- **מי רשאי לשתף:** מי שיצר את התהליך; מנהל סביבת העבודה, בסביבת עבודה שבה מנהלים רשאים לערוך את העבודה של חברים; וכל מי שרשאי לערוך את התהליך, בסביבת עבודה שמאפשרת לשותפים להזמין.
- **הוספה לפי אימייל** מוצאת כל חשבון עם הכתובת הזו, בלי קשר לארגון שלו. התגובה `404` פירושה שאין חשבון כזה, ושום מידע נוסף לא נחשף.
- **מי שנמצא מחוץ לסביבת העבודה מוגבל לצפייה**, גם עם הרשאת עריכה. הרצה דורשת חברות פעילה, ולכן שותף מבחוץ אף פעם לא יכול להוציא את הקרדיטים של סביבת העבודה.
- אי אפשר לתת הרשאת גישה למי שיצר את התהליך או לעצמכם (`400`). ההרשאה תקפה מיד, בלי שלב של אישור.
- השדות `canChangeVisibility`, `canShare` ו-`canRun` הם שלושה כללים נפרדים. אף פעם אל תסיקו אחד מהם מהאחר.

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

### יומן ביקורת
| שיטה | נתיב | מי | גוף או שאילתה |
| --- | --- | --- | --- |
| `GET` | `/v1/orgs/:id/audit` | בעלים, מנהל | `?cursor&limit`, מהחדש לישן. כל רשומה: `{ id, workspaceId, action, targetType, targetId, details, createdAt, actor }`. השדה `actor` הוא `null` בפעולות שהמערכת ביצעה. |

אפשר לקרוא את יומן הביקורת גם כשהארגון מושעה. הערכים של `action` הם רשימה פתוחה שגדלה יחד עם המוצר: הציגו את הפעולות שאתם מכירים, ואת השאר הציגו בערך הגולמי שלהן.

## תפקידים והגדרות
| היקף | תפקיד | מה התפקיד יכול לעשות |
| --- | --- | --- |
| ארגון | `owner` | כל מה שמנהל יכול לעשות, ובנוסף להעביר את הבעלות ולמחוק את הארגון. יש בעלים אחד בדיוק. אי אפשר להשעות את הבעלים, להסיר אותו או להוריד אותו בדרגה. |
| ארגון | `admin` | לנהל את ההגדרות, את החברים מלבד הבעלים ואת סביבות העבודה. מנהל בכל סביבת עבודה. |
| ארגון | `member` | להשתייך לארגון ולעבוד בסביבות העבודה שהוסיפו אותו אליהן. |
| סביבת עבודה | `admin` | לנהל את ההגדרות ואת החברים של סביבת העבודה. |
| סביבת עבודה | `member` | לעבוד בסביבת העבודה. |

ההגדרות בנויות בשכבות: הגדרה של סביבת עבודה גוברת על ההגדרה של הארגון, שגוברת על ברירת המחדל של סוג הארגון. מפתח שלא הוגדר מקבל את הערך מהשכבה שמתחתיו, והערך `false` הוא ערך לכל דבר.

| מפתח | ערכים | מה הוא קובע |
| --- | --- | --- |
| `admin_access` | `view` או `edit` | מה מנהלים רשאים לעשות עם תהליך של חבר. |
| `default_workflow_visibility` | `private` או `workspace` | הנראות של תהליך חדש. |
| `member_access_to_shared` | `view` או `edit` | מה חברים רשאים לעשות עם תהליך ששותף עם סביבת העבודה. |
| `members_can_create_projects` | boolean | אם חברים רשאים ליצור פרויקטים. |
| `member_caps_enabled` | boolean | אם חלות תקרות קרדיטים לכל חבר. |
| `personal_space_enabled` | boolean | אם לחברים נשאר מרחב אישי. |
| `workspace_admins_can_invite` | boolean | אם מנהלי סביבות עבודה רשאים להזמין אנשים חדשים לארגון. |
| `collaborators_can_invite` | boolean | אם שותף עם הרשאת עריכה רשאי להזמין שותפים נוספים. |

לארגון בלבד יש עוד שני מפתחות: `allowed_email_domains`, למשל `["school.example"]`, ו-`vocabulary_overrides`, למשל `{ "workspace": "Cohort" }`. בעדכון, שני המפתחות מוחלפים בשלמותם.

כשאתם קובעים מילה משלכם לסביבת עבודה והמילה היא בלשון זכר, הגדירו גם את `workspace_gender` ב-`vocabulary_overrides` לערך `"m"`, למשל `{ "workspace": "Grupo", "workspace_gender": "m" }`. כך משפטים בעברית ובפורטוגזית (ברזיל) שמזכירים את סביבת העבודה מתאימים את עצמם למין הדקדוקי של המילה. בלי `"m"`, המשפטים מתייחסים למילה כנקבה. ההגדרה הזו קיימת רק ב-API.

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

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

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

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

| נתיב | מי | מה הוא עושה |
| --- | --- | --- |
| `GET /v1/orgs/:id/credits` | בעלים, מנהל ארגון | המאגר, סך הרכישות מאז הקמת הארגון וההקצאה של כל סביבת עבודה, עם הקרדיטים שמשוריינים בה ושהוצאו בה. |
| `POST /v1/orgs/:id/credits/checkout` | בעלים | כתובת URL לתשלום על חבילה אחת בתשלום מראש. גוף הבקשה: `{ packId }`. |
| `POST /v1/orgs/:id/workspaces/:wsId/allocate` | בעלים | העברת קרדיטים בין המאגר לסביבת עבודה. גוף הבקשה: `{ delta }`. מחזיר את היתרה הפנויה החדשה. |
| `GET /v1/workspaces/:id/budget` | חבר | ההוצאה, התקרה והיתרה הפנויה שלכם. מנהלים מקבלים גם רשומה לכל חבר. |
| `GET /v1/orgs/:id/usage` | בעלים, מנהל ארגון | דוח שימוש של הארגון. |
| `GET /v1/workspaces/:id/usage` | חבר, מנהל סביבת העבודה | אותו דוח לסביבת עבודה אחת. |

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

**אוטומציות מוציאות את הקרדיטים של סביבת העבודה.** כתובת ה-URL של רכיב [טריגר Webhook](https://nodaro.ai/docs/nodes/automate/webhook-trigger) היא פרטי גישה מסוג Bearer: כל מי שהכתובת בידיו יכול להתחיל הרצה, ועל הרצה של תהליך בסביבת עבודה משלמת סביבת העבודה הזו. כל הרצה אוטומטית, מ-Webhook, מתזמון או מטלגרם, בודקת קודם שמי שיצר את הטריגר עדיין רשאי להריץ את התהליך. כשהוא כבר לא רשאי, האוטומציה נעצרת, והיסטוריית ההרצות מציגה רשומה אחת של הרצה שנכשלה, עם הקוד `run_requires_authenticated_member`.

### דוחות שימוש
שני נתיבי השימוש עונים על השאלה מי הוציא כמה ועל מה, בטווח תאריכים:

| פרמטר שאילתה | משמעות |
| --- | --- |
| `from`, `to` | תאריך ההתחלה ותאריך הסיום, ששניהם נכללים בטווח, בפורמט `YYYY-MM-DD`. הטווח הוא 366 ימים לכל היותר, וברירת המחדל היא 30 הימים האחרונים. |
| `tz` | אזור זמן של IANA לחלוקה לימים. ברירת המחדל היא `UTC`. |
| `groupBy` | `workspace` (בדוח הארגון בלבד), `member`, `model`, `day` או `none` להרצות הבודדות, מהחדש לישן, בעמודים עם `cursor`. |
| `workspaceId`, `userId` | צמצום הדוח. |
| `format` | הערך `csv` מחזיר את הדוח כקובץ CSV. |

- בכל רשומה יש שלושה נתוני קרדיטים. השדה `credits` הוא מה שההרצות עלו עד עכשיו: הסכום אחרי החיוב הסופי בהרצות שהסתיימו, והשריון בהרצות שעדיין רצות. השדות `settledCredits` ו-`inFlightCredits` מפצלים את הסכום הזה לשני חלקיו.
- הרצה שמחויבת לפי שימוש ועלתה יותר ממה שנשאר לסביבת העבודה מחויבת עד גובה היתרה הפנויה, והפלטפורמה סופגת את השאר. הסיכומים מדווחים על הסכום הזה ב-`platformAbsorbedCredits`, וההפרש בין `settledCredits` ל-`platformAbsorbedCredits` הוא מה שירד מהתקציב בפועל.
- תוספת המחיר של היוצר על אפליקציה שהארגון אישר נגבית מהתקציב של סביבת העבודה עד גובה היתרה הפנויה. היוצר של האפליקציה מקבל בדיוק את מה שהתקציב שילם. את השאר לא גובים מאף אחד ולא משלמים לאף אחד, ולכן שום דבר לא נספג. הרצה של אפליקציה כזו נדחית מראש עם `402 budget_exceeded` כשהיתרה הפנויה לא מכסה את המחיר המוצג של האפליקציה יחד עם תוספת המחיר.
- לפני הכלל הזה, הפלטפורמה ספגה תוספת מחיר שהתקציב לא יכול היה לכסות, והיוצר קיבל את מלוא הסכום. הסכומים הקודמים האלה עדיין מסוכמים בשדה `appMarkupAbsorbedCredits`, והרצות חדשות כבר לא מוסיפות לו. אין הרצה בדוח לא לתוספת המחיר שנגבית מהתקציב ולא לסכום הזה, ואף אחד מהם לא חלק מ-`chargedToBudget`. לכן השדה `chargedToBudget` לבדו הוא לא הסכום שסביבת העבודה הוציאה.
- בדוח עם יותר מ-5,000 קבוצות, התגובה כוללת `truncated: true`. רק הרשומות המקובצות נחתכות: הסיכומים מכסים את כל הטווח.
- חבר רגיל רואה רק את ההרצות שלו, ובקשה שלו לקבץ לפי חבר נדחית. מנהל סביבת עבודה רואה את כולם ויכול לסנן לפי `userId`. דוח הארגון מיועד לבעלים ולמנהלי הארגון.
- קובצי ה-CSV המיוצאים הם בקידוד UTF-8 לפי RFC 4180, עם סופי שורה מסוג CRLF ובלי סימן סדר בתים (BOM). תא שמתחיל ב-`=`, `+`, `-` או `@` מקבל אפוסטרוף בתחילתו, כדי שגיליון אלקטרוני לא יוכל להריץ אותו. הייצוא מוגבל ל-10 בדקה לכל משתמש, וכל ייצוא נרשם ביומן הביקורת.
- כשחבר מוחק את החשבון שלו, היסטוריית ההרצות שלו נמחקת יחד איתו, והדוח מציג פער. התהליכים והפרויקטים שלו נשארים בסביבת העבודה.

במופע בלי דוחות שימוש, הנתיבים מחזירים `404`, וכל עוד הדיווח עדיין לא זמין הם מחזירים `503 billing_unavailable`.

## שגיאות
| סטטוס | קוד | מתי |
| --- | --- | --- |
| 400 | `validation_error` | שדה לא תקין, cursor פגום, שינוי שאסור ברשומה הזו או דוח שימוש שהתאריך, אזור הזמן, הטווח או הקיבוץ שלו לא תקינים. |
| 400 | `terms_required` | יצירת בית ספר בלי `acceptTerms: true`. |
| 400 | `not_org_member` | הוספה לסביבת עבודה של מישהו שהוא לא חבר פעיל בארגון שלה. |
| 400 | `token_workspace_mismatch` | אסימון שמשויך לסביבת עבודה אחת, עם כותרת שמציינת סביבת עבודה אחרת. |
| 400 | `join_code_invalid` | אין קוד כזה, הקוד מושבת או שסביבת העבודה שלו בארכיון. אותה תשובה לשלושת המקרים. |
| 400 | `invitation_expired`, `invitation_revoked`, `invitation_accepted` | תוקף ההזמנה פג אחרי 14 ימים, ההזמנה בוטלה או שהיא כבר אושרה. |
| 400 | `email_mismatch` | האימייל של החשבון המחובר הוא לא הכתובת שהוזמנה. |
| 401 | `unauthorized` | אין פרטי גישה תקפים. |
| 402 | `budget_exceeded` | התקציב של סביבת העבודה לא מכסה את ההרצה. |
| 402 | `member_cap_exceeded` | תקרת ההוצאה שלכם בסביבת העבודה לא מכסה את ההרצה. |
| 403 | `insufficient_role` | אתם חברים, אבל התפקיד שלכם לא מספיק לפעולה הזו. |
| 403 | `member_suspended` | החברות שלכם מושעית. |
| 403 | `org_not_active` | הארגון ממתין לאישור או מושעה, והפעולה משנה משהו. |
| 403 | `workspace_archived` | כתיבה אחרת לסביבת עבודה שבארכיון, כגון שיתוף תהליך. |
| 403 | `not_a_member` | הכותרת מציינת סביבת עבודה שאתם לא יכולים לבחור. |
| 403 | `domain_not_allowed` | הארגון מקבל רק כתובות אימייל מהדומיינים שברשימה שלו. |
| 403 | `project_create_not_allowed` | יצירת פרויקט בסביבת עבודה שבה רק מנהלים יכולים ליצור פרויקטים. |
| 403 | `personal_space_disabled` | בקשת יצירה בלי הכותרת, כשהארגון סגר את המרחב האישי. |
| 403 | `forbidden` | ניהול אסימונים עם אסימון API או עם אסימון OAuth, במקום עם סשן מחובר. |
| 404 | `not_found` | הארגון, סביבת העבודה או החבר לא קיימים, או שאתם לא חברים בארגון או בסביבת העבודה. |
| 404 | `invitation_not_found` | אין הזמנה לאסימון הזה, כולל הזמנה שהארגון שלה לא פעיל. |
| 409 | `name_taken` | ה-slug ששלחתם כבר בשימוש. |
| 409 | `already_a_member` | האדם כבר נמצא בסביבת העבודה. |
| 409 | `owner_cannot_leave` | העבירו את הבעלות לפני שאתם עוזבים. |
| 409 | `has_active_workspaces` | העבירו את כל סביבות העבודה לארכיון לפני מחיקת הארגון. |
| 409 | `workspace_archived` | בקשת יצירה בסביבת עבודה שבארכיון, או העברה של עבודה לתוכה. |
| 409 | `workspace_has_no_default_project` | בקשת יצירה שלא מציינת פרויקט, בסביבת עבודה שעדיין אין בה פרויקט. |
| 429 | `rate_limit_exceeded` | יותר מדי ארגונים חדשים, ניסיונות הצטרפות או ייצואי CSV. |
| 429 | `bulk_invite_cap_exceeded` | הארגון הגיע למגבלת ההזמנות היומית שלו. |
| 503 | `billing_unavailable` | דוחות השימוש עדיין לא זמינים במופע הזה. |
| 503 | `audit_unavailable` | אי אפשר היה לרשום ייצוא CSV ביומן הביקורת, ולכן הוא נדחה. נסו שוב. |

## דרך ה-SDK, ה-CLI ו-MCP
- **SDK:** המשאבים `client.organizations` ו-`client.workspaces` מכסים את נקודות הקצה האלה, כולל `usage`, `usageRows` ו-`usageCsv` לדוחות. התגובה של `client.me()` כוללת את שדות הארגונים.
- **CLI:** קבוצות הפקודות `nodaro org` ו-`nodaro workspace`, למשל `nodaro org invite <orgId> --email ada@school.example --workspace <id>` ו-`nodaro org usage <orgId> --from 2026-09-01 --to 2026-09-30 --group-by member --csv > september.csv`. פקודת ההזמנה מדפיסה שורה אחת לכל כתובת, עם הקישור לכל כתובת שלא נשלח אליה אימייל.
- **MCP:** הכלים `list_workspaces` ו-`select_workspace`. הבחירה נשמרת לסשנים הבאים ונבדקת שוב בכל סשן. אפליקציית OAuth שמשתמשת בכלים האלה צריכה את ההרשאות `workspaces:read` ו-`workspaces:write`.

ראו את הדפים [CLI](https://nodaro.ai/docs/developers/cli) ו-[SDK ל-TypeScript](https://nodaro.ai/docs/developers/sdk) לרשימות המלאות של הפקודות והמתודות.

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

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

שלחו את המזהה של סביבת העבודה בכותרת X-Nodaro-Workspace. הכותרת קובעת מאיזו סביבת עבודה רשימה קוראת ולאן נכנס מה שיוצרים. ב-SDK משתמשים ב-createClient({ workspaceId }) או ב-client.withWorkspace(id), וב-CLI בדגל ‎--workspace.

### האם כותרת סביבת עבודה שגויה יכולה להסתיר את העבודה שלי או להגיע לעבודה של מישהו אחר?

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

### האם אפשר לשייך אסימון API לסביבת עבודה אחת?

כן. משייכים אסימון עם PATCH /v1/api-tokens/:id ושדה workspaceId, מתוך סשן מחובר. מאותו רגע האסימון פועל כאילו שלח את הכותרת בכל בקשה, ובקשה עם כותרת שמציינת סביבת עבודה אחרת נדחית עם 400 token_workspace_mismatch.

### למה בקשת יצירה נדחית עם 403 personal_space_disabled?

הארגון שלכם מחייב את החברים שלו ליצור עבודה בתוך סביבת עבודה. כשהבקשה כוללת את הכותרת X-Nodaro-Workspace עם אחת מסביבות העבודה שלכם, אותה קריאה מצליחה.

### האם ארגונים זמינים בהתקנות באירוח עצמי?

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

### למה הזמנה חזרה עם קישור במקום להישלח באימייל?

להתקנה אין שירות אימייל, או ששליחת האימייל נכשלה. ההזמנה קיימת בכל מקרה, ולכן שלחו בעצמכם את הקישור שחזר לאדם שהוזמן.
