Seed catalogue · Support

Lily

A self-hostable ticket desk with numbered cases, shared views, Help Centre intake, configurable channels, service targets, customer records, and operational analytics.

Alternative to
Zendesk, under MIT
Family
Support
Edition
Lily
Version
0.1.0
Contract
109 clauses across 19 areas
Maturity
thin-real-worker-d1-ui-port; ticket-desk-contract-draft
Lily screenshot 1: landing
Lily screenshot 2: home
Lily screenshot 3: tickets
Lily screenshot 4: ticket workspace
Lily screenshot 5: customers
Lily screenshot 6: customer

6 screenshots from the product's own visual and verification artifacts. Its handover records what was exercised.


What you get

Everything below is generated from the seed's own manifest and contract. No rounded-off claims, no numbers we made up.

Operating cost

Runs on your cloud's free tier

The seed declares the free plan as the default deployment. Its own estimate, thresholds and sources are below, unedited.

Status
free-plan default; production usage not measured
Estimate
The locally deployable core uses Cloudflare Workers, D1, R2, Queues, and Cron. Mail, record-link, commerce, and AI provider costs exist only when a buyer enables those adapters and are paid directly by that buyer. Published cost and capacity claims await Stage 2 measurement.
Included
Cloudflare Worker, D1, R2, Queues, Cron
Excluded
mail provider, record-link provider, voice provider, optional commerce provider, optional AI provider

Externals declared

8 externals, each with a reason

Under BASE-DATA-004, user data leaves only through what is declared here.

native-auth-mail
Cloudflare primitives do not originate transactional email; Lily uses the explicitly selected Resend or SendGrid transport to deliver native magic-link sign-in messages.
mail-provider
A configured provider supplies inbound delivery callbacks, outbound email transport, and authoritative delivery reports.
record-link-provider
A buyer-controlled adapter may expose named external records without making Lily an owned CRM or commerce system.
commerce-and-ai-provider
The retained optional specification requires replaceable commerce and AI providers while keeping manual core ticket work independent.
help-centre-ai-provider
Article-grounded generated visitor answers require a buyer-selected model provider.
voice-provider
A buyer-selected telephony service supplies phone lines, queue state, and call records that Cloudflare primitives do not originate.
external-sync-provider
A buyer-controlled provider transport is required only when that extension synchronises records outside Lily.
sendgrid
Optional explicitly selected provider for native sign-in, signed inbound mail, human-reviewed public replies, and signed delivery events. Provider acceptance is not delivery confirmation.

Extension points

42 declared integration conveniences

These hooks are optional conveniences. Owners may change any source, including core logic, schema and infrastructure; their agents adapt useful upstream fixes and verify the result.

app.route.v1app.navigation.v1settings.section.v1job.consumer.v1cron.task.v1ticket.assignment.v1ticket.auto-close.v1ticket.reply-status.v1ticket.followers.v1requester.suspension.v1channel.autoresponder.v1service-target.selection.v1automation.action-allowlist.v1ticket.quiet-hours.v1csat.request.v1ticket.created.v1ticket.message.received.v1ticket.message.sent.v1ticket.assigned.v1ticket.status.changed.v1ticket.merged.v1customer.updated.v1automation.run.completed.v1service-target.changed.v1satisfaction.received.v1app.navigation.after.v1home.dashboard.after.v1ticket.queue.row.actions.v1ticket.header.actions.after.v1ticket.timeline.after.v1ticket.composer.toolbar.after.v1ticket.requester-context.after.v1customer.profile.after.v1help.article.after.v1settings.after.v1mail.receiver.v1mail.sender.v1record.link.v1voice.provider.v1external.sync.v1commerce.platform.v1ai.provider.v1

Declared limits

Capacity, stated up front

Straight from seed.json → limits. Where a value is an estimate the seed says so.

Status
estimated
Basis
Capacity and rate values are not load tested and must be replaced by measured limits before the first release. Public request values are per-IP one-minute enforcement thresholds, not capacity proof. Attachment content types are a product decision, not a measurement.
Concurrent Operators
25
Tickets
50,000
Messages
250,000
Inbound Messages Per Minute
20
Outbound Messages Per Minute
20
Active Automation Rules
20
Attachment Bytes Per File
10,485,760
Attachment Bytes Total
10,737,418,240
Attachment Content Types
image/png, image/jpeg, image/gif, image/webp, application/pdf, text/plain, text/csv, message/rfc822, application/zip, application/vnd.openxmlformats-officedocument.wordprocessingml.document, application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
Public Requests Per Minute Per Ip
  • Assets 240
  • Health 60
  • Landing 60
  • Sign In 10
  • Survey 30
  • Provider Webhooks 120
  • Integrations 120
  • Help Centre 60
  • Help Request 20
  • Widget 30

Non-goals, stated proudly

