Nodaro 문서
문서노드 레퍼런스모델템플릿AI 에이전트(MCP)개발자셀프 호스팅리서치비교
REST API

Webhook

서명된 job.completed와 job.failed 이벤트를 내 서버에서 받고, Webhook 트리거 URL로 워크플로를 시작하고, 일정을 만들고, 결과를 보내는 방법입니다.

Webhook은 Nodaro와 내 다른 시스템을 양방향으로 연결합니다. 내가 등록한 Webhook 엔드포인트는 내 작업 하나가 끝날 때마다 서명된 이벤트를 받으므로, 내 서버가 폴링할 필요가 없습니다. Webhook 트리거 노드는 어떤 시스템이든 호출해 실행을 시작할 수 있는 URL을 워크플로에 부여하고, 일정 트리거는 정해진 시간표에 따라 워크플로를 실행하며, Webhook 출력 노드는 실행 결과를 내 서버로 보냅니다.

프롬프트Webhook 트리거prompt, imageUrl이미지 생성Nano Banana ProWebhook 출력내 서버
외부 시스템이 Webhook 트리거 URL을 호출하면 이미지 생성이 실행되고, Webhook 출력이 이미지 URL을 내 서버로 보냅니다.

엔드포인트

이 라우트들은 Nodaro가 작업 이벤트를 보내는 엔드포인트를 관리합니다. 내 서버에서 작업 이벤트 받기를 참고하세요.

메서드경로기능
POST/v1/webhook-endpoints엔드포인트를 등록합니다. 응답에 서명 시크릿이 한 번만 담깁니다.
GET/v1/webhook-endpoints내 엔드포인트를 최신순으로 반환합니다. 시크릿은 절대 나열되지 않습니다.
GET/v1/webhook-endpoints/:id엔드포인트 하나입니다.
PATCH/v1/webhook-endpoints/:idurl, 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의 URL, 토큰과 함께 반환합니다.
PATCH/v1/workflow-triggers/:idisActive로 트리거를 일시 중지하거나 재개하고, 일정의 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 seconds
nodaro webhooks create --url https://example.com/nodaro-webhook
nodaro webhooks test <id> --wait

응답은 { data, secret }과 함께 201입니다. 시크릿은 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에 도달했을 때입니다.
pingPOST /v1/webhook-endpoints/:id/test에서만 보냅니다. 엔드포인트는 이 이벤트를 구독하지 않습니다.

내 작업 목록에 있는 모든 작업이 해당합니다. 단일 생성도, 워크플로 실행 안의 각 노드 작업도 포함됩니다. 취소된 작업은 아무것도 보내지 않으며, pending과 processing 같은 중간 상태도 마찬가지입니다. 실패했다가 나중에 완료된 작업은 끝날 때마다 이벤트를 하나씩 보냅니다. 컴포넌트 노드 실행 안의 작업은 내 작업 목록에 없으므로 아무것도 보내지 않습니다. 실행을 시작한 노드가 대신 보고합니다.

요청

Nodaro는 Content-Type: application/json과 다음 헤더를 담아 POST를 보냅니다.

헤더값
Nodaro-Signaturet=<unix seconds>,v1=<hex>. 시크릿을 교체하는 동안에는 시크릿마다 v1=이 하나씩 붙습니다.
Nodaro-Eventjob.completed, job.failed, ping
Nodaro-Delivery전송 ID이며, 이 이벤트의 모든 재시도에서 같습니다
Nodaro-Webhook엔드포인트 ID
Nodaro-Attempt첫 시도는 1, 그다음은 2, 3 등
User-AgentNodaro-Webhooks/1

서명되는 것은 타임스탬프와 본문뿐입니다. 라우팅과 중복 제거는 Nodaro-Event나 Nodaro-Delivery 헤더가 아니라, 검증된 본문의 type과 id로 하세요.

