calcom/cal.com
API v2
Active contributors: Lauris, Carlos, Morgan, Eunjae
Purpose
apps/api/v2 is the official Cal.com Platform REST API — a separate NestJS service that lives next to the Next.js web app. It powers the documented public REST endpoints, the OAuth-client tenant model, and the React "atom" components in packages/platform/atoms that embed Cal.com scheduling into third-party products.
The API is documented at apps/docs/content/api-reference and exposes a Swagger UI when running locally.
Directory layout
apps/api/v2/
├── src/
│ ├── app.module.ts # Root NestJS module
│ ├── app.controller.ts
│ ├── bootstrap.ts # Bootstraps the Nest app (used by main.ts and tests)
│ ├── main.ts # Production entry point
│ ├── env.ts # Typed env loading
│ ├── instrument.ts # Sentry instrumentation
│ ├── config/ # NestJS config providers
│ ├── filters/ # Exception filters (HTTP, Prisma, validation)
│ ├── lib/ # 16 lib subdirectories (api-keys, jwt, prisma, ...)
│ ├── middleware/ # Logger, redirects, rewrites, body parsers, request IDs
│ ├── modules/ # 32 NestJS modules (one per resource + cross-cutting)
│ ├── platform/ # Versioned platform endpoints (event-types_2024_06_14, bookings/2024-08-13, ...)
│ ├── swagger/ # Swagger doc generation
│ ├── vercel-webhook.controller.ts
│ ├── vercel-webhook.guard.ts
│ └── app.e2e-spec.ts
├── test/ # Test fixtures + utilities
├── scripts/
├── jest.config.ts
├── jest-e2e.ts
├── nest-cli.json
├── Dockerfile
└── README.md / README-PLATFORM.mdKey abstractions
| Symbol | File | Purpose |
|---|---|---|
AppModule |
apps/api/v2/src/app.module.ts |
Root module; wires Sentry, Throttler (Redis-backed), Bull (jobs), Auth, JWT, Prisma, Endpoints |
EndpointsModule |
apps/api/v2/src/modules/endpoints.module.ts |
Aggregates every resource module so the app boots from one import |
CustomThrottlerGuard |
apps/api/v2/src/lib/throttler-guard.ts |
The actual rate-limiter used by the app (ThrottlerModule is configured to keep the guard active) |
RequestIdMiddleware |
apps/api/v2/src/middleware/request-ids/request-id.middleware.ts |
Stamps every request with a request ID for log correlation |
ResponseInterceptor |
apps/api/v2/src/middleware/request-ids/request-id.interceptor.ts |
Wraps responses in the standard envelope and propagates request IDs |
RawBodyMiddleware / JsonBodyMiddleware / UrlencodedBodyMiddleware |
apps/api/v2/src/middleware/body/* |
Per-route body parsing — webhooks need raw bodies, OAuth token endpoint needs urlencoded |
VercelWebhookController |
apps/api/v2/src/vercel-webhook.controller.ts |
Handles Vercel deployment webhooks |
PrismaModule |
apps/api/v2/src/modules/prisma/prisma.module.ts |
Provides the shared Prisma client across the app |
RedisModule |
apps/api/v2/src/modules/redis/redis.module.ts |
Connects to Redis for throttling, BullMQ, and short-lived caches |
AuthModule / JwtModule |
apps/api/v2/src/modules/{auth,jwt} |
API-key, OAuth, and JWT authentication strategies |
How it works
graph TD
Client -->|HTTPS| Nest[apps/api/v2 NestJS]
Nest --> Middleware[middleware/*]
Middleware --> Throttler[CustomThrottlerGuard via Redis]
Throttler --> AuthGuards[Auth + JWT guards]
AuthGuards --> Controllers[modules/*/controllers]
Controllers --> Services[modules/*/services]
Services --> PlatformLibs[packages/platform/libraries]
PlatformLibs --> Features[packages/features/*]
Services --> Prisma[(PostgreSQL)]
Services --> Redis[(Redis)]
Services --> Bull[BullMQ jobs]
Nest --> Sentry[Sentry]The crucial detail is the packages/platform/libraries/index.ts indirection. apps/api/v2's tsconfig.json does not have path mappings for @calcom/features or @calcom/trpc, so those modules are re-exported through @calcom/platform-libraries. AGENTS.md documents this rule with examples.
Modules (resources)
The 32 directories under apps/api/v2/src/modules/ map to public resources, one per concept:
api-keys, apps, atoms, auth, booking-seat, cal-unified-calendars,
conferencing, credentials, deployments, destination-calendars,
email, event-types, jwt, kysely, memberships, oauth-clients, ooo,
organizations, prisma, profiles, redis, selected-calendars, slots,
stripe, teams, timezones, tokens, users, verified-resources,
webhooksVersioned endpoints live under apps/api/v2/src/platform/. Each version is its own folder (e.g., event-types_2024_06_14/, bookings/2024-08-13/) so the team can ship breaking changes by introducing a new version while keeping the old one available.
Almost every controller has an *.e2e-spec.ts neighbor. The repo has 42 such e2e spec files, run by .github/workflows/e2e-api-v2.yml.
Authentication
apps/api/v2/src/modules/auth/ implements three flows:
- API key — long-lived bearer tokens. The
Cal-Api-Keylegacypackages/features/api-keys-legacypackage handles compatibility. - OAuth 2.0 (clients) — for Platform tenants. The
oauth-clientsmodule owns client registration and token issuance; thetokensmodule manages access/refresh tokens. - JWT — for short-lived service-to-service auth.
The RawBodyMiddleware is explicitly applied to /v2/auth/oauth2/token because OAuth requires application/x-www-form-urlencoded parsing, and to billing webhooks which need raw bodies for signature verification.
Rate limiting
CustomThrottlerGuard is registered as APP_GUARD so it runs on every request. The ThrottlerModule configuration in app.module.ts keeps the upstream @nestjs/throttler machinery live (with a dummy throttler) but the actual rules are read by CustomThrottlerGuard via Redis.
Background jobs
Bull/BullMQ runs through BullModule.forRoot in app.module.ts, connected to REDIS_URL. Per-resource queues live inside individual modules.
Integration points
- Web app — There is no direct call between web and API v2 in production. They share the same database via
PrismaModule. The web app uses tRPC; the API v2 is a standalone REST service. - Platform atoms —
packages/platform/atoms(React) calls API v2 endpoints over HTTPS using a typed SDK inpackages/platform/typesandpackages/platform/libraries. - Documentation — Swagger decorators in controllers feed
apps/api/v2/src/swagger/which generatesapps/docs/content/api-reference/v2/openapi.json. - Sentry —
apps/api/v2/src/instrument.tsinitializes Sentry;SentryGlobalFilteris registered asAPP_FILTER.
Entry points for modification
- Add a new resource: scaffold a NestJS module under
apps/api/v2/src/modules/<resource>/(controller, service, module). Register it inapps/api/v2/src/modules/endpoints.module.ts. Add Swagger decorators on the controller methods so the OpenAPI spec picks them up. Re-export shared logic viapackages/platform/libraries/index.tsif you need symbols from@calcom/features. - Add a new endpoint version: create
apps/api/v2/src/platform/<resource>/<YYYY-MM-DD>/with controllers and DTOs scoped to the new version. Update the version routing in the resource module. - Auth changes: anything that touches sessions, scopes, or guards belongs in
apps/api/v2/src/modules/auth/plusapps/api/v2/src/modules/jwt/.
Related pages
- Web — the Next.js front end
- Packages → platform — atoms, libraries, types, examples
- API overview — tRPC vs REST v2
Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.