---
title: "Create a link type (personal, or a team link with `team`)"
method: POST
path: "/api/event-types"
tags: ["Booking links"]
---

# Create a link type (personal, or a team link with \`team\`)

`POST /api/event-types`

## Request

- Base URL: `https://whenmeet.me`
- URL: `https://whenmeet.me/api/event-types`
- Auth: one of:
  - HTTP bearer
  - API key in cookie `__Host-session`

With team set, invitees get an email with a Join button (/join/<token>); before they join, their free/busy is read only through YOUR calendar account when your org lets you see it. Every teammate is checked against their own working hours. Personal links may set weeklyHours and hoursTimezone together for link-specific availability; team links reject this override. teamHandle gives a team link its own address (whenmeet.me/@acme-sales); it is claimed before any invite goes out, and a taken handle undoes the create (409). Reminder emails go out 24 h and 1 h before by default (reminders).

## Request body

- EventTypeInput: Link type settings. Only title is needed to create one; everything else has a sensible default.
  - `title` string
  - `slug` string: Default: from the title (-2, -3 … appended if taken).
  - `description` string, nullable
  - `durationMin` integer
  - `conference` 'meet' | 'teams' | 'null', nullable: Default: meet for a Google calendar, teams for Microsoft.
  - `bufferBeforeMin` integer
  - `bufferAfterMin` integer
  - `minNoticeMin` integer
  - `maxDaysAhead` integer
  - `slotStepMin` 5 | 10 | 15 | 20 | 30 | 45 | 60 | 90 | 120
  - `dailyLimit` integer, nullable
  - `questions` BookingQuestion[]: Name and email are always collected.
    - `id` string, required
    - `label` string, required
    - `type` 'text' | 'textarea', required
    - `required` boolean, required
  - `weeklyHours` WeeklyHours: Working hours per weekday in the profile timezone, or in hoursTimezone for a personal link override: up to 6 ["HH:MM","HH:MM"] intervals per day ("24:00" allowed as an end). Missing days mean no hours; overlapping intervals merge. Profile default Mon–Fri 09:00–17:00.
    - `mon` array[]
      - string[]
    - `tue` array[]
      - string[]
    - `wed` array[]
      - string[]
    - `thu` array[]
      - string[]
    - `fri` array[]
      - string[]
    - `sat` array[]
      - string[]
    - `sun` array[]
      - string[]
  - `hoursTimezone` string, nullable: IANA timezone for weeklyHours. A non-null weeklyHours needs a valid non-null zone; clear both to follow the profile (invalid_hours_timezone on a mismatched pair).
  - `reminders` integer[]: Reminder emails to the invitee and guests, in minutes before the start (24 h and 1 h by default). Any 3 of the allowed values; duplicates collapse, stored sorted descending. [] turns reminders off; null resets to the default. Error: invalid_reminders.
  - `active` boolean
  - `team` TeamSettings: Turns a link into a TEAM link. mode "all" = every member must be free (e.g. a two-person interview panel); "any" = whoever is free, assigned in turn (round-robin). Members get an email invite and join with one calendar connect.
    - `mode` 'all' | 'any', required
    - `members` string[]: Colleagues to invite (you excluded).
    - `includeMe` boolean: You attend / can be assigned too. Default true.
  - `teamHandle` string, nullable: Team links only: the link’s own public address, whenmeet.me/@<teamHandle> (same rules as nicknames and the same namespace; a leading @ and upper case are forgiven). null or "" releases it; turning the link personal releases it too. Errors: invalid_team_handle (bad handle, or the link is / becomes personal), 409 handle_taken (held by any nickname — yours included — or another link; nothing changes), 400 too_many_team_handles (you already hold 5 team handles on other links; nothing changes).
  - `teamName` string, nullable: Shown with the team handle ("Acme Sales"); null → the link title. Stored only next to a team handle (ignored without one). Error: invalid_team_name.

## Response `200`

{ eventType }