{
  "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",
      "source": "sdk",
      "session_id": null,
      "created_at": "2026-10-15T10:00:00.000Z",
      "completed_at": "2026-10-15T10:00:04.512Z"
    }
  }
}
  • id는 전송 ID입니다. 이벤트와 엔드포인트마다 고유하고 모든 재시도에서 같으므로, 이 값으로 중복을 제거하세요.
  • createdAt은 작업이 이 상태에 도달한 시각입니다.
  • data.job은 GET /v1/jobs/:id가 반환하는 형식의 작업이며, 첫 시도 시점의 상태입니다. 재시도는 바이트 하나까지 같은 본문을 보냅니다. 청구가 아직 정산되지 않았으면 credits_charged가 여전히 null이고 credit_status: "reserved"일 수 있습니다. 최종 수치는 작업을 다시 읽으세요.
  • 이벤트는 최대 64KB입니다. 더 큰 작업은 input_data부터, 그다음 output_data를 빼며, data.omitted에 뺀 항목이 나열됩니다. 작업 전체는 GET /v1/jobs/:id에 있습니다.
  • ping에는 "data": { "webhookId": "<endpoint id>" }가 담깁니다.

서명 검증

v1은 엔드포인트의 시크릿 전체(whsec_..., UTF-8)를 키로 한 <t>.<raw body>의 HMAC-SHA256입니다. 요청을 신뢰하기 전에 세 가지를 확인하세요.

  1. 서명이 받은 그대로의 바이트와 일치하는지 확인합니다. 먼저 JSON을 다시 직렬화하지 마세요.
  2. 타임스탬프가 최근인지 확인합니다. 일반적인 허용 범위는 5분입니다. 이 확인은 가로챈 요청을 나중에 다시 보내는 것을 막습니다.
  3. 이 id를 이미 처리하지 않았는지 확인합니다.

SDK에서는 @nodaro/sdk의 verifyWebhookSignature가 이 확인을 처리합니다. 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)을 보관하므로, 실패한 확인을 바이트 하나까지 재현할 수 있습니다.

재시도

10초 안에 받은 2xx 응답은 모두 전송된 것으로 봅니다. 그 밖의 경우는 시도가 실패한 것입니다. 3xx(리디렉션은 따라가지 않음), 4xx, 5xx, 시간 초과, 응답하지 않는 주소입니다. 그러면 전송을 다시 시도합니다.

시도 후다음 시도까지
130초
22분
310분
41시간
56시간
612시간
724시간

각 대기 시간은 위아래로 최대 10%까지 달라집니다. 8번째 시도, 곧 첫 시도로부터 약 43시간 뒤에는 전송이 최종 실패합니다. 테스트 ping은 한 번만 시도합니다.

  • 전송은 최소 한 번입니다. 예를 들어 내 서버가 너무 늦게 응답했을 때처럼, 서버가 첫 시도를 처리한 뒤에 재시도가 도착할 수 있습니다. 전송 id로 중복을 제거하세요.
  • 이벤트는 순서가 뒤바뀌어 도착할 수 있습니다. 한 엔드포인트에는 한 번에 최대 5개, 한 계정에는 10개의 전송이 갑니다. 도착 순서가 아니라 createdAt과 작업 자체의 status를 쓰세요.
  • 전혀 응답하지 않는 서버는 엔드포인트를 쉬게 합니다. HTTP 응답 없이 5번 연속 시도하면 엔드포인트는 1분간 쉬며, 다시 일어날 때마다 두 배씩 늘어나 최대 1시간까지 쉽니다. 대기 중인 전송은 시도 횟수를 유지하며, HTTP 응답이 하나라도 오면 휴식이 끝납니다.
  • Nodaro 쪽 문제는 내 시도 횟수로 세지 않습니다. 전송은 몇 분 기다렸다가 시도 횟수를 쓰지 않고 다시 시도합니다. 그동안 기록 항목에는 signing_unavailable 또는 job_unreadable이 표시됩니다.

자동 꺼짐

