calcom/cal.diy · error · ConflictException

Webhook with this subscriber url already exists for this eve

Error message

Webhook with this subscriber url already exists for this event type

What it means

TeamEventTypeWebhooksService.createTeamEventTypeWebhook calls getEventTypeWebhookByUrl(eventTypeId, subscriberUrl); a pre-existing row causes ConflictException (HTTP 409). The same backing repository method is shared with regular event-type webhooks, so the (eventTypeId, subscriberUrl) uniqueness applies.

Source

Thrown at apps/api/v2/src/modules/webhooks/services/team-event-type-webhooks.service.ts:25

@Injectable()
export class TeamEventTypeWebhooksService {
  constructor(private readonly webhooksRepository: WebhooksRepository) {}

  async createTeamEventTypeWebhook(eventTypeId: number, body: PipedInputWebhookType) {
    validateWebhookUrl(body.subscriberUrl);

    if (body.eventTriggers.includes(WebhookTriggerEvents.DELEGATION_CREDENTIAL_ERROR)) {
      throw new BadRequestException(
        "DELEGATION_CREDENTIAL_ERROR trigger is only available for organization webhooks"
      );
    }

    const existingWebhook = await this.webhooksRepository.getEventTypeWebhookByUrl(
      eventTypeId,
      body.subscriberUrl
    );
    if (existingWebhook) {
      throw new ConflictException("Webhook with this subscriber url already exists for this event type");
    }
    return this.webhooksRepository.createEventTypeWebhook(eventTypeId, {
      ...body,
      payloadTemplate: body.payloadTemplate ?? null,
      secret: body.secret ?? null,
    });
  }

  getTeamEventTypeWebhooksPaginated(eventTypeId: number, skip: number, take: number) {
    return this.webhooksRepository.getEventTypeWebhooksPaginated(eventTypeId, skip, take);
  }

  async deleteAllTeamEventTypeWebhooks(eventTypeId: number): Promise<{ count: number }> {
    return this.webhooksRepository.deleteAllEventTypeWebhooks(eventTypeId);
  }
}

View on GitHub (pinned to 176037d0af)

Solutions

  1. List team event-type webhooks and check subscriberUrl before creating.
  2. Use unique subscriber URLs.
  3. Treat 409 as idempotent success.

Example fix

// before
await api.createTeamEventTypeWebhook(eventTypeId, { subscriberUrl, ... });
// after
const list = await api.listTeamEventTypeWebhooks(eventTypeId);
if (list.find(w => w.subscriberUrl === subscriberUrl)) return;
await api.createTeamEventTypeWebhook(eventTypeId, { subscriberUrl, ... });
Defensive patterns

Strategy: validation

Validate before calling

async function createIfMissingTeamEventType(api, eventTypeId, body) {
  const list = await api.getTeamEventTypeWebhooksPaginated(eventTypeId, 0, 1000);
  if (list.find(w => w.subscriberUrl === body.subscriberUrl)) return;
  return api.createTeamEventTypeWebhook(eventTypeId, body);
}

Type guard

const isDuplicateUrl = (list: { subscriberUrl: string }[], url: string): boolean =>
  list.some(w => w.subscriberUrl === url);

Try / catch

try { await api.createTeamEventTypeWebhook(eventTypeId, body); }
catch (e) { if (e.status === 409) return; else throw e; }

Prevention

When it happens

Trigger: POSTing a team event-type webhook with a subscriberUrl already registered against the same eventTypeId.

Common situations: Retries that succeeded server-side; shared callback URL across team integrations; reused ngrok/dev URL.

Related errors


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