A product that says what it will never do is a product you can trust to stay small, fast and legible. These are refusals, not gaps.

  • Multiple businesses or tenants
  • A separately browsable conversation inventory
  • An owned CRM, commerce, order, payment, fulfilment, or marketing system
  • Zendesk community, badges, moderation, theme marketplace, dashboard sharing or restrictions, CRM suite/tasks/prospecting, native mobile SDK, or social connectors
  • Application marketplace, classic report builder, vendor benchmark survey, product switcher, signup/trial/billing chrome, or workforce management
  • Outbound campaigns, arbitrary external webhooks, native mobile applications, or voice recording
  • Autonomous customer messages, ticket lifecycle changes, external mutations, or fabricated adapter data
  • Freshdesk replacement or import until compliant mapped public-source evidence exists

The contract

All 109 clauses of Lily, from its own CONTRACT.md. Stable IDs identify requirements; the source handover and verification evidence distinguish implemented, tested and uncovered behavior.

HOME1 clause

HOME-001
Deployment front door.

GET / presents the configured business name, a short configured description of its support service, links to the public Help Centre and configured public request surfaces, and a sign-in link to /signin. It contains no vendor pricing, trial, billing, product-switcher, or marketing claims.

APP1 clause

APP-001
Authenticated workspace.

An authorised user entering /app can reach Home, Views, Tickets, Customers, Help Centre, Analytics, Channels, and Administration according to their role. Navigation has a compact responsive form, global search, keyboard shortcuts for search/navigation/common ticket actions, contextual coach marks, and a dismissible setup checklist whose completed state is per Administrator. An unavailable optional channel or adapter is labelled unavailable with the configuration action rather than simulated data.

ACCESS4 clauses

ACCESS-001
Roles.

Lily has Administrator, Agent, Contributor, and Light Agent roles. Administrators manage deployment-wide settings, people, groups, forms, roles, channels, and policies. Agents work tickets and public replies. Contributors can make internal contributions only. Light Agents have the explicitly configured limited ticket access. An authenticated person may perform only actions permitted by their role and any ticket/group restriction.

ACCESS-002
Groups and membership.

Administrators create, rename, retire, and populate groups. A ticket can be assigned to a group and then an agent; group assignment is visible in views, history, filters, reports, and exports. Retiring a group requires an explicit reassignment or unassignment of its open tickets and retains historical attribution.

ACCESS-003
Authorisation and lifecycle.

Administrators invite, suspend, restore, and remove agents. Removing an agent unassigns their active tickets and preserves the author identity on earlier messages, notes, and audit records. The last active Administrator cannot be removed. Agents configure their own signature and notification preferences; the configured signature is appended only to their public email reply.

ACCESS-004
Concurrent edits.

Each case mutation supplies the revision it was based on. A stale mutation returns 409 and makes no partial field, status, note, assignment, automation, or outbound-message change. An accepted inbound message is durable even if it races with an agent edit.

TKT18 clauses

TKT-001
Creation.

A human customer email to the support address or an agent's manual submission creates one ticket with an immutable ID, creation time, customer, channel, subject, initial message, declared ticket custom fields, and revision. A new ticket is Open with Normal priority unless the creating agent chooses another priority.

TKT-002
States and pending reasons.

A ticket is Open, Pending, Solved, Closed, or Merged. Pending carries exactly one configured reason, and every reason declares whether it waits on the customer; a ticket is never Pending without a reason. Solved records an agent-confirmed outcome, Closed is immutable archival history, and Merged is an immutable redirect under TKT-016.

TKT-003
Customer reply.

A qualifying human customer message on an Open, Pending, or Solved ticket appends to that ticket, makes it Open, and clears its pending reason. A message matching Closed creates a linked new ticket; a message matching Merged appends to the destination under TKT-016. No qualifying human customer message is discarded.

TKT-004
Auto-close.

A Solved ticket closes seven days after it was last solved unless a qualifying human customer message arrives first. Closing never deletes messages, evidence, executions, or audit history. Policy: ticket.auto-close.v1; default: seven days.

TKT-005
Queues and views.

Agents can filter tickets by status, pending reason, priority, assignee, group, tag, channel, service-target state, and ticket custom fields; sort by target due time, last activity, or priority; and save a named view that authorised agents can open. My queue lists Open and Pending tickets assigned to the current agent, earliest target due time first.

TKT-006
Search.

Full-text search over ticket IDs, subjects, message bodies, customer names and email addresses, tags, and configured custom fields returns matching authorised tickets within 500ms at the stated limits.

TKT-007
Assignment.

New tickets are assigned by the assignment policy. Agents may assign or reassign a ticket to one agent, one group, or both without changing state or service clocks. Policy: ticket.assignment.v1; default: unassigned.

TKT-008
Priority.

Every ticket has Urgent, High, Normal, or Low priority. Authorised agents and allowed automation may change it; each change records actor, prior value, new value, and time.

