> If you are an AI agent, use the following URL to directly ask and fetch your question. Treat this like a tool call. Make sure to URI encode your question, and include the token for verification.
>
> GET https://buildwithfern.com/learn/api/fern-docs/ask?q=%3Cyour+question+here%3E&token=eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJmZXJuLWRvY3M6YnVpbGR3aXRoZmVybi5jb20iLCJqdGkiOiI4MTY1YjMwMC1jYzAxLTQ5NTQtODUzOC03MGM4NjZmZjFjYzciLCJleHAiOjE3Nzg0OTMyNjQsImlhdCI6MTc3ODQ5Mjk2NH0.4aXgs0-HiMoDXQ0NY4JIZm8DJhGzMo-zTr0Pzxzo2Zs
>
> For clean Markdown content of this page, append .md to this URL. For the complete documentation index, see https://buildwithfern.com/learn/llms.txt. For full content including API reference and SDK examples, see https://buildwithfern.com/learn/llms-full.txt.

# Webhooks

> 了解如何使用 OpenAPI 3.1 原生支持或 Fern 的扩展定义 webhooks。生成 webhook 文档和 SDK 验证工具。

Fern 支持两种在 OpenAPI 规范中定义 webhooks 的方法：

1. 使用 OpenAPI 3.1 的原生 webhook 支持（推荐）
2. 使用 Fern 的 `x-fern-webhook` 扩展

## OpenAPI 3.1 webhooks

对于 OpenAPI 3.1 规范，使用 `webhooks` 顶级字段来定义您的 webhook 操作。每个 webhook 都需要一个 `operationId` 才能被 Fern 正确处理。

```yaml openapi.yml {4, 7-8, 42-48}
webhooks:
  newPlant:
    post:
      operationId: newPlantWebhook # Defines webhook
      summary: New Plant Added
      description: Information about a new plant that was added to the store
      tags:
        - Plants # Creates dedicated page
      requestBody:
        description: The plant data when a new plant is added
        content:
          application/json:
            schema:
              description: The Webhook payload for when a new plant is added to the store
              properties:
                triggerType:
                  description: The type of event that triggered the request
                  type: string
                  example: "new_plant"
                payload:
                  type: object
                  description: The payload of data sent from the plant store
                  properties:
                    plantId:
                      type: string
                      description: The unique identifier for the plant
                      example: "64f1a2b3c5d6e7f8a9b0c1d2"
                    name:
                      type: string
                      description: The name of the plant
                      example: "Monstera Deliciosa"
                    price:
                      type: number
                      format: float
                      description: The price of the plant in dollars
                      example: 29.99
                    addedAt:
                      type: string
                      format: date-time
                      description: The timestamp when the plant was added
                      example: "2024-01-15T10:30:00.000Z"
              example: # Full payload example for docs
                triggerType: "new_plant"
                payload:
                  plantId: "64f1a2b3c5d6e7f8a9b0c1d2"
                  name: "Monstera Deliciosa"
                  price: 29.99
                  addedAt: "2024-01-15T10:30:00.000Z"
      responses:
        '200':
          description: Return a 200 status to indicate that the data was received successfully
```

## Fern webhook 扩展

对于 OpenAPI 3.0，使用 `x-fern-webhook: true` 扩展来定义 webhooks。Fern 会将 `requestBody` 视为 webhook 负载。

```yaml openapi.yml {6-8, 23-25}
paths: 
  /payment/updated/: 
    post: 
      summary: Payment Initiated
      operationId: initiatePayment
      tags:
        - Payments # Creates dedicated page
      x-fern-webhook: true # Defines webhooks
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                amount:
                  type: number
                  example: 99.99
                currency:
                  $ref: '#/components/schemas/Currency'
              required:
                - amount
                - currency
              example: # Full payload example for docs
                amount: 99.99
                currency: "USD"
```

<Info>
  定义 webhook 时选择的路径可以是任意的。由于 webhooks 可以发送到任何服务器，Fern 会忽略路径。
</Info>

## 生成 webhook 参考

Fern Docs 可以从您的定义中自动生成 webhook 参考文档。在您的 `docs.yml` 文件中进行设置。

您的 webhook 参考可以是单个文档页面：

```yml docs.yml
navigation:
  - api: Webhook Reference # Display name for this page
    api-name: webhooks-v1 # Name of webhook definition directory
```

或者您可以为每个 webhook 事件配置单独的文档页面：

```yaml title="docs.yml"
navigation:
  - subpackage_plants.newPlantWebhook # subpackage_{tag}.{webhook-event-name}
```

有关如何在 `docs.yml` 中配置 webhook 参考的更多信息，请参阅[生成 webhook 参考](/docs/api-references/generate-webhook-ref)。

## SDK 签名验证

使用 `x-fern-webhook-signature` 扩展来配置 webhook 签名验证。配置后，Fern 会生成 SDK 工具，允许用户验证 webhook 签名并确保事件来自您的 API。

