> ## 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.

# App Types & Services

> Understanding different Cal.com app types and service implementations

Cal.com apps can implement various service types depending on their functionality. Each service type provides specific interfaces and lifecycle hooks.

## Service Types

### Calendar Apps

Calendar apps sync booking data with external calendar services.

**Category:** `calendar`

**Service File:** `lib/CalendarService.ts`

#### Interface

```typescript theme={null}
import type { Calendar, CalendarEvent, IntegrationCalendar } from "@calcom/types/Calendar";

class MyCalendarService implements Calendar {
  constructor(credential: Credential) {
    // Initialize with stored credentials
  }

  async createEvent(event: CalendarEvent): Promise<NewCalendarEventType> {
    // Create event in external calendar
    return {
      uid: "external-event-id",
      id: "external-event-id",
      type: "my_calendar",
      password: "",
      url: "https://example.com/event/123",
    };
  }

  async updateEvent(
    uid: string,
    event: CalendarEvent,
    externalCalendarId?: string
  ): Promise<NewCalendarEventType> {
    // Update existing event
  }

  async deleteEvent(
    uid: string,
    event: CalendarEvent,
    externalCalendarId?: string
  ): Promise<void> {
    // Delete event from external calendar
  }

  async getAvailability(
    dateFrom: string,
    dateTo: string,
    selectedCalendars: IntegrationCalendar[]
  ): Promise<EventBusyDate[]> {
    // Fetch busy times from external calendar
    return [
      {
        start: "2024-01-01T10:00:00Z",
        end: "2024-01-01T11:00:00Z",
      },
    ];
  }

  async listCalendars(): Promise<IntegrationCalendar[]> {
    // List available calendars for the user
    return [
      {
        externalId: "calendar-id",
        integration: "my_calendar",
        name: "My Calendar",
        primary: true,
      },
    ];
  }
}

export default MyCalendarService;
```

#### Registration

Export as default from `lib/CalendarService.ts`. The build process automatically registers it:

```typescript theme={null}
// Generated in calendar.services.generated.ts
export const CalendarServiceMap = {
  "my-calendar": import("./my-calendar/lib/CalendarService"),
};
```

#### OAuth Support

For OAuth-based calendars, set `isOAuth: true` in `config.json`:

```json theme={null}
{
  "isOAuth": true,
  "variant": "calendar"
}
```

### CRM Apps

CRM apps sync contact and booking data with customer relationship management systems.

**Category:** `crm`

**Service File:** `lib/CrmService.ts`

#### Interface

```typescript theme={null}
import type { CRM, Contact, CRMEvent } from "@calcom/types/CRM";

class MyCrmService implements CRM {
  constructor(credential: Credential) {
    // Initialize with credentials
  }

  async createEvent(event: CalendarEvent, contacts: Contact[]): Promise<void> {
    // Create or update contacts and event in CRM
  }

  async updateEvent(uid: string, event: CalendarEvent): Promise<void> {
    // Update CRM records
  }

  async deleteEvent(uid: string): Promise<void> {
    // Delete from CRM
  }

  async getContacts(emails: string[]): Promise<Contact[]> {
    // Fetch existing contacts
    return [
      {
        email: "user@example.com",
        id: "crm-contact-id",
        name: "John Doe",
      },
    ];
  }
}

export default MyCrmService;
```

#### Example Apps

* **Attio** - `packages/app-store/attio/`
* **Basecamp3** - `packages/app-store/basecamp3/`

### Payment Apps

Payment apps process payments for bookings.

**Category:** `payment`

**Service File:** `lib/PaymentService.ts`

#### Interface

```typescript theme={null}
import type { Payment, PaymentData } from "@calcom/types/Payment";

export class PaymentService implements Payment {
  constructor(private credential: Credential) {}

  async create(payment: PaymentData) {
    // Create payment intent
    return {
      id: "payment-intent-id",
      success: false,
      url: "https://payment-provider.com/checkout/xyz",
    };
  }

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

  async refund(paymentId: string): Promise<PaymentData> {
    // Process refund
  }

  async afterPayment(
    event: CalendarEvent,
    booking: Booking,
    paymentData: PaymentData
  ): Promise<void> {
    // Post-payment processing (send receipts, etc.)
  }

  async deletePayment(paymentId: string): Promise<boolean> {
    // Cancel payment
  }
}
```