전송 50개가 연속으로 최종 실패하면 Nodaro가 엔드포인트를 끕니다. isActive는 false가 되고 disabledReason은 "consecutive_failures"가 됩니다. 수신 측을 고치고 테스트 ping으로 확인한 다음, PATCH /v1/webhook-endpoints/:id에 { "isActive": true }를 보내 엔드포인트를 켜세요. 그러면 횟수도 초기화되며, 성공이 하나라도 있어도 초기화됩니다.

엔드포인트가 꺼져 있는 동안에는, 내가 껐든 Nodaro가 껐든 이벤트가 기록되지 않습니다. 이미 대기 중인 전송은 error: "endpoint_disabled"와 함께 failed로 끝납니다. 테스트 ping은 그래도 보냅니다.

전송 기록

GET /v1/webhook-endpoints/:id/deliveries는 엔드포인트의 전송을 최신순으로, 기본 페이지당 20개, 최대 100개까지 나열합니다. status(pending, delivered, failed)로 필터링하고, limit과 cursor로 페이지를 넘기세요. 응답은 { data, next }입니다. 기록은 30일 동안 보관됩니다.

각 전송에는 event, jobId, status, attempts, 수신 측의 마지막 responseStatus, responseBody의 처음 2KB, durationMs, error, 그리고 대기 중일 때는 nextAttemptAt이 담깁니다. requestBody는 보낸 그대로의 본문으로, 전송이 대기 중이거나 실패한 뒤에 보관되며, 전송되면 null이 됩니다. GET /v1/webhook-endpoints/:id/deliveries/:deliveryId는 테스트가 대기열에 넣은 ping처럼 전송 하나를 읽습니다.

error는 다음 값 중 하나입니다.

error의미
http_<status>내 서버가 그 상태로 응답했습니다.
timeout10초 안에 응답이 없었습니다.
blocked_address주소가 공개 인터넷 주소로 확인되지 않습니다. Nodaro가 연결하기 전에 거부했습니다.
connection_refused, connection_reset, tls_error, network_error연결이 실패했습니다.
endpoint_disabled전송이 대기하는 동안 엔드포인트가 꺼졌습니다.
app_revoked엔드포인트를 등록한 연결된 앱을 내가 해지했습니다.
job_deleted보내기 전에 작업이 삭제되었습니다.
attempts_exhausted마지막 시도가 응답이 기록되기 전에 Nodaro 쪽에서 끊겼습니다.
signing_unavailable, job_unreadableNodaro 쪽 문제입니다. 전송은 기다렸다가 시도 횟수를 쓰지 않고 다시 시도합니다.

시크릿 교체

POST /v1/webhook-endpoints/:id/rotate-secret은 { secret, previousSecretExpiresAt }로 응답하며, 새 시크릿은 이 응답에만 나타납니다. 그때부터 새 시크릿이 재시도를 포함한 모든 전송에 서명합니다. 교체된 시크릿은 gracePeriodSeconds 동안 함께 계속 서명합니다. 기본값은 하루이고 최대 7일입니다. 그동안 공백 없이 수신 측을 업데이트할 수 있습니다. 유출된 뒤처럼 이전 시크릿을 즉시 중단하려면 { "gracePeriodSeconds": 0 }을 보내세요.

