# Webhooks

Webhooks는 MLOps 관련 기능의 기반입니다. 특정 저장소 또는 특정 사용자/조직에 속한 모든 저장소(본인의 저장소뿐만 아니라 어떤 저장소든)의 새로운 변경 사항을 수신할 수 있습니다.

모델의 자동 변환, 커뮤니티 봇 구축, 모델/데이터셋/Spaces/버킷에 대한 CI/CD 구축 등에 사용할 수 있습니다(그 외 다양한 용도). Webhooks는 리포지토리 이벤트에 반응하여 계산 작업을 자동화하기 위해 [Jobs를 트리거](./jobs-webhooks)할 수도 있습니다.

Webhooks에 대한 문서가 아래에 있습니다. 또는 Webhooks의 몇 가지 가능한 사용 사례를 보여주는 **가이드**를 둘러보실 수도 있습니다:
- [데이터셋이 업데이트될 때마다 새 모델을 파인튜닝합니다 (Python)](./webhooks-guide-auto-retrain)
- [LLM API를 사용하여 Hub에 토론 봇 만들기 (NodeJS)](./webhooks-guide-discussion-bot)
- [메타데이터 품질 보고서 생성 (Python)](./webhooks-guide-metadata-review)
- [Jobs(CLI)를 사용하여 버킷의 새 파일 처리](./webhooks-guide-bucket-jobs)
- 그리고 더 많은 것이 추가될 예정입니다…

## Webhook 생성

