Quickstart

From install to your first answer.

Seven steps in an existing Next.js app. You need Node 20, PostgreSQL 14, React 19 and Next.js 15 or newer.

  1. Install the package

    react and react-dom are peer dependencies that a Next.js app already has.

    terminal
    npm install better-helpdesk pg
  2. Create the schema

    The CLI opens one connection, creates the helpdesk schema and its tables, and exits, so it fits into a release step next to your own migrations.

    terminal
    HELPDESK_DATABASE_URL=postgres://… npx better-helpdesk-migrate
  3. Let Next.js compile the package

    Both UIs request paths with a trailing slash. Without it, every call pays a redirect first, and a widget on another origin fails its CORS preflight.

    next.config.mjs
    import { withHelpdesk } from 'better-helpdesk/next';
    
    export default withHelpdesk({ trailingSlash: true });
  4. Build the helpdesk and mount the handler

    identify runs on every request. Return null for an anonymous visitor. adminUrl must be the URL your team opens: the handler refuses mutations from any other origin.

    lib/helpdesk.ts
    import { buildHelpdesk, postgresAdapter } from 'better-helpdesk';
    import pg from 'pg';
    
    const pool = new pg.Pool({ connectionString: process.env.HELPDESK_DATABASE_URL });
    
    export const helpdesk = buildHelpdesk({
      db: postgresAdapter({ pool }),
      referencePrefix: 'ACME',
      adminUrl: 'https://app.example.com/helpdesk/',
      inboxes: { support: { receipt: true } },
      identify: async request => {
        const session = await getSession(request); // however your app does it
        if (!session) return null; // an anonymous visitor
        return {
          user: { id: session.user.id, email: session.user.email, emailVerified: true, name: session.user.name },
          orgs: session.orgs.map(org => ({ id: org.id, name: org.name })),
          isAgent: session.user.role === 'support',
        };
      },
    });
  5. Mount the route handler

    One handler serves the widget, the agent UI, inbound mail and jobs under your basePath.

    app/api/helpdesk/[...slug]/route.ts
    import { helpdesk } from '@/lib/helpdesk';
    
    const handle = (request: Request) => helpdesk.handler(request);
    export { handle as GET, handle as POST, handle as PATCH, handle as DELETE, handle as OPTIONS };
  6. Render the agent UI

    Put the page behind your own agent check as well. The API refuses anyone who is not an agent either way.

    app/helpdesk/[[...slug]]/page.tsx
    import { HelpdeskAdmin } from 'better-helpdesk/admin';
    
    export default function Page() {
      return <HelpdeskAdmin api="/api/helpdesk" basePath="/helpdesk" locale="en" />;
    }
  7. Add the widget

    Send a message from the widget, open /helpdesk as an agent and answer it. That is the whole loop.

    app/layout.tsx
    import { HelpdeskWidget } from 'better-helpdesk/widget';
    
    export function Layout({ children }) {
      return (
        <>
          {children}
          <HelpdeskWidget inbox="support" locale="en" />
        </>
      );
    }

Next: the guides.

Email in and out, jobs and retention, storage, AI, theming and signed-in users on another origin are in the README.