Integrate Bolt.new with Zendesk by getting an API token from Admin → Channels → API, storing it in .env with your Zendesk subdomain and agent email, and calling the Zendesk Support REST API through a Next.js API route using Basic auth (email/token). Build support ticket dashboards, create tickets from Bolt contact forms, and embed the Zendesk Web Widget for live chat. The Web Widget works in Bolt's WebContainer preview; API calls are outbound and work without deployment too.
Building Custom Zendesk Support Experiences in Bolt.new
Zendesk is the market leader in customer support software with over 100,000 paying customers and the largest ecosystem of integrations and apps. For Bolt developers building products that need support functionality, Zendesk offers two distinct integration paths: the REST API for custom ticket management and dashboards, and the Web Widget for embedded live chat. Both are worth understanding because they address different use cases — the API is for building custom interfaces and syncing data, while the Widget is for adding a support chat button to your existing Bolt app with minimal effort.
The simplest Zendesk integration in Bolt is the Web Widget. Add a single JavaScript snippet to your Next.js layout, set a few configuration options, and your app has a professional-looking chat button in the bottom-right corner that connects to your Zendesk team. The widget works directly in Bolt's WebContainer preview since it's just a client-side script — no API calls, no server setup. Users clicking the chat icon can start a conversation with your support team without leaving your app.
For custom ticket workflows — creating tickets from specific forms, building an admin dashboard showing support queue status, or syncing ticket data to your own database — the Zendesk Support REST API is the right tool. All API calls use Basic authentication with your agent email and API token (not your password). The API base URL is https://{subdomain}.zendesk.com/api/v2/ where subdomain is the part before .zendesk.com in your account URL. All endpoints are standard REST with JSON responses. Since all Zendesk API calls are outbound HTTPS requests from your server-side routes, they work without any issues from Bolt's WebContainer development preview.
Integration method
Bolt generates the Zendesk integration through conversation — describe what support features you need and Bolt writes the API routes and React components. Zendesk uses HTTP Basic authentication with your agent email and API token, making it straightforward to implement in server-side routes. All Zendesk API calls run through Next.js routes to keep credentials out of the browser. The Zendesk Web Widget (for live chat) can be added as a script tag that works in Bolt's WebContainer preview immediately. Zendesk webhooks for real-time ticket updates require a deployed public URL.
Prerequisites
- A Zendesk Support account (free trial available, or any paid plan with API access)
- Your Zendesk subdomain (the part before .zendesk.com in your account URL, e.g., 'mycompany' from mycompany.zendesk.com)
- A Zendesk API token from Admin Center → Apps and Integrations → APIs → Zendesk API → Settings tab → Token Access
- Your Zendesk agent email address (used alongside the API token for Basic auth)
- A Next.js project in Bolt.new — prompt 'Create a Next.js app' if starting fresh
Step-by-step guide
Get your Zendesk API token and configure authentication
Get your Zendesk API token and configure authentication
Log in to your Zendesk account and navigate to the Admin Center. You can find Admin Center at the bottom of the left sidebar (the gear icon) or directly at https://your-subdomain.zendesk.com/admin. In Admin Center, go to Apps and Integrations → APIs → Zendesk API. On the Settings tab, enable the Token Access toggle if it's not already on. Click Add API token, optionally add a description, and click Create. The token is displayed once — copy it. If you navigate away before copying, you'll need to create a new token. Zendesk's API uses HTTP Basic authentication. The username is your agent email address followed by /token (literally the string '/token', not a token value). The password is your API token. When combined with Base64 encoding for the Authorization header, the format is: Authorization: Basic base64(email/token:api_token). In Node.js, use Buffer.from('email@example.com/token:your_api_token').toString('base64'). In your Bolt project, store three values in .env.local: ZENDESK_SUBDOMAIN (e.g., 'mycompany'), ZENDESK_EMAIL (your agent email), and ZENDESK_API_TOKEN (the token you copied). The subdomain and email are not strictly secret but treat them as server-side variables anyway to keep your configuration consistent. The API token must be server-side only — it grants full API access to your Zendesk account. For the Web Widget (step 4), you also need NEXT_PUBLIC_ZENDESK_KEY which is a public key safe to expose in client-side code — it's different from the API token and only controls which Zendesk account the widget connects to.
Set up Zendesk API integration in my Next.js app. Add ZENDESK_SUBDOMAIN, ZENDESK_EMAIL, and ZENDESK_API_TOKEN to .env.local as server-side placeholders. Also add NEXT_PUBLIC_ZENDESK_KEY as a placeholder for the Web Widget. Create lib/zendesk.ts with a zendeskFetch helper that builds the Basic auth header from email+token and constructs the full API URL using the subdomain.
Paste this in Bolt.new chat
1// .env.local2ZENDESK_SUBDOMAIN=your_subdomain_here3ZENDESK_EMAIL=your_agent_email@company.com4ZENDESK_API_TOKEN=your_api_token_here5# Public key for Web Widget (safe to expose in client code)6NEXT_PUBLIC_ZENDESK_KEY=your_zendesk_widget_key78// lib/zendesk.ts9export function getZendeskAuth(): string {10 const email = process.env.ZENDESK_EMAIL!;11 const token = process.env.ZENDESK_API_TOKEN!;12 return Buffer.from(`${email}/token:${token}`).toString('base64');13}1415export async function zendeskFetch(16 endpoint: string,17 method: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' = 'GET',18 body?: Record<string, unknown>19): Promise<Response> {20 const subdomain = process.env.ZENDESK_SUBDOMAIN!;21 const url = `https://${subdomain}.zendesk.com/api/v2${endpoint}`;2223 return fetch(url, {24 method,25 headers: {26 Authorization: `Basic ${getZendeskAuth()}`,27 'Content-Type': 'application/json',28 Accept: 'application/json',29 },30 ...(body ? { body: JSON.stringify(body) } : {}),31 });32}Pro tip: The Zendesk Basic auth format is unusual — the username must be email@domain.com/token (with '/token' appended, as a literal string), not just the email address. Using just the email as the username will fail with 401 even with a valid API token.
Expected result: Your .env.local contains Zendesk credentials and the zendeskFetch utility is ready. Test it by calling GET /tickets.json?per_page=5 — it should return a JSON list of your recent tickets.
Create support tickets from Bolt contact forms
Create support tickets from Bolt contact forms
The Zendesk Tickets API creates tickets via POST /api/v2/tickets.json with a ticket object in the request body. The minimum required fields are subject and a comment object with a body string. The requester field (email and name) identifies who submitted the ticket — if a user with that email already exists in Zendesk, the ticket is linked to their account. If not, Zendesk creates a new end-user record automatically. Useful optional fields for ticket creation: priority (urgent, high, normal, low), type (question, incident, problem, task), tags (array of strings for categorization and routing), assignee_id (Zendesk agent ID to assign the ticket to), and group_id (support team/group to assign it to). For contact forms from your Bolt app, setting a tag like 'bolt-contact-form' is valuable for filtering tickets by source in Zendesk. The API response returns the created ticket with its id (numeric), status (new), url, and all other fields. The ticket id is what you display to the user as their reference number. You can also use it to link subsequent comments or status checks to the original ticket. For attachments (like screenshots or files the user uploads with their ticket), use Zendesk's two-step upload process: POST to /api/v2/uploads.json to upload the file and get a token, then include the token in the ticket creation request under comment.uploads. This is optional — most contact forms work fine with text-only ticket creation. All these operations are outbound POST and GET requests to Zendesk's API — they work from Bolt's WebContainer without any deployment required.
Create the Zendesk ticket creation API route. Build app/api/zendesk/tickets/create/route.ts that accepts a POST with requesterName, requesterEmail, subject, message, and optional priority (low/normal/high/urgent). Create a Zendesk ticket via the API. Tag the ticket with 'bolt-contact-form'. Return the ticket id, status, and created_at timestamp. Also build a SupportContactForm component that posts to this route and shows the ticket ID in a success state.
Paste this in Bolt.new chat
1// app/api/zendesk/tickets/create/route.ts2import { NextRequest, NextResponse } from 'next/server';3import { zendeskFetch } from '@/lib/zendesk';45export async function POST(request: NextRequest) {6 const {7 requesterName,8 requesterEmail,9 subject,10 message,11 priority = 'normal',12 } = await request.json();1314 if (!requesterEmail || !subject || !message) {15 return NextResponse.json(16 { error: 'requesterEmail, subject, and message are required' },17 { status: 400 }18 );19 }2021 const body = {22 ticket: {23 subject,24 comment: { body: message },25 requester: {26 name: requesterName ?? requesterEmail,27 email: requesterEmail,28 },29 priority,30 tags: ['bolt-contact-form'],31 via: {32 channel: 'web_widget',33 },34 },35 };3637 const response = await zendeskFetch('/tickets.json', 'POST', body);3839 if (!response.ok) {40 const error = await response.json();41 return NextResponse.json({ error }, { status: response.status });42 }4344 const data = await response.json();45 const ticket = data.ticket;4647 return NextResponse.json({48 ticketId: ticket.id,49 status: ticket.status,50 subject: ticket.subject,51 createdAt: ticket.created_at,52 url: ticket.url,53 });54}Pro tip: Set the via.channel field to 'web_widget' or 'api' when creating tickets. This appears in Zendesk's ticket channel indicator and helps agents understand how the ticket was submitted. 'api' shows 'API' in the Zendesk UI; 'web_widget' shows 'Web Widget'.
Expected result: POST /api/zendesk/tickets/create creates a real ticket in Zendesk and returns the ticket ID. Check your Zendesk dashboard to confirm the ticket appears with the correct subject, requester, and 'bolt-contact-form' tag.
Build the support ticket dashboard
Build the support ticket dashboard
The Zendesk Tickets API supports powerful filtering for building dashboards. The main listing endpoint is GET /api/v2/tickets.json with filter parameters. To get tickets by status: GET /api/v2/tickets.json?status=open returns all open tickets. Combining multiple filters uses the Zendesk Search API: GET /api/v2/search.json?query=type:ticket+status:open+priority:urgent returns high-priority open tickets. The search API is the most flexible tool for building dashboards: it supports sorting (sort_by=created_at&sort_order=desc), filtering by date range (created>2024-01-01), assignee (assignee:me), group, and any ticket field. The query syntax is documented at developer.zendesk.com/api-reference/ticketing/ticket-management/search/. Each ticket object in the response includes id, subject, status, priority, requester_id, assignee_id, created_at, updated_at, and tags. To display the requester's email and name, you'll need to fetch user details using GET /api/v2/users/{userId}.json or use the sideload feature: appending ?include=users to the tickets endpoint fetches user data in the same request, avoiding N+1 queries. For the dashboard UI, Bolt can generate a data table component with sortable columns, status badge pills, priority color coding, and a link to the Zendesk ticket. Describe the layout in a Bolt chat prompt and it will scaffold the React component that consumes your /api/zendesk/tickets route.
Build a Zendesk support dashboard. Create app/api/zendesk/tickets/route.ts that fetches open and pending tickets sorted by created_at descending using the Zendesk Search API. Include requester name and email using the sideload parameter. Return tickets with: id, subject, status, priority, requesterName, requesterEmail, createdAt, tags. Build a TicketsDashboard component that displays this data in a table with status badge pills and priority color-coding.
Paste this in Bolt.new chat
1// app/api/zendesk/tickets/route.ts2import { NextRequest, NextResponse } from 'next/server';3import { zendeskFetch } from '@/lib/zendesk';45export async function GET(request: NextRequest) {6 const { searchParams } = new URL(request.url);7 const status = searchParams.get('status') ?? 'open';8 const perPage = searchParams.get('per_page') ?? '25';910 // Use Search API for flexible filtering with user sideloading11 const query = `type:ticket status:${status}`;12 const params = new URLSearchParams({13 query,14 sort_by: 'created_at',15 sort_order: 'desc',16 per_page: perPage,17 include: 'users',18 });1920 const response = await zendeskFetch(`/search.json?${params}`);2122 if (!response.ok) {23 return NextResponse.json({ error: 'Zendesk API error' }, { status: response.status });24 }2526 const data = await response.json();27 const usersMap = new Map(28 (data.users ?? []).map((u: Record<string, unknown>) => [u.id, u])29 );3031 const tickets = (data.results ?? []).map((t: Record<string, unknown>) => {32 const requester = usersMap.get(t.requester_id) as Record<string, unknown> | undefined;33 return {34 id: t.id,35 subject: t.subject,36 status: t.status,37 priority: t.priority ?? 'normal',38 requesterName: requester?.name ?? 'Unknown',39 requesterEmail: requester?.email ?? '',40 createdAt: t.created_at,41 updatedAt: t.updated_at,42 tags: t.tags ?? [],43 };44 });4546 return NextResponse.json({ tickets, total: data.count });47}Pro tip: Use the Zendesk Search API (/search.json) rather than the plain tickets list (/tickets.json) for dashboard queries — it supports multi-field filtering, full-text search, and the include= parameter for sideloading related data in a single request.
Expected result: GET /api/zendesk/tickets returns open tickets sorted by creation date with requester name and email included. The dashboard renders tickets in a table with color-coded priority badges and status pills.
Embed the Zendesk Web Widget for live chat
Embed the Zendesk Web Widget for live chat
The Zendesk Web Widget is the fastest way to add live chat support to your Bolt app. Unlike the REST API integration which requires server routes and authentication, the Web Widget is a client-side JavaScript snippet that loads Zendesk's own chat interface. You add one script tag to your layout and users get a professionally designed chat button with live chat, ticket submission, and knowledge base search. To get your Widget key, go to Zendesk Admin Center → Channels → Classic → Widget. The Widget key (sometimes called the web widget key) is displayed on this page — it looks like a UUID. This is a public key safe to expose in client-side code — store it as NEXT_PUBLIC_ZENDESK_KEY in your .env.local. In Next.js App Router, load the Widget in a client component to avoid SSR issues (the script accesses window and document). Create a component that uses useEffect to append the Zendesk script to the document head. Load the component in your root layout so it appears on all pages. You can customize the Widget's appearance, default tab (chat vs. help center vs. contact form), and pre-fill fields with known user data using the window.zE API. For logged-in users, pre-fill the name and email fields so they don't have to type them when opening a support chat. This is done by calling window.zE('webWidget', 'identify', { name, email }) after the Widget loads. The Web Widget script loads asynchronously from Zendesk's CDN and renders in the browser — it works immediately in Bolt's WebContainer preview. You can test the full chat experience including connecting to your Zendesk agent workspace without deploying.
Add the Zendesk Web Widget to my Next.js app. Create components/ZendeskWidget.tsx as a 'use client' component that loads the Zendesk script using useEffect with the key from NEXT_PUBLIC_ZENDESK_KEY env var. Configure it to show the chat tab by default. Add the component to app/layout.tsx. The widget should float in the bottom-right corner on all pages.
Paste this in Bolt.new chat
1// components/ZendeskWidget.tsx2'use client';34import { useEffect } from 'react';56declare global {7 interface Window {8 zE?: (...args: unknown[]) => void;9 zESettings?: Record<string, unknown>;10 }11}1213export function ZendeskWidget() {14 useEffect(() => {15 const key = process.env.NEXT_PUBLIC_ZENDESK_KEY;16 if (!key || document.getElementById('ze-snippet')) return;1718 // Configure widget before loading19 window.zESettings = {20 webWidget: {21 chat: { suppress: false },22 contactForm: { suppress: false },23 helpCenter: { suppress: false },24 launcher: { chatLabel: { 'en-US': 'Chat with us' } },25 color: { theme: '#0073e6' },26 },27 };2829 const script = document.createElement('script');30 script.id = 'ze-snippet';31 script.src = `https://static.zdassets.com/ekr/snippet.js?key=${key}`;32 script.async = true;33 document.head.appendChild(script);3435 return () => {36 const existing = document.getElementById('ze-snippet');37 if (existing) existing.remove();38 };39 }, []);4041 return null;42}4344// app/layout.tsx — add to your existing layout:45// import { ZendeskWidget } from '@/components/ZendeskWidget';46// Inside <body>:47// <ZendeskWidget />Pro tip: To pre-fill the chat widget with a logged-in user's details, call window.zE('webWidget', 'identify', { name: user.name, email: user.email }) after the script loads. This saves your users from typing their contact information when opening a support chat.
Expected result: A Zendesk chat button appears in the bottom-right corner of your Bolt app. Clicking it opens the Web Widget panel with chat, contact form, and help center tabs. The widget connects to your Zendesk team and agents can respond through their Zendesk dashboard.
Set up Zendesk webhooks for real-time ticket notifications
Set up Zendesk webhooks for real-time ticket notifications
Zendesk's webhook system sends HTTP notifications to your app when tickets are created, updated, or when specific trigger conditions are met. This is useful for syncing ticket data to your own database, posting ticket updates to Slack or Teams, or triggering automated workflows when a ticket reaches a certain status. Like all incoming HTTP connections, Zendesk webhook calls cannot reach Bolt's WebContainer. The WebContainer runs inside a browser tab without a public URL. Deploy your app to Netlify or Vercel first, then create the webhook target in Zendesk. Create a Zendesk webhook target: go to Admin Center → Apps and Integrations → Webhooks → Create webhook. Set the endpoint URL to your deployed route (e.g., https://your-app.netlify.app/api/zendesk/webhook), method to POST, request format to JSON. Add a request signing secret — Zendesk will include a signature header you can use to verify the request is genuine. Click Create webhook. Webhooks alone don't fire automatically — you need a Zendesk Trigger to link events to the webhook. Go to Admin Center → Business Rules → Triggers → Add trigger. Set conditions (e.g., 'Ticket is created' or 'Ticket status changed to Solved') and add a notification action: Notify active webhook → select your webhook → configure the JSON payload. The trigger fires whenever its conditions are met, sending the configured payload to your webhook URL. Add your Zendesk webhook signing secret to your hosting platform's environment variables as ZENDESK_WEBHOOK_SECRET before enabling the webhook.
Create a Zendesk webhook handler at /api/zendesk/webhook. It should verify the X-Zendesk-Webhook-Signature header using ZENDESK_WEBHOOK_SECRET. Parse the JSON payload and log ticket id, status, subject, and requester email for new ticket events. Return 200 OK for valid events, 401 for invalid signatures. Note: this requires deployment to work.
Paste this in Bolt.new chat
1// app/api/zendesk/webhook/route.ts2import { NextRequest, NextResponse } from 'next/server';3import crypto from 'crypto';45export async function POST(request: NextRequest) {6 const rawBody = await request.text();7 const secret = process.env.ZENDESK_WEBHOOK_SECRET;89 if (secret) {10 const timestamp = request.headers.get('X-Zendesk-Webhook-Request-Timestamp') ?? '';11 const webhookId = request.headers.get('X-Zendesk-Webhook-Id') ?? '';12 const signature = request.headers.get('X-Zendesk-Webhook-Signature') ?? '';1314 const toSign = timestamp + webhookId + rawBody;15 const expected = crypto16 .createHmac('sha256', secret)17 .update(toSign)18 .digest('base64');1920 if (signature !== expected) {21 return NextResponse.json({ error: 'Invalid signature' }, { status: 401 });22 }23 }2425 let payload: Record<string, unknown>;26 try {27 payload = JSON.parse(rawBody);28 } catch {29 return NextResponse.json({ error: 'Invalid JSON' }, { status: 400 });30 }3132 // Process the ticket event33 const ticketId = payload.ticket_id ?? payload.id;34 const status = payload.status ?? payload.ticket_status;35 const subject = payload.subject ?? payload.ticket_subject;3637 console.log(`Zendesk ticket event: ID=${ticketId}, status=${status}, subject=${subject}`);3839 // Add your business logic here:40 // - Update your database41 // - Send a Slack/Teams notification42 // - Trigger a workflow4344 return NextResponse.json({ received: true });45}Pro tip: Zendesk webhook payloads are fully customizable — you define the JSON structure in the Trigger's notification action using Zendesk's liquid template syntax ({{ticket.id}}, {{ticket.status}}, etc.). Design the payload to include exactly the fields your webhook handler needs.
Expected result: After deploying and creating the Zendesk webhook + trigger, ticket creation or status changes trigger POST requests to your deployed webhook endpoint. Events are logged and can trigger downstream actions in your app.
Common use cases
Contact form that creates Zendesk support tickets
Replace a generic contact form with a Bolt form that creates Zendesk tickets directly. When a user submits the form with their email, subject, and issue description, a ticket is created in Zendesk and assigned to the appropriate team. The user receives Zendesk's automated confirmation email with their ticket number.
Create a support contact form with fields for name, email, subject, and message. When submitted, call /api/zendesk/tickets/create to create a Zendesk ticket with the form data. Tag the ticket with 'bolt-contact-form'. Show a success message with the ticket ID after creation so the user can reference it.
Copy this prompt to try it in Bolt.new
Support ticket status dashboard for agents
Build an internal support dashboard that shows the current queue: open tickets sorted by priority, tickets waiting for customer reply, and recently resolved tickets. Each row shows ticket ID, subject, requester email, status, and a direct link to the ticket in Zendesk. Support managers can use this as a focused view without Zendesk's full UI.
Create a support dashboard at /support that fetches Zendesk tickets from /api/zendesk/tickets with status=open. Show a table with ticket ID, subject, requester, created date, and priority badge. Color-code priority: urgent=red, high=orange, normal=blue, low=gray. Add a 'View in Zendesk' link for each row.
Copy this prompt to try it in Bolt.new
Embedded live chat widget for end users
Add Zendesk's Web Widget to your Bolt app's layout so users can open a live chat window from any page. The widget connects to your Zendesk team and supports live chat, ticket form submission, and knowledge base article search — all in a customizable floating button and chat panel.
Add the Zendesk Web Widget to my Next.js app's root layout. Use my Zendesk key from the NEXT_PUBLIC_ZENDESK_KEY environment variable. The widget should open with the chat tab by default. Add it as a client component in the layout that only loads on the client side to avoid SSR issues.
Copy this prompt to try it in Bolt.new
Troubleshooting
API returns 401 with 'Couldn't authenticate you' even with correct credentials
Cause: The Basic auth username format is wrong. Zendesk requires email/token as the username (the string '/token' must be appended to the email), not just the email address. Using email@example.com as the username instead of email@example.com/token is the most common mistake.
Solution: Verify your zendeskFetch utility builds the auth string as email + '/token:' + apiToken — specifically that it's: Buffer.from('user@email.com/token:the_api_token').toString('base64'). The '/token' suffix is literal and required. Double-check that the API token was copied correctly from Admin Center with no trailing spaces.
1// Correct Basic auth format for Zendesk2const auth = Buffer.from(3 `${process.env.ZENDESK_EMAIL}/token:${process.env.ZENDESK_API_TOKEN}`4).toString('base64');Ticket creation returns 422 with 'Requester email must be a valid email' even though the email is valid
Cause: The ticket body structure is incorrect — the requester object must be nested inside the ticket object, not at the top level. Zendesk's 422 errors are often about nesting rather than validation.
Solution: Ensure the ticket creation body wraps everything in a ticket key: { ticket: { subject, comment, requester: { name, email } } }. The requester object must be inside the ticket object, not at the root level of the JSON body.
1// Correct ticket body structure2const body = {3 ticket: { // required wrapper4 subject: 'My issue',5 comment: { body: 'Issue details here' },6 requester: { // must be inside ticket, not at root7 name: 'John Doe',8 email: 'john@example.com',9 },10 },11};Web Widget does not appear in the Bolt preview or shows a 'Key not found' error
Cause: The NEXT_PUBLIC_ZENDESK_KEY is not set or contains an incorrect value — either the wrong type of key (API token instead of Widget key) or a key from a different Zendesk account.
Solution: Verify the Widget key in Zendesk Admin Center → Channels → Classic → Widget. The Widget key is a UUID-format string (different from your API token). Confirm NEXT_PUBLIC_ZENDESK_KEY in .env.local exactly matches this value. Restart the Bolt dev server after updating the env file.
Zendesk webhook events are not arriving after creating the webhook target
Cause: The webhook target was created without any Triggers linked to it, or the Trigger conditions don't match the events you're testing. Zendesk webhooks don't fire on their own — they only fire when a Trigger's conditions are met.
Solution: Go to Admin Center → Business Rules → Triggers and verify a trigger exists that: (1) has the right conditions (e.g., 'Ticket is created'), and (2) has an action of 'Notify active webhook' pointing to your webhook target. Test by creating a test ticket that matches the trigger conditions and check your server logs for the incoming webhook POST.
Best practices
- Store ZENDESK_EMAIL, ZENDESK_API_TOKEN, and ZENDESK_SUBDOMAIN as server-side environment variables. Only NEXT_PUBLIC_ZENDESK_KEY (the Widget key) is safe for client-side exposure — it only identifies the Zendesk account, not provides API access.
- Use the Zendesk Search API (/search.json) for filtered ticket queries rather than the basic tickets list endpoint — it supports powerful multi-field filtering, text search, date ranges, and user sideloading in a single request.
- Include sideloaded user data (?include=users) when fetching ticket lists to avoid making separate API calls for each ticket's requester information — this reduces API calls and keeps dashboard loading times fast.
- Tag tickets from your Bolt app consistently (e.g., 'bolt-contact-form', 'bolt-in-app-report') so support agents can filter tickets by source and prioritize accordingly.
- Test ticket creation and the Web Widget in Bolt's WebContainer preview — both work without deployment. The Web Widget loads as a client-side script, and ticket creation is an outbound API call. Only deploy when you need Zendesk webhook triggers pointing to your server.
- Implement Zendesk webhook signature verification using the HMAC-SHA256 pattern to confirm requests are genuinely from Zendesk. While not strictly required for internal integrations, it prevents unauthorized payloads from triggering your business logic.
- Rate limit your ticket search queries with ISR caching (revalidate: 60) for dashboard pages — Zendesk's Support API rate limit is 700 requests per minute for Enterprise, lower for other plans. Dashboard pages rarely need real-time data.
Alternatives
Freshdesk (Freshworks) offers similar helpdesk functionality at a more affordable price point and is a good alternative for teams already using other Freshworks products like Freshsales CRM.
Intercom combines customer support with product messaging and user engagement, making it a better choice for SaaS products that want to blend support with onboarding and activation messaging.
LiveChat focuses specifically on real-time chat support with a simpler setup than Zendesk's full ticketing system, better for businesses where live chat is the primary support channel.
Zendesk Sunshine Conversations (formerly Smooch) provides the messaging API layer for building custom chat experiences on top of Zendesk's platform, better for developers building embedded messaging rather than using the Web Widget.
Frequently asked questions
Does Bolt.new have a native Zendesk integration?
No. Zendesk is not one of Bolt's native connectors. You build the integration manually using Next.js API routes for the Zendesk Support REST API, or by adding the Web Widget script tag for live chat. Bolt's AI can generate the API routes and React components when you describe what support features you need.
Can I test the Zendesk integration in Bolt's preview without deploying?
Yes for two main features: the Web Widget (client-side script that loads in the browser, works immediately in preview) and all REST API calls for ticket management (outbound HTTPS requests that work from the WebContainer). The only Zendesk feature requiring deployment is webhook reception — Zendesk's trigger-to-webhook notifications need a publicly accessible URL to POST events to.
What is the difference between a Zendesk API token and the Web Widget key?
The API token is a server-side credential that grants full programmatic access to your Zendesk account — use it with email/token Basic auth for REST API calls. It should never be exposed in client-side code. The Web Widget key is a public identifier that tells the Widget script which Zendesk account to connect to — it only controls routing, not access. Store the API token in server-side env vars and the Widget key in NEXT_PUBLIC_ env vars.
How do I create tickets on behalf of customers who aren't Zendesk users?
Include a requester field in the ticket body with the customer's name and email: { requester: { name: 'Customer Name', email: 'customer@email.com' } }. Zendesk will automatically create an end-user record for that email if one doesn't exist, and link the ticket to that user. The customer receives Zendesk's automated email notifications about their ticket and can reply to follow up.
Can I customize the Zendesk Web Widget appearance to match my app's design?
Yes, to a limited extent. The Widget supports color theming (set the theme color to match your brand), launcher label customization, and tab visibility control (show/hide chat, contact form, or help center). However, it's a hosted widget with Zendesk's design — you cannot completely restyle it. For fully custom chat UI, use Zendesk Sunshine Conversations (messaging API) which provides the backend while you build the frontend.
Talk to an Expert
Our team has built 600+ apps. Get personalized help with your project.
Book a free consultation