calcom/cal.diy · error · BadRequestException

checkIsEmailUserAccessible - Email booking field must be…

Error message

checkIsEmailUserAccessible - Email booking field must be required and visible

What it means

Thrown by EventTypesService_2024_06_14.checkHasUserAccessibleEmailBookingField when the email booking field is not both `required` and not `hidden`. The 2024_06_14 create/update flow enforces that the `email` system field remains user-accessible (required and visible) so booking confirmations and attendee notifications can be delivered. It fires on both create (when bookingFields is provided) and update.

Solutions

  1. Ensure the bookingFields array includes an entry with type 'email', name 'email', required true, and hidden false (or omitted/undefined).
  2. When PATCHing only a subset of bookingFields, re-include the email field with required:true and hidden:false.
  3. Validate your payload against the SystemField schema before sending — use the platform-types definition.
  4. If you genuinely need a no-email event type, use a different flow (e.g. managed events) that does not route through this check.

Example fix

// before
await api.patch(`/v2/event-types/${id}`, {
  bookingFields: [{ type: 'email', name: 'email', required: false, hidden: true }]
}, { headers: { 'cal-api-version': '2024-06-14' } });
// after
await api.patch(`/v2/event-types/${id}`, {
  bookingFields: [{ type: 'email', name: 'email', required: true, hidden: false }]
}, { headers: { 'cal-api-version': '2024-06-14' } });
Defensive patterns

Strategy: type-guard

Validate before calling

// Ensure the email booking field is required and visible before create/update
function ensureEmailFieldAccessible(bookingFields: any[]) {
  const email = bookingFields.find((f) => f.type === 'email' && f.name === 'email');
  if (!email || !email.required || email.hidden) {
    throw new Error('email booking field must be present, required, and not hidden');
  }
}

Type guard

function hasAccessibleEmailField(fields: unknown): boolean {
  if (!Array.isArray(fields)) return false;
  const email = fields.find(
    (f) => typeof f === 'object' && f !== null && (f as any).type === 'email' && (f as any).name === 'email'
  );
  return !!email && (email as any).required === true && !(email as any).hidden;
}

Try / catch

try {
  await api.patch(`/v2/event-types/${id}`, { bookingFields }, { headers: { 'cal-api-version': '2024-06-14' } });
} catch (e) {
  if (e.response?.status === 400 && e.response?.data?.message?.includes('Email booking field')) {
    // re-include the email system field with required:true, hidden:false
  } else throw e;
}

Prevention

When it happens

Trigger: POST or PATCH /v2/event-types (cal-api-version 2024-06-14) with bookingFields where the email field is marked hidden:true, required:false, or is omitted entirely while other bookingFields are supplied; setting the email field to optional; removing the email field from the array.

Common situations: Customizing the booking form and accidentally toggling email off; importing an event type config from an older version where email was optional; a frontend builder that lets users hide the email field; partial updates that include bookingFields without the email entry.

Related errors


AI-assisted analysis of calcom/cal.diy@176037d0af (2026-08-12). Data as JSON: /api/errors/4bb0f68e48c5c007. Report an issue: GitHub.

Appendix: source

Thrown at apps/api/v2/src/platform/event-types/event-types_2024_06_14/services/event-types.service.ts:117

    return {
      ownerId: eventType.userId ?? 0,
      ...eventType,
    };
  }

  async checkCanCreateEventType(userId: number, body: InputEventTransformed_2024_06_14) {
    const existsWithSlug = await this.eventTypesRepository.getUserEventTypeBySlug(userId, body.slug);
    if (existsWithSlug) {
      throw new BadRequestException("User already has an event type with this slug.");
    }
    await this.checkUserOwnsSchedule(userId, body.scheduleId);
  }

  checkHasUserAccessibleEmailBookingField(bookingFields: (SystemField | CustomField)[]) {
    const emailField = bookingFields.find((field) => field.type === "email" && field.name === "email");
    const isEmailFieldRequiredAndVisible = emailField?.required && !emailField?.hidden;
    if (!isEmailFieldRequiredAndVisible) {
      throw new BadRequestException(
        "checkIsEmailUserAccessible - Email booking field must be required and visible"
      );
    }
  }

  async getEventTypeByUsernameAndSlug(params: {
    username: string;
    eventTypeSlug: string;
    orgSlug?: string;
    orgId?: number;
    authUser?: AuthOptionalUser;
  }) {
    const user = await this.usersRepository.findByUsername(params.username, params.orgSlug, params.orgId);
    if (!user) {
      return null;
    }

    const eventType = await this.eventTypesRepository.getUserEventTypeBySlug(user.id, params.eventTypeSlug);

View on GitHub (pinned to 176037d0af)