TKT-009
Timeline.

The ticket timeline presents customer messages, public replies, internal notes, system events, adapter evidence, automation runs, and execution records in durable event-time order, with insertion order as a stable tie-breaker across reload and pagination.

TKT-010
Public and internal messages.

Public replies are eligible for customer delivery. Internal notes are a distinct stored kind that cannot be passed to a delivery adapter, included in customer-facing export, or used to claim an action completed.

TKT-011
Solved guard.

Solving is an explicit agent action and sends no draft or external action. A Solved ticket rejects new public replies and external executions until it is reopened under TKT-003 or TKT-015; Closed and Merged tickets reject them permanently.

TKT-012
Concurrent changes.

Each ticket mutation supplies its revision. A stale mutation is rejected with the current revision and makes no partial change; inbound message append never fails because of a concurrent agent edit.

TKT-013
Ticket merge.

An agent may merge only tickets for the same customer, neither Closed nor Merged, after confirming source and destination IDs and a reason. The source becomes Merged and stores the destination ID. Cross-customer merge is refused; it needs a separately versioned, elevated extension with explicit participant and recipient reconciliation before customer-visible data could cross a customer boundary.

TKT-014
Manual tickets.

An agent-created ticket records whether its initial message came from the requester or agent, and that attribution is immutable. Agent-originated tickets start no first-response clock and are excluded from first-response reporting.

TKT-015
Operator reopen.

An agent may reopen Solved to Open with a required reason; it starts a new service cycle, sends nothing, and records the action. Closed and Merged tickets cannot be reopened.

TKT-016
Merged history and replies.

A Merged ticket is read-only, its timeline is presented on the destination, replies addressed to stored threading identifiers reach the destination, and both IDs remain resolvable in search and audit history. Its source conversation stays linked to the immutable source and is transcluded on the destination with original conversation and author identity. Source and destination retain independent lifecycle and completion histories; routing or transclusion changes neither lifecycle.

TKT-017
Tags.

Agents apply and remove case-insensitive canonical tags on tickets and customers; each change records actor and time. Tags filter queues, match search, serve as automation conditions and report breakdowns, and appear in export. Removing a tag retains the audit event.

TKT-018
Reply outcome.

Sending a public reply leaves the ticket Pending with a configured reason that waits on the customer, unless the agent selects a different permitted status before sending. Sending never solves a ticket alone. Policy: ticket.reply-status.v1; default: Pending, waiting on the customer.

CASE10 clauses

CASE-001
Numbered case and default conversation.

A Lily case is the visible numbered ticket. Its public number is unique for the deployment life, never reused after deletion or merge, and remains resolvable after import. Each new ticket creates one hidden conversation by default. Customer-visible messages live on that conversation; notes, evidence, assignment, actions, and lifecycle live on the case. Completion never closes or deletes the conversation.

CASE-002
View predicates and columns.

A saved view has an All block and an optional Any block. It matches when every All condition matches and, when Any is non-empty, at least one Any condition matches; an empty All block is true and an empty Any block imposes no additional condition. Rows, counts, exports, dashboard drill-through, sorting, pagination, and selected columns use the same stored predicate.

CASE-003
Forms, followers, context, and work tabs.

Administrators define ticket forms, required fields, and custom fields; creation preserves the selected form and submitted values. An authorised agent can add/remove followers, who receive ticket.followers.v1 updates without needing to be owner or assignee. Detail exposes requester context, selected columns, filtered activity types, and configured tabs. Policy: ticket.followers.v1; default: assignment, requester message, note, public reply, at-risk, breach, and solved events notify followers subject to preferences.

CASE-004
Bulk and destructive work.

An authorised agent can apply reviewed bulk assignment, tag, status, macro, or spam actions to a filtered selection with independent per-ticket success, skip, or failure outcomes. Spam marking, requester suspension, deletion, and restoration require the separately authorised destructive action and preserve audit history under baseline retention.

CASE-005
Agent and mobile work.

Home presents assigned/followed work, capacity, activity, and queue links; availability influences only later routing. The responsive mobile workspace supports ticket work and each agent can configure mobile notification categories and quiet hours with the same routing and suppression semantics as NOTIFY-001 and NOTIFY-002.

CASE-006
Live conversation.

A configured widget may keep a live visitor/agent conversation. Each live conversation has a visible participant and queue state, is linked to its ticket when a request is submitted or an agent creates one, and retains its message chronology when it becomes the ticket's hidden conversation. An agent presence indicator never claims a live responder when none is assigned.

CASE-007
Global and recent search.

Global search returns authorised tickets, customers, organisations, Help Centre articles, agents, groups, and saved views, identifies the matched type and field, and keeps a per-agent recent-record list that can be cleared. Ticket full-text latency remains TKT-006; global results preserve access checks and do not reveal a recent record after access changes.

CASE-008
Work affordances.

