Skip to main content
doc0blade47/dubdocumentation home
Sign in

Audit Logs

Last updated Oct 12, 202677% grounded
Relevant source files

The following files were used as context for generating this wiki page:

Overview

Dub maintains a multi-layered audit and telemetry architecture that captures administrative actions, partner modifications, workspace activities, and API or webhook requests across the platform. Administrative security events and audit records are ingested directly into Tinybird Analytics and ClickHouse apps/web/lib/api/audit-logs/record-audit-log.ts:75-79, apps/web/lib/api/audit-logs/get-audit-logs.ts:38-49, whereas granular workspace operational changes—such as partner state transitions, reward overrides, and commission adjustments—are persisted relationally via Prisma apps/web/prisma/schema/activity.prisma:1-16, apps/web/lib/api/activity-log/track-activity-log.ts:34-65. This infrastructure allows enterprises to query structured historical logs, enforce strict date range retention limits based on subscription plans apps/web/app/api/logs/route.ts:13-21, export audit datasets or event streams as downloadable CSVs apps/web/app/ee/api/audit-logs/export/route.ts:40-47, and render dedicated activity timelines directly inside the dashboard UI apps/web/ui/activity-logs/activity-log-registry.tsx:71-129.

Sources: apps/web/lib/api/audit-logs/record-audit-log.ts:75-79, apps/web/lib/api/audit-logs/get-audit-logs.ts:38-49, apps/web/prisma/schema/activity.prisma:1-16, apps/web/lib/api/activity-log/track-activity-log.ts:34-65, apps/web/app/api/logs/route.ts:13-21, apps/web/app/ee/api/audit-logs/export/route.ts:40-47, apps/web/ui/activity-logs/activity-log-registry.tsx:71-129

Tinybird Audit Event Ingestion

Event Transformation and Enrichment

Administrative and security audit events across Dub are processed through a structured ingestion pipeline that shapes incoming records, resolves network context, and transmits payload telemetry to Tinybird. When recordAuditLog is invoked with single or multiple audit log inputs, the system resolves client IP addresses by inspecting request instances via @vercel/functions or falling back to workspace header helpers before transformation apps/web/lib/api/audit-logs/record-audit-log.ts:47-56.

The transformAuditLogTB helper validates input schemas against recordAuditLogInputSchema, extracts the user-agent from incoming Next.js headers, and builds structured records apps/web/lib/api/audit-logs/record-audit-log.ts:13-44. Unique identifiers are generated with an audit_ prefix, workspace identifiers are normalized via prefixWorkspaceId, and complex structures such as targets and metadata are serialized into JSON strings apps/web/lib/api/audit-logs/record-audit-log.ts:28-44. Batch or individual payloads are then submitted through recordAuditLogTB, which builds an ingest endpoint targeting the dub_audit_logs datasource with wait: true configuration apps/web/lib/api/audit-logs/record-audit-log.ts:47-79.

Note

If recording an audit log fails during transmission to Tinybird, the error is caught, logged to console alongside the serialized audit payloads, and dispatched to error tracking with user notification flags enabled via @dub/utils loggers apps/web/lib/api/audit-logs/record-audit-log.ts:58-72.

Sources: apps/web/lib/api/audit-logs/record-audit-log.ts:13-79

Action Types and Target Schemas

Audit events track specific domain operations across programs, rewards, discounts, partner enrollments, commissions, payouts, groups, bounties, and submissions. The accepted action identifiers are strictly constrained by validation schemas before reaching the persistence layer apps/web/lib/api/audit-logs/schemas.ts:32-94.

Domain CategoryValid Action IdentifiersSources
Programprogram.created, program.updatedapps/web/lib/api/audit-logs/schemas.ts:32-36
Rewardsreward.created, reward.updated, reward.deletedapps/web/lib/api/audit-logs/schemas.ts:37-41
Discountsdiscount.created, discount.updated, discount.deleted, discount_code.created, discount_code.deletedapps/web/lib/api/audit-logs/schemas.ts:42-48
Partner Applicationspartner_application.approved, partner_application.rejectedapps/web/lib/api/audit-logs/schemas.ts:49-52
Partner Enrollmentspartner.created, partner.archived, partner.invited, partner.invite_deleted, partner.invite_resent, partner.enrollment_updated, partner.deactivated, partner.reactivated, partner.banned, partner.unbanned, partner.deletedapps/web/lib/api/audit-logs/schemas.ts:53-65
Auto Approve Partnerauto_approve_partner.enabled, auto_approve_partner.disabledapps/web/lib/api/audit-logs/schemas.ts:66-69
Commissions & Clawbackscommission.created, commission.updated, clawback.created, commission.canceled, commission.marked_fraud, commission.marked_duplicateapps/web/lib/api/audit-logs/schemas.ts:70-77
Payoutspayout.confirmed, payout.marked_paidapps/web/lib/api/audit-logs/schemas.ts:78-81
Groupsgroup.created, group.updated, group.deletedapps/web/lib/api/audit-logs/schemas.ts:82-86
Bountiesbounty.created, bounty.updated, bounty.deleted, bounty_submission.approved, bounty_submission.rejected, bounty_submission.reopenedapps/web/lib/api/audit-logs/schemas.ts:87-94

Sources: apps/web/lib/api/audit-logs/schemas.ts:32-94

Audit Log Retrieval and Export

Querying Tinybird and ClickHouse

Audit records are retrieved by querying ClickHouse via Tinybird pipes using the getAuditLogs function. This utility executes the get_audit_logs pipe configured with strict input validation schemas and response serialization rules.

export const getAuditLogs = async ({
  workspaceId,
  programId,
  start,
  end,
}: {
  start: Date;
  end: Date;
  workspaceId: string;
  programId: string;
}) => {
  const pipe = tb.buildPipe({
    pipe: "get_audit_logs",
    parameters: auditLogFilterSchemaTB,
    data: auditLogResponseSchemaTB,
  });
 
  const events = await pipe({
    workspaceId,
    programId,
    start: formatUTCDateTimeClickhouse(start),
    end: formatUTCDateTimeClickhouse(end),
  });
 
  return events.data;

Sources: apps/web/lib/api/audit-logs/get-audit-logs.ts:27-51

The query parameters require a prefixed workspace identifier, program identifier, and formatted UTC date strings parsed through ClickHouse datetime formatting utilities.

export const auditLogFilterSchemaTB = z.object({
  workspaceId: z.string().transform(prefixWorkspaceId),
  programId: z.string(),
  start: z.string(),
  end: z.string(),
});
 
export const auditLogResponseSchemaTB = z.object({
  id: z.string(),
  timestamp: z.string(),
  action: z.string(),
  actor_id: z.string(),
  actor_type: z.string(),
  actor_name: z.string(),
  description: z.string(),
  ip_address: z.string(),
  user_agent: z.string(),
  targets: z.string(),
  metadata: z.string(),
});

Sources: apps/web/lib/api/audit-logs/get-audit-logs.ts:6-25

API Export Endpoint and Plan Enforcement

The backend export workflow is handled by a POST route secured via withWorkspace. The route requires an enterprise plan capability and ownership or member roles before processing requests.

export const POST = withWorkspace(
  async ({ req, workspace }) => {
    const { start, end } = auditLogExportQuerySchema.parse(
      await parseRequestBody(req),
    );
 
    if (!start || !end) {
      throw new DubApiError({
        code: "bad_request",
        message: "Must provide start and end dates.",
      });
    }
 
    const { canExportAuditLogs } = getPlanCapabilities(workspace.plan);
 
    if (!canExportAuditLogs) {
      throw new DubApiError({
        code: "forbidden",
        message: "You are not authorized to export audit logs.",
      });
    }
 
    const programId = getDefaultProgramIdOrThrow(workspace);
 
    const auditLogs = await getAuditLogs({
      workspaceId: workspace.id,
      programId,
      start: new Date(start),
      end: new Date(end),
    });
 
    const csvData = convertToCSV(auditLogs);
 
    return new Response(csvData, {
      headers: {
        "Content-Type": "application/csv",
        "Content-Disposition": `attachment;`,
      },
    });
  },
  {
    requiredRoles: ["owner", "member"],
    requiredPlan: ["enterprise"],
  },
);

Sources: apps/web/app/ee/api/audit-logs/export/route.ts:16-60

Warning

Requests lacking start and end query parameters or originating from workspaces without enterprise plan capabilities throw explicit DubApiError exceptions with bad_request or forbidden error codes.

Sources: apps/web/app/ee/api/audit-logs/export/route.ts:22-36

Frontend Client and CSV Generation

The client interface component manages date interval selections using SimpleDateRangePicker and triggers file downloads by converting retrieved audit logs into CSV payloads.

Sources: apps/web/app/app.dub.co/dashboard/slug/ee/settings/security/audit-logs.tsx:22-66

When export permissions are absent on lower-tier workspace plans, the interface disables the date picker and export button, presenting an upgrade callout prompting users to transition to the Enterprise Plan.

Sources: apps/web/app/app.dub.co/dashboard/slug/ee/settings/security/audit-logs.tsx:106-130

Workspace Activity Log Persistence

Database Schema and Prisma Modeling

Activity logs within partner programs are modeled using the ActivityLog Prisma model inside the database schema. Each record captures identifiers for the workspace, program, parent resource, and specific target resource, along with action metadata, descriptive text, optional batching tokens, and JSON-encoded change sets.

model ActivityLog {
  id                 String   @id @default(cuid())
  workspaceId        String
  programId          String
  parentResourceType String?
  parentResourceId   String?
  resourceType       String
  resourceId         String
  userId             String?
  action             String
  description        String?  @db.Text
  batchId            String?
  changeSet          Json?
  createdAt          DateTime @default(now())

Sources: apps/web/prisma/schema/activity.prisma:1-15

The table references user entities via a foreign key relation on userId and maintains performance indexes on [resourceType, resourceId] and userId.

  user User? @relation(fields: [userId], references: [id])

  @@index([resourceType, resourceId])
  @@index(userId)
}

Sources: apps/web/prisma/schema/activity.prisma:16-20

Activity Tracking and Filtering Logic

The programmatic insertion of activity logs is managed by trackActivityLog and transaction-bound trackActivityLogsTx utilities. These functions filter incoming entries to ensure that actions either require no change set or contain a non-empty change object before executing bulk creations.

const ACTIONS_WITHOUT_CHANGE_SET: ActivityLogAction[] = [
  "submittedLead.created",
  "reward.created",
  "reward.deleted",
];
 
export interface TrackActivityLogInput
  extends Pick<
    Prisma.ActivityLogUncheckedCreateInput,
    | "workspaceId"
    | "programId"
    | "resourceId"
    | "userId"
    | "description"
    | "parentResourceType"
    | "parentResourceId"
    | "batchId"
  > {
  resourceType: ActivityLogResourceType;
  action: ActivityLogAction;
  changeSet?: ChangeSet;
}
 
export const trackActivityLog = async (
  input: TrackActivityLogInput | TrackActivityLogInput[],
) => {
  let inputs = Array.isArray(input) ? input : [input];
 
  inputs = inputs.filter(
    (i) =>
      ACTIONS_WITHOUT_CHANGE_SET.includes(i.action) ||
      (i.changeSet && Object.keys(i.changeSet).length > 0),
  );
 
  if (inputs.length === 0) {
    return;
  }

Sources: apps/web/lib/api/activity-log/track-activity-log.ts:11-47

export const trackActivityLogsTx = async ({
  tx,
  logs,
}: {
  tx: Prisma.TransactionClient;
  logs: TrackActivityLogInput | TrackActivityLogInput[];
}) => {
  let inputs = Array.isArray(logs) ? logs : [logs];
 
  inputs = inputs.filter(
    (i) =>
      ACTIONS_WITHOUT_CHANGE_SET.includes(i.action) ||
      (i.changeSet && Object.keys(i.changeSet).length > 0),
  );
 
  if (inputs.length === 0) {
    return;
  }

Sources: apps/web/lib/api/activity-log/track-activity-log.ts:68-85

Actions configured to bypass the change set requirement include submission creations and reward creations or deletions.

Action NameDescription / BehaviorSources
submittedLead.createdBypasses change set validation during lead submission creationapps/web/lib/api/activity-log/track-activity-log.ts:11-15
reward.createdBypasses change set validation when a new reward entity is instantiatedapps/web/lib/api/activity-log/track-activity-log.ts:11-15
reward.deletedBypasses change set validation upon reward removalapps/web/lib/api/activity-log/track-activity-log.ts:11-15

Sources: apps/web/lib/api/activity-log/track-activity-log.ts:11-15

Note

If an input array contains entries where actions lack the explicit exemption and have an empty or missing changeSet, those entries are filtered out and omitted from database writes entirely.

Sources: apps/web/lib/api/activity-log/track-activity-log.ts:39-43

Reward and Commission Activity Trackers

Partner and link-level reward overrides fetch previous and subsequent reward and discount records to construct serialized activity snapshots. The tracking functions verify that changes have occurred across click, lead, or sale reward identifiers or discount configurations before persisting log entries.

const rewardFields = [
  ["clickReward", previous.clickRewardId, next.clickRewardId],
  ["leadReward", previous.leadRewardId, next.leadRewardId],
  ["saleReward", previous.saleRewardId, next.saleRewardId],
] as const;
 
const changedRewardFields = rewardFields.filter(
  ([, oldId, newId]) => oldId !== newId,
);
const discountChanged = previous.discountId !== next.discountId;
 
if (changedRewardFields.length === 0 && !discountChanged) {
  return;
}

Sources: apps/web/lib/api/activity-log/track-reward-overrides.ts:44-57

Commission updates monitor modifications specifically across amount, earnings, and status fields using resource diff utilities. Bulk status updates group commissions by program to delegate consistent status changes efficiently.

const COMMISSION_ACTIVITY_FIELDS = ["amount", "earnings", "status"];
 
export function toCommissionActivitySnapshot(
  commission: Pick<Commission, "id" | "amount" | "earnings" | "status">,
): CommissionActivitySnapshot {
  return {
    amount: commission.amount,
    earnings: commission.earnings,
    status: commission.status,
  };
}

Sources: apps/web/lib/api/commissions/track-commission-update-activity-log.ts:19-29

export async function trackCommissionStatusUpdatesByProgram({
  commissions,
  payouts,
  newStatus = "paid",
}: {
  commissions: Pick<
    Commission,
    "id" | "amount" | "earnings" | "status" | "programId"
  >[];
  payouts: Array<{ program: { id: string; workspaceId: string } }>;
  newStatus?: CommissionStatus;
}) {
  if (commissions.length === 0) {
    return;
  }
 
  const workspaceByProgram = new Map(
    payouts.map((p) => [p.program.id, p.program.workspaceId]),
  );
 
  const commissionsByProgram = groupBy(commissions, (c) => c.programId);
 

Sources: apps/web/lib/api/commissions/track-commission-update-activity-log.ts:103-124

Activity Log Querying and UI Rendering

Workspace and Partner API Endpoints

Activity log retrieval is divided between internal workspace management routes and partner-facing profile endpoints. The workspace route /api/activity-logs is protected by withWorkspace middleware, validating query parameters via getActivityLogsQuerySchema and fetching up to 100 entries ordered by creation date descending with user relations included.

export const GET = withWorkspace(async ({ workspace, searchParams }) => {
  const { resourceType, resourceId, parentResourceId, action } =
    getActivityLogsQuerySchema.parse(searchParams);
 
  const programId = getDefaultProgramIdOrThrow(workspace);
 
  const activityLogs = await prisma.activityLog.findMany({
    where: {
      programId,
      resourceType,
      resourceId,
      parentResourceId,
      action,
    },
    orderBy: {
      createdAt: "desc",
    },
    take: 100,
    include: {
      user: true,
    },
  });

Sources: apps/web/app/api/activity-logs/route.ts:13-34

When querying partner resources, the workspace endpoint automatically polyfills a initial partner.groupChanged activity log if a corresponding program enrollment exists, referencing the enrollment creation date and group details.

if (resourceType === "partner" && resourceId) {
  const programEnrollment = await prisma.programEnrollment.findUnique({
    where: {
      partnerId_programId: {
        partnerId: resourceId,
        programId,
      },
    },
    select: {
      createdAt: true,
      groupId: true,
      partnerGroup: {
        select: {
          name: true,
        },
      },
    },
  });
  if (programEnrollment) {
    parsedActivityLogs.push({
      id: uuid(),
      action: "partner.groupChanged",
      description: null,
      createdAt: programEnrollment.createdAt,
      user: null,
      changeSet: {
        group: {
          old: null,
          new: parsedActivityLogs[parsedActivityLogs.length - 1]?.changeSet
            ?.group?.old ?? {
            id: programEnrollment.groupId,
            name: programEnrollment.partnerGroup?.name,
          },
        },
      },
    });
  }

Sources: apps/web/app/api/activity-logs/route.ts:39-75

Conversely, the partner profile endpoint at /(ee)/api/partner-profile/programs/[programId]/activity-logs/route.ts is secured with withPartnerProfile and restricts querying strictly to the submittedLead resource type. It validates that the requested lead identifier belongs to the authenticated partner and program, and explicitly strips internal user identities (user: null) from the serialized response payload.

export const GET = withPartnerProfile(
  async ({ partner, params, searchParams }) => {
    const { resourceType, resourceId, action } =
      getActivityLogsQuerySchema.parse(searchParams);
 
    // Limit to submitted lead for now
    if (resourceType !== "submittedLead") {
      throw new DubApiError({
        code: "bad_request",
        message: "Resource type must be submitted lead.",
      });
    }
 
    const programEnrollment = await getProgramEnrollmentOrThrow({
      partnerId: partner.id,
      programId: params.programId,
      include: {},
    });
 
    // Check if the resource is a referral and belongs to the program and partner
    const lead = await prisma.submittedLead.findUnique({
      where: {
        id: resourceId,
        programId: programEnrollment.programId,
        partnerId: partner.id,
      },
      select: {
        id: true,
      },
    });
 
    if (!lead) {
      throw new DubApiError({
        code: "not_found",
        message: "Submitted lead not found.",
      });
    }
 
    const activityLogs = await prisma.activityLog.findMany({
      where: {
        programId: programEnrollment.programId,
        resourceType,
        resourceId,
        action,
      },
      orderBy: {
        createdAt: "desc",
      },
      take: 100,
    });
 
    // Hide the workspace user who made each change from the partner
    return NextResponse.json(
      z
        .array(activityLogSchema)
        .parse(activityLogs.map((log) => ({ ...log, user: null }))),
    );

Sources: apps/web/app/ee/api/partner-profile/programs/programId/activity-logs/route.ts:12-68

Warning

Requests to the partner profile activity logs endpoint with any resourceType other than submittedLead throw an immediate DubApiError with a bad_request status code.

Sources: apps/web/app/ee/api/partner-profile/programs/programId/activity-logs/route.ts:17-23

Client SWR Hook and Request Guardrails

The client-side useActivityLogs hook wraps SWR to fetch activity logs for a specific resource within the current workspace context retrieved from useWorkspace.

export function useActivityLogs({
  query,
  enabled = true,
}: {
  query?: GetActivityLogsQuery;
  enabled?: boolean;
} = {}) {
  const { id: workspaceId } = useWorkspace();
 
  const searchParams = query
    ? new URLSearchParams({
        workspaceId: workspaceId!,
        ...query,
      }).toString()
    : "";
 
  const requestEnabled =
    enabled &&
    workspaceId &&
    query?.resourceType &&
    (query?.parentResourceId || query?.resourceId);
 
  const { data, error, isLoading, mutate } = useSWR<ActivityLog[]>(
    requestEnabled && `/api/activity-logs?${searchParams}`,
    fetcher,
    {
      keepPreviousData: true,
    },
  );
 
  return {
    activityLogs: data,
    error,
    loading: isLoading,
    mutate,
  };

Sources: apps/web/lib/swr/use-activity-logs.ts:6-41

The hook evaluates an explicit requestEnabled boolean to determine whether SWR should execute the network request.

const requestEnabled =
  enabled &&
  workspaceId &&
  query?.resourceType &&
  (query?.parentResourceId || query?.resourceId);
 

Sources: apps/web/lib/swr/use-activity-logs.ts:22-27

Condition VariableType / RequirementDescriptionSources
enabledboolean (default: true)Optional toggle flag passed to the hook argumentapps/web/lib/swr/use-activity-logs.ts:8-11
workspaceIdstringActive workspace identifier resolved from useWorkspace()apps/web/lib/swr/use-activity-logs.ts:13-24
resourceTypestringMandatory property inside the query objectapps/web/lib/swr/use-activity-logs.ts:25-25
parentResourceId | resourceIdstringAt least one resource identifier must be provided in the queryapps/web/lib/swr/use-activity-logs.ts:26-26

Sources: apps/web/lib/swr/use-activity-logs.ts:8-27

UI Registry and Action Renderers

The activity log UI layer maps distinct action strings to specialized React components and UI icons using registries defined in activity-log-registry.tsx. The actor type is classified via getActorType, returning "USER" if a user relation is present or "SYSTEM" otherwise.

export type ActorType = "USER" | "SYSTEM";
 
type ActivityLogRenderer = (props: { log: ActivityLog }) => ReactNode;
 
export function getActorType(log: ActivityLog): ActorType {
  return log.user ? "USER" : "SYSTEM";
}

Sources: apps/web/ui/activity-logs/activity-log-registry.tsx:35-41

Partner status actions encompass a fixed set of lifecycle events mapped to status change renderers and dotted circle icons.

const PARTNER_STATUS_ACTIONS = [
  "partner_application.approved",
  "partner_application.rejected",
  "partner.banned",
  "partner.unbanned",
  "partner.deactivated",
  "partner.reactivated",
  "partner.archived",
  "partner.unarchived",
] as const satisfies readonly ActivityLogAction[];

Sources: apps/web/ui/activity-logs/activity-log-registry.tsx:24-33

Activity Action ConstantIcon ComponentRenderer ComponentSources
partner.groupChangedUserArrowRightPartnerGroupChangedRendererapps/web/ui/activity-logs/activity-log-registry.tsx:46-78
partner.rewardChangedMoneyBill2PartnerRewardChangedRendererapps/web/ui/activity-logs/activity-log-registry.tsx:47-82
partner.discountChangedDiscountCodePartnerDiscountChangedRendererapps/web/ui/activity-logs/activity-log-registry.tsx:48-86
link.rewardChangedMoneyBill2PartnerRewardChangedRendererapps/web/ui/activity-logs/activity-log-registry.tsx:49-90
link.discountChangedDiscountCodePartnerDiscountChangedRendererapps/web/ui/activity-logs/activity-log-registry.tsx:50-94
Partner Status Actions (8 types)CircleDottedPartnerStatusChangedRendererapps/web/ui/activity-logs/activity-log-registry.tsx:24-98
submittedLead.createdFileSendSubmittedLeadCreatedRendererapps/web/ui/activity-logs/activity-log-registry.tsx:55-102
Submitted Lead Status Actions (6 types)UserClockSubmittedLeadStatusChangedRendererapps/web/ui/activity-logs/activity-log-registry.tsx:56-115
Reward Actions (6 types)MoneyBill2 / Pen2 / CircleMinus / CirclePlusIconRewardActivityRendererapps/web/ui/activity-logs/activity-log-registry.tsx:63-128

Sources: apps/web/ui/activity-logs/activity-log-registry.tsx:24-128

Tip

If an activity log action lacks a registered icon mapping in ACTIVITY_LOG_ICONS, getActivityLogIcon automatically falls back to rendering a neutral informational icon (CircleInfo).

Sources: apps/web/ui/activity-logs/activity-log-registry.tsx:141-149

API Request and Webhook Logging

Request Capture and Endpoints

Incoming API requests and webhooks are recorded via logging utilities and exposed through workspace endpoints. The webhook capture utility parses request and response payloads, mapping incoming paths to specific actors using WEBHOOK_REQUEST_ACTORS_BY_PATH before recording logs with recordApiLog.

export async function captureWebhookLog({
  workspaceId,
  method,
  path,
  statusCode,
  duration,
  requestBody,
  responseBody,
  userAgent,
}: {
  workspaceId: string;
  method: string;
  path: keyof typeof WEBHOOK_REQUEST_ACTORS_BY_PATH;
  statusCode: number;
  duration: number;
  requestBody: unknown;
  responseBody: unknown;
  userAgent: string | null;
}) {
  const actor = WEBHOOK_REQUEST_ACTORS_BY_PATH[path];
 
  return await recordApiLog({
    workspaceId,
    method,
    path,
    routePattern: path,
    statusCode,
    duration,
    userAgent,
    requestBody,
    responseBody: await parseResponseBody(responseBody),
    tokenId: null,
    userId: actor.id,
    requestType: "webhook",
  });
}

Sources: apps/web/lib/api-logs/capture-webhook-log.ts:16-51

Workspace API routes process query parameters, enforce plan retention rules, and return JSON responses for request logs, counts, and timeseries aggregations.

Endpoint RouteHTTP MethodRequired PermissionsRequired PlanSources
/api/logsGETworkspaces.readAnyapps/web/app/api/logs/route.ts:9-40
/api/logs/:logIdGETworkspaces.readAnyapps/web/app/api/logs/logId/route.ts:9-44
/api/logs/timeseriesGETworkspaces.readAnyapps/web/app/api/logs/timeseries/route.ts:8-36
/api/logs/countGETworkspaces.readAnyapps/web/app/api/logs/count/route.ts:8-34
/api/webhooks/[webhookId]/eventsGETwebhooks.readbusiness, advanced, enterpriseapps/web/app/api/webhooks/webhookId/events/route.ts:7-33

Sources: apps/web/app/api/logs/route.ts:9-40, apps/web/app/api/logs/logId/route.ts:9-44, apps/web/app/api/logs/timeseries/route.ts:8-36, apps/web/app/api/logs/count/route.ts:8-34, apps/web/app/api/webhooks/webhookId/events/route.ts:7-33

Was this page helpful?