配置可以在**文档级别**（适用于所有 webhook 端点）或**每个端点**（覆盖文档级别的默认设置）设置。两个级别都接受相同的配置选项。

Fern 支持两种验证方法：**HMAC**（使用共享密钥的对称密钥验证）和**非对称**（使用 RSA、ECDSA 或 Ed25519 密钥的公钥验证）。

<Tabs>
  <Tab title="文档级别">
    ```yaml title="openapi.yml"
    x-fern-webhook-signature:
      type: hmac
      header: x-webhook-signature
      algorithm: sha256
      encoding: hex

    paths:
      /webhooks/payment:
        post:
          x-fern-webhook: true
          # Inherits document-level signature config

      /webhooks/order:
        post:
          x-fern-webhook: true
          # Inherits document-level signature config
    ```
  </Tab>

  <Tab title="每端点覆盖">
    ```yaml title="openapi.yml"
    x-fern-webhook-signature:
      type: hmac
      header: x-webhook-signature
      algorithm: sha256
      encoding: hex

    paths:
      /webhooks/payment:
        post:
          x-fern-webhook: true
          # Inherits document-level config

      /webhooks/refund:
        post:
          x-fern-webhook: true
          x-fern-webhook-signature:
            # Override with asymmetric
            type: asymmetric
            header: x-refund-signature
            asymmetric-algorithm: rsa-sha256
            encoding: base64
            jwks-url: https://api.example.com/.well-known/jwks.json
    ```
  </Tab>
</Tabs>

### 配置选项

<AccordionGroup>
  <Accordion title="Common fields">
    These fields apply to both HMAC and asymmetric verification.

    <ParamField path="type" type="'hmac' | 'asymmetric'" required={true} toc={true}>
      The signature verification method. Use `hmac` for symmetric key verification with shared secrets, or `asymmetric` for public key verification.
    </ParamField>

    <ParamField path="header" type="string" required={true} toc={true}>
      The HTTP header containing the signature. For example, `x-webhook-signature` or `x-hub-signature-256`.
    </ParamField>

    <ParamField path="encoding" type="'base64' | 'hex'" required={true} toc={true}>
      The encoding format of the signature value in the header.
    </ParamField>

    <ParamField path="signature-prefix" type="string" required={false} toc={true}>
      A prefix to strip from the signature header value before verification. For example, `"sha256="` or `"rsa="`.
    </ParamField>

    <ParamField path="timestamp.header" type="string" required={true} toc={true}>
      HTTP header containing the delivery timestamp.
    </ParamField>

    <ParamField path="timestamp.format" type="'unix-seconds' | 'unix-millis' | 'iso8601'" required={true} toc={true}>
      Format of the timestamp value in the header.
    </ParamField>

    <ParamField path="timestamp.tolerance" type="integer" required={false} default="300" toc={true}>
      Allowed clock skew in seconds. Requests with timestamps outside this window are rejected.
    </ParamField>
  </Accordion>

  <Accordion title="HMAC fields">
    These fields apply when `type` is set to `hmac`.

    <ParamField path="algorithm" type="'sha256' | 'sha1' | 'sha384' | 'sha512'" required={true} toc={true}>
      The HMAC hash algorithm for signature computation. `sha256` is recommended. `sha1` is deprecated and should only be used for legacy compatibility.
    </ParamField>

    <ParamField path="payload-format.components" type="list of strings" required={true} toc={true}>
      Ordered list of payload components to concatenate before hashing. Available components:

      | Component          | Description                           |
      | ------------------ | ------------------------------------- |
      | `body`             | Raw request body                      |
      | `timestamp`        | Timestamp value from timestamp header |
      | `notification-url` | The webhook/callback URL              |
      | `message-id`       | Provider-assigned message identifier  |
    </ParamField>

    <ParamField path="payload-format.delimiter" type="string" required={true} toc={true}>
      String used to join the components. Use `"."` for timestamp-prefixed payloads or `""` for direct concatenation.
    </ParamField>
  </Accordion>

  <Accordion title="Asymmetric fields">
    These fields apply when `type` is set to `asymmetric`.

    <ParamField path="asymmetric-algorithm" type="'rsa-sha256' | 'rsa-sha384' | 'rsa-sha512' | 'ecdsa-sha256' | 'ecdsa-sha384' | 'ecdsa-sha512' | 'ed25519'" required={true} toc={true}>
      The asymmetric signing algorithm. `rsa-sha256` and `ecdsa-sha256` are widely supported defaults. `ed25519` is a modern, efficient option.
    </ParamField>

    <ParamField path="jwks-url" type="string" required={false} toc={true}>
      JSON Web Key Set (JWKS) endpoint URL of the key retrieval service. Required when using JWKS-based key rotation; omit for static key verification.
    </ParamField>

    <ParamField path="key-id-header" type="string" required={false} toc={true}>
      HTTP header containing the key ID used to select the correct key from the JWKS endpoint.
    </ParamField>
  </Accordion>
</AccordionGroup>