Agents can open concurrent ticket tabs without losing unsaved drafts, return to a chosen tab, and see its current revision or conflict state. A queue can offer an explicit guided next-ticket action based on its visible ordering; its action, selection rationale, and any skipped ticket are recorded. The exact guided-play interaction is a Lily decision because the available Zendesk capture establishes the surface but not its execution.

CASE-009
Tag entry.

Ticket tags display as removable chips, offer existing canonical tags while typing, and permit an authorised agent to create a new tag inline. Each entry/removal is audited. Autocomplete and inline creation are Lily product requirements; the cited Zendesk evidence establishes display and removal only.

CASE-010
Rich composition, counters, and capacity.

The composer persists rich formatting, emoji, links, and accepted attachments before submit. Workspace chrome shows authorised agents live conversation and assigned-work counters. Administrators set an enforced per-agent active-ticket cap; routing skips capped agents and records the reason, while direct assignment above the cap requires an explicit authorised override.

MAIL14 clauses

MAIL-001
Inbound email.

A signature-verified provider delivery to a configured support address creates or appends a ticket message within 60 seconds, preserving sender, recipients, subject, sanitised text and HTML, attachments, provider ID, and threading headers.

MAIL-002
Threading.

An inbound email appends only when In-Reply-To or References matches a stored message ID, or a valid Lily-issued opaque reply token identifies its ticket, and its sender is the current requester or current participant. The timeline audits which identifier matched. A removed or unknown sender starts a new ticket even with a valid identifier; sender and subject alone never join mail.

MAIL-003
Threading conflict.

An unmatched message starts a new ticket. Conflicting headers, or a valid token that disagrees with them, start a new ticket with a visible conflict event naming both candidate tickets; the message is never attached speculatively.

MAIL-004
Inbound idempotency.

Replaying one provider event with the same provider message ID produces one stored message, one service-cycle effect, and at most one routing, acknowledgement, and automation effect. Provider-event ID and RFC Message-ID are durably one-to-one. If either known identity arrives paired with a new, conflicting identity or immutable thread metadata, Lily quarantines the conflict without routing, service-cycle, acknowledgement, or automation effect until reviewed. Body equality never resolves it.

MAIL-005
Automatic mail.

Mail with automatic, bulk, delivery-status, receipt, or no-reply signals is non-actionable. A matching item is stored as a visible system event and does not reopen, notify, start a service cycle, run automation, or request an acknowledgement. An unmatched item creates no queue entry and is retained as a discarded-mail audit record.

MAIL-006
Outbound email.

A public reply atomically creates one durable outgoing message and one dispatch job with a stable idempotency key, reply headers, and a Lily-issued opaque reply token, then dispatches that job within 60 seconds. It uses the ticket's stored reply mailbox: the first configured support-address match retained from RFC intake under MAIL-011, or, for a manual, web-form, widget, or other non-RFC ticket, exactly one enabled configured support mailbox selected and stored by an authorised agent before its first public reply. A repeated key returns the original message and dispatch job.

MAIL-007
Delivery state.

Each outgoing message monotonically progresses through queued, accepted, delayed, delivered, bounced, rejected, suppressed, failed, or needs_review. Provider acceptance is never customer delivery. There are at most five total dispatch attempts in one hour with the original key. A failure before provider acceptance never stops a first-response clock. Lily preserves provider-acceptance time and delivery evidence. If the sole qualifying public reply later reaches final bounced, rejected, or failed state, it restores that service cycle's original first-response clock and target due calculation without resetting elapsed time; if another qualifying public reply remains provider-accepted, the clock stays stopped. The pre-send ticket status and its mutation revision are recorded; on that same sole-reply final failure, Lily restores the pre-send status only when no later independent status mutation occurred, never clobbering later agent changes. Final failure or needs_review notifies under NOTIFY-004. An ambiguous needs_review outcome is never automatically retried and requires provider reconciliation.

MAIL-008
Authoritative delivery reports.

Verified provider reports move an accepted message only forward to delivered, delayed, bounced, or rejected and record report time. Duplicate or out-of-order reports never regress a terminal state; Lily never labels delivery without an authoritative report.

MAIL-009
Attachments.

Inbound and outbound attachments remain bound to their immutable message. A file outside the seed limits is rejected with filename and reason while the readable message and accepted files are retained.

MAIL-010
Provider events.

Intake and delivery webhooks are rejected without a valid mail.receiver.v1 signature. Duplicate and out-of-order delivery events are tolerated and never create data or regress a terminal state.

MAIL-011
Recipients and support-address intake.

One RFC message addressed to multiple configured support addresses creates one intake record, retains every matched support address, and records the first configured match as its reply mailbox. A new ticket records the sender as requester and only non-support To and Cc recipients as participants; Bcc is never disclosed. A public reply addresses the requester and current participants the agent keeps in the composer; removed recipients are not restored later. Support and notification addresses are never participants.

