ה-API של Webhooks
קבלו בשרת שלכם אירועי job.completed ו-job.failed חתומים, הריצו תהליכים דרך הכתובת של טריגר Webhook, צרו תזמונים ושלחו תוצאות החוצה.
Webhooks מחברים את Nodaro למערכות האחרות שלכם, בשני הכיוונים. נקודת קצה של Webhook שאתם רושמים מקבלת אירוע חתום בכל פעם שאחת המשימות שלכם מסתיימת, ולכן השרת שלכם לא צריך לבדוק את הסטטוס מדי כמה שניות. רכיב טריגר Webhook נותן לתהליך כתובת שכל מערכת יכולה לקרוא לה כדי להתחיל הרצה, רכיב טריגר מתוזמן מריץ את התהליך לפי לוח זמנים, ורכיב פלט Webhook שולח את התוצאות של הרצה לשרת שלכם.
נקודות קצה
הנתיבים האלה מנהלים את נקודות הקצה ש-Nodaro שולח אליהן אירועי משימות. ראו את הסעיף קבלת אירועי משימות בשרת שלכם.
| שיטה | נתיב | מה היא עושה |
|---|---|---|
POST | /v1/webhook-endpoints | רישום נקודת קצה. התגובה כוללת את סוד החתימה שלה, פעם אחת בלבד. |
GET | /v1/webhook-endpoints | נקודות הקצה שלכם, מהחדש לישן. סוד אף פעם לא מופיע ברשימה. |
GET | /v1/webhook-endpoints/:id | נקודת קצה אחת. |
PATCH | /v1/webhook-endpoints/:id | שינוי של url, events, description או isActive. |
DELETE | /v1/webhook-endpoints/:id | מחיקת נקודת הקצה ויומן המסירות שלה. |
POST | /v1/webhook-endpoints/:id/rotate-secret | החלפת סוד החתימה בסוד חדש, עם תקופת חסד לסוד הישן. |
GET | /v1/webhook-endpoints/:id/deliveries | יומן המסירות, מהחדש לישן. |
GET | /v1/webhook-endpoints/:id/deliveries/:deliveryId | מסירה אחת. |
POST | /v1/webhook-endpoints/:id/test | שליחת ping לבדיקה. |
הנתיבים האלה מריצים תהליכים ומנהלים את הטריגרים שלהם. הם לא נמצאים תחת /v1/webhook-endpoints.
| שיטה | נתיב | מה היא עושה |
|---|---|---|
POST | /v1/webhooks/:token | התחלת הרצה. ציבורית: האסימון שבנתיב הוא פרטי הגישה. |
GET | /v1/workflows/:id/triggers | הטריגרים של תהליך, עם הכתובת והאסימון של כל Webhook. |
PATCH | /v1/workflow-triggers/:id | השהיה או חידוש של טריגר עם isActive, או שינוי ה-config של תזמון. |
POST | /v1/workflow-triggers | יצירה ידנית של טריגר שלא קשור לשום רכיב. |
POST | /v1/workflows/:id/sync-triggers | רישום מחדש של רכיבי הטריגר של תהליך, מהגרסה השמורה שלו. |
קבלת אירועי משימות בשרת שלכם
רשמו נקודת קצה עם כתובת https, ו-Nodaro שולח אליה בקשת POST בכל פעם שאחת המשימות שלכם מסתיימת. כך השרת שלכם יודע על יצירה שהסתיימה בלי לקרוא ל-GET /v1/jobs/:id מדי כמה שניות. כל בקשה נחתמת בסוד של נקודת הקצה עצמה, נשלחת שוב במרווחים הולכים וגדלים עד שהשרת שלכם עונה, ונרשמת ביומן המסירות של נקודת הקצה.
רישום נקודת קצה
curl -s -X POST "https://app.nodaro.ai/v1/webhook-endpoints" \
-H "Authorization: Bearer $NODARO_TOKEN" \
-H "Content-Type: application/json" \
-d '{"url": "https://example.com/nodaro-webhook", "description": "order fulfilment"}'const { data: endpoint, secret } = await client.webhooks.create({
url: "https://example.com/nodaro-webhook",
description: "order fulfilment",
})
// Store `secret` (whsec_...) with your server's config: it is shown only once.
await client.webhooks.test(endpoint.id) // a ping, sent within secondsnodaro webhooks create --url https://example.com/nodaro-webhook
nodaro webhooks test <id> --waitהתגובה היא 201 עם { data, secret }. הסוד מתחיל ב-whsec_ ומופיע רק בתגובה הזו, ולכן שמרו אותו מיד. השדה events אופציונלי: נקודת קצה מקבלת את שני אירועי המשימות, אלא אם השדה events מציין רק אחד מהם.
כך נראית נקודת קצה כשקוראים אותה:
{
"id": "3f6c2a1e-8b4d-4c7a-9e1f-2d5b6a7c8e90",
"url": "https://example.com/nodaro-webhook",
"description": "order fulfilment",
"events": ["job.completed", "job.failed"],
"isActive": true,
"disabledReason": null,
"disabledAt": null,
"consecutiveFailures": 0,
"appId": null,
"secretCreatedAt": "2026-10-15T10:00:00Z",
"previousSecretExpiresAt": null,
"createdAt": "2026-10-15T10:00:00Z",
"updatedAt": "2026-10-15T10:00:00Z"
}אירועים
| אירוע | מתי הוא נשלח |
|---|---|
job.completed | משימה שלכם הגיעה לסטטוס completed. משימה שממתינה לבדיקה (pending_review) שולחת את האירוע שלה כשהיא מאושרת. |
job.failed | משימה שלכם הגיעה לסטטוס failed. |
ping | רק מ-POST /v1/webhook-endpoints/:id/test. נקודת קצה אף פעם לא נרשמת אליו. |
כל משימה ברשימת המשימות שלכם נחשבת: יצירה בודדת, וגם המשימה של כל רכיב בתוך הרצת תהליך. משימה שבוטלה לא שולחת כלום, וגם הסטטוסים שבדרך, כגון pending ו-processing, לא שולחים אירוע. משימה שנכשלת ומסתיימת בהצלחה מאוחר יותר שולחת אירוע אחד על כל סיום. המשימות שבתוך ההרצה של רכיב שמור לא נמצאות ברשימת המשימות שלכם, ולכן הן לא שולחות כלום: הרכיב שהתחיל את ההרצה מדווח בשמה.
הבקשה
Nodaro שולח בקשת POST עם Content-Type: application/json ועם הכותרות האלה:
| כותרת | ערך |
|---|---|
Nodaro-Signature | t=<unix seconds>,v1=<hex>. בזמן החלפת סוד, v1= אחד לכל סוד. |
Nodaro-Event | job.completed, job.failed או ping |
Nodaro-Delivery | מזהה המסירה, זהה בכל ניסיון חוזר של האירוע הזה |
Nodaro-Webhook | מזהה נקודת הקצה |
Nodaro-Attempt | 1 בניסיון הראשון, ואחר כך 2, 3 וכן הלאה |
User-Agent | Nodaro-Webhooks/1 |
רק חותמת הזמן והגוף חתומים. נתבו וסננו כפילויות לפי ה-type וה-id של הגוף המאומת, ואף פעם לא לפי הכותרות Nodaro-Event או Nodaro-Delivery.
{
"id": "6f1d0c0e-1c7e-4d0a-9f2b-0a4e2f7b9c11",
"type": "job.completed",
"createdAt": "2026-10-15T10:00:04.512Z",
"data": {
"job": {
"id": "0b9a5c1d-2e3f-4a5b-8c6d-7e8f9a0b1c2d",
"status": "completed",
"progress": 100,
"job_type": "generate-image",
"input_data": { "type": "generate-image", "prompt": "a lighthouse at dusk" },
"output_data": { "imageUrl": "https://cdn.nodaro.ai/images/0b9a5c1d.png" },
"error_message": null,
"error_hint": null,
"credit_status": "committed",
"credits": 40,
"credits_charged": 40,
"credits_held": 40,
"credits_released": 0,
"source": "sdk",
"session_id": null,
"created_at": "2026-10-15T10:00:00.000Z",
"completed_at": "2026-10-15T10:00:04.512Z"
}
}
}idהוא מזהה המסירה. הוא ייחודי לכל צירוף של אירוע ונקודת קצה, וזהה בכל ניסיון חוזר, ולכן סננו כפילויות לפיו.createdAtהוא המועד שבו המשימה הגיעה לסטטוס הזה.data.jobהוא המשימה במבנה ש-GET /v1/jobs/:idמחזיר, כמו שהיא הייתה בניסיון הראשון. ניסיונות חוזרים שולחים את אותו גוף, בייט אחר בייט. השדהcredits_chargedעדיין יכול להיותnull, עםcredit_status: "reserved", כשעוד לא היה חיוב סופי. בדקו שוב את המשימה כדי לקבל את הסכום הסופי. כמו ב-GET /v1/jobs/:id, השדהcredits_chargedכולל את החיוב על השלבים שהמשימה הריצה,credits_heldהוא השריון של ההרצה יחד עם השריונים של השלבים שלה, ו-credits_releasedהוא מה שחזר אחרי שכל השריונים בהרצה קיבלו חיוב סופי (nullעד אז). ראו בדף ה-API הקרדיטים של משימה.- אירוע הוא לכל היותר 64 KB. במשימה גדולה יותר, קודם מושמט
input_data, ואחריוoutput_data, והשדהdata.omittedמפרט מה הושמט. הנתיבGET /v1/jobs/:idמחזיר את המשימה כולה. - אירוע
pingכולל"data": { "webhookId": "<endpoint id>" }.
אימות החתימה
הערך v1 הוא ה-HMAC-SHA256 של <t>.<raw body>, עם הסוד המלא של נקודת הקצה כמפתח (whsec_..., ב-UTF-8). בדקו שלושה דברים לפני שאתם סומכים על בקשה:
- החתימה תואמת בדיוק את הבייטים שקיבלתם. אף פעם אל תעשו סריאליזציה מחדש ל-JSON לפני הבדיקה.
- חותמת הזמן עדכנית. חמש דקות הן מרווח הסובלנות המקובל. הבדיקה מונעת מצב שבו בקשה שיורטה נשלחת שוב מאוחר יותר.
- עוד לא טיפלתם ב-
idהזה.
עם ה-SDK, הפונקציה verifyWebhookSignature מ-@nodaro/sdk מבצעת את הבדיקות. היא משתמשת רק ב-Web Crypto, ולכן היא רצה ב-Node 20 ואילך, ב-Deno, ב-Bun, ב-Cloudflare Workers, ב-Vercel Edge ובדפדפנים.
import express from "express"
import { verifyWebhookSignature, WebhookSignatureError } from "@nodaro/sdk"
const app = express()
app.post("/nodaro-webhook", express.raw({ type: "application/json" }), async (req, res) => {
try {
const event = await verifyWebhookSignature({
payload: req.body, // the raw bytes
header: req.headers["nodaro-signature"],
secret: process.env.NODARO_WEBHOOK_SECRET!,
toleranceSeconds: 300,
})
if (event.type === "job.completed") {
await saveResult(event.data.job.id, event.data.job.output_data)
}
res.sendStatus(204) // any 2xx within 10 seconds counts as delivered
} catch (err) {
if (err instanceof WebhookSignatureError) return res.status(400).send(err.code)
throw err
}
})הפונקציה verifyWebhookSignature מחזירה את האירוע המפוענח, או זורקת WebhookSignatureError. ה-code של השגיאה הוא missing_header, malformed_header, signature_mismatch, timestamp_outside_tolerance, invalid_payload, missing_secret או crypto_unavailable.
בלי ה-SDK, ב-Node:
import { createHmac, timingSafeEqual } from "node:crypto"
// rawBody: the request body as received (a Buffer or a string).
function verify(rawBody: Buffer | string, header: string, secret: string, toleranceSeconds = 300) {
const pairs = header.split(",").map((part) => part.trim().split("=", 2))
const t = pairs.find(([key]) => key === "t")?.[1]
const expected = createHmac("sha256", secret).update(`${t}.`).update(rawBody).digest()
const matches = pairs.some(([key, value]) => {
if (key !== "v1") return false
const got = Buffer.from(value, "hex")
return got.length === expected.length && timingSafeEqual(got, expected)
})
return matches && Math.abs(Date.now() / 1000 - Number(t)) <= toleranceSeconds
}בתקופת החסד של החלפת סוד, הכותרת כוללת v1= לכל סוד, והחדש ראשון. קבלו את הבקשה כשאחד מהם תואם. יומן המסירות שומר את הגוף המדויק של מסירה שלא הצליחה (requestBody), כדי שתוכלו לשחזר בדיקה שנכשלת, בייט אחר בייט.
ניסיונות חוזרים
כל תגובת 2xx בתוך 10 שניות נחשבת למסירה שהצליחה. כל דבר אחר מכשיל את הניסיון: 3xx (Nodaro לא עוקב אחרי הפניות), 4xx, 5xx, פסק זמן או כתובת שלא עונה. אז המסירה נשלחת שוב:
| אחרי ניסיון | הניסיון הבא בעוד |
|---|---|
| 1 | 30 שנ׳ |
| 2 | 2 דק׳ |
| 3 | 10 דק׳ |
| 4 | שעה אחת |
| 5 | 6 שעות |
| 6 | 12 שעות |
| 7 | 24 שעות |
משך כל המתנה משתנה, עד 10% לכל כיוון. אחרי הניסיון ה-8, כ-43 שעות אחרי הראשון, המסירה נכשלת סופית. אירוע ping של בדיקה נשלח פעם אחת בלבד.
- כל מסירה מגיעה לפחות פעם אחת. ניסיון חוזר יכול להגיע אחרי שהשרת שלכם כבר טיפל בניסיון הראשון, למשל כשהוא ענה מאוחר מדי. סננו כפילויות לפי ה-
idשל המסירה. - אירועים יכולים להגיע שלא לפי הסדר. עד 5 מסירות נשלחות במקביל לנקודת קצה אחת, ועד 10 לחשבון אחד. הסתמכו על
createdAtועל ה-statusשל המשימה עצמה, לא על סדר ההגעה. - שרת שלא עונה בכלל מעביר את נקודת הקצה להפסקה. אחרי 5 ניסיונות ברצף בלי תגובת HTTP, נקודת הקצה נכנסת להפסקה של דקה, ובכל פעם שזה קורה שוב ההפסקה ארוכה פי שניים, עד שעה. מסירות שממתינות לא מאבדות ניסיונות, וכל תגובת HTTP מסיימת את ההפסקה.
- בעיה בצד של Nodaro לא נספרת לחובתכם. המסירה ממתינה כמה דקות ונשלחת שוב, בלי שהניסיון נספר. בינתיים, הרשומה שלה ביומן מציגה
signing_unavailableאוjob_unreadable.
כיבוי אוטומטי
כש-50 מסירות ברצף נכשלות סופית, Nodaro מכבה את נקודת הקצה: השדה isActive הופך ל-false, והשדה disabledReason הופך ל-"consecutive_failures". תקנו את השרת המקבל, בדקו אותו עם ping של בדיקה, ואז הפעילו את נקודת הקצה עם PATCH /v1/webhook-endpoints/:id ועם { "isActive": true }. ההפעלה גם מאפסת את הספירה, וכך גם כל מסירה שמצליחה.
כל עוד נקודת קצה כבויה, בין שאתם כיביתם אותה ובין ש-Nodaro כיבה אותה, לא נרשמים בשבילה אירועים. מסירות שכבר ממתינות מסתיימות ב-failed עם error: "endpoint_disabled". אירוע ping של בדיקה עדיין נשלח.
יומן המסירות
הנתיב GET /v1/webhook-endpoints/:id/deliveries מחזיר את המסירות של נקודת קצה, מהחדש לישן, 20 בכל עמוד כברירת מחדל ועד 100. סננו עם status (pending, delivered או failed), ועברו בין העמודים עם limit ו-cursor. התגובה היא { data, next }. היומן נשמר 30 ימים.
כל מסירה כוללת את event, jobId, status ו-attempts, את responseStatus האחרון של השרת המקבל, את 2 ה-KB הראשונים של responseBody שלו, את durationMs ואת error, וכל עוד היא ממתינה, גם את nextAttemptAt. השדה requestBody הוא הגוף המדויק שנשלח. הוא נשמר כל עוד המסירה ממתינה או אחרי שהיא נכשלה, והוא null אחרי שהמסירה הצליחה. הנתיב GET /v1/webhook-endpoints/:id/deliveries/:deliveryId מחזיר מסירה אחת, למשל את ה-ping שבדיקה הכניסה לתור.
השדה error מקבל אחד מהערכים האלה:
error | משמעות |
|---|---|
http_<status> | השרת שלכם החזיר את הסטטוס הזה. |
timeout | לא הגיעה תגובה בתוך 10 שניות. |
blocked_address | הכתובת לא מצביעה על כתובת ציבורית באינטרנט. Nodaro דחה אותה לפני שהתחבר. |
connection_refused, connection_reset, tls_error, network_error | החיבור נכשל. |
endpoint_disabled | נקודת הקצה כובתה בזמן שהמסירה המתינה. |
app_revoked | ביטלתם את הגישה של האפליקציה המחוברת שרשמה את נקודת הקצה. |
job_deleted | המשימה נמחקה לפני שהיה אפשר לשלוח אותה. |
attempts_exhausted | הניסיון האחרון נקטע בצד של Nodaro לפני שהתגובה אליו נרשמה. |
signing_unavailable, job_unreadable | בעיה בצד של Nodaro. המסירה ממתינה ונשלחת שוב, בלי שהניסיון נספר. |
החלפת הסוד
הנתיב POST /v1/webhook-endpoints/:id/rotate-secret מחזיר { secret, previousSecretExpiresAt }, והסוד החדש מופיע רק בתגובה הזו. מאותו רגע, הסוד החדש חותם על כל מסירה, כולל ניסיונות חוזרים. הסוד שהוחלף ממשיך לחתום לצידו במשך gracePeriodSeconds: יום אחד כברירת מחדל, ו-7 ימים לכל היותר. כך יש לכם זמן לעדכן את השרת המקבל בלי פער בקבלת האירועים. שלחו { "gracePeriodSeconds": 0 } כדי להפסיק מיד את הסוד הישן, למשל אחרי דליפה.
כללים ומגבלות
httpsבמארח ציבורי בלבד. בליhttp://, בליlocalhost, בלי כתובת פרטית, loopback או link-local, ובלי שם שעובד רק בתוך רשת פרטית, כגון שם שמסתיים ב-.internalאו ב-.local. הכתובת לא יכולה לכלול שם משתמש או סיסמה. שם המארח נבדק מחדש ב-DNS בכל ניסיון. כדי לקבל אירועים במחשב שלכם, השתמשו בטאנל ורשמו את כתובת ה-httpsשלו.- נקודת קצה אחת לכל כתובת. רישום של אותה כתובת פעמיים נדחה עם
409 url_taken. במקום זאת, רשמו נקודת קצה אחת לשני האירועים. - 10 נקודות קצה לכל חשבון. רישום של נקודת הקצה ה-11 נדחה עם
400 limit_reached. - לפעולות כתיבה יש מגבלת קצב: 30 בדקה ליצירה, לעדכון, למחיקה ולהחלפת סוד, ו-10 בקשות
pingשל בדיקה בדקה. - מי יכול לנהל נקודות קצה: סשן מחובר, אסימון API אישי, או אסימון של אפליקציה מחוברת עם ההרשאה
jobs:read. אסימון אישי שמוגבל לתהליכים מסוימים לא יכול. - נקודת קצה שאפליקציה מחוברת רושמת (
appIdמוגדר) מקבלת רק את המשימות שהאפליקציה שלחה בעצמה, ורק כל עוד ההרשאה שנתתם לאפליקציה כוללת אתjobs:read. המשימות של הרצת תהליך שהאפליקציה התחילה לא כלולות: בדקו את מצב ההרצה עםGET /v1/workflow-executions/:id. האפליקציה רואה רק את נקודות הקצה שהיא רשמה. אתם רואים את כולן ויכולים למחוק כל אחת מהן. - התקנות באירוח עצמי שולחות מהשרת שלהן, וצריכות את
NODARO_ENCRYPTION_KEY, שמערך השירותים של Docker Compose יוצר. בלעדיו, הנתיבים מחזירים503 encryption_key_missing. - לא זמין דרך MCP. רישום נקודת קצה מחזיר סוד חתימה ומגדיר מסירה לכל החשבון, וזו פעולה לאפליקציה או ל-CLI, לא לסוכן AI.
שגיאות אחרות הן 400 validation_error לכתובת או לגוף בקשה לא תקינים, 403 insufficient_scope, 404 not_found למזהה שלא שייך לכם ו-409 url_invalid לנקודת קצה שהכללים כבר לא מקבלים את הכתובת השמורה שלה. שנו את ה-url שלה לפני שאתם מפעילים או בודקים אותה. הקוד 409 secret_required פירושו נקודת קצה שנרשמה לפני שהייתה חתימה: החליפו את הסוד שלה בסוד חדש לפני שאתם מפעילים או בודקים אותה.
הרצת תהליך מקריאת HTTP
הוספת רכיב טריגר Webhook
הוסיפו לתהליך רכיב טריגר Webhook. תחת פרמטרי פלט, הוסיפו פרמטר אחד לכל ערך שהצד הקורא ישלח. לכל פרמטר יש שם, שתואם מפתח בגוף ה-JSON של הבקשה, וסוג: text, imageUrl, videoUrl או audioUrl. חברו את הפרמטרים לרכיבים שצריכים להשתמש בהם.
שמירת התהליך
השמירה יוצרת את נקודת הקצה: Nodaro מנפיק אסימון באורך 32 בייטים ורושם את POST /v1/webhooks/<token>. זה קורה בכל דרך שבה התהליך נשמר: מהעורך, מה-API, מה-SDK, מייבוא או מ-MCP. האסימון מונפק פעם אחת ואז נשמר, ולכן הכתובת שאתם נותנים למערכת חיצונית נשארת תקפה בכל השמירות הבאות. הסרת הרכיב מבטלת את הכתובת.
קריאת הכתובת
הנתיב GET /v1/workflows/<id>/triggers מחזיר את הטריגרים של התהליך, כולל ה-webhookUrl וה-webhookToken של כל Webhook. הערך webhookUrl הוא נתיב, /v1/webhooks/<token>, ולכן הוסיפו לפניו את הכתובת של Nodaro שלכם. הטריגר שה-config.nodeId שלו הוא המזהה של הרכיב שייך לרכיב טריגר Webhook הזה. הרשימה מציגה רק טריגרים של תהליכים שבבעלותכם.
בעורך, חלונית ההגדרות של הרכיב מציגה את אותה כתובת במלואה תחת כתובת Webhook, עם כפתור העתקה.
קריאה לכתובת
שלחו בקשת POST עם גוף JSON. לא צריך כותרת Authorization: האסימון שבכתובת הוא פרטי הגישה.
curl -s -X POST "https://app.nodaro.ai/v1/webhooks/$WEBHOOK_TOKEN" \
-H "Content-Type: application/json" \
-d '{"prompt": "a lighthouse in a storm", "imageUrl": "https://example.com/lighthouse.jpg"}'הגוף הופך למטען של הטריגר. כל פרמטר קורא את המפתח בעל אותו שם, והסוג שלו קובע לאן הערך עובר: הסוג imageUrl מזין קלטים של תמונה, והסוג videoUrl מזין קלטים של וידאו.
כל טריגר Webhook מקבל עד 10 בקשות בדקה. המגבלה הזו נפרדת מהמגבלות של אסימוני ה-API שלכם.
דוגמאות נפוצות לצד הקורא: מערכת תשלומים שמתחילה יצירה של סרטון קבלת פנים כשלקוח נרשם, מאגר קוד שמבקש סיכום של עדכוני גרסה, מערכת תוכן שמפרסמת כתבה חדשה, או כלי אוטומציה ללא קוד.
מה רץ בהרצה שטריגר התחיל
- טריגר שמחובר לרכיבים מריץ רק את הענף שלו: הרכיבים שאחרי הטריגר, וכל רכיב שהם צריכים כקלט.
- טריגר שלא מחובר לשום דבר מריץ את התהליך כולו.
- לכן בתהליך אחד יכולים להיות כמה טריגרים, וכל אחד מהם מתחיל ענף משלו.
- ״מחובר״ פירושו כל מה שמזין רכיב אחר: חיבור שמצויר על הקנבס, רכיב בתוך רכיב קבוצה, או מיפוי שדות.
- טריגרים לא מגבילים הרצות מהעורך, מה-API ומאפליקציות שפורסמו. הן מריצות את מה שהן מריצות תמיד.
טריגר שנוצר ידנית דרך ה-API לא מציין שום רכיב. Nodaro משתמש אז ברכיב טריגר Webhook היחיד שעל הקנבס; כשיש שניים כאלה, התהליך כולו רץ.
שמירת הכתובת בסוד
האסימון הוא פרטי הגישה היחידים, ולכן כל מי שהכתובת בידיו יכול להתחיל הרצות ולהוציא את הקרדיטים שלכם. בתהליך שנמצא בסביבת עבודה של ארגון, התשלום על ההרצות יוצא מהתקציב של סביבת העבודה: כל מי שאתם משתפים איתו את הכתובת יכול להוציא את הקרדיטים של הכיתה או של הצוות.
כל הרצה אוטומטית, מ-Webhook, מתזמון או מטלגרם, בודקת קודם שמי שיצר את הטריגר עדיין רשאי להריץ את התהליך. כשהוא כבר לא רשאי, כי הרשאה בוטלה, חברות הושעתה או שסביבת העבודה הועברה לארכיון, האוטומציה נעצרת. היסטוריית ההרצות של התהליך מציגה אז רשומה אחת שנכשלה, עם הקוד run_requires_authenticated_member.
ניהול טריגרים דרך ה-API
רכיבי טריגר נרשמים מעצמם כשהתהליך נשמר. ארבעה נתיבים מנהלים אותם ישירות:
GET /v1/workflows/<id>/triggersמחזיר את הטריגרים של תהליך.PATCH /v1/workflow-triggers/<id>עם{ "isActive": false }משהה טריגר, והערךtrueמחזיר אותו לפעולה. בטריגר ששייך לרכיב, השמירה הבאה מחילה שוב את המתג של הרכיב עצמו, ולכן השתמשו במתג של הרכיב לכל שינוי שצריך להישאר. ערךconfigששולחים ממוזג לתוך ה-configהשמור: שלחו רק את מה שמשתנה. הקישור של הטריגר לרכיב שלו ומספר ההרצות שלו נשמרים.POST /v1/workflows/<id>/sync-triggersרושם מחדש את רכיבי הטריגר של התהליך מהגרסה השמורה שלו. העורך קורא לו אחרי כל שמירה, וכל מי שיש לו גישת עריכה יכול לקרוא לו. הנתיב מחזיר{ "data": { "synced": …, "created": …, "updated": …, "removed": … } }.POST /v1/workflow-triggersיוצר טריגר שלא שייך לשום רכיב, ולכן שמירת התהליך אף פעם לא משנה או מסירה אותו. כדי ליצור טריגר כזה צריך את אותה הרשאה שצריך כדי להריץ את התהליך, כי טריגר הוא הרצה שאף אחד לא משגיח עליה.
תזמונים
רכיב טריגר מתוזמן מריץ תהליך לפי לוח זמנים. תזמון הוא רשימה של כללים, והתהליך רץ בכל פעם שכלל כלשהו מתאים לדקה הנוכחית.
הפעלת תזמון
רכיב טריגר מתוזמן מתחיל הרצות רק כל עוד בנתונים שלו כתוב "active": true, כלומר כל עוד המתג של הרכיב מופעל. רכיב שנכתב דרך ה-API, ה-SDK או MCP בלי השדה active נרשם מושהה. המתג בעורך וכפתור התזמון בסרגל העליון של העורך (תזמון פעיל או תזמון כבוי) קובעים את אותו שדה. ייצוא כתבנית אף פעם לא כולל אותו, ולכן תזמון מיובא תמיד מתחיל מושהה.
יצירת תזמון ידנית
{
"workflowId": "8c2d7f1e-3a4b-4c5d-9e6f-7a8b9c0d1e2f",
"type": "schedule",
"config": {
"rules": [
{ "kind": "days", "every": 1, "hour": 9, "minute": 0 },
{ "kind": "weeks", "every": 2, "weekdays": [1, 3], "hour": 18, "minute": 30 }
],
"timezone": "Asia/Jerusalem"
}
}שלחו אותו עם POST /v1/workflow-triggers. רכיב טריגר מתוזמן שומר config באותו מבנה.
שדה ב-config | משמעות |
|---|---|
rules | כלל אחד או יותר. התהליך רץ כשכלל כלשהו מתאים. |
timezone | השעון שלפיו מחושבים הכללים, כשם של אזור זמן כגון Asia/Jerusalem. כשהשדה חסר, הכללים מחושבים לפי UTC. |
maxExecutions | עצירה אחרי מספר ההרצות הזה. התזמון נשאר רשום: כדי להמשיך, הגדילו את המספר או מחקו אותו. |
retry | אופציונלי. ההגדרה ניסיון חוזר לאחר כישלון של הרכיב: { afterMinutes, maxAttempts, cheapLanesOnly }. הרצה מתוזמנת שמסתיימת ב-failed או ב-timed_out רצה שוב afterMinutes דקות אחרי שהסתיימה (5–1,440, ברירת המחדל 30), עד maxAttempts פעמים (1–5, ברירת המחדל 3). עם cheapLanesOnly (ברירת המחדל true), קריאות המודל של ההרצה ושל הניסיונות החוזרים שלה לא עוברות לשירות הישיר של יצרן המודל. שדה שלא נשלח מקבל את ברירת המחדל שלו. כדי שלא יהיה ניסיון חוזר, השמיטו את retry; בבקשת PATCH, הערך "retry": null מכבה אותו. ראו בדף טריגר מתוזמן. |
kind | שדות | מתי התהליך רץ |
|---|---|---|
minutes | every בין 1 ל-59 | בדקה 0, N, 2N וכן הלאה של כל שעה |
hours | every בין 1 ל-23, minute | בשעה 0, N, 2N וכן הלאה של כל יום, באותה דקה |
days | every בין 1 ל-31, hour, minute | כל N ימים בלוח השנה, באותה שעה |
weeks | every בין 1 ל-52, weekdays (0 הוא יום ראשון, 6 הוא שבת), hour, minute | בימי השבוע האלה, כל N שבועות |
months | every בין 1 ל-12, dayOfMonth בין 1 ל-31, hour, minute | באותו יום, או ביום האחרון של החודש כשהחודש קצר יותר, כל N חודשים |
cron | cron, ביטוי עם 5 שדות | בכל פעם שהביטוי מתאים |
- כל N ימים, שבועות או חודשים נספרים מנקודת התחלה קבועה, והשבוע מתחיל ביום שני, ולכן שמירה חוזרת אף פעם לא מזיזה את הימים שבהם תזמון רץ.
- בכלל
cron, כל השדות חייבים להתאים, גם היום בחודש וגם היום בשבוע, אף שבכלי cron מסוימים מספיק שאחד מהם יתאים. הערך7פירושו יום ראשון, כמו0. - ערכים מחוץ לטווח נדחים עם
400, וכך גם אזור זמן שהשרת לא מכיר או ערךretryמחוץ לטווח. - הרצה של ניסיון חוזר מופיעה ב-
GET /v1/workflows/<id>/executionsעםretry: { "attempt": 1, "maxAttempts": 3, "retryOf": "<failed execution id>" }. - צורות ישנות יותר עדיין עובדות בטריגרים שאתם יוצרים ידנית: ערך
intervalכגון5m,1hאו1d, או מחרוזתcron. רכיב טריגר מתוזמן שנשמר איתן מומר לכללים.
איך תזמונים מתחילים הרצות
- השרת בודק את התזמונים פעם בדקה, ומריץ תהליך כשאחד הכללים שלו מתאים לדקה הזו.
- אותה דקה אף פעם לא מתחילה הרצה פעמיים, גם אחרי אתחול מחדש או שינוי שעון. על דקה שהשעון מדלג עליה אין הרצה באותו יום.
- אם ההרצה הקודמת של התהליך עדיין רצה, Nodaro מדלג על הדקה הזו.
- רכיב שלא יכול לרוץ נשאר מושהה גם כשהמתג שלו מופעל, והרכיב מציג את הסטטוס לא ירוץ. כך קורה, למשל, עם כלל
cronבלי ביטוי או עם אזור זמן שאי אפשר לקרוא. Nodaro לא מנחש כלום: תקנו את הרכיב ושמרו שוב.
שליחת תוצאות לשרת שלכם
רכיב פלט Webhook שולח לכתובת שלכם, בבקשת POST, את התוצאה שמגיעה אליו, עם הפרמטרים שהגדרתם. לפרמטרים שלו יש אותם סוגים כמו לפרמטרים של הטריגר: text, imageUrl, videoUrl ו-audioUrl. ערכי מדיה הם כתובות URL של קבצים ש-Nodaro שומר, והשרת שלכם יכול להוריד אותם.
אם נקודת הקצה שלכם עונה בשגיאה, הרכיב נכשל, והשגיאה מופיעה בהיסטוריית ההרצה. השתמשו בכתובת https.
שליחת מפתח עם הבקשה
נקודות קצה רבות מקבלות בקשה רק עם מפתח בכותרת. שמרו את שם הכותרת ואת המפתח פעם אחת כפרטי גישה באפליקציית הווב, תחת אינטגרציות › מפתחות גישה ל-Webhook, ובחרו אותם ברכיב. המפתח אף פעם לא נכנס לתהליך, לייצוא או לתבנית.
כששומרים מפתח, בוחרים מי יכול להשתמש בו:
| אפשרות | איפה היא עובדת |
|---|---|
| כל כתובת (פרטי גישה רגילים) | הרצות שאתם מתחילים בעצמכם: הרצה מהעורך ותזמון שהגדרתם בעורך. |
| רק כתובת אחת (פרטי גישה נעולים) | כל הרצה, אבל רק לכתובת הזו, או לנתיבים שמתחתיה כשאתם מאפשרים זאת. |
הרצות שהתחילו עם אסימון API או אסימון OAuth, מטריגר Webhook, מתזמון שנוצר דרך ה-API או מאפליקציה שפורסמה לא נחשבות להרצות שלכם, אף שהן רצות בשמכם. בהרצות כאלה, נעלו את פרטי הגישה: עם פרטי גישה רגילים, הרכיב נכשל עם הודעה ברורה במקום לשלוח. פרטי גישה נעולים נשלחים רק כשהכתובת של הרכיב תואמת לכתובת הנעולה, הרכיב לא עוקב אחרי הפניה לכתובת אחרת, והמפתח אף פעם לא נשלח על גבי http רגיל.
כשמצורפים פרטי גישה, הרכיב לא שומר ולא מציג את גוף התגובה של נקודת הקצה שלכם, רק את קוד הסטטוס שלה. ניהול פרטי הגישה נעשה רק באפליקציית הווב: הנתיבים של פרטי הגישה מחזירים 403 in_app_only לאסימונים. פרסום אפליקציה או שיתוף תהליך כדי שאחרים יריצו אותו נדחים עם 409 credential_unbound, עד שכל פרטי הגישה שהתהליך שולח איתם, כולל בתוך תת־תהליכים, נעולים לכתובת שהרכיב שולח אליה.
קבלת התוצאה של הרצה בלי נקודת קצה
יש עוד שתי דרכים לדעת מה התוצאה של הרצה:
- בדקו את הסטטוס מדי כמה שניות. בדקו את מצב ההרצה עם
GET /v1/workflow-executions/:id, או את מצב המשימה של רכיב יחיד עםGET /v1/jobs/:id/status. ראו את דפי ה-API הרצות ומשימות. - תנו לתהליך לדווח לכם. סיימו את התהליך ברכיב פלט Webhook ששולח את התוצאות לשרת שלכם.
שאלות נפוצות
איך מריצים תהליך של Nodaro עם Webhook?
הוסיפו רכיב טריגר Webhook, הגדירו את הפרמטרים שלו ושמרו את התהליך. השמירה יוצרת כתובת מהצורה POST /v1/webhooks/ עם אסימון בסוף. שלחו לכתובת הזו גוף JSON שהמפתחות שלו תואמים לשמות הפרמטרים, בלי שום אימות נוסף.
האם Nodaro קורא לשרת שלי כשמשימה מסתיימת?
כן, אחרי שרושמים נקודת קצה של Webhook עם POST /v1/webhook-endpoints. מאותו רגע, Nodaro שולח אירוע job.completed או job.failed חתום לכתובת ה-HTTPS שלכם בכל פעם שאחת המשימות שלכם מסתיימת, ומנסה שוב במשך כ-43 שעות.
איך מאמתים אירוע Webhook של Nodaro?
חשבו HMAC-SHA256 של חותמת הזמן, נקודה והגוף הגולמי, עם הסוד של נקודת הקצה שלכם, והשוו אותו לערך v1 בכותרת Nodaro-Signature. בדקו גם שחותמת הזמן עדכנית. הפונקציה verifyWebhookSignature של ה-SDK מבצעת את שתי הבדיקות.
האם בטוח לשתף כתובת Webhook?
התייחסו לכתובת Webhook כמו לסיסמה. האסימון שבכתובת הוא פרטי הגישה היחידים, ולכן כל מי שהכתובת בידיו יכול להתחיל הרצות ולהוציא את הקרדיטים שלכם, או את התקציב של סביבת העבודה. כל טריגר מקבל עד 10 בקשות בדקה.
איך מתזמנים תהליך דרך ה-API?
שמרו רכיב טריגר מתוזמן שבנתונים שלו השדה active הוא true, או צרו תזמון עם POST /v1/workflow-triggers ועם config של כללים ואזור זמן. Nodaro בודק כל תזמון פעם בדקה.
למה רכיב טריגר מתוזמן שנוצר דרך ה-API לא רץ?
רכיב טריגר מתוזמן רץ רק כל עוד בנתונים שלו השדה active הוא true. רכיב שנכתב דרך ה-API, ה-SDK או MCP בלי השדה הזה נרשם מושהה. הגדירו את השדה ושמרו שוב.
דפים קשורים
טריגר Webhook
טריגר מתוזמן
פלט Webhook
אוטומציות
הרצות
עודכן לאחרונה