answerLoopsanswerLoopsDocs
Reference

Data model

A map of the workspace, ticket, knowledge, and configuration records.

This reference describes the main records used by answerLoops and how they connect. Use it to find where a piece of workspace, ticket, or knowledge data lives. The schema definitions are in lib/db/schema.ts; migrations define changes applied to an existing database.

How the records relate

Almost everything belongs to a workspace (orgs). People reach a workspace through memberships, tickets collect replies and history, and knowledge articles can come from an imported source or be promoted from a ticket.

The main records and how they connect: a workspace has members, tickets, knowledge articles and sources, integrations and API keys; tickets have replies; articles keep a history of changes.

The diagram shows the key relationships only. The link from an article to its import source (source_id) is a plain reference rather than an enforced foreign key. The tables below list every record.

Workspace and access

RecordsPurpose
orgsWorkspace identity and configuration.
users, membershipsUser accounts and their workspace roles. Each user row records the sign-in provider's account id, when the terms of service were accepted and which version, an explicit marketing opt-in, and the last sign-in.
trial_claimsOne row per person who has started the free trial, keyed by a hash of the normalized email and the provider account id (each unique). It is kept when a workspace or account is deleted, so a trial cannot be restarted.
invitationsPending team invitations.
api_keysAPI key metadata and permissions.

Support conversations

RecordsPurpose
ticketsQuestion, source, category, priority, status, AI draft state, and the reason a ticket went to a person without a draft.
ticket_repliesEvery message in a ticket's conversation after the first: staff replies, customer follow-ups, replies answerLoops posted, and maintainer messages kept for context. Each row records who wrote it (author_role) and the channel's own message id, which makes redeliveries harmless and lets a reply to a reply find its ticket.
ticket_eventsRecorded changes in the ticket's history.
ticket_linksRelationships between tickets.
ai_assessmentsResults of AI assessment.
ticket_feedbackPositive and negative answer feedback.
csat_ratingsCustomer satisfaction ratings.

Knowledge

RecordsPurpose
kb_sourcesImported source metadata.
kb_articlesSearchable knowledge articles, their publication state, which team member added each one (created_by, null when unknown or imported), and whether a person has verified the answer (verified_at, verified_by, verified_content_hash, verification_note; all null until someone verifies it), and, for a saved resolution, when a repo sync last changed documentation related to it (docs_changed_at, docs_changed_source; the notice shows only while that is later than the last save and the last verification).
kb_sync_jobsProgress of background imports.
kb_article_eventsAn append-only history of each article: created, promoted from a ticket, edited, verified by a person, and the review decisions behind it. Each row records the type, who did it (empty for imports and syncs), where it came from, and a hash of the question and answer at that moment. The article text itself is not copied.
kb_answersOne row per generated answer: an id, the channel, whether it was generated or failed, when, and, for a support ticket's draft, the ticket's number. No question or answer text and no visitor or ticket details. Kept 90 days.
kb_answer_referencesThe articles supplied to each answer: their order, retrieval score and a fingerprint of the article's question and answer at that moment. Deleting an article keeps the row with the article cleared.
kb_support_settingsOne row per workspace, created when a switch is first changed: which channels assess how well articles support an answer, the sampling rate, the daily limit, and whether short evidence text may be stored. Everything is off or unset until an owner or admin changes it.
kb_support_assessmentsOne row per answer and assessment method version: whether the check completed, failed or was skipped (and why), the overall result for the answer, the model used, and the tokens spent. Removed with its answer, so it follows the same 90 days.
kb_support_article_findingsFor each assessed article on an answer: supporting evidence found, no supporting evidence found, or inconclusive, with the fixed reasons and any articles that carry the same support. A pair with no row was not assessed. Deleting an article keeps the row, with the fingerprint of the version that was supplied.
kb_support_claim_findingsFor each claim tied to a passage: the label (supports, partial, contradicts or inconclusive) and the passage number. When a workspace has turned on evidence storage it also holds one sentence of the answer and one of the article, each under 300 characters, which are deleted when they expire or when the setting is turned off. The finding stays.
kb_support_flagsA team member's agreement or disagreement with a finding, with an optional note of up to 500 characters. Kept apart from the findings, which a flag never changes.
kb_article_usageA daily count, per article, of the answers it was supplied to as context. Counts only: no question text, visitor or ticket is stored.
kb_review_itemsUnseen questions waiting for a person to review, edit and approve them into articles.
ticket_embeddingsSearch representations associated with tickets.
faq_snapshotsGenerated FAQ content.

Product feedback

TableContents
product_feedbackFeedback entries from the in-app widget: text, moderation status, and the team's reply. Shared across workspaces; each row records its author's workspace for display.
product_feedback_votesOne row per person per approved entry they up-voted, with the voter's workspace so the votes go when the workspace does. Counts are computed from these rows at read time; nothing identifies a voter to other users.
product_updatesThe "what's new" feed shown on the widget's Updates tab.

Changes to the feedback, votes and updates tables notify connected dashboards so the widget updates live.

Configuration and billing

Integration records store channel configuration, and credentials are stored protected rather than in plain text. Lookups are always made for one platform at a time. At most one enabled Slack, Discourse or Google Chat integration can hold a given workspace, site or space (team_id).

Message de-duplication is scoped to the workspace: tickets is unique on (org_id, source_message_id) and email_messages on (org_id, rfc_message_id). Telegram tickets use telegram:<chat id>:<message id> as their source message id, because Telegram message ids repeat across chats; tickets created before that format keep the bare message id and are still recognised as duplicates.

ai_configs stores workspace model settings. sla_configs stores response and resolution targets per priority. Rows with a null org_id are the global defaults; a workspace that saves its own targets gets its own row (unique per org_id and priority, removed with the workspace). Deadlines for a ticket use the workspace's row for the ticket's priority and fall back to the default row. subscriptions records hosted-plan state, and api_generations records standalone answer generation for usage accounting.

Use application actions and documented APIs for routine changes. For self-hosted upgrades, apply the documented migrations and retain a tested backup. See Upgrading.

Where to go next

On this page