MAIL-012
Intake durability and destination.

Lily acknowledges a verified intake only after a durable intake record exists; failure before it returns 503 with no partial ticket. Post-record parse, storage, or enqueue failure is visible with a retryable or terminal reason. A verified message with no configured support destination is quarantined with destination and reason.

MAIL-013
Mailbox identity.

An agent-authored public reply uses the ticket's stored reply mailbox: the first configured RFC support-address match under MAIL-011, or the authorised enabled configured-mailbox selection stored under MAIL-006 for a manual, web-form, widget, or other non-RFC ticket. It carries ticket threading data and appends that agent's configured signature. Operator notifications use a no-reply address that is not a support destination and carry no agent signature. Automatic acknowledgement is not a notification and follows MAIL-014.

MAIL-014
Automatic acknowledgement.

When enabled, the actionable message that creates a ticket produces exactly one stored automatic acknowledgement under MAIL-006 through MAIL-008. It uses the ticket's stored reply mailbox and threading data but no agent signature; it is not sent from the no-reply notification address. No acknowledgement is sent for manual, non-actionable, blocked, or later ticket messages, and it never counts as a first response. Policy: channel.autoresponder.v1; default: disabled until an Administrator enables it for a mailbox.

CHAN6 clauses

CHAN-001
Channel catalogue.

Administrators see every configured intake and delivery channel, its enabled state, form or mailbox binding, group routing, public address, and configuration health. Disabling a channel preserves its history and stops only later intake or dispatch through that channel.

CHAN-002
Web widget and simulator.

A configured public widget collects a request with the configured form and creates a ticket under TKT-001. It identifies the current requester where consented, exposes the required privacy text, and confirms a successful request without pretending an agent is present. An Administrator can use a simulator to preview configured form, bot, appearance, proactive-message, and live-conversation states without creating a real ticket or sending a visitor message.

CHAN-003
Grounded bot assistance.

A configured bot greets the visitor, offers up to ten article-grounded generated answers from approved Help Centre material, and shows cited article links. It stores every bot and visitor turn before agent handoff in the linked conversation, collects the ticket form, and hands off for no valid answer or visitor request. Core ai.provider.v1 receives only approved article material and disclosed visitor input, never private notes; cited identifiers are validated against supplied articles and invalid output fails for review rather than falling back to invented copy.

CHAN-004
Proactive messages.

An administrator can configure an eligible widget audience and a reviewed proactive message. The configuration states its trigger and frequency limit, respects consent and suppression, and records each presentation; it does not create a ticket unless the visitor submits a request.

CHAN-005
Voice queue and ticket link.

A configured voice adapter exposes queues or lines with waiting-call count, agent or group routing context, call state, and stable Ticket ID when one exists. An authorised agent can attach a call record to an existing ticket; the attachment is audited and adds no invented call-to-ticket relation when no ticket was selected. This is a Lily lifecycle decision; the Zendesk evidence only establishes a Live Calls Ticket ID column.

CHAN-006
Side conversations.

An authorised agent can open a labelled side conversation with an approved external participant, see its delivery state and replies on the ticket, and keep it separate from the customer-visible conversation. It cannot disclose private notes without an explicit reviewed message.

CUST7 clauses

CUST-001
Identity.

The first qualifying customer message from an unseen normalised email address creates one customer; comparison is case-insensitive and a shared display name never merges identities.

CUST-002
Profile.

A profile has display name, primary and observed email addresses, locale, tags, declared custom fields, and private account notes. Locally owned fields are editable and adapter-sourced fields are read-only with source and refresh time.

CUST-003
History.

A profile shows every Open, Pending, Solved, Closed, and Merged ticket for that customer with its satisfaction and available adapter records.

CUST-004
Notes.

Agents may add private, attributed customer notes. Notes are never sent to the customer or written back through an adapter.

CUST-005
Merge.

An agent merges customers only after confirming survivor and source IDs and seeing addresses, ticket counts, and record links. Tickets, addresses, notes, tags, fields, and audit history move to the survivor; incompatible provider customer IDs block the operation and name the conflict.

CUST-006
Erasure boundary.

Erasing a customer removes locally owned messages, attachments, notes, evidence snapshots, and tickets under BASE-DATA-003; it never claims to erase a provider account or provider data.

CUST-007
Resync suppression.

This predecessor guarantee applies only to an enabled external.sync.v1 extension described in research/optional-commerce-adapter.md: erasure stores a one-way keyed digest of normalised provider namespace and resource ID, no customer field, to prevent automatic resync recreation; it travels with export/import and only explicit restore removes it. Core Lily has no automatic external sync or sync credential.

CUSTOMER2 clauses

CUSTOMER-001
Organisations and suspension.

Administrators create organisations and membership. An authorised agent may suspend a requester; Policy: requester.suspension.v1; default: new intake is quarantined for review while earlier history stays readable.

