> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/calcom/cal.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Build Your First App

> Step-by-step guide to creating a Cal.com app

This guide walks you through creating a custom Cal.com app using the app-store CLI.

## Prerequisites

* Cal.com development environment set up
* Node.js 10+ installed
* Yarn package manager
* Basic knowledge of TypeScript and React

## Step 1: Create Your App

Use the app-store CLI to scaffold a new app:

### Interactive Mode

```bash theme={null}
cd packages/app-store-cli
yarn cli create
```

The CLI will prompt you for:

* **App name** - Display name for your app
* **Description** - Short description (keep under 10 words)
* **Category** - Choose from: analytics, automation, calendar, conferencing, crm, messaging, payment, other
* **Publisher** - Your name or company
* **Email** - Contact email
* **Template** - Starting template for your app

### Non-Interactive Mode

For automation or CI/CD, pass all options as flags:

```bash theme={null}
yarn cli create \
  --name "My Custom App" \
  --description "Integrates with external service" \
  --category other \
  --publisher "Your Name" \
  --email "email@example.com" \
  --template basic
```

<Info>
  The app slug is automatically generated from the name by converting to lowercase and replacing spaces with hyphens.
</Info>

## Step 2: Choose a Template

The CLI offers several templates based on your app's functionality:

### basic

A minimal app that can be installed with no additional features. Good starting point for simple integrations.

### event-type-app-card

Adds a card to the event type configuration. Use for apps that extend event types (e.g., Giphy, QR Code).

**Use case:** Payment apps, custom booking fields, event-specific features

### booking-pages-tag

Injects tags into booking pages (e.g., analytics, tracking scripts).

**Use case:** Google Analytics, Facebook Pixel, custom tracking

### event-type-location-video-static

Adds a static video conferencing location to event types.

**Use case:** Custom video providers that don't require dynamic room creation

### general-app-settings

Provides a settings interface in the app configuration.

**Use case:** Apps requiring user configuration

### link-as-an-app

Creates an app that redirects to an external URL.

**Use case:** External tools, documentation links

```bash theme={null}
yarn cli create \
  --name "External Tool" \
  --description "Link to external service" \
  --category other \
  --template link-as-an-app \
  --external-link-url "https://external-service.com"
```

## Step 3: App Structure

Your new app is created in `packages/app-store/your-app-slug/` with this structure:

```
my-custom-app/
├── config.json          # App configuration
├── package.json         # Dependencies
├── index.ts            # Exports
├── zod.ts             # Validation schemas
├── DESCRIPTION.md     # App store description
├── api/
│   ├── index.ts       # API exports
│   └── add.ts         # Installation handler
├── components/        # React components (template-dependent)
└── static/
    └── icon.svg       # App icon
```

## Step 4: Configure Your App

Edit `config.json` to customize your app's metadata:

```json theme={null}
{
  "name": "My Custom App",
  "slug": "my-custom-app",
  "type": "my-custom-app_other",
  "logo": "icon.svg",
  "url": "https://example.com",
  "variant": "other",
  "categories": ["other"],
  "publisher": "Your Name",
  "email": "email@example.com",
  "description": "Short app description",
  "extendsFeature": "EventType",
  "isOAuth": false
}
```

<Warning>
  Don't modify the `slug` field directly. Use `yarn cli edit --slug your-app` if you need to change it.
</Warning>

## Step 5: Add Dependencies

Update `package.json` to include any required dependencies:

```json theme={null}
{
  "name": "@calcom/my-custom-app",
  "version": "0.0.0",
  "private": true,
  "main": "./index.ts",
  "dependencies": {
    "@calcom/lib": "workspace:*",
    "axios": "^1.6.0"
  },
  "devDependencies": {
    "@calcom/types": "workspace:*"
  }
}
```

Install dependencies:

```bash theme={null}
yarn install
```

## Step 6: Define Schemas

Edit `zod.ts` to define validation schemas for your app:

```typescript theme={null}
import { z } from "zod";
import { eventTypeAppCardZod } from "@calcom/app-store/eventTypeAppCardZod";

// Schema for app settings on event types
export const appDataSchema = eventTypeAppCardZod.merge(
  z.object({
    enabled: z.boolean().optional(),
    apiKey: z.string().optional(),
    customSetting: z.string().default("default-value"),
  })
);

// Schema for app credentials (stored securely)
export const appKeysSchema = z.object({
  api_key: z.string().min(1),
  api_secret: z.string().min(1),
  webhook_url: z.string().url().optional(),
});
```

<Info>
  `appKeysSchema` credentials are stored encrypted and never exposed in API responses.
</Info>

## Step 7: Implement Installation Handler

Edit `api/add.ts` to handle app installation:

```typescript theme={null}
import type { NextApiRequest, NextApiResponse } from "next";
import { appKeysSchema } from "../zod";

export default async function handler(
  req: NextApiRequest,
  res: NextApiResponse
) {
  if (req.method !== "POST") {
    return res.status(405).json({ message: "Method not allowed" });
  }

  try {
    // Validate incoming credentials
    const credentials = appKeysSchema.parse(req.body);

    // Verify credentials with external service
    const isValid = await verifyCredentials(credentials);
    
    if (!isValid) {
      return res.status(401).json({ message: "Invalid credentials" });
    }

    // Store credentials (handled by Cal.com)
    return res.status(200).json({ 
      message: "App installed successfully"
    });
  } catch (error) {
    return res.status(400).json({ 
      message: "Invalid request",
      error: error.message 
    });
  }
}

async function verifyCredentials(credentials: any): Promise<boolean> {
  // Implement your credential verification logic
  return true;
}
```