#### Payment Options

Define payment timing in `zod.ts`:

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

export const PaymentOptions = [
  {
    label: "on_booking_option",
    value: "ON_BOOKING",
  },
  {
    label: "after_booking_option",
    value: "AFTER_BOOKING",
  },
];

export const appDataSchema = eventTypeAppCardZod.merge(
  z.object({
    price: z.number(),
    currency: z.string(),
    paymentOption: z.string().optional(),
    enabled: z.boolean().optional(),
    credentialId: z.number().optional(),
  })
);
```

#### Example Apps

* **Alby** (Bitcoin) - `packages/app-store/alby/`
* **BTCPay Server** - `packages/app-store/btcpayserver/`

### Video Apps

Video apps provide video conferencing for bookings.

**Category:** `conferencing`

**Service File:** `lib/VideoApiAdapter.ts`

#### Interface

```typescript theme={null}
import type { VideoApiAdapter, VideoCallData } from "@calcom/types/VideoApiAdapter";

class MyVideoService implements VideoApiAdapter {
  constructor(private credential: Credential) {}

  async createMeeting(event: CalendarEvent): Promise<VideoCallData> {
    // Create video meeting room
    return {
      type: "my_video",
      id: "meeting-id",
      password: "meeting-password",
      url: "https://video-provider.com/join/xyz",
    };
  }

  async updateMeeting(
    bookingRef: PartialReference,
    event: CalendarEvent
  ): Promise<VideoCallData> {
    // Update meeting details
  }

  async deleteMeeting(uid: string): Promise<void> {
    // Delete meeting room
  }

  async getAvailability(dateFrom: string, dateTo: string): Promise<any[]> {
    // Optional: Check video service availability
    return [];
  }
}

export default MyVideoService;
```

#### Static Video Locations

For video services without dynamic room creation, use the `event-type-location-video-static` template:

```json theme={null}
{
  "variant": "conferencing",
  "appData": {
    "location": {
      "type": "my_video",
      "label": "My Video",
      "linkType": "static"
    }
  }
}
```

### Analytics Apps

Analytics apps track booking metrics and user behavior.

**Category:** `analytics`

**Service File:** `lib/AnalyticsService.ts`

#### Interface

```typescript theme={null}
class MyAnalyticsService {
  constructor(private credential: Credential) {}

  async track(event: string, data: Record<string, any>): Promise<void> {
    // Send tracking event
  }

  async identify(userId: string, traits: Record<string, any>): Promise<void> {
    // Identify user
  }
}

export default MyAnalyticsService;
```

#### Tag-Based Analytics

For tag/script injection (Google Analytics, GTM, etc.), use the `booking-pages-tag` template:

```typescript components/TagManagerScript.tsx theme={null}
export default function TagManagerScript() {
  return (
    <script
      dangerouslySetInnerHTML={{
        __html: `
          (function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
          new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
          j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
          'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
          })(window,document,'script','dataLayer','GTM-XXXX');
        `,
      }}
    />
  );
}
```

### Automation Apps

Automation apps trigger workflows based on booking events.

**Category:** `automation`

#### Webhook-Based

Most automation apps use webhooks:

```typescript api/webhook.ts theme={null}
import type { NextApiRequest, NextApiResponse } from "next";

export default async function handler(
  req: NextApiRequest,
  res: NextApiResponse
) {
  const { triggerEvent, payload } = req.body;

  switch (triggerEvent) {
    case "BOOKING_CREATED":
      // Handle booking creation
      break;
    case "BOOKING_RESCHEDULED":
      // Handle reschedule
      break;
    case "BOOKING_CANCELLED":
      // Handle cancellation
      break;
    case "MEETING_ENDED":
      // Handle meeting end
      break;
  }

  return res.status(200).json({ received: true });
}
```

## App Features

### Event Type Extensions

Apps can add UI to event type configuration using `extendsFeature: "EventType"`:

```typescript components/EventTypeAppCardInterface.tsx theme={null}
import { useFormContext } from "react-hook-form";