규칙과 한도

  • 공개 호스트의 https만 가능합니다. http://, localhost, 사설·루프백·링크 로컬 주소, 그리고 .internal이나 .local로 끝나는 이름처럼 사설 네트워크 안에서만 작동하는 이름은 안 됩니다. URL에 사용자 이름이나 비밀번호를 넣을 수 없습니다. 주소는 시도할 때마다 다시 확인됩니다. 내 컴퓨터에서 받으려면 터널을 쓰고 그 https 주소를 등록하세요.
  • 주소마다 엔드포인트 하나. 같은 주소를 두 번 등록하면 409 url_taken으로 응답합니다. 대신 엔드포인트 하나로 두 이벤트를 모두 구독하세요.
  • 계정마다 엔드포인트 10개. 11번째는 400 limit_reached로 응답합니다.
  • 쓰기에는 요청 한도가 있습니다. 만들기, 업데이트, 삭제, 시크릿 교체는 분당 30회, 테스트 ping은 분당 10회입니다.
  • 엔드포인트를 관리할 수 있는 주체: 로그인한 세션, 개인 API 토큰, jobs:read 범위가 있는 연결된 앱의 토큰입니다. 일부 워크플로로 제한된 개인 토큰은 관리할 수 없습니다.
  • 연결된 앱이 등록한 엔드포인트(appId가 설정됨)는 그 앱이 직접 제출한 작업만 받으며, 앱에 대한 내 승인에 jobs:read가 포함되어 있는 동안에만 받습니다. 앱이 시작한 워크플로 실행의 작업은 포함되지 않으므로, GET /v1/workflow-executions/:id로 실행을 추적하세요. 앱은 자신이 등록한 엔드포인트만 봅니다. 나는 모두 보고 삭제할 수 있습니다.
  • 셀프 호스팅 설치는 자체 서버에서 보내며 NODARO_ENCRYPTION_KEY가 필요합니다. Docker Compose 스택이 이를 생성합니다. 이 값이 없으면 라우트는 503 encryption_key_missing으로 응답합니다.
  • MCP에서는 사용할 수 없습니다. 엔드포인트를 등록하면 서명 시크릿이 반환되고 계정 전체의 전송이 설정되므로, AI 에이전트가 아니라 앱이나 CLI가 할 일입니다.

그 밖의 오류: 잘못된 주소나 본문에는 400 validation_error, 403 insufficient_scope, 내 것이 아닌 ID에는 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 중 어떤 방법으로 저장하든 똑같이 동작합니다. 토큰은 한 번 발급된 뒤 유지되므로, 외부 시스템에 준 URL은 이후 저장해도 계속 유효합니다. 노드를 제거하면 URL도 폐기됩니다.

URL 확인

GET /v1/workflows/<id>/triggers는 각 Webhook의 webhookUrl과 webhookToken을 포함해 워크플로의 트리거를 반환합니다. webhookUrl은 /v1/webhooks/<token> 형태의 경로이므로 앞에 내 Nodaro 주소를 붙이세요. config.nodeId가 노드의 ID인 트리거가 그 Webhook 트리거 노드의 트리거입니다. 목록에는 내가 소유한 워크플로의 트리거만 나옵니다.

편집기에서는 노드의 설정 패널에 있는 Webhook URL에 같은 주소가 전체로 표시되며, 복사 버튼도 있습니다.

호출

JSON 본문과 함께 POST를 보내세요. URL에 든 토큰이 자격 증명이므로 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 트리거를 사용하며, 두 개가 있으면 워크플로 전체가 실행됩니다.

URL을 비밀로 유지

토큰이 유일한 자격 증명이므로 URL을 가진 사람은 누구나 실행을 시작하고 내 크레딧을 쓸 수 있습니다. 조직의 워크스페이스에 있는 워크플로라면 실행 비용은 워크스페이스의 예산에서 나갑니다. URL을 공유받은 사람은 누구나 클래스나 팀의 크레딧을 쓸 수 있습니다.

Webhook, 일정, Telegram 등 모든 자동 실행은 먼저 트리거를 만든 사람이 아직 워크플로를 실행할 수 있는지 확인합니다. 권한이 취소되었거나, 멤버십이 정지되었거나, 워크스페이스가 보관되어 더 이상 실행할 수 없으면 자동화가 멈춥니다. 그러면 워크플로의 실행 기록에 run_requires_authenticated_member 코드와 함께 실패 항목이 하나 표시됩니다.

API로 트리거 관리

