Skip to content

웹훅 ​

클라우드 전용

웹훅은 클라우드 버전에서만 사용할 수 있습니다.

웹훅을 사용하면 ADHDev Cloud가 머신 및 에이전트 이벤트를 자체 HTTP 엔드포인트로 전송할 수 있습니다.

현재 주요 동작:

  • 웹훅 API는 운영 중입니다.
  • 전용 /webhooks 대시보드 페이지는 현재 안정적인 표준 탐색 서피스가 아닙니다.
  • 페이로드, 이벤트, 전달 히스토리 구조의 정식 계약은 REST API 레퍼런스를 사용하세요.
  • 웹훅 생성/토글/삭제/테스트 작업은 최근 로그인이 보호하는 클라우드 계정 작업으로, API 키가 아닌 신선한 대시보드 세션 JWT가 필요합니다.

웹훅 생성 ​

현재 로그인된 클라우드 대시보드 세션에서 웹훅을 생성하세요. 요청 본문은 다음과 같습니다:

json
{
  "url": "https://example.com/hooks/adhdev",
  "events": ["agent:status", "agent:approve_request"]
}

만료된 브라우저 세션이나 API 키로 변경 웹훅 엔드포인트를 호출하면 서버는 훅을 프로비저닝하는 대신 403 RECENT_LOGIN_REQUIRED를 반환합니다.

생성 응답에는 다음이 포함됩니다:

  • 웹훅 레코드
  • 서명 검증을 위해 한 번만 표시되는 secret

events는 자유 형식이 아닌 허용 목록입니다: 아래 이름(및 "*")만 허용되며, 그 외는 조용히 무시됩니다. 개별 구독이 불가능한 세부 이벤트까지 모두 받으려면 ["*"]를 사용하세요.

이벤트 ​

개별 구독 가능(events에서 허용):

이벤트트리거
daemon:connect데몬이 서버에 연결됨
daemon:disconnect데몬 연결이 끊김
agent:status에이전트 상태 변경(스로틀됨, 데몬당 10초에 최대 1회)
agent:approve_request예약됨 — 허용 목록에는 있지만 서버가 현재 발행하지 않음

서버가 발행하지만 "*"로 구독한 웹훅에만 전달됨(개별 구독 불가):

이벤트트리거
agent:generating_started에이전트가 응답 생성을 시작함
agent:generating_completed에이전트가 생성을 완료함
agent:waiting_approval에이전트가 승인을 기다리는 중
agent:waiting_choice에이전트가 다중 선택 프롬프트를 기다리는 중
agent:stopped에이전트 세션이 중지됨
monitor:no_progress에이전트가 진행 없이 정체된 것으로 보임(레거시 별칭: monitor:long_generating)
webhook:testPOST /webhooks/{id}/test로 명시적으로 요청된 테스트 전달 — 구독 필터링을 완전히 우회하며 테스트 대상 웹훅 하나에만 전달됨

페이로드 형식 ​

모든 전송은 최상위 필드 세 개 — event, payload, timestamp(서버가 전송한 시각, epoch 밀리초) — 로 이루어진 JSON 본문입니다. 에이전트·모니터 이벤트(agent:status를 제외한 agent:*, 그리고 monitor:no_progress)의 페이로드는 다음과 같습니다:

json
{
  "event": "agent:generating_completed",
  "payload": {
    "daemonId": "8e1f4c2a9b7d3e5f6a0c1b2d3e4f5a6b7c8d9e0f1a2b3c4d5e6f7a8b9c0d1e2f",
    "daemonType": "adhdev-daemon",
    "event": "agent:generating_completed",
    "timestamp": 1714000000000,
    "targetSessionId": "9b2d7c4e-1f3a-4b5c-8d6e-0a1b2c3d4e5f",
    "providerType": "claude-cli",
    "providerSessionId": "3f2a9c1e-5b7d-4e8a-9c0f-1a2b3c4d5e6f",
    "workspaceName": "/home/dev/projects/myproject",
    "duration": 42,
    "surfaceHidden": false,
    "muted": false
  },
  "timestamp": 1714000000215
}
필드포함 조건
daemonId항상 — 이벤트를 보고한 데몬 연결
event, timestamp항상 — 이벤트 이름과 데몬이 관측한 시각(epoch 밀리초)
targetSessionId특정 에이전트 세션에 관한 이벤트일 때 — agent:generating_completed와 agent:stopped에는 항상 포함
providerType데몬이 세션을 찾을 수 있을 때. 에이전트 프로바이더(예: claude-cli, cursor). 찾지 못하면 필드가 없으며, 데몬 종류를 담지 않습니다
daemonType항상. 이벤트를 보낸 데몬의 종류, 현재 adhdev-daemon
providerSessionId프로바이더가 자체 대화 ID를 제공할 때
workspaceName세션에 워크스페이스가 있을 때 — CLI 에이전트의 작업 디렉터리 또는 IDE 워크스페이스
durationagent:generating_completed에만, 데몬이 턴 시작을 관측했을 때 — 정수 초 단위
elapsedSecmonitor:no_progress — 진행이 없었던 시간(초)
modalMessage, modalButtonsagent:waiting_approval / agent:waiting_choice — 승인 요청 문구와 버튼 레이블
surfaceHidden, muted데몬이 세션을 호스팅할 때 — 대시보드에서 숨김/음소거 상태인지 여부