- object
  - `eventType` EventType
    - `id` string
    - `slug` string
    - `title` string
    - `description` string, nullable
    - `durationMin` integer
    - `conference` 'meet' | 'teams' | 'null', nullable
    - `bufferBeforeMin` integer
    - `bufferAfterMin` integer
    - `minNoticeMin` integer
    - `maxDaysAhead` integer
    - `slotStepMin` integer
    - `dailyLimit` integer, nullable
    - `questions` BookingQuestion[]
      - `id` string, required
      - `label` string, required
      - `type` 'text' | 'textarea', required
      - `required` boolean, required
    - `weeklyHours` WeeklyHours: Working hours per weekday in the profile timezone, or in hoursTimezone for a personal link override: up to 6 ["HH:MM","HH:MM"] intervals per day ("24:00" allowed as an end). Missing days mean no hours; overlapping intervals merge. Profile default Mon–Fri 09:00–17:00.
      - `mon` array[]
        - string[]
      - `tue` array[]
        - string[]
      - `wed` array[]
        - string[]
      - `thu` array[]
        - string[]
      - `fri` array[]
        - string[]
      - `sat` array[]
        - string[]
      - `sun` array[]
        - string[]
    - `hoursTimezone` string, nullable: Timezone for the personal link’s weeklyHours override; null when following the profile.
    - `reminders` integer[]: Minutes before the start, sorted descending; [] = off.
    - `active` boolean
    - `position` integer
    - `url` string, uri: Public booking URL, e.g. https://whenmeet.me/@anna/intro
    - `team` object, nullable
      - `mode` 'all' | 'any'
      - `members` TeamMember[]
        - `email` string, email
        - `name` string, nullable
        - `status` 'pending' | 'active' | 'declined'
        - `isOwner` boolean
        - `visibility` 'self' | 'org' | 'none': self = joined, their own calendar is used; org = not joined yet but your calendar account can see their free/busy (same Workspace / Microsoft 365 org); none = not visible until they join (excluded from the check).
        - `hours` ParticipantHours: The working hours a team participant is checked against (owner view only — never public). Every teammate is checked against their OWN hours: own = their booking profile (also your own entry); default = joined but no profile yet: Mon–Fri 09:00–17:00 in their account timezone (UTC if unknown); owner = not joined yet (pending / declined): your hours and zone.
          - `weeklyHours` WeeklyHours, required: Working hours per weekday in the profile timezone, or in hoursTimezone for a personal link override: up to 6 ["HH:MM","HH:MM"] intervals per day ("24:00" allowed as an end). Missing days mean no hours; overlapping intervals merge. Profile default Mon–Fri 09:00–17:00.
            - `mon` array[]
              - …
            - `tue` array[]
              - …
            - `wed` array[]
              - …
            - `thu` array[]
              - …
            - `fri` array[]
              - …
            - `sat` array[]
              - …
            - `sun` array[]
              - …
          - `tz` string, required: IANA zone the hours are in.
          - `source` 'own' | 'default' | 'owner', required
      - `health` TeamHealth: Can this team link take bookings right now? The same strict calendar read the public page does (today in the link zone + 14 days), served from its 60 s cache. Inactive links are not read and report ok.
        - `ok` boolean, required
        - `reason` 'member_unreadable' | 'no_checkable_member' | 'null', nullable, required: member_unreadable = an "all" participant, or every "any" participant, can’t be read (an "any" link with only some unreadable stays ok); no_checkable_member = nobody can be checked, or you have no calendar connected.
        - `memberEmail` string, nullable, required: The first unreadable participant (member_unreadable only).
    - `teamHandle` string, nullable: The team link’s own handle (null for personal links and team links without one).
    - `teamName` string, nullable
    - `teamUrl` string, uri, nullable: https://whenmeet.me/@<teamHandle>, or null.

## Other responses

- `400`: no_profile | invalid_body | invalid_<field> (e.g. invalid_title, invalid_duration_min, invalid_questions, invalid_reminders, invalid_weekly_hours, invalid_hours_timezone, invalid_team, invalid_team_member, invalid_team_handle, invalid_team_name) | too_many_team_members | too_many_team_handles (at most 5 team handles per owner).
- `401`: unauthenticated (no session or token), or invalid_token (a Bearer token that is malformed, unknown or revoked — never a silent anonymous fallback).
- `409`: slug_taken (only for an explicit slug; derived slugs get -2, -3 …) | handle_taken (the team handle).

---

[API](https://skmtc.dev/whenmeet/apis/whenmeet-me-api.md) · [All operations](https://skmtc.dev/whenmeet/apis/whenmeet-me-api/llms.txt) · [OpenAPI document](https://skmtc.dev/whenmeet/apis/whenmeet-me-api/revisions/202b80022215?raw)
