Better Helpdesk
Reference

Adapters

The interfaces the host implements for storage, email, AI, help search and DNS, with every method signature.

Adapters are the interfaces the host implements for what is optional: storage, email, AI, help search and DNS. Each is an optional field of the configuration; the package calls it. StorageAdapter, AiAdapter, HelpResult and HelpdeskEmail are exported as types from better-helpdesk. Worked implementations are in examples/adapters. Related guides: Store attachments, Send email, Add AI suggestions and drafts, Search your own docs from the widget.

FieldTypeUnset
storageStorageAdapterNo attachments.
email{ send(message: HelpdeskEmail): Promise<void> }No email.
aiAiAdapterNo triage, drafts or rewrites.
help{ search(query: string, locale: Locale): Promise<HelpResult[]> }No help suggestions.
dnsResolverDNSResolverThe system resolver.

Storage

type StorageAdapter = {
  presignUpload(
    key: string,
    opts: { contentType: string; maxBytes: number }
  ): Promise<{ url: string; fields: Record<string, string> }>;
  presignDownload(key: string, filename: string): Promise<string>;
  put(key: string, body: Uint8Array, contentType: string): Promise<void>;
  exists(key: string): Promise<boolean>;
  delete(key: string): Promise<void>;
};

Prop

Type

Keys have the form support/{conversationId}/{16 hex characters}-{filename}, where the filename keeps only a-z, A-Z, 0-9, . and - (others become _) and at most its last 100 characters.

LimitValue
Content types a customer or an agent may uploadimage/png, image/jpeg, image/gif, image/webp, application/pdf, text/plain
Attachments a customer may add to a conversation20
Attachments an agent may add to a message20
SizemaxAttachmentBytes, 10 MiB by default

Of an inbound email's first 20 attachments, those within the size are stored. A content type outside the list is stored as application/octet-stream.

Examples: storage-s3.ts and storage-vercel-blob.ts.

Email

email: { send(message: HelpdeskEmail): Promise<void> }

send receives one message at a time and renders and delivers it. It runs inside a job, so a throw makes the job retry. The five message kinds are on Email messages.

Examples: email-smtp.ts, email-resend.ts and email-postmark.ts.

AI

type AiAdapter = {
  generate<T>(input: {
    system: string;
    prompt: string;
    schema: z.ZodType<T>;
  }): Promise<T>;
};

Prop

Type

generate returns an object that satisfies schema. The package calls it for:

UseWhenResult schema
TriageThe ai-triage job queued for each new conversation, or POST agent/conversations/{id}/triage{ type, priority, title, summary }
DuplicatesDuring triage, when similar conversations exist and the type is not lead{ duplicates: { number, reason }[] }
Draft replyPOST agent/conversations/{id}/draft without a body{ reply }
RewritePOST agent/conversations/{id}/draft with mode and text{ reply }

The triage result is stored as the conversation's ai_suggestion, which an agent accepts or dismisses. For a lead, triage suggests no type or priority. Triage jobs are capped at 100 an hour, counted apart for conversations from the host's signed-in users and from everyone else.

Examples: ai-anthropic.ts and ai-openai-compatible.ts; Add AI suggestions and drafts walks through the adapter.

Captcha

type CaptchaAdapter = {
  client: { script: string; global: string; siteKey: string };
  verify(token: string, context: { ip: string | null }): Promise<boolean>;
  for?: 'visitors' | 'everyone';
};

Prop

Type

The widget renders the check in the page's own DOM, beside the widget element, and shows it inside its form, so a provider's script finds it as it would anywhere else. Examples: captcha-turnstile.ts, captcha-hcaptcha.ts and captcha-recaptcha.ts; Keep bots out of the widget walks through it.

help: { search(query: string, locale: Locale): Promise<HelpResult[]> }

type HelpResult = { title: string; url: string; excerpt?: string };

Prop

Type

The widget searches while a customer writes a bug, feature or question, once the subject or message has four characters or more. The query is at most 200 characters, and GET widget/help returns the first five results. A draft reply also searches, by the conversation's subject or, without one, the first 200 characters of its transcript.

Example: help-search.ts.

DNS resolver

dnsResolver has the DNSResolver type from mailauth and looks up the DKIM keys of inbound mail. Unset, the system resolver is used.

Scheduled work

Jobs have no adapter. The host calls helpdesk.runJobs() or posts to {basePath}/jobs on its own schedule. Examples: jobs-vercel-cron.ts and jobs-interval.ts.

Host functions

identify, resolveContext, orgNames, links, replyToAddress, clientIp and onEvent are functions the host passes as well. They are listed with their signatures on Configuration.

On this page