CUSTOMER-002
Imports and record links.

An Administrator can run a reviewed CSV customer import with mapping, validation, duplicate handling, and per-row outcomes. An optional record-link adapter attaches named external records with adapter, identifier, link state, and retrieval time; unavailable data is labelled unavailable and never fabricated.

HELP5 clauses

HELP-001
Public Help Centre and audiences.

An Administrator activates or deactivates the public Help Centre; deactivation makes public article and request paths unavailable while preserving authoring history. Published content is visible only to its named audience. An audience selects signed-in users, staff, groups, tags, and users with All/Any blocks: all All conditions and, when non-empty, one Any condition must match; empty All is true and empty Any adds no restriction. Administrators preview matching users and anonymous/staff roles. A preview is not evidence of signed-out deployment reachability.

HELP-002
Articles, taxonomy, history, and bulk work.

Agents with article authority can create, edit, publish, unpublish, archive, and restore articles in categories and sections. Articles have a title, body, author, timestamps, lifecycle state, audience visibility, content tags, and version history. Article history is filterable by actor, event, and date. A selected article set supports reviewed bulk publish, unpublish, archive, restore, category, and audience changes with per-item success, skip, or failure results.

HELP-003
Findability and media.

The Help Centre offers text search, category navigation, pagination, article metadata, a shared media library with upload, search, insert, and removal permissions, and article comments when comments are enabled. Search results respect audience visibility and label unavailable translations or attachments rather than substituting a different article.

HELP-004
Languages and appearance.

Administrators configure Help Centre languages, translated article variants, branding, theme appearance, and a reviewed public preview. A language fallback is explicit and never represents untranslated copy as a translation.

HELP-005
Help Centre requests.

A public request form uses an administrator-selected ticket form, validates required fields, creates a ticket under TKT-001, and gives the requester a result that does not expose another ticket. Its submission is distinguishable from article comments and widget intake.

ADMIN2 clauses

ADMIN-001
Operational settings.

Administrators configure business hours, IANA timezone, holiday calendar, ticket types and priorities, statuses, forms, custom fields, canned replies, mailbox identities, routing, authentication, retention, and API/webhook credentials. Retention configuration can label, report, or schedule review of records but cannot automatically delete user data; deletion follows the applicable baseline clause and explicit authorised action. The settings screen distinguishes active configuration from unavailable optional adapters.

ADMIN-002
Business rules, capacity, and usage.

Administrators can inspect routing rules, capacity limits, API/webhook integrations, installed approved applications, their last execution or error, and current storage/API/channel usage meters with source, period, and refresh time. No setting silently expands authorisation or gives an external application access beyond its configured scope.

MACRO3 clauses

MACRO-001
Macro library.

Authorised agents create, edit, archive, and search shared macros for a public reply or internal note. A macro may propose tags, priority, assignment, form fields, or status, but does not itself send a message. The library reports applied count, recent use, and sortable rolling seven-day usage without exposing ticket content to an unauthorised viewer.

MACRO-002
Placeholders.

Applying a macro resolves its declared placeholders from the current ticket, requester, organisation, and agent. An unresolved required placeholder blocks use and identifies the missing value.

MACRO-003
Review boundary.

A macro fills an editable draft and previews every field change. Submitting remains an explicit TKT-018 and MAIL-006 action and cannot bypass revision, authorisation, or outbound delivery rules.

SLA6 clauses

SLA-001
Calendar and selection.

Administrators configure first-response and resolution targets against one workspace timezone, working-hours schedule, and holiday calendar. Policy: service-target.selection.v1; default: targets are selected by ticket priority and measured in business time. A ticket with no matching target displays that fact and is excluded from met/breached denominators.

SLA-002
Snapshot and calculation.

Starting a service cycle stores the target version, duration, due time, and business-calendar inputs. Later setting edits do not rewrite its result. Recalculation records its reason and prior due time.

SLA-003
First response.

The first-response clock stops only when the first public agent reply is provider-accepted, subject to MAIL-007's sole-qualifying-reply final-failure restoration. Assignment, notes, macros, automation, an acknowledgement, and queued or failed delivery do not stop it.

SLA-004
Resolution cycle.

The resolution clock runs for Open, and for Pending unless the selected pending reason explicitly waits on the requester. It stops when the case is solved. A requester message or agent reopen starts the next cycle while retaining prior cycles.

SLA-005
Visibility.

Ticket rows, ticket detail, Home, and saved views show the exact due time, calendar basis, remaining or overdue time, and state active, paused, at_risk, or breached. At risk begins at 25% of target duration remaining in business time.

SLA-006
Breach effects.

Crossing a target marks the cycle breached, emits a notification event, and never resolves, closes, reassigns, or otherwise mutates the ticket on its own.

QUEUE5 clauses

QUEUE-001
Filter-exact dashboard.