## Step 8: Add Business Logic

Create service files in the `lib/` directory based on your app type:

### Calendar App

```typescript packages/app-store/my-calendar/lib/CalendarService.ts theme={null}
import type { Calendar, CalendarEvent } from "@calcom/types/Calendar";

export default class MyCalendarService implements Calendar {
  constructor(private credential: any) {}

  async createEvent(event: CalendarEvent) {
    // Implement event creation
  }

  async updateEvent(uid: string, event: CalendarEvent) {
    // Implement event update
  }

  async deleteEvent(uid: string) {
    // Implement event deletion
  }

  async getAvailability(dateFrom: string, dateTo: string) {
    // Implement availability check
  }
}
```

### Payment App

```typescript packages/app-store/my-payment/lib/PaymentService.ts theme={null}
import type { Payment, PaymentData } from "@calcom/types/Payment";

export class PaymentService implements Payment {
  async create(paymentData: PaymentData) {
    // Create payment intent
  }

  async update(paymentId: string, data: Partial<PaymentData>) {
    // Update payment
  }

  async refund(paymentId: string) {
    // Process refund
  }

  async afterPayment(event: CalendarEvent, booking: Booking) {
    // Post-payment processing
  }
}
```

## Step 9: Add UI Components

Create React components for your app's UI:

```tsx packages/app-store/my-app/components/EventTypeAppCardInterface.tsx theme={null}
import { useState } from "react";
import { useFormContext } from "react-hook-form";

export default function EventTypeAppCard() {
  const { register } = useFormContext();
  
  return (
    <div className="space-y-4">
      <h3>My App Settings</h3>
      
      <label>
        <input
          type="checkbox"
          {...register("enabled")}
        />
        Enable My App
      </label>
      
      <input
        type="text"
        placeholder="API Key"
        {...register("apiKey")}
      />
    </div>
  );
}
```

## Step 10: Generate App Files

Generate the app registry files:

```bash theme={null}
cd packages/app-store-cli
yarn build
```

This creates `*.generated.ts` files that register your app with the Cal.com platform.

## Step 11: Test Your App

Start the development server:

```bash theme={null}
yarn dev
```

1. Navigate to `http://localhost:3000/settings/admin/apps`
2. Find your app in the list
3. Enable it as an admin
4. Install it from `http://localhost:3000/apps/your-app-slug`
5. Test the installation flow

## Step 12: Add App Store Assets

Customize your app's App Store presence:

### DESCRIPTION.md

Add images and detailed description:

```markdown theme={null}
---
items:
  - iframe: { src: https://www.youtube.com/embed/VIDEO_ID }
  - screenshot-1.jpg
  - screenshot-2.png
  - screenshot-3.jpg
---

## Overview

Detailed description of your app and what it does.

## Features

- Feature 1
- Feature 2
- Feature 3

## Setup Instructions

Step-by-step guide to setting up your app.
```

<Info>
  Include at least 4 images. Use only filenames (e.g., `1.jpeg`), not paths.
</Info>

### README.md

Add installation instructions:

```markdown theme={null}
# My Custom App

## Installation

1. Install the app from the Cal.com app store
2. Get your API credentials from https://example.com/api-keys
3. Enter your credentials in the app settings
4. Configure your event types

## Configuration

For OAuth-based apps, use `<baseUrl>/api/integrations` as the callback URL.

## Support

For questions or issues, contact support@example.com
```

## Step 13: Handle Environment Variables

<Warning>
  Never add environment variables directly to `.env` files. Use the `zod.ts` approach instead.
</Warning>

Define admin-configurable settings in `zod.ts`:

```typescript theme={null}
import { z } from "zod";

export const appKeysSchema = z.object({
  api_endpoint: z.string().url().default("https://api.example.com"),
  api_key: z.string(),
});
```

These settings will be available in `/settings/admin` for modification.

## Watch Mode

During development, use watch mode to automatically regenerate files:

```bash theme={null}
cd packages/app-store-cli
yarn watch
```

## Troubleshooting

### App Not Appearing

1. Ensure `yarn build` completed successfully
2. Check that `config.json` is valid JSON
3. Restart the dev server
4. Enable the app in `/settings/admin/apps`

### Type Errors

Run type checks:

```bash theme={null}
yarn type-check:ci --force
```

### Generated Files Not Updating

```bash theme={null}
cd packages/app-store-cli
yarn build
```

## Next Steps

<CardGroup cols={2}>
  <Card title="App Types" icon="layer-group" href="/developers/apps/app-types">
    Learn about different app service types
  </Card>

  <Card title="CLI Reference" icon="terminal" href="/developers/apps/app-store-cli">
    Complete CLI command reference
  </Card>

  <Card title="Contributing" icon="code-pull-request" href="https://github.com/calcom/cal.com/blob/main/packages/app-store/CONTRIBUTING.md">
    Guidelines for contributing apps to Cal.com
  </Card>

  <Card title="Examples" icon="code" href="https://github.com/calcom/cal.com/tree/main/packages/app-store">
    Browse existing apps for examples
  </Card>
</CardGroup>