트리거 노드는 워크플로를 저장할 때 스스로 등록됩니다. 다음 네 경로로 트리거를 직접 관리합니다.

  • GET /v1/workflows/<id>/triggers는 워크플로의 트리거 목록을 반환합니다.
  • PATCH /v1/workflow-triggers/<id>에 { "isActive": false }를 보내면 트리거가 일시 중지되고, true를 보내면 재개됩니다. 노드에 속한 트리거는 다음 저장 때 노드 자체의 스위치가 다시 적용되므로, 계속 유지해야 하는 설정은 노드의 스위치를 사용하세요. 보낸 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이 횟수만큼 실행한 뒤 멈춥니다. 일정은 등록된 채로 남으므로, 계속하려면 숫자를 늘리거나 지우세요.
kind필드실행 시점
minutesevery 1~59매시간 0분, N분, 2N분, …
hoursevery 1~23, minute매일 0시, N시, 2N시, …의 해당 분
daysevery 1~31, hour, minuteN번째 날마다 해당 시각
weeksevery 1~52, weekdays(0은 일요일, 6은 토요일), hour, minuteN번째 주마다 해당 요일
monthsevery 112, dayOfMonth 131, hour, minuteN번째 달마다 해당 일. 그달이 더 짧으면 마지막 날
cron5개 필드로 된 표현식 cron표현식이 맞을 때마다
  • N일, N주, N개월 간격은 고정된 시작점부터 셉니다. 주는 월요일에 시작하므로, 다시 저장해도 일정이 실행되는 날이 밀리지 않습니다.
  • cron 규칙에서는 모든 필드가 맞아야 합니다. 날짜와 요일이 모두 맞아야 하며, 둘 중 하나만 맞아도 되는 일부 cron 도구와 다릅니다. 7은 0과 마찬가지로 일요일입니다.
  • 범위를 벗어난 값은 400으로 응답합니다. 서버가 읽을 수 없는 시간대도 마찬가지입니다.
  • 예전 형식도 계속 동작합니다. 직접 만드는 트리거에는 5m, 1h, 1d 같은 interval이나 cron 문자열을 쓸 수 있습니다. 이 형식으로 저장한 일정 트리거 노드는 규칙으로 변환됩니다.

일정이 실행되는 방식

  • 서버는 1분에 한 번 일정을 확인하고, 그 분에 맞는 규칙이 있는 워크플로를 실행합니다.
  • 다시 시작하거나 시계가 바뀌어도 같은 분이 두 번 실행되지는 않습니다. 시계가 건너뛴 분은 그날 실행되지 않습니다.
  • 워크플로의 이전 실행이 아직 진행 중이면 그 분은 건너뜁니다.
  • 실행할 수 없는 노드는 보류됩니다. 예를 들어 표현식이 없는 cron 규칙이나 읽을 수 없는 시간대가 그렇습니다. 아무것도 추측하지 않으므로 노드를 고친 뒤 다시 저장하세요.

결과를 내 서버로 보내기

Webhook 출력 노드는 도달한 결과를 설정한 파라미터와 함께 POST 요청으로 내 URL에 보냅니다. 파라미터의 유형은 트리거와 같은 text, imageUrl, videoUrl, audioUrl입니다. 미디어 값은 Nodaro가 저장한 파일의 URL이며, 내 서버가 다운로드할 수 있습니다.

엔드포인트가 오류로 응답하면 노드가 실패하고, 오류가 실행 기록에 표시됩니다. https URL을 사용하세요.

요청에 키 함께 보내기

많은 엔드포인트는 헤더에 키가 있어야만 요청을 받습니다. 웹 앱의 연동 › HTTP 자격 증명에 헤더 이름과 키를 자격 증명으로 한 번 저장하고 노드에서 선택하세요. 키는 워크플로, 내보내기, 템플릿에 절대 들어가지 않습니다.

키를 저장할 때 누가 사용할 수 있는지 고릅니다.

선택적용 대상
모든 주소(일반 자격 증명)내가 직접 시작한 실행: 편집기에서 시작한 실행과 편집기에서 설정한 일정입니다.
주소 한 곳만(고정 자격 증명)모든 실행. 단, 그 주소로만, 또는 허용한 경우 그 아래 경로로만 보냅니다.