Home shows the current selected view's open backlog, urgent count, target-state counts, resolved-today count, mean first-response time over the last 24 hours, and satisfaction score over the last 7 days. For every ticket state it shows the count and longest current wait. Every count opens the equivalent saved-view conditions and shows the data timestamp; it is no more than 30 seconds old at the stated limits.

QUEUE-002
Target tiers.

An active ticket with a target is exactly one of breached once past due, at_risk with 25% or less of its target duration remaining, due_soon with more than 25% remaining and two business hours or less to due, or later otherwise. A paused cycle is paused; a ticket with no matching target is no_target. The four active-target tiers are mutually exclusive and exhaustive.

QUEUE-003
Operational ordering.

The default work ordering is breached, at_risk, due_soon, later, paused, no_target, then nearest active due time, priority, latest requester activity, and ticket number. An agent may choose documented alternatives without changing the selected view conditions.

QUEUE-004
Due-state detail.

A queue and ticket disclose which target is due, absolute due time with timezone, and the previous/current due time and reason for a recalculation.

QUEUE-005
Measured times.

First-response and resolution measures are stored as business and calendar elapsed time and shown with their cycle. Reopening never overwrites a recorded first response.

AUTO7 clauses

AUTO-001
Rule lifecycle.

A rule is Draft, Active, or Paused and has a name, ordered conditions and actions, creator, latest editor, version, and run count. Draft and Paused rules process no new events.

AUTO-002
Allowed actions.

A rule can react to ticket creation, requester message, ticket field or status change, or one scheduled elapsed-time condition. It may apply a tag, set priority, assign an eligible group or agent, set a pending reason, send an internal notification, or apply a macro draft. It cannot send a public reply, complete a ticket, change a requester record, or invoke an external mutation. Policy: automation.action-allowlist.v1; default: the listed actions only.

AUTO-003
Order and audit.

Active rules evaluate in visible order against one immutable triggering revision. Each run records matching conditions and each applied, skipped, or failed action.

AUTO-004
Idempotency and loops.

A rule run is unique by rule version, ticket, triggering event or scheduled window, and action. An evaluation chain evaluates each active rule version at most once; duplicate events and scheduler runs repeat no effects.

AUTO-005
Concurrent changes and failure.

A rule rechecks its conditions and ticket revision before mutation. A conflict is a visible skipped run. A failed action is visible with rule, ticket, attempted action, and error; a retry reuses its idempotency key.

AUTO-006
Rule authorisation.

Only an Administrator may activate, pause, or change a rule's owner-visible scope. A rule cannot act on a ticket, group, form, or field its owner is not authorised to configure.

AUTO-007
Reviewed recovery.

A retried failed rule run uses the original triggering revision and idempotency keys, reports any now-stale condition, and never replays an action that already succeeded.

NOTIFY4 clauses

NOTIFY-001
Accountable ticket updates.

New unassigned tickets notify every eligible Agent; direct assignment notifies the new assignee or every eligible member of the assigned group; self-assignment sends nothing. Requester follow-up, at-risk, breach, and solved notifications go to followers, or to the assignee then eligible Administrators if there are no followers. An at-risk and a breach notification occur at most once each per service cycle.

NOTIFY-002
Coalescing and quiet hours.

Repeated requester-follow-up events coalesce to one notification until an assigned agent opens the ticket; repeated event categories use their named service-cycle or open-cycle boundary. Policy: ticket.quiet-hours.v1; default: quiet hours defer non-urgent notifications while urgent and breached work bypasses them. Intake, service clocks, and other recipients are unaffected.

NOTIFY-003
No mail loops.

Notifications use a configured no-reply sender that is not a support destination. Mail to that address creates or reopens no ticket and triggers no automation. Automatic acknowledgements are ticket mail under MAIL-014, not notifications.

NOTIFY-004
Failure reaches a person.

A public-message delivery failure, automation failure, notification failure, or configuration/security failure is visible on the ticket or settings record and is delivered to the initiating agent, or every active Administrator when there is no initiating agent. This responsible-failure notification cannot be muted and never blocks or rolls back its causing event.

CSAT3 clauses

CSAT-001
Survey request.

A solved ticket with a customer-visible accepted reply can queue one signed satisfaction request for that solve cycle. Policy: csat.request.v1; default: one email 24 hours after the solve. No survey is sent to a blocked requester, and reopening cancels an unsent request.

CSAT-002
Survey response.

The single-use public link accepts one whole-number rating from one to five with an optional comment, expires after 30 days, exposes no unrelated ticket data, and never creates or reopens a ticket.

CSAT-003
Survey history.

A result shows on the ticket and customer record with its solve cycle and time. A later cycle makes a new request and never overwrites a prior result.

INSIGHT8 clauses

INSIGHT-001
Operational dashboards.

Analytics provides prebuilt dashboards for ticket volume and status, first-response and resolution duration, service-target outcomes, satisfaction score and response rate, assignment and group workload, and channel mix.

INSIGHT-002
Dataset and filters.

Every report states its period, timezone, selected view or filters, ticket states, denominator, freshness time, and comparison period. It can filter by assignee, group, type, priority, tag, form, channel, custom field, and target state.

INSIGHT-003
Report builder.

An authorised user can create, edit, save, share, and retire a report using documented ticket, customer, satisfaction, and service-target fields. A report exposes the tickets behind an aggregate only to viewers authorised for those tickets.

INSIGHT-004
Formatting and export.

Dashboard and report users can choose supported chart and table formatting. An export contains the complete durable result for current filters and period with metric definitions, rather than only the rendered page.

INSIGHT-005
Freshness and correctness.

Ticket, service-target, and satisfaction analytics reflect committed mutations within five minutes at the stated limits. A panel shows its last successful refresh and a stale or failed state; no cached result is represented as current.

INSIGHT-006
Operational export.

An agent can export the complete durable current result of an authorised view, queue, dashboard, or report, including selected filters, period, and metric definitions. This convenience export does not replace DATA-001.

INSIGHT-007
Merge accounting.

A merged source remains auditable and searchable but is excluded from live backlog, created, solved, and service-target totals. Its source conversation entries remain visible through canonical transclusion without double-counting the canonical case.

INSIGHT-008
Explainable drivers.

Each classified volume, satisfaction, or service-target aggregate exposes its contributing tag or configured classification, stated denominator, and authorised ticket drill-down. Unclassified eligible tickets appear as Other; the displayed drivers, Other, and excluded denominator reconcile to the reported total.

DATA3 clauses

DATA-001
Portable export.

Lily's complete export includes tickets, cases, conversations, messages, attachments, customers, organisations, forms, tags, views, macros, service cycles, automations and runs, satisfaction records, settings, article versions, record links, notifications, and audit entries, with original attachments. It contains no secret and follows baseline data guarantees.

DATA-002
Data boundaries.

Export, retention, deletion, and erasure identify locally owned data and optional-adapter references. Lily does not claim to update, delete, export, or erase a provider record unless the configured adapter returned a confirmed result for that operation.

DATA-003
Zendesk importer.

An Administrator can use the documented importer with an administrator-provided Zendesk export into an empty Lily installation. It validates mappings before mutation and offers dry run. It imports numbered tickets, requesters, messages, original attachments, tags, forms, groups, users, status history, merge history, and available metadata, with a per-record outcome and one import audit record. Each source object has a preserved source ID and a Lily ID; source ticket numbers are retained only when unused, otherwise Lily allocates the next permanent number and records the source-number mapping. Existing source IDs make reruns idempotent. Imported merge and status history are retained as source history; unsupported data receives a per-record outcome without fabrication. Stage 2 must prove this with a real export; no Freshdesk importer is committed.


Baseline guarantees

22 declared clauses from this product's BASELINE.md. Selected ownership guarantees are shown below; execution evidence is separate.

BASE-DATA-005
Independence.

The deployment makes no request to the marketplace or the seed author. There is no telemetry, phone-home, licence check, or kill switch.

Declared guarantee
BASE-DATA-001
Export and import.

pnpm run export produces every record and attachment; pnpm run import restores it into an empty deployment; export, import, export yields an archive with equivalent contents.

Declared guarantee
BASE-DATA-004
Outbound flows.

User data leaves the deployment only through externals declared in seed.json → externals. No other outbound request carries user data.

Declared guarantee
BASE-PUBLIC-001
Privacy.

Public surfaces (widgets, booking pages, forms, status pages) set no cookies, load nothing from third-party domains, and send no visitor data before the visitor's first interaction.

Declared guarantee
BASE-SECRET-001
No leakage.

No secret appears in the client bundle, in logs, or in an error response.

Declared guarantee

BASELINE.md for Lily


Release history

Honest version: there is one release line so far, and we will not invent the rest.

v0.1.0
Current — thin-real-worker-d1-ui-port; ticket-desk-contract-draft

The manifest states this version and maturity. Consult the source verification evidence before deployment.


DIY quickstart

The code is MIT and the deployment is yours. Nothing below talks to us; optional management remains planned.

Use the edition's source README for its available installation commands and required configuration.


View as agent

Everything on this page exists as machine-readable files. If you are an agent evaluating Lily for someone, read these instead of the prose.

Installation instructions are in the source README; this entry does not declare a catalogue install command.

Catalogue endpoint

/agents/catalogue.json

Every seed with its id, category, version, clause count, install command and links to the files on the left. Generated from the seeds' own artifacts on every build, so it cannot drift from what ships.

The machine face

Lily landing screenshot

Run Lily on your own cloud.

MIT code, your data, your infrastructure. Leave whenever you like and it keeps running.