Skip to content

Comments

feat(internal): IR-side support for automatic webhook signature verification#12567

Open
patrickthornton wants to merge 4 commits intomainfrom
patrick/ir/webhook-verification
Open

feat(internal): IR-side support for automatic webhook signature verification#12567
patrickthornton wants to merge 4 commits intomainfrom
patrick/ir/webhook-verification

Conversation

@patrickthornton
Copy link
Contributor

@patrickthornton patrickthornton commented Feb 19, 2026

Description

Adds IR support for automatic webhook signature verification through an optional parameter:

# packages/ir-sdk/fern/apis/ir-types-latest/definition/webhooks.yml

  Webhook:
    extends: commons.Declaration
    properties:
      id: commons.WebhookId
      name: WebhookName
      displayName: optional<string>
      method: WebhookHttpMethod
      headers: list<http.HttpHeader>
      payload: WebhookPayload
      fileUploadPayload: optional<http.FileUploadRequest>
      signatureVerification:                          # <-- new field
        type: optional<WebhookSignatureVerification>
        docs: |
          Optional configuration for webhook signature verification.
          When present, generators can produce utilities to verify that
          incoming webhook requests originate from the API provider.
      responses: optional<list<http.HttpResponse>>
      examples: optional<list<ExampleWebhookCall>>
      v2Examples: optional<examples.V2WebhookExamples>

  And similarly on Webhook in the OpenAPI IR
  (packages/cli/api-importers/openapi/openapi-ir/fern/definition/finalIr.yml):

  Webhook:
    properties:
      # ... existing fields ...
      signatureVerification: optional<WebhookSignatureVerification>
      # ...

  And on WebhookWithExample in the OpenAPI parse IR (parseIr.yml):

  WebhookWithExample:
    properties:
      # ... existing fields ...
      signatureVerification: optional<finalIr.WebhookSignatureVerification>
      # ...

With the following schema:

# IR Schema (packages/ir-sdk/fern/apis/ir-types-latest/definition/webhooks.yml)

  WebhookSignatureVerification:
    docs: |
      Configuration for verifying webhook signatures. When present, SDK generators
      can produce utilities that verify the HMAC signature of incoming webhook requests.
    properties:
      signatureHeaderName:
        type: commons.NameAndWireValue
        docs: |
          The HTTP header that contains the webhook signature
          (e.g. x-webhook-signature, x-hub-signature-256).
      algorithm: WebhookSignatureAlgorithm
      encoding: WebhookSignatureEncoding
      payloadFormat: WebhookPayloadFormat

  WebhookSignatureAlgorithm:
    docs: The HMAC algorithm used to compute the webhook signature.
    enum:
      - SHA256
      - SHA1
      - SHA384
      - SHA512

  WebhookSignatureEncoding:
    docs: The encoding of the computed HMAC signature.
    enum:
      - BASE64
      - HEX

  WebhookPayloadFormat:
    docs: How the signed payload is constructed from the webhook request.
    union:
      bodyOnly:
        type: WebhookPayloadFormatBodyOnly
        docs: The raw request body is signed directly.
      urlPrefixed:
        type: WebhookPayloadFormatUrlPrefixed
        docs: |
          The notification URL is prepended to the request body before signing.
          Used by APIs like Square where payload = notificationUrl + requestBody.

  WebhookPayloadFormatBodyOnly:
    properties: {}

  WebhookPayloadFormatUrlPrefixed:
    docs: |
      The notification URL is prepended to the request body before computing
      the HMAC signature.
    properties: {}

# Fern Definition Schema (fern/apis/fern-definition/definition/webhooks.yml)

  WebhookSignatureSchema:
    properties:
      header: string
      algorithm:
        type: optional<WebhookSignatureAlgorithmSchema>
        docs: Defaults to sha256.
      encoding:
        type: optional<WebhookSignatureEncodingSchema>
        docs: Defaults to base64.
      payload-format:
        type: optional<WebhookPayloadFormatSchema>
        docs: Defaults to body-only.

  WebhookSignatureAlgorithmSchema:
    enum:
      - sha256
      - sha1
      - sha384
      - sha512

  WebhookSignatureEncodingSchema:
    enum:
      - base64
      - hex

  WebhookPayloadFormatSchema:
    enum:
      - name: bodyOnly
        value: body-only
      - name: urlPrefixed
        value: url-prefixed

# OpenAPI IR (packages/cli/api-importers/openapi/openapi-ir/fern/definition/finalIr.yml)

  WebhookSignatureVerification:
    docs: |
      Configuration for verifying webhook signatures, populated from x-fern-webhook-signature.
    properties:
      header: string
      algorithm: optional<WebhookSignatureAlgorithm>
      encoding: optional<WebhookSignatureEncoding>
      payloadFormat: optional<WebhookPayloadFormat>

  WebhookSignatureAlgorithm:
    enum:
      - sha256
      - sha1
      - sha384
      - sha512

  WebhookSignatureEncoding:
    enum:
      - base64
      - hex

  WebhookPayloadFormat:
    enum:
      - name: bodyOnly
        value: body-only
      - name: urlPrefixed
        value: url-prefixed

Changes Made

To the OpenAPI-IR, the Fern Definition, and the IR.

Testing

Added to the IR test fixture webhooks to test that this passes through correctly.

  • Unit tests added/updated
  • Manual testing completed

@patrickthornton patrickthornton changed the title great big webhook schema addition feat(internal): great big webhook schema addition Feb 19, 2026
@github-actions
Copy link
Contributor

🌱 Seed Test Selector

Select languages to run seed tests for:

  • Python
  • TypeScript
  • Java
  • Go
  • Ruby
  • C#
  • PHP
  • Swift
  • Rust
  • OpenAPI
  • Postman

How to use: Click the ⋯ menu above → "Edit" → check the boxes you want → click "Update comment". Tests will run automatically and snapshots will be committed to this PR.

Comment on lines 137 to 145
signatureVerification:
signatureExtension != null
? {
header: signatureExtension.header,
algorithm: signatureExtension.algorithm,
encoding: signatureExtension.encoding,
payloadFormat: signatureExtension["payload-format"]
}
: undefined,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing default values for optional signature fields when converting from OpenAPI. This creates an inconsistency with the Fern definition converter which applies defaults (sha256, base64, body-only). When these fields are undefined from OpenAPI sources, downstream generators may fail or behave differently than when using Fern definitions.

signatureVerification:
    signatureExtension != null
        ? {
              header: signatureExtension.header,
              algorithm: signatureExtension.algorithm ?? "sha256",
              encoding: signatureExtension.encoding ?? "base64",
              payloadFormat: signatureExtension["payload-format"] ?? "body-only"
          }
        : undefined,

Spotted by Graphite Agent

Fix in Graphite


Is this helpful? React 👍 or 👎 to let us know.

@patrickthornton patrickthornton changed the title feat(internal): great big webhook schema addition feat(internal): IR-side support for automatic webhook signature verification Feb 19, 2026
@patrickthornton patrickthornton force-pushed the patrick/ir/webhook-verification branch from 24bcf35 to 486a207 Compare February 20, 2026 16:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

1 participant