API 토큰이나 OAuth 토큰으로 시작한 실행, Webhook 트리거, API로 만든 일정, 게시된 앱에서 시작한 실행은 나로서 실행되더라도 내가 직접 시작한 실행이 아닙니다. 이런 실행에는 자격 증명을 고정하세요. 일반 자격 증명을 쓰면 노드가 키를 보내지 않고 명확한 메시지와 함께 실패합니다. 고정 자격 증명은 노드의 URL이 그 주소와 맞을 때만 보내지고, 다른 주소로의 리디렉션은 따라가지 않으며, 키는 평문 http로는 절대 보내지 않습니다.

자격 증명이 연결된 노드는 엔드포인트의 응답 본문을 보관하거나 표시하지 않고 상태 코드만 남깁니다. 자격 증명은 웹 앱에서만 관리할 수 있으며, 자격 증명 경로는 토큰에 403 in_app_only로 응답합니다. 앱을 게시하거나 다른 사람이 실행하도록 워크플로를 공유하면, 하위 워크플로 안의 것까지 포함해 함께 보내는 모든 자격 증명이 노드가 보내는 주소로 고정될 때까지 409 credential_unbound로 응답합니다.

엔드포인트 없이 실행 추적

실행 결과를 알 수 있는 다른 방법이 두 가지 있습니다.

  • 폴링하세요. GET /v1/workflow-executions/:id로 실행을 추적하거나, GET /v1/jobs/:id/status로 노드 하나의 작업을 추적하세요. 실행과 작업을 참고하세요.
  • 워크플로가 알려 주게 하세요. 결과를 내 서버로 보내는 Webhook 출력 노드로 워크플로를 마치세요.

자주 묻는 질문

Webhook으로 Nodaro 워크플로를 시작하려면 어떻게 하나요?

Webhook 트리거 노드를 추가하고 파라미터를 정의한 뒤 워크플로를 저장하세요. 저장하면 POST /v1/webhooks/ 뒤에 토큰이 붙은 형태의 URL이 만들어집니다. 키가 파라미터 이름과 일치하는 JSON 본문을 보내면 되며, 다른 인증은 필요하지 않습니다.

작업이 끝나면 Nodaro가 내 서버를 호출하나요?

네. POST /v1/webhook-endpoints로 Webhook 엔드포인트를 등록하면 됩니다. 그러면 내 작업 하나가 끝날 때마다 Nodaro가 서명된 job.completed 또는 job.failed 이벤트를 내 https URL로 보내며, 약 43시간 동안 재시도합니다.

Nodaro Webhook 이벤트는 어떻게 검증하나요?

엔드포인트의 시크릿으로 타임스탬프, 점, 원본 본문의 HMAC-SHA256을 계산해 Nodaro-Signature 헤더의 v1 값과 비교하세요. 타임스탬프가 최근인지도 확인하세요. SDK의 verifyWebhookSignature가 둘 다 처리합니다.

Webhook URL을 공유해도 안전한가요?

비밀번호처럼 다루세요. URL에 든 토큰이 유일한 자격 증명이므로, 토큰을 가진 사람은 누구나 실행을 시작하고 내 크레딧이나 워크스페이스의 예산을 쓸 수 있습니다. 트리거마다 분당 10개의 요청을 받습니다.

API로 워크플로 일정을 만들려면 어떻게 하나요?

데이터에 “active”를 true로 설정한 일정 트리거 노드를 저장하거나, POST /v1/workflow-triggers에 규칙과 시간대로 된 config를 보내 일정을 만드세요. Nodaro는 1분에 한 번 모든 일정을 확인합니다.

API로 만든 일정 트리거가 실행되지 않는 이유는 무엇인가요?

일정 트리거는 데이터에 “active”가 true일 때만 실행됩니다. API, SDK, MCP로 작성한 노드에 이 필드가 없으면 일시 중지 상태로 등록됩니다. 필드를 설정하고 다시 저장하세요.

최종 업데이트

목차