Skip to content

emailOTP plugin: missing requestBody in OpenAPI spec for all endpoints #9298

@albertorizzi

Description

@albertorizzi

Is this suited for github?

  • Yes, this is suited for github

Reproduction

Description

The emailOTP plugin generates OpenAPI metadata (responses, operationId, description)
for all its endpoints, but none of them include a requestBody definition.

The body schemas are defined internally in the plugin (the Zod schemas exist and are used for
validation), but they are not serialized into the OpenAPI spec output.

Affected endpoints

All endpoints in the emailOTP plugin, including:

  • POST /api/auth/email-otp/send-verification-otp
  • POST /api/auth/sign-in/email-otp
  • POST /api/auth/email-otp/verify-email
  • POST /api/auth/email-otp/check-verification-otp
  • POST /api/auth/email-otp/reset-password
  • (and others)

Expected behavior

Each endpoint's OpenAPI spec should include a requestBody with the correct schema.

For example, POST /api/auth/sign-in/email-otp should produce:

"requestBody": {
  "required": true,
  "content": {
    "application/json": {
      "schema": {
        "type": "object",
        "required": ["email", "otp"],
        "properties": {
          "email": { "type": "string" },
          "otp": { "type": "string" },
          "name": { "type": "string" },
          "image": { "type": "string" }
        }
      }
    }
  }
}


### Current vs. Expected behavior

Actual behavior
The requestBody field is absent. The /api/auth/reference Scalar UI shows the endpoint with no body schema, making it impossible to test directly from the docs.

Root cause (observed)
Inspecting the plugin at runtime confirms the body Zod schemas exist and are used for validation, but the openapi metadata object does not include them:


const { emailOTP } = require('better-auth/plugins');
const plugin = emailOTP({ sendVerificationOTP: async () => {} });

// Body schema exists:
console.log(plugin.endpoints.signInEmailOTP.options.body);
// → ZodIntersection { email, otp, name?, image?, ...Record<string, any> }

// OpenAPI metadata has no requestBody:
console.log(plugin.endpoints.signInEmailOTP.options.metadata.openapi);
// → { operationId: 'signInWithEmailOTP', description: '...', responses: { ... } }
//   no requestBody field
The openapi metadata object is manually authored per-endpoint and does not derive requestBody from the Zod body schema automatically. This is consistent across all emailOTP endpoints.

### What version of Better Auth are you using?

1.6.4

### System info

```bash
{
  "system": {
    "platform": "darwin",
    "arch": "arm64",
    "version": "Darwin Kernel Version 25.4.0: Thu Mar 19 19:30:44 PDT 2026; root:xnu-12377.101.15~1/RELEASE_ARM64_T6000",
    "release": "25.4.0",
    "cpuCount": 8,
    "cpuModel": "Apple M1 Pro",
    "totalMemory": "16.00 GB",
    "freeMemory": "0.12 GB"
  },
  "node": {
    "version": "v22.20.0",
    "env": "development"
  },
  "packageManager": {
    "name": "npm",
    "version": "10.9.3"
  },
  "frameworks": [
    {
      "name": "fastify",
      "version": "^5.8.5"
    }
  ],
  "databases": [
    {
      "name": "pg",
      "version": "^8.20.0"
    },
    {
      "name": "@prisma/client",
      "version": "^7.7.0"
    }
  ],
  "betterAuth": {
    "version": "^1.6.4",
    "config": null
  }
}

Which area(s) are affected? (Select all that apply)

Backend

Auth config (if applicable)

import { betterAuth } from "better-auth"
export const auth = betterAuth({
  emailAndPassword: {  
    enabled: true
  },
});

Additional context

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    devtoolsCLI, OpenAPI, telemetry, i18n, test-utils

    Type

    No fields configured for Bug.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions