### Navigate to the cli folder Source: https://github.com/dubinc/dub/blob/main/packages/cli/README.md First step in both the development and production-like setup: clone the repository, install dependencies, and navigate to the cli folder. ```bash cd packages/cli ``` -------------------------------- ### Verify the installation Source: https://github.com/dubinc/dub/blob/main/packages/cli/README.md Fourth step in the production-like setup: verify the installation by checking the version. ```bash dub -v ``` -------------------------------- ### Development commands for Dub HubSpot App Source: https://github.com/dubinc/dub/blob/main/packages/hubspot-app/README.md Run these commands in the packages/hubspot-app directory to manage the project. `pnpm install` installs dependencies. `pnpm hs project upload --account=` uploads the project to the specified HubSpot account. `pnpm hs project dev` sets up a test environment and starts local development. `pnpm hs project install-deps` installs all project dependencies. `pnpm hs project add` adds a new feature to the app. `pnpm hs account list` lists all connected accounts. ```bash # Install dependencies pnpm install # Upload the project to HubSpot account pnpm hs project upload --account= # Set up a test environment and start local development pnpm hs project dev # Install all project dependencies pnpm hs project install-deps # Add new feature to app pnpm hs project add # List all accounts connected pnpm hs account list ``` -------------------------------- ### Install Chromium for Playwright Source: https://github.com/dubinc/dub/blob/main/apps/web/playwright/README.md One-time setup to install the Chromium browser required by Playwright. ```sh pnpm --filter web exec playwright install chromium ``` -------------------------------- ### See all available commands and options Source: https://github.com/dubinc/dub/blob/main/packages/cli/README.md Fourth step in the development setup: see all available commands and options. ```bash pnpm start help ``` -------------------------------- ### Install Appwrite Node.js SDK Source: https://github.com/dubinc/dub/blob/main/apps/web/guides/appwrite.md Run this command in your Next.js app to install the Appwrite Node.js SDK, which is required for the integration. ```bash npm i node-appwrite ``` -------------------------------- ### Install @dub/utils with pnpm Source: https://github.com/dubinc/dub/blob/main/packages/utils/README.md Install the @dub/utils package using pnpm. ```bash pnpm i @dub/utils ``` -------------------------------- ### Build the package Source: https://github.com/dubinc/dub/blob/main/packages/cli/README.md Second step in the production-like setup: build the package using pnpm build. ```bash pnpm build ``` -------------------------------- ### Run commands Source: https://github.com/dubinc/dub/blob/main/packages/cli/README.md Fifth step in the production-like setup: run commands using dub. ```bash dub [command] ``` -------------------------------- ### Install @dub/better-auth with package managers Source: https://github.com/dubinc/dub/blob/main/apps/web/guides/better-auth.md Install the @dub/better-auth plugin using npm, yarn, pnpm, or bun. ```bash npm install @dub/better-auth ``` ```bash yarn add @dub/better-auth ``` ```bash pnpm add @dub/better-auth ``` ```bash bun add @dub/better-auth ``` -------------------------------- ### Install @dub/ui with pnpm Source: https://github.com/dubinc/dub/blob/main/packages/ui/README.md Install the @dub/ui package using the pnpm package manager. ```bash pnpm i @dub/ui ``` -------------------------------- ### Install @dub/embed-react with pnpm Source: https://github.com/dubinc/dub/blob/main/packages/embeds/react/README.md Use this command to install the @dub/embed-react package. It is the only installation method shown. ```bash pnpm i @dub/embed-react ``` -------------------------------- ### Dub API Request Example in Node.js Source: https://github.com/dubinc/dub/blob/main/apps/web/guides/rest-api.md Example of making a GET request to the Dub API using Node.js fetch. Replace the API key with your own. ```javascript const response = await fetch("https://api.dub.co/links", { method: "GET", headers: { Authorization: "Bearer dub_xxxxxx", }, }); const data = await response.json(); ``` -------------------------------- ### Start MailHog with Docker Compose Source: https://github.com/dubinc/dub/blob/main/apps/web/playwright/README.md Starts MailHog, used for email verification during signup tests. ```sh docker-compose -f apps/web/docker-compose.yml up -d mailhog ``` -------------------------------- ### Build the package in watch mode Source: https://github.com/dubinc/dub/blob/main/packages/cli/README.md Second step in the development setup: build the package in watch mode using pnpm dev. ```bash pnpm dev ``` -------------------------------- ### Install @dub/analytics package Source: https://github.com/dubinc/dub/blob/main/apps/web/guides/react.md Use your package manager to install the @dub/analytics package. This is the first step to integrate Dub analytics into your React project. ```bash npm install @dub/analytics ``` -------------------------------- ### Run Stripe app locally Source: https://github.com/dubinc/dub/blob/main/packages/stripe-app/README.md Starts the Stripe app locally for development. Run this command from the packages/stripe-app directory. ```bash stripe apps start ``` -------------------------------- ### Link the package globally Source: https://github.com/dubinc/dub/blob/main/packages/cli/README.md Third step in the production-like setup: link the package globally using npm link. ```bash npm link ``` -------------------------------- ### Initialize component in Next.js root layout Source: https://github.com/dubinc/dub/blob/main/apps/web/guides/react.md Add the component (imported as DubAnalytics) to your root layout or any page where you want to track conversions. This example shows the Next.js root layout usage. ```jsx import { Analytics as DubAnalytics } from '@dub/analytics/react'; export default function RootLayout({ children, }: Readonly<{ children: React.ReactNode; }>) { return ( {children} ); } ``` -------------------------------- ### Run a command in a separate terminal Source: https://github.com/dubinc/dub/blob/main/packages/cli/README.md Third step in the development setup: in a separate terminal, navigate to the cli folder again and run an available command. ```bash pnpm start [command] ``` -------------------------------- ### Track lead with Dub TypeScript SDK Source: https://github.com/dubinc/dub/blob/main/apps/web/guides/manual-track-lead.md Use the Dub TypeScript SDK in Node.js to track a lead event. Requires a DUB_API_KEY environment variable or a token option. The example sends lead details including clickId, eventName, and customer information. ```typescript import { Dub } from "dub"; const dub = new Dub({ // optional, defaults to the DUB_API_KEY environment variable token: process.env.DUB_API_KEY, }); await dub.track.lead({ clickId: "rLnWe1uz9t282v7g", eventName: "Sign up", customerExternalId: "cus_oFUYbZYqHFR0knk0MjsMC6b0", customerName: "John Doe", customerEmail: "john.doe@example.com", customerAvatar: "https://example.com/avatar.png", }); ``` -------------------------------- ### Send Lead to Dub with dub.track.lead() Source: https://github.com/dubinc/dub/blob/main/apps/web/guides/appwrite.md Creates a Dub client using the NEXT_DUB_API_KEY environment variable and exports addDubLead() to track a 'Sign Up' lead event. The function takes an Appwrite user object and a Dub click ID, sending customer details to Dub. ```javascript import type { Models } from "node-appwrite"; import { Dub } from "dub"; const dub = new Dub({ token: process.env.NEXT_DUB_API_KEY, }); export function addDubLead( user: Models.User, dub_id: string, ) { dub.track.lead({ clickId: dub_id, eventName: "Sign Up", customerExternalId: user.$id, customerName: user.name, customerEmail: user.email, }); } ``` -------------------------------- ### Add Appwrite and Dub environment variables Source: https://github.com/dubinc/dub/blob/main/apps/web/guides/appwrite.md Add these environment variables to your app. Replace , , and with your actual values. ```bash NEXT_PUBLIC_APPWRITE_ENDPOINT=https://cloud.appwrite.io/v1 NEXT_PUBLIC_APPWRITE_PROJECT= NEXT_APPWRITE_KEY= NEXT_DUB_API_KEY= ``` -------------------------------- ### Sign Up with Appwrite and Track Dub Lead Source: https://github.com/dubinc/dub/blob/main/apps/web/guides/appwrite.md Use this server action in the /auth page to handle user sign-up with Appwrite. It creates an account and email/password session, sets a 'my-custom-session' cookie, and checks for a 'dub_id' cookie to send a lead event to Dub before deleting it. The form component redirects to /auth/success if a user is already logged in. ```javascript import { ID } from "node-appwrite"; import { createAdminClient, getLoggedInUser } from "@/lib/server/appwrite"; import { cookies } from "next/headers"; import { redirect } from "next/navigation"; import { addDubLead } from "@/lib/server/dub"; async function signUpWithEmail(formData: any) { "use server"; // Get sign up info from form const email = formData.get("email"); const password = formData.get("password"); const name = formData.get("name"); // Create account and session using Appwrite const { account } = await createAdminClient(); const user = await account.create(ID.unique(), email, password, name); const session = await account.createEmailPasswordSession(email, password); (await cookies()).set("my-custom-session", session.secret, { path: "/", httpOnly: true, sameSite: "strict", secure: true, }); // Check if Dub ID is present in cookies and track lead if found const dub_id = (await cookies()).get("dub_id")?.value; if (dub_id) { addDubLead(user, dub_id); (await cookies()).delete("dub_id"); } // Redirect to success page redirect("/auth/success"); } export default async function SignUpPage() { // Verify active user session and redirect to success page if found const user = await getLoggedInUser(); if (user) redirect("/auth/success"); return ( <>
); } ``` -------------------------------- ### Dub Analytics Script Tag for Framer Source: https://github.com/dubinc/dub/blob/main/apps/web/guides/framer.md Paste this script into the 'Start of head tag' section in Framer's Custom Code settings. It loads the Dub analytics script from dubcdn.com with defer attribute. ```html ``` -------------------------------- ### Initialize Dub Embed with Dub.init Source: https://github.com/dubinc/dub/blob/main/packages/embeds/core/src/example/index.html Initializes the Dub embed on a page after the DOM is ready. Replace the placeholder token with your actual embed token. ```javascript document.addEventListener("DOMContentLoaded", () => { const embed = Dub.init({ root: document.getElementById("root"), token: "dub_embed_", // Add your token here }); }); ``` -------------------------------- ### Run basic dev seed script Source: https://github.com/dubinc/dub/blob/main/README.md Basic seeding command that adds development data without deleting existing data. Run from the apps/web directory. ```bash cd apps/web pnpm run script dev/seed ``` -------------------------------- ### Stripe Payment Link with client_reference_id Source: https://github.com/dubinc/dub/blob/main/apps/web/guides/stripe-payment-links.md Append the Dub click ID (prefixed with dub_id_) as the client_reference_id query parameter to your Stripe Payment Link URL. This is used when you have a marketing site that redirects users first, after installing the @dub/analytics SDK to capture the dub_id. ```javascript https://buy.stripe.com/xxxxxx?client_reference_id=dub_id_xxxxxxxxxxxxxx ``` -------------------------------- ### Run E2E tests in different modes Source: https://github.com/dubinc/dub/blob/main/apps/web/playwright/README.md Commands to run tests headless, with a visible browser, or in interactive UI mode. The dev server must be running first. ```sh pnpm --filter web test:e2e ``` ```sh pnpm --filter web test:e2e:headed ``` ```sh pnpm --filter web test:e2e:ui ``` -------------------------------- ### Create Appwrite Session and Admin Clients Source: https://github.com/dubinc/dub/blob/main/apps/web/guides/appwrite.md Defines createSessionClient() and createAdminClient() for server-side rendering. createSessionClient() reads the session cookie 'my-custom-session' and throws an error if missing. createAdminClient() uses an API key for admin operations. Both return an account getter for Appwrite Account methods. ```javascript "use server"; import { Client, Account } from "node-appwrite"; import { cookies } from "next/headers"; export async function createSessionClient() { const client = new Client() .setEndpoint(process.env.NEXT_PUBLIC_APPWRITE_ENDPOINT as string) .setProject(process.env.NEXT_PUBLIC_APPWRITE_PROJECT as string); const session = (await cookies()).get("my-custom-session"); if (!session || !session.value) { throw new Error("No session"); } client.setSession(session.value); return { get account() { return new Account(client); }, }; } export async function createAdminClient() { const client = new Client() .setEndpoint(process.env.NEXT_PUBLIC_APPWRITE_ENDPOINT as string) .setProject(process.env.NEXT_PUBLIC_APPWRITE_PROJECT as string) .setKey(process.env.NEXT_APPWRITE_KEY as string); return { get account() { return new Account(client); }, }; } ``` -------------------------------- ### Extend @dub/analytics with Clerk's useUser hook and add to root layout Source: https://github.com/dubinc/dub/blob/main/apps/web/guides/clerk.md Extends the @dub/analytics package with a trackLead server action and adds the DubAnalytics component to the root layout. The component uses Clerk's useUser hook to track the lead event when the user is loaded and hasn't been persisted to Dub yet. The root layout must include the DubAnalytics component to enable tracking. ```javascript "use client"; import { trackLead } from "@/actions/track-lead"; import { useUser } from "@clerk/nextjs"; import { Analytics, AnalyticsProps } from "@dub/analytics/react"; import { useEffect } from "react"; export function DubAnalytics(props: AnalyticsProps) { const { user } = useUser(); useEffect(() => { if (!user || user.publicMetadata.dubClickId) return; // if the user is loaded but hasn't been persisted to Dub yet, track the lead event trackLead({ id: user.id, name: user.fullName!, email: user.primaryEmailAddress?.emailAddress, avatar: user.imageUrl, }).then(async (res) => { if (res.ok) await user.reload(); else console.error(res.error); }); // you can also use an API route instead of a server action /* fetch("/api/track-lead", { method: "POST", body: JSON.stringify({ id: user.id, name: user.fullName, email: user.primaryEmailAddress?.emailAddress, avatar: user.imageUrl, }), }).then(res => { if (res.ok) await user.reload(); else console.error(res.statusText); }); */ }, [user]); return ; } ``` ```javascript import { DubAnalytics } from "@/components/dub-analytics"; export default function RootLayout({ children, }: { children: React.ReactNode; }) { return ( {children} ); } ``` -------------------------------- ### Tinybird project directory structure Source: https://github.com/dubinc/dub/blob/main/packages/tinybird/README.md Shows the file layout for the Tinybird project, including datasources, endpoints, and pipes. Use this to understand where each component is defined. ```bash ├── datasources │ └── dub_click_events.datasource │ └── dub_click_events_mv.datasource │ └── dub_links_metadata.datasource │ └── dub_links_metadata_latest.datasource ├── endpoints │ ├── browser.pipe │ ├── city.pipe │ ├── clicks.pipe │ ├── country.pipe │ ├── device.pipe │ ├── os.pipe │ └── referer.pipe │ └── timeseries.pipe │ └── top_links.pipe │ └── top_urls.pipe ├── pipes │ ├── dub_click_events_pipe.pipe │ ├── dub_links_metadata_pipe.pipe ``` -------------------------------- ### Track lead conversion in Auth0 afterCallback with dub.track.lead Source: https://github.com/dubinc/dub/blob/main/apps/web/guides/auth0.md Configure Auth0's afterCallback to track lead conversion events in Dub. Checks if the user is a new sign up, reads the dub_id cookie, sends a lead event via dub.track.lead, and deletes the cookie. Requires the dub client and a getUser/createUser implementation. ```javascript import { handleAuth, handleCallback, type Session } from "@auth0/nextjs-auth0"; import { cookies } from "next/headers"; import { dub } from "@/lib/dub"; const afterCallback = async (req: Request, session: Session) => { const userExists = await getUser(session.user.email); if (!userExists) { createUser(session.user); // check if dub_id cookie is present const clickId = cookies().get("dub_id")?.value; if (clickId) { // send lead event to Dub await dub.track.lead({ clickId, eventName: "Sign Up", customerExternalId: session.user.id, customerName: session.user.name, customerEmail: session.user.email, customerAvatar: session.user.image, }); // delete the dub_id cookie cookies().set("dub_id", "", { expires: new Date(0), }); } return session; } }; export default handleAuth({ callback: handleCallback({ afterCallback }), }); ``` -------------------------------- ### Set environment variables in apps/web/.env Source: https://github.com/dubinc/dub/blob/main/apps/web/playwright/README.md Required environment variables for login and signup tests. SMTP must point to MailHog, and RESEND_API_KEY must not be set. ```sh # Required for login tests (seeded user) E2E_PARTNER_EMAIL=partner1@dub-internal-test.com E2E_PARTNER_PASSWORD=password # SMTP must point to MailHog — signup tests read OTP emails from it SMTP_HOST=localhost SMTP_PORT=1025 # RESEND_API_KEY must NOT be set, otherwise emails go to Resend instead of MailHog ``` -------------------------------- ### Track sale with Dub TypeScript SDK Source: https://github.com/dubinc/dub/blob/main/apps/web/guides/manual-track-sale.md Use the Dub TypeScript SDK in Node.js to track a sale. The token is optional and defaults to the DUB_API_KEY environment variable. The amount is in cents. ```typescript import { Dub } from "dub"; const dub = new Dub({ // optional, defaults to the DUB_API_KEY environment variable token: process.env.DUB_API_KEY, }); await dub.track.sale({ customerExternalId: "cus_oFUYbZYqHFR0knk0MjsMC6b0", amount: 3000, // sale amount in cents currency: "usd", paymentProcessor: "stripe", eventName: "Invoice paid", invoiceId: "INV_1234567890", }); ``` -------------------------------- ### Add environment variables for Clerk and Dub Source: https://github.com/dubinc/dub/blob/main/apps/web/guides/clerk.md Required environment variables for Clerk and Dub integration. Obtain Clerk keys from the Clerk dashboard and the Dub API key from d.to/tokens. ```bash # get it here: https://dashboard.clerk.com/apps/new NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=your_publishable_key CLERK_SECRET_KEY=your_secret_key # get it here: https://d.to/tokens DUB_API_KEY=your_api_key ``` -------------------------------- ### NextAuth signIn event to track lead with dub.track.lead Source: https://github.com/dubinc/dub/blob/main/apps/web/guides/next-auth.md Configure the NextAuth signIn event to detect new user sign-ups, check for the dub_id cookie, send a lead event to Dub, and delete the cookie. The user's unique ID becomes the source of truth for future events. ```javascript // app/api/auth/[...nextauth]/options.ts import type { NextAuthOptions } from "next-auth"; import { cookies } from "next/headers"; import { dub } from "@/lib/dub"; export const authOptions: NextAuthOptions = { ...otherAuthOptions, // your other NextAuth options events: { async signIn(message) { // if it's a new sign up if (message.isNewUser) { // check if dub_id cookie is present const dub_id = cookies().get("dub_id")?.value; if (dub_id) { // send lead event to Dub await dub.track.lead({ clickId: dub_id, eventName: "Sign Up", customerExternalId: user.id, customerName: user.name, customerEmail: user.email, customerAvatar: user.image, }); // delete the dub_id cookie cookies().set("dub_id", "", { expires: new Date(0), }); } } }, }, }; ``` -------------------------------- ### Run dev seed script with --truncate Source: https://github.com/dubinc/dub/blob/main/README.md Seeds the database after truncating all existing data. The script will ask for confirmation before deleting any data. ```bash cd apps/web pnpm run script dev/seed --truncate ``` -------------------------------- ### Configure dubAnalytics plugin in auth.ts Source: https://github.com/dubinc/dub/blob/main/apps/web/guides/better-auth.md Add the dubAnalytics plugin to your Better Auth configuration, passing a Dub client instance. ```ts import { dubAnalytics } from "@dub/better-auth"; import { betterAuth } from "better-auth"; import { Dub } from "dub"; const dub = new Dub(); export const auth = betterAuth({ plugins: [ dubAnalytics({ dubClient: dub, }), ], }); ``` -------------------------------- ### Track lead conversion in Supabase auth callback Source: https://github.com/dubinc/dub/blob/main/apps/web/guides/supabase.md In the Supabase auth callback route, exchange the authorization code for a session, then check for the `dub_id` cookie and whether the user was created in the last 10 minutes. If both conditions hold, track a lead event with `dub.track.lead` and delete the cookie. Redirects to the `next` URL on success or to an error page on failure. ```typescript // app/api/auth/callback/route.ts import { dub } from "@/lib/dub"; import { createClient } from "@/lib/supabase/server"; import { waitUntil } from "@vercel/functions"; import { cookies } from "next/headers"; import { NextResponse } from "next/server"; export async function GET(request: Request) { const { searchParams, origin } = new URL(request.url); const code = searchParams.get("code"); // if "next" is in param, use it as the redirect URL const next = searchParams.get("next") ?? "/"; if (code) { const supabase = createClient(cookies()); const { data, error } = await supabase.auth.exchangeCodeForSession(code); if (!error) { const { user } = data; const dub_id = cookies().get("dub_id")?.value; // if the user is created in the last 10 minutes, consider them new const isNewUser = new Date(user.created_at) > new Date(Date.now() - 10 * 60 * 1000); // if the user is new and has a dub_id cookie, track the lead if (dub_id && isNewUser) { waitUntil( dub.track.lead({ clickId: dub_id, eventName: "Sign Up", customerExternalId: user.id, customerName: user.user_metadata.name, customerEmail: user.email, customerAvatar: user.user_metadata.avatar_url, }), ); // delete the clickId cookie cookies().delete("dub_id"); } return NextResponse.redirect(`${origin}${next}`); } } // return the user to an error page with instructions return NextResponse.redirect(`${origin}/auth/auth-code-error`); } ``` -------------------------------- ### Stripe Pricing Table with client-reference-id Source: https://github.com/dubinc/dub/blob/main/apps/web/guides/stripe-payment-links.md Pass the Dub click ID as the client-reference-id attribute on the stripe-pricing-table element when using Stripe Pricing Tables. Replace {{PRICING_TABLE_ID}} with your actual pricing table ID and the publishable-key with your own key. ```html

We offer plans that help any business!

``` -------------------------------- ### Create Stripe customer with Dub metadata Source: https://github.com/dubinc/dub/blob/main/apps/web/guides/stripe-customers.md Use this snippet when creating a Stripe customer outside the checkout session flow. It passes the user's database ID as `dubCustomerExternalId` and the click event ID (`dub_id`) as `dubClickId` in the `metadata` field so Dub can associate purchase details with the original click. ```javascript import { stripe } from "@/lib/stripe"; const user = { id: "user_123", email: "user@example.com", teamId: "team_xxxxxxxxx", }; const dub_id = req.headers.get("dub_id"); await stripe.customers.create({ email: user.email, name: user.name, metadata: { dubCustomerExternalId: user.id, dubClickId: dub_id, }, }); ``` -------------------------------- ### Track lead with REST API Source: https://github.com/dubinc/dub/blob/main/apps/web/guides/manual-track-lead.md Use the REST API to track a lead by sending a POST request to https://api.dub.co/track/lead. Include your API key in the Authorization header and pass the lead data as JSON in the request body. ```javascript const response = await fetch("https://api.dub.co/track/lead", { method: "POST", headers: { Authorization: "Bearer dub_xxxxxx", "Content-Type": "application/json", }, body: JSON.stringify({ clickId: "rLnWe1uz9t282v7g", eventName: "Sign up", customerExternalId: "cus_oFUYbZYqHFR0knk0MjsMC6b0", customerName: "John Doe", customerEmail: "john.doe@example.com", customerAvatar: "https://example.com/avatar.png", }), }); const data = await response.json(); ``` -------------------------------- ### Track sale with Dub REST API Source: https://github.com/dubinc/dub/blob/main/apps/web/guides/manual-track-sale.md Use the REST API to track a sale by sending a POST request to https://api.dub.co/track/sale. Include your API key in the Authorization header and pass the sale data as JSON in the request body. ```javascript const response = await fetch("https://api.dub.co/track/sale", { method: "POST", headers: { Authorization: "Bearer dub_xxxxxx", "Content-Type": "application/json", }, body: JSON.stringify({ customerExternalId: "cus_oFUYbZYqHFR0knk0MjsMC6b0", amount: 3000, // sale amount in cents paymentProcessor: "stripe", eventName: "Invoice paid", invoiceId: "INV_1234567890", currency: "usd", }), }); const data = await response.json(); ``` -------------------------------- ### Send lead events with @segment/analytics-node Source: https://github.com/dubinc/dub/blob/main/apps/web/guides/segment-track-lead.md Use the Segment Node.js SDK to track a 'Sign Up' event. Ensure the 'clickId' is retrieved from cookies and included in the traits payload to enable proper mapping in Dub. ```javascript import { Analytics } from "@segment/analytics-node"; const segment = new Analytics({ writeKey: "", }); const cookieStore = await cookies(); const clickId = cookieStore.get("dub_id")?.value; segment.track({ userId: id, event: "Sign Up", context: { traits: { name, email, avatar, clickId, }, }, integrations: { All: true, }, }); ``` -------------------------------- ### Add Dub client-side script to HTML Source: https://github.com/dubinc/dub/blob/main/apps/web/guides/manual-client-sdk.md Add this script to the section of your HTML file to load the Dub client-side script. The defer attribute ensures the script executes after the document has been parsed. ```html ``` -------------------------------- ### Add Dub client-side script to GTM Custom HTML tag Source: https://github.com/dubinc/dub/blob/main/apps/web/guides/gtm-client-sdk.md Paste this script into the Custom HTML field of a new GTM tag. It initializes the dubAnalytics global with trackClick, trackLead, and trackSale methods, then loads the Dub analytics script from https://www.dubcdn.com/analytics/script.js with defer. Configure the tag to fire on All Pages and publish it as 'Dub Analytics'. ```html ``` -------------------------------- ### Run API tests with Playwright Source: https://github.com/dubinc/dub/blob/main/apps/web/playwright/README.md Commands to run API tests using the api project. The -g flag filters by test title regex. ```sh pnpm --filter web test:e2e --project=api ``` ```sh pnpm --filter web test:e2e --project=api playwright/api/tags/tags.spec.ts ``` ```sh pnpm --filter web test:e2e --project=api playwright/api/tags/tags.spec.ts -g "POST /tags" ```