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.
| Field | Type | Unset |
|---|---|---|
storage | StorageAdapter | No attachments. |
email | { send(message: HelpdeskEmail): Promise<void> } | No email. |
ai | AiAdapter | No triage, drafts or rewrites. |
help | { search(query: string, locale: Locale): Promise<HelpResult[]> } | No help suggestions. |
dnsResolver | DNSResolver | The 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.
| Limit | Value |
|---|---|
| Content types a customer or an agent may upload | image/png, image/jpeg, image/gif, image/webp, application/pdf, text/plain |
| Attachments a customer may add to a conversation | 20 |
| Attachments an agent may add to a message | 20 |
| Size | maxAttachmentBytes, 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: { 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:
| Use | When | Result schema |
|---|---|---|
| Triage | The ai-triage job queued for each new conversation, or POST agent/conversations/{id}/triage | { type, priority, title, summary } |
| Duplicates | During triage, when similar conversations exist and the type is not lead | { duplicates: { number, reason }[] } |
| Draft reply | POST agent/conversations/{id}/draft without a body | { reply } |
| Rewrite | POST 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
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.