Webhooks [설정](https://huggingface.co/settings/webhooks)에서 새 Webhook을 생성하고 기존 Webhook을 편집할 수 있습니다:

![Settings of an individual webhook](https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/hub/webhook-settings.png)

Webhooks는 저장소 업데이트, Pull Requests, 토론, 새 댓글을 감시할 수 있습니다. Webhooks에 반응하는 Space를 만드는 것도 가능합니다!

## Webhook 페이로드

Webhook을 등록하면 지정된 대상 URL에 `HTTP POST` 호출을 통해 새 이벤트에 대한 알림을 받게 됩니다. 페이로드는 JSON으로 인코딩됩니다.

webhook 설정 페이지의 활동 탭에서 전송된 페이로드의 이력을 볼 수 있으며, 디버깅을 쉽게 하기 위해 과거의 webhook을 다시 실행할 수도 있습니다:

![image.png](https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/hub/webhook-activity.png)

예를 들어, Pull Request가 열릴 때의 전체 페이로드는 다음과 같습니다:

```json
{
  "event": {
    "action": "create",
    "scope": "discussion"
  },
  "repo": {
    "type": "model",
    "name": "openai-community/gpt2",
    "id": "621ffdc036468d709f17434d",
    "private": false,
    "url": {
      "web": "https://huggingface.co/openai-community/gpt2",
      "api": "https://huggingface.co/api/models/openai-community/gpt2"
    },
    "owner": {
      "id": "628b753283ef59b5be89e937"
    }
  },
  "discussion": {
    "id": "6399f58518721fdd27fc9ca9",
    "title": "Update co2 emissions",
    "url": {
      "web": "https://huggingface.co/openai-community/gpt2/discussions/19",
      "api": "https://huggingface.co/api/models/openai-community/gpt2/discussions/19"
    },
    "status": "open",
    "author": {
      "id": "61d2f90c3c2083e1c08af22d"
    },
    "num": 19,
    "isPullRequest": true,
    "changes": {
      "base": "refs/heads/main"
    }
  },
  "comment": {
    "id": "6399f58518721fdd27fc9caa",
    "author": {
      "id": "61d2f90c3c2083e1c08af22d"
    },
    "content": "Add co2 emissions information to the model card",
    "hidden": false,
    // Note: when `hidden` is `true`, `content` will be undefined
    "url": {
      "web": "https://huggingface.co/openai-community/gpt2/discussions/19#6399f58518721fdd27fc9caa"
    }
  },
  "webhook": {
    "id": "6390e855e30d9209411de93b",
    "version": 3
  }
}
```

### 이벤트

최상위 속성 `event`은 항상 지정되며 이벤트의 성격을 결정하는 데 사용됩니다.

두 개의 하위 속성이 있습니다: `event.action`와 `event.scope`입니다.

`event.scope`는 다음 값 중 하나가 됩니다:

- `"repo"` - 저장소 전역 이벤트. 관련 `action`의 가능한 값: `"create"`, `"delete"`, `"update"`, `"move"`.
- `"repo.content"` - 저장소의 콘텐츠에 대한 이벤트로, 새로운 커밋이나 태그 등이 해당됩니다. 새로 생성된 참조/커밋으로 인해 새로운 Pull Request에서도 트리거됩니다. Buckets의 경우 Git refs가 아니라 파일이 추가되거나 삭제될 때 트리거됩니다. 관련 `action`은 항상 `"update"`입니다.
- `"repo.config"` - config에 대한 이벤트: Space secrets 업데이트, 설정 업데이트, DOI 업데이트, 비활성화 여부 등. 관련 `action`은 항상 `"update"`입니다.
- `"discussion"` - 토론 또는 Pull Request 생성, 제목 또는 상태 업데이트, 병합. 관련 `action`의 가능한 값: `"create"`, `"delete"`, `"update"`.
- `"discussion.comment"` - 댓글 생성, 업데이트 및 숨김. 관련 `action`의 가능한 값: `"create"`, `"update"`.

향후 더 많은 스코프가 추가될 수 있습니다. 알려지지 않은 이벤트를 처리하려면, 웹훅 핸들러는 좁혀진 스코프에 대한 모든 작업을 더 넓은 스코프에 대한 `"update"` 작업으로 간주할 수 있습니다.

예를 들어, 향후 `"repo.config.dois"` 스코프가 추가되면, 해당 스코프를 가진 모든 이벤트는 웹훅 핸들러에서 `"repo.config"` 스코프에 대한 `"update"` 액션으로 간주될 수 있습니다.

### 리포지토리

webhooks의 현재 버전에서는 이벤트가 항상 저장소와 연관될 수 있으므로 최상위 속성 `repo`는 항상 지정됩니다. 예를 들어, 다음 값을 고려해 보겠습니다:

```json
"repo": {
	"type": "model",
	"name": "some-user/some-repo",
	"id": "6366c000a2abcdf2fd69a080",
	"private": false,
	"url": {
		"web": "https://huggingface.co/some-user/some-repo",
		"api": "https://huggingface.co/api/models/some-user/some-repo"
	},
	"headSha": "c379e821c9c95d613899e8c4343e4bfee2b0c600",
	"owner": {
		"id": "61d2000c3c2083e1c08af22d"
	}
}
```

`repo.headSha`는 저장소의 `main` 브랜치에서 최신 커밋의 sha입니다. `event.scope`가 `"repo"`로 시작하는 경우에만 전송되며, 토론이나 댓글과 같은 커뮤니티 이벤트나 Git 히스토리가 없는 버킷에는 전송되지 않습니다.

### 코드 변경 사항

코드 변경 시, 리포지토리 이벤트에는 최상위 속성 `updatedRefs`가 지정됩니다. 이는 업데이트된 참조(reference)들의 배열입니다. 예시 값은 다음과 같습니다:

```json
"updatedRefs": [
  {
    "ref": "refs/heads/main",
    "oldSha": "ce9a4674fa833a68d5a73ec355f0ea95eedd60b7",
    "newSha": "575db8b7a51b6f85eb06eee540738584589f131c"
  },
  {
    "ref": "refs/tags/test",
    "oldSha": null,
    "newSha": "575db8b7a51b6f85eb06eee540738584589f131c"
  }
]
```

새로 생성된 참조는 `oldSha`가 `null`로 설정됩니다. 삭제된 참조는 `newSha`가 `null`로 설정됩니다.

특정 pull request, 새 태그, 또는 새 브랜치에 대한 새 커밋에 반응할 수 있습니다.

### 버킷

[Buckets](./storage-buckets)은 Git 저장소가 아닙니다. 커밋, 브랜치, 태그가 없습니다. 라이프사이클 이벤트(`create`, `delete`, `move` 및 가시성 설정 업데이트 등)는 다른 저장소 유형과 동일한 `"repo"` / `"repo.config"` 스코프를 사용합니다.

파일 변경은 `"repo.content"`를 사용하지만, 페이로드에는 `updatedRefs` 대신 `updatedFiles` 속성이 있습니다. 기존 파일을 덮어쓰는 경우 `"add"`로 보고됩니다. 파일 하나가 추가되고 다른 파일 하나가 삭제된 후의 페이로드 예시는 다음과 같습니다:

```json
{
  "event": {
    "action": "update",
    "scope": "repo.content"
  },
  "repo": {
    "type": "bucket",
    "name": "some-user/some-bucket",
    "id": "6366c000a2abcdf2fd69a080",
    "private": false,
    "url": {
      "web": "https://huggingface.co/buckets/some-user/some-bucket",
      "api": "https://huggingface.co/api/buckets/some-user/some-bucket"
    },
    "owner": {
      "id": "61d2000c3c2083e1c08af22d"
    }
  },
  "updatedFiles": [
    {
      "path": "data/train.txt",
      "action": "add",
      "xetHash": "55faef2f2f80cd1a087c35b729f228960739441d38073cd5aa4320751e137166",
      "size": 20
    },
    {
      "path": "data/old.txt",
      "action": "delete"
    },
    ...
  ],
  "updatedFilesTruncated": true,
  "webhook": {
    "id": "6390e855e30d9209411de93b",
    "version": 3
  }
}
```

10,000개 항목을 초과하면 목록이 잘려 나가며 `updatedFilesTruncated`가 `true`로 설정됩니다. 이 경우 전체 내용을 확인하려면 버킷을 나열하세요.

Buckets에는 토론이나 Pull Request가 없으므로, 해당 이벤트에 대해 `"discussion"` 및 `"discussion.comment"` 이벤트를 절대 수신하지 않습니다.

클라이언트가 자체 서버로의 콜백을 통해 간접적으로 추적하는 대신 버킷의 파일 변경 사항을 직접 추적하려면, 버킷은 [live follow stream](./storage-buckets#live-follow)도 제공합니다.

### 설정 변경

최상위 속성 `event.scope`가 `"repo.config"`인 경우, `updatedConfig` 속성이 지정됩니다. 이 속성은 업데이트된 config를 포함하는 객체입니다. 값의 예시는 다음과 같습니다:

```json
"updatedConfig": {
  "private": false
}
```

업데이트된 config 키가 webhook에 의해 지원되지 않으면 객체가 비어 있습니다:

```json
"updatedConfig": {}
```

현재 `private`만 지원됩니다. 여기에 더 많은 config 키가 존재하면 도움이 된다면, website@huggingface.co로 알려 주시기 바랍니다.

### 토론 및 Pull Requests

최상위 속성 `discussion`는 커뮤니티 이벤트(토론 및 Pull Request)에 지정됩니다. `discussion.isPullRequest` 속성은 해당 토론이 Pull Request이기도 한지를 나타내는 boolean 값입니다(Hub에서 PR은 토론의 특수한 유형입니다). 아래는 예시 값입니다:

```json
"discussion": {
	"id": "639885d811ae2bad2b7ba461",
	"title": "Hello!",
	"url": {
		"web": "https://huggingface.co/some-user/some-repo/discussions/3",
		"api": "https://huggingface.co/api/models/some-user/some-repo/discussions/3"
	},
	"status": "open",
	"author": {
		"id": "61d2000c3c2083e1c08af22d"
	},
	"isPullRequest": true,
	"changes": {
		"base": "refs/heads/main"
	}
	"num": 3
}
```

### 댓글

최상위 속성 `comment`은 댓글이 생성될 때(토론 생성 포함) 또는 업데이트될 때 지정됩니다. 다음은 예시 값입니다:

```json
"comment": {
	"id": "6398872887bfcfb93a306f18",
	"author": {
		"id": "61d2000c3c2083e1c08af22d"
	},
	"content": "This adds an env key",
	"hidden": false,
	"url": {
		"web": "https://huggingface.co/some-user/some-repo/discussions/4#6398872887bfcfb93a306f18"
	}
}
```

## Webhook secret

Webhook 시크릿을 설정하면 Webhook 핸들러 URL로 전송되는 페이로드가 실제로 Hugging Face에서 온 것인지 확인할 수 있어 유용합니다.

Webhook에 시크릿을 설정하면 모든 요청에 `X-Webhook-Secret` HTTP 헤더로 함께 전송됩니다. ASCII 문자만 지원됩니다.

> [!TIP]
> 핸들러 URL에 시크릿을 직접 추가하는 것도 가능합니다. 예를 들어, 쿼리 파라미터로 설정하는 경우: https://example.com/webhook?secret=XXX.
>
> Webhook 핸들러에서 요청의 HTTP 헤더에 액세스하는 것이 복잡한 경우 이 기능이 유용할 수 있습니다.

## 전달 및 재시도

Webhook 페이로드가 Hub에서 이벤트가 발생한 직후 비동기적으로 전달됩니다. 순서가 보장되지 않습니다: 여러 이벤트가 짧은 시간 간격으로 발생하면 순서대로 도착하지 않을 수 있습니다.

핸들러는 `2xx` 상태 코드로 전달을 확인해야 합니다. 다른 상태 코드는 연결 오류와 마찬가지로 전달 실패로 간주되며, 백오프를 적용하여 재시도됩니다. 처리가 느린 경우, 전달이 실패로 간주되지 않도록 즉시 `2xx`로 응답하고 작업을 비동기적으로 수행하세요.

각 전달에는 고유한 `Webhook-Id` HTTP 헤더가 있습니다. 실패한 전달의 재시도는 동일한 ID를 재사용하므로, 이를 멱등성 키로 취급하여 각 이벤트를 한 번만 처리할 수 있습니다.

Webhook으로의 전송이 계속 실패하면 Webhook이 자동으로 중단되고 소유자에게 이메일로 알림이 전송됩니다. [설정](https://huggingface.co/settings/webhooks)에서 문제를 해결하고 다시 활성화할 수 있습니다.

## 요청 제한

각 Webhook은 24시간당 1,000회 트리거로 제한됩니다. 사용량은 Webhook 설정 페이지의 "Activity" 탭에서 확인할 수 있습니다.

Webhook의 트리거 수를 늘려야 하는 경우, PRO, Team 또는 Enterprise로 업그레이드한 후 website@huggingface.co로 문의해 주세요.

## Webhook 개발하기

HTTPS 엔드포인트/URL이 없는 경우, 웹훅 테스트를 위한 공개 도구를 사용할 수 있습니다. 이러한 도구는 수신된 모든 요청을 캡처하는 catch-all 역할을 하며 200 OK 상태 코드를 반환합니다. [Beeceptor](https://beeceptor.com/)는 임시 HTTP 엔드포인트를 생성하고 수신된 페이로드를 검토하는 데 사용할 수 있는 도구 중 하나입니다. 또 다른 유사한 도구는 [Webhook.site](https://webhook.site/)입니다.

또한, 개발 중에는 실제 Webhook 페이로드를 로컬 머신에서 실행 중인 코드로 라우팅할 수 있습니다. 이는 더 빠른 통합을 위해 테스트 및 디버깅을 수행하는 좋은 방법입니다. 이를 위해 로컬호스트 포트를 인터넷에 노출하면 됩니다. 이 경로를 사용하려면 [ngrok](https://ngrok.com/) 또는 [localtunnel](https://theboroer.github.io/localtunnel-www/)을 사용할 수 있습니다.

## Webhook 디버깅

웹훅에서 최근 생성된 이벤트를 쉽게 찾을 수 있습니다. 웹훅의 activity 탭을 열면 최근 이벤트 목록을 볼 수 있습니다.

![image.png](https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/hub/webhook-payload.png)
 
여기에서 생성된 이벤트의 HTTP 상태 코드와 페이로드를 검토할 수 있습니다. 또한, `Replay` 버튼을 클릭하여 이러한 이벤트를 다시 재생할 수 있습니다!

Note: Webhook의 대상 URL 또는 secret을 변경한 경우, 이벤트를 재생하면 payload가 업데이트된 URL로 전송됩니다.

참고: 재전송된 이벤트는 원래 전달과 동일한 `Webhook-Id`로 전송됩니다.

## 자주 묻는 질문

##### 조직 계정과 사용자 계정 중 어디에 웹훅을 정의할 수 있나요?

아니요, 현재는 지원되지 않습니다.

##### HF(또는 모든 모델과 같은 전체 저장소 유형)의 모든 이벤트에 구독하는 방법은 무엇인가요?

현재 최종 사용자에게 노출되지 않지만, website@huggingface.co로 이메일을 보내주시면 해당 설정을 변경해 드릴 수 있습니다.