daemon:connect, daemon:disconnect, agent:status, webhook:test는 각자 다른 형태의 페이로드를 보냅니다. modalMessage의 승인 요청 문구를 제외하면, 페이로드에는 채팅 메시지, 채팅 제목, 에이전트 응답이 포함되지 않습니다.

서명 검증 ​

각 웹훅 요청에는 다음이 포함됩니다:

  • X-ADHDev-Signature
  • X-ADHDev-Event

서명 형식:

text
X-ADHDev-Signature: t=1714000000000,v1=a3f9c2b1...

원시 요청 본문을 기준으로 검증하세요.

js
import crypto from 'crypto'

function verifySignature(body, signature, secret) {
  const parts = Object.fromEntries(signature.split(',').map((p) => p.split('=')))
  const timestamp = parts.t
  const received = parts.v1
  const payload = `${timestamp}.${body}`
  const expected = crypto
    .createHmac('sha256', secret)
    .update(payload)
    .digest('hex')

  return crypto.timingSafeEqual(
    Buffer.from(received),
    Buffer.from(expected)
  )
}

// Express 예시
app.post('/webhook', express.raw({ type: 'application/json' }), (req, res) => {
  const sig = req.headers['x-adhdev-signature']
  const body = req.body.toString('utf8')
  if (!verifySignature(body, sig, process.env.WEBHOOK_SECRET)) {
    return res.status(401).send('Invalid signature')
  }
  const event = JSON.parse(body)
  // 이벤트 처리...
  res.sendStatus(200)
})
python
import hmac
import hashlib

def verify_signature(body: bytes, signature: str, secret: str) -> bool:
    parts = dict(p.split('=', 1) for p in signature.split(','))
    timestamp = parts.get('t', '')
    received = parts.get('v1', '')
    payload = f"{timestamp}.{body.decode('utf-8')}".encode()
    expected = hmac.new(secret.encode(), payload, hashlib.sha256).hexdigest()
    return hmac.compare_digest(received, expected)

# Flask 예시
@app.route('/webhook', methods=['POST'])
def webhook():
    sig = request.headers.get('X-ADHDev-Signature', '')
    if not verify_signature(request.data, sig, WEBHOOK_SECRET):
        return 'Invalid signature', 401
    event = request.get_json()
    # 이벤트 처리...
    return '', 200

웹훅 시크릿은 웹훅 생성 시 한 번만 표시됩니다 — 안전하게 저장하세요.

관리 엔드포인트 ​

text
GET    /api/v1/webhooks
POST   /api/v1/webhooks
PATCH  /api/v1/webhooks/{webhookId}
DELETE /api/v1/webhooks/{webhookId}
GET    /api/v1/webhooks/{webhookId}/deliveries?limit=20
POST   /api/v1/webhooks/{webhookId}/test

GET 엔드포인트가 가장 안전한 자동화 서피스입니다. 변경 엔드포인트(POST, PATCH, DELETE, test)는 최근 로그인이 보호하는 대시보드 세션 작업입니다.

재시도 정책 ​

실패한 전달은 지수 백오프로 최대 3회 재시도됩니다.

플랜 제한 ​

플랜최대 웹훅 수
Free2
Pro10
Ultra50
Enterprise무제한

호스팅 클라우드 문서는 여기에 있습니다. 오픈소스 및 셀프호스트 문서는 OSS 레포지토리에 있습니다.