export default function EventTypeAppCard() {
  const { register } = useFormContext();

  return (
    <div>
      <label>
        <input type="checkbox" {...register("enabled")} />
        Enable for this event type
      </label>
    </div>
  );
}
```

### App Settings

Provide a settings interface for installed apps:

```typescript components/AppSettingsInterface.tsx theme={null}
import { useState } from "react";

export default function AppSettings({ credential }: { credential: Credential }) {
  const [settings, setSettings] = useState(credential.key);

  return (
    <div>
      <h3>App Settings</h3>
      {/* Settings form */}
    </div>
  );
}
```

### Install Button

Custom installation flow:

```typescript components/InstallAppButton.tsx theme={null}
export default function InstallAppButton() {
  const handleInstall = async () => {
    // Custom installation logic
  };

  return <button onClick={handleInstall}>Install</button>;
}
```

## Configuration Options

### config.json

Complete configuration reference:

```json theme={null}
{
  "name": "App Name",
  "slug": "app-slug",
  "type": "app-slug_category",
  "logo": "icon.svg",
  "url": "https://example.com",
  "variant": "category",
  "categories": ["category"],
  "publisher": "Publisher Name",
  "email": "email@example.com",
  "description": "Short description",
  "extendsFeature": "EventType",
  "isTemplate": false,
  "isOAuth": false,
  "dependencies": ["other-app-slug"],
  "appData": {
    "location": {
      "type": "app-slug",
      "label": "App Name",
      "linkType": "dynamic"
    },
    "tag": {
      "script": "https://example.com/script.js"
    }
  },
  "externalLink": {
    "url": "https://external-service.com",
    "newTab": true
  }
}
```

<ParamField path="name" type="string" required>
  Display name shown in the app store
</ParamField>

<ParamField path="slug" type="string" required>
  URL-safe identifier (auto-generated from name)
</ParamField>

<ParamField path="type" type="string" required>
  Format: `{slug}_{category}`
</ParamField>

<ParamField path="variant" type="string" required>
  Must match category (or "conferencing" for video category)
</ParamField>

<ParamField path="extendsFeature" type="string">
  "EventType" - Adds settings to event types
</ParamField>

<ParamField path="isOAuth" type="boolean" default={false}>
  Whether the app uses OAuth authentication
</ParamField>

<ParamField path="dependencies" type="string[]">
  Other apps required for this app to function
</ParamField>

## Best Practices

### Security

* Never expose `credential.key` in API responses
* Use `appKeysSchema` for sensitive credentials
* Validate all inputs with Zod schemas
* Use environment-specific API endpoints

### Performance

* Implement caching for external API calls
* Use lazy imports for UI components
* Batch operations when possible
* Handle rate limiting gracefully

### Error Handling

```typescript theme={null}
import { ErrorWithCode } from "@calcom/lib/errorWithCode";

try {
  await externalService.createEvent(event);
} catch (error) {
  throw new ErrorWithCode(
    `Failed to create event in ${serviceName}: ${error.message}`,
    "SERVICE_ERROR"
  );
}
```

### Testing

Test your service implementations:

```typescript theme={null}
import { describe, it, expect } from "vitest";
import MyCalendarService from "./CalendarService";

describe("MyCalendarService", () => {
  it("creates events", async () => {
    const service = new MyCalendarService(mockCredential);
    const result = await service.createEvent(mockEvent);
    expect(result.uid).toBeDefined();
  });
});
```

## Examples by Type

### Calendar

* **Apple Calendar** - `packages/app-store/applecalendar/`
* **CalDAV** - `packages/app-store/caldavcalendar/`

### CRM

* **Attio** - `packages/app-store/attio/`
* **Basecamp3** - `packages/app-store/basecamp3/`

### Payment

* **Alby** - `packages/app-store/alby/`
* **BTCPay Server** - `packages/app-store/btcpayserver/`

### Video

* **Huddle01** - `packages/app-store/huddle01video/`

### Automation

* **Make** - `packages/app-store/make/`

## Next Steps

<CardGroup cols={2}>
  <Card title="Build an App" icon="hammer" href="/developers/apps/build-an-app">
    Complete guide to building your first app
  </Card>

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

  <Card title="Type Definitions" icon="code" href="https://github.com/calcom/cal.com/tree/main/packages/types">
    Explore TypeScript type definitions
  </Card>
</CardGroup>
