API referenceממשק API
Two surfaces, deliberately separated. Everything you would build against lives under /v1 and is described by a generated OpenAPI document. The Turborepo remote cache lives outside it, on purpose, and this page explains why.
שני משטחים, מופרדים בכוונה. כל מה שתבנו מולו נמצא תחת /v1 ומתואר במסמך OpenAPI שנוצר אוטומטית. המטמון המרוחק של Turborepo נמצא מחוץ לו, בכוונה, והעמוד הזה מסביר למה.
Base URL and authenticationכתובת בסיס ואימות
https://api.shipyard1.comBase URL for every route on this page.כתובת הבסיס לכל נתיב בעמוד הזה.Authorization: BearerAn sy_live_… API key. This is the path for the CLI, the SDK, CI and agents.מפתח API מסוג sy_live_…. זה המסלול ל-CLI, ל-SDK, ל-CI ולסוכנים.shipyard_sessionSigned HttpOnly cookie. Used by the web panel only. Do not try to obtain one programmatically.עוגייה חתומה מסוג HttpOnly. בשימוש הפאנל בלבד. אל תנסו להשיג אותה תכנותית.curl -sS https://api.shipyard1.com/v1/projects \ -H "Authorization: Bearer $SHIPYARD_API_KEY" | jq
The generated document is the contractהמסמך הנוצר הוא החוזה
GET /openapi.json is generated from the route definitions themselves, not maintained by hand, so it cannot drift from the implementation. Schemas are zod, compiled by fastify-type-provider-zod, which means the same declaration validates the request and produces the documentation. Anything in that document is a promise under your frame. The SDK is generated from it too.
GET /openapi.json נוצר מהגדרות הנתיבים עצמן, לא מתוחזק ידנית, ולכן הוא לא יכול לסטות מהמימוש. הסכמות הן zod, מהודרות על ידי fastify-type-provider-zod, כלומר אותה הצהרה מאמתת את הבקשה ומייצרת את התיעוד. כל מה שבמסמך הזה הוא הבטחה תחת המסגרת שלכם. גם ה-SDK נוצר ממנו.
One error envelopeמעטפת שגיאה אחת
Every failure on every route, without exception:
כל כישלון בכל נתיב, ללא יוצא מן הכלל:
{
"error": {
"code": "not_found",
"message": "Not found",
"request_id": "req_01J91TTB2P4Q"
}
}request_id echoes your x-request-id header when you send a sane one, and is generated otherwise. Put it in your support mail and we can find the request without asking you twenty questions.
request_id מחזיר את כותרת x-request-id ששלחתם אם היא תקינה, ואחרת נוצר אוטומטית. שימו אותו בפנייה לתמיכה ונוכל למצוא את הבקשה בלי לשאול אתכם עשרים שאלות.
“Does not exist” and “exists but is not yours” return the identical status, body and headers. That is not sloppiness; distinguishing them would turn the API into an enumeration oracle for other tenants’ resource ids. There is a single notFound() helper for this reason, and constructing a different not-found response anywhere in the codebase is treated as a bug.
”לא קיים“ ו”קיים אבל לא שלך“ מחזירים סטטוס, גוף וכותרות זהים. זו לא רשלנות; הבחנה ביניהם הייתה הופכת את ה-API לאורקל לספירת מזהי משאבים של דיירים אחרים. בדיוק לשם כך יש פונקציית notFound() אחת, ובניית תשובת not-found אחרת בכל מקום בקוד נחשבת באג.
Core routesנתיבים מרכזיים
GET /v1/whoamiOrg, role and scopes for the presented credential.ארגון, תפקיד והרשאות של האישור שהוצג.GET /v1/projectsProjects visible to this credential.הפרויקטים הגלויים לאישור הזה.POST /v1/projectsCreate a project.יצירת פרויקט.GET /v1/projects/:idOne project with its current releases.פרויקט אחד עם השחרורים הנוכחיים שלו.POST /v1/deploymentsStart a deployment. Returns a dep_… immediately.התחלת פריסה. מחזיר dep_… מיד.GET /v1/deployments/:idStatus, timings and scan verdict.סטטוס, זמנים ופסיקת סריקה.GET /v1/deployments/:id/logsBuild and runtime logs. Supports streaming.לוגים של בנייה וריצה. תומך בזרימה.POST /v1/deployments/:id/rollbackPromote a previous release.קידום שחרור קודם.GET /v1/projects/:id/envVariable names and metadata. Never values.שמות משתנים ומטא-דאטה. לעולם לא ערכים.PUT /v1/projects/:id/env/:nameSet a variable.הגדרת משתנה.GET /v1/projects/:id/domainsHostnames with TLS state.שמות מארח עם מצב TLS.GET /v1/auditAudit events, filterable by actor and time.אירועי ביקורת, ניתנים לסינון לפי מבצע וזמן.The remote cache: /v8/artifactsהמטמון המרוחק: /v8/artifacts
Five routes that sit outside /v1 and are hidden from the OpenAPI document, which also excludes them from SDK generation. That is an exception to everything above and it is intentional.
חמישה נתיבים שיושבים מחוץ ל-/v1 ומוסתרים ממסמך ה-OpenAPI, מה שגם מוציא אותם מייצור ה-SDK. זהו חריג לכל מה שלמעלה, והוא מכוון.
- The paths are not ours. They are dictated by the
turboclient, which hardcodes/v8/artifacts/…against whateverTURBO_APIpoints at. We cannot version them because we do not own the version. Advertising av8in our OpenAPI document would imply a v8 of the Shipyard API that does not exist.הנתיבים אינם שלנו. הם מוכתבים על ידי הלקוחturbo, שמקודד קשיח/v8/artifacts/…מול מה ש-TURBO_APIמצביע אליו. אנחנו לא יכולים לגרסן אותם כי הגרסה אינה שלנו. פרסוםv8במסמך ה-OpenAPI שלנו היה רומז על v8 של Shipyard API שלא קיים. - Shipping them in the SDK would be a lie. The only correct client is
turboitself. Generating typed methods would invite exactly the wrong usage.לשלוח אותם ב-SDK היה שקר. הלקוח הנכון היחיד הואturboעצמו. ייצור מתודות מוקלדות היה מזמין בדיוק את השימוש השגוי. - Authentication is different. API key only, scope
cache:rw, never a session cookie. The cache accepts cross-originPUTs with no CSRF token, so a cookie-authenticated write path here would be a CSRF hole by construction.האימות שונה. מפתח API בלבד, הרשאהcache:rw, לעולם לא עוגיית סשן. המטמון מקבלPUTחוצה-מקורות ללא טוקן CSRF, ולכן מסלול כתיבה מאומת-עוגייה כאן היה חור CSRF מעצם בנייתו.
GET /v8/artifacts/statusenabled | disabled | over_limit | pausedenabled | disabled | over_limit | pausedHEAD /v8/artifacts/:hashExistence and size, no body.קיום וגודל, ללא גוף.GET /v8/artifacts/:hashArtifact bytes, application/octet-stream.בתי הפריט, application/octet-stream.PUT /v8/artifacts/:hashStreamed upload, 202 on success.העלאה בזרימה, 202 בהצלחה.POST /v8/artifacts/eventsClient telemetry. Always 200.טלמטריה מהלקוח. תמיד 200.TURBO_API=https://api.shipyard1.com TURBO_TOKEN=sy_live_xxxxxxxxxxxxxxxxxxxx TURBO_TEAM=your-org-slug
Tenancyהפרדת דיירים
An artifact is addressed by (org_id, hash) and by nothing the client sends. teamId and slug arrive as query parameters and are treated as untrusted hints: they are compared against the authenticated key’s org, and a mismatch returns the same 404 as a nonexistent hash. Two organisations building the same open-source dependency legitimately produce the same hash and each must read back its own bytes, so uniqueness is on the pair, never on the hash alone.
פריט ממוען לפי (org_id, hash) ולפי שום דבר שהלקוח שולח. teamId ו-slug מגיעים כפרמטרי שאילתה ומטופלים כרמזים לא אמינים: הם מושווים לארגון של המפתח המאומת, ואי-התאמה מחזירה את אותו 404 כמו hash שלא קיים. שני ארגונים שבונים את אותה תלות קוד פתוח מייצרים באופן לגיטימי את אותו hash וכל אחד חייב לקרוא את הבתים של עצמו, ולכן הייחודיות היא על הזוג, לעולם לא על ה-hash לבדו.
Rate limitsמגבלות קצב
- Reads: 600 requests per minute per key.קריאות: 600 בקשות לדקה לכל מפתח.
- Writes: 60 per minute per key.כתיבות: 60 לדקה לכל מפתח.
- Deploys: 20 per hour per project.פריסות: 20 לשעה לכל פרויקט.
- The cache is not rate limited by request count; it is limited by storage quota, and
statusreturnsover_limitrather than failing your build.המטמון אינו מוגבל בכמות בקשות; הוא מוגבל במכסת אחסון, ו-statusמחזירover_limitבמקום להכשיל את הבנייה שלכם. - Exceeding a limit returns
429withretry-after. The SDK and CLI back off automatically.חריגה ממגבלה מחזירה429עםretry-after. ה-SDK וה-CLI נסוגים אוטומטית.