Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion frontend/src/Page/Dashboard.elm
Original file line number Diff line number Diff line change
Expand Up @@ -819,7 +819,6 @@ tabs current =
[ ( Overview, Route.Dashboard, "Overview" )
, ( Channels, Route.DashboardChannels, "Channels" )
, ( Persona, Route.DashboardPersona, "Voice" )
, ( Billing, Route.DashboardBilling, "Credits" )
, ( Settings, Route.DashboardSettings, "Settings" )
]
)
Expand Down
43 changes: 11 additions & 32 deletions frontend/src/Page/Wizard.elm
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ local until you press the button that advances the step.
-}

import Api
import Format
import Html exposing (Html, div, h1, h2, li, ol, p, section, span, text, ul)
import Html.Attributes exposing (attribute, class, classList)
import Html.Events
Expand Down Expand Up @@ -752,39 +751,19 @@ safetyNote status =

launchStep : Model -> Api.Wizard -> Html Msg
launchStep model wizard =
let
l =
wizard.launch
in
section [ class "wizard-step" ]
[ h1 [] [ text "One last thing" ]
, Ui.card
[ h2 [] [ text "What you'll pay" ]
, p []
[ text "Every AI reply costs "
, span [ class "price" ]
[ text (Format.milliRate "en-IN" l.currency l.unitPriceMilli) ]
, text ". Canned replies are free, and you're never charged for a message Concierge decides not to answer."
]
[ h1 [] [ text "You're set" ]
, p [ class "step-sub" ]
[ text
(if String.isEmpty wizard.business.name then
"Concierge will start answering on the channels you connected."

else
"Concierge will start answering for "
++ wizard.business.name
++ " on the channels you connected."
)
]
, if l.metered && not l.verified then
Ui.card
[ h2 [] [ text "Verify your card" ]
, p []
[ text "We charge "
, span [ class "price" ]
[ text (Format.money "en-IN" l.currency l.verificationAmount) ]
, text " and refund it straight away. It's how we keep throwaway signups out."
]
, Ui.linkButton
{ label = "Verify now"
, url = "/dashboard/billing"
, primary = True
}
]

else
text ""
, div [ class "wizard-actions" ]
[ Ui.button
{ label = "Back"
Expand Down
2 changes: 1 addition & 1 deletion public/features.html
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
<link rel="stylesheet" href="/css/layout.css" integrity="sha384-T9CS4wzZOLS7ir8UQ0Ncd7BtTCD/6evKMxLvAVypuPWjtSsdqpc1hsKFMFsnQoLL" crossorigin="anonymous">
<link rel="stylesheet" href="/css/components.css" integrity="sha384-yC/QTZKApI25/gdkkM6YnHtL3S9ChV/tdjQiHn5ngdSIGjqvslzzGLzT3aBWhq0F" crossorigin="anonymous">
<link rel="stylesheet" href="/css/responsive.css" integrity="sha384-uClijP3+k5Ah5Ah32u3Vc4EcPhRGVN5eJjIcpeJkJzM9h9lRWDpwdCY5OBQpJfQR" crossorigin="anonymous">
<script defer="" src="/app.js" integrity="sha384-FtlH8XZDN++jZlRTs44cFGjdMLXvu5KjxXwgEMbi/3GvKEMTsBrI47dHzxT5o+W3" crossorigin="anonymous"></script>
<script defer="" src="/app.js" integrity="sha384-YjWrVacJzR1QmM/it71Rw8wKbWgkq2x9GcI0fF95ED0ntcEW88sm+4TFXAA90IrZ" crossorigin="anonymous"></script>
<script defer="" src="/boot.js" integrity="sha384-faMimiIBzyqWV/0Sz8gXyLSivDCGriFnQ/N0BwQtlzLorOUbxjSE+Ql02LGldJ14" crossorigin="anonymous"></script>
</head>
<body><header class="site-header"><a href="/" class="brand"><img src="/logo.svg" alt="" width="32" height="32"><span>Concierge</span></a><nav class="site-nav"><a href="/features" class="nav-link">Features</a><a href="/pricing" class="nav-link">Pricing</a><a href="/auth/login" class="btn btn-primary">Sign in</a></nav></header><main class="site-main"><div class="page page-features"><h1>What Concierge does</h1><section class="feature-list"><div class="feature"><h2>Answers WhatsApp for you</h2><p>Messages to your business number get a reply in your own voice, within seconds, at any hour. Your number and your customers' threads stay exactly where they are.</p></div><div class="feature"><h2>Knows when to stop</h2><p>Anything that quotes a price, promises a date or strays outside what you said it should discuss is never sent. Concierge tells the customer a person is picking it up, and emails you.</p></div><div class="feature"><h2>Waits for people to finish typing</h2><p>A burst of three short messages gets one considered answer, not three. You choose how long it waits.</p></div><div class="feature"><h2>Remembers the conversation</h2><p>Replies use the thread's recent history. After a few hours of silence the conversation is over and the next message starts fresh.</p></div><div class="feature"><h2>A prompt you can actually read</h2><p>Pick a voice, answer a few questions, and Concierge writes the prompt. The fixed guardrails around it are visible on the page — nothing about what the model is told is hidden from you.</p></div><div class="feature"><h2>Checked before it speaks</h2><p>Every prompt goes past a safety classifier before it's allowed to answer a customer. Change the prompt and AI replies pause until the new one clears.</p></div><div class="feature"><h2>Keeps almost nothing</h2><p>Message bodies pass through and are never written down. Only channel, direction, sender, recipient and timestamp are stored.</p></div></section><div class="page-cta"><a href="/auth/login" class="btn btn-primary btn-lg">Get started</a></div></div></main><footer class="site-footer"><ul class="footer-links"><li><a href="/features">Features</a></li><li><a href="/pricing">Pricing</a></li><li><a href="/terms">Terms</a></li><li><a href="/privacy">Privacy</a></li><li><a href="https://ananthb.github.io/concierge/">Docs</a></li></ul><p class="footer-note">Message bodies pass through but are never stored. Only channel, direction, sender, recipient and timestamp are kept.</p></footer></body></html>
2 changes: 1 addition & 1 deletion public/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
<link rel="stylesheet" href="/css/layout.css" integrity="sha384-T9CS4wzZOLS7ir8UQ0Ncd7BtTCD/6evKMxLvAVypuPWjtSsdqpc1hsKFMFsnQoLL" crossorigin="anonymous">
<link rel="stylesheet" href="/css/components.css" integrity="sha384-yC/QTZKApI25/gdkkM6YnHtL3S9ChV/tdjQiHn5ngdSIGjqvslzzGLzT3aBWhq0F" crossorigin="anonymous">
<link rel="stylesheet" href="/css/responsive.css" integrity="sha384-uClijP3+k5Ah5Ah32u3Vc4EcPhRGVN5eJjIcpeJkJzM9h9lRWDpwdCY5OBQpJfQR" crossorigin="anonymous">
<script defer="" src="/app.js" integrity="sha384-FtlH8XZDN++jZlRTs44cFGjdMLXvu5KjxXwgEMbi/3GvKEMTsBrI47dHzxT5o+W3" crossorigin="anonymous"></script>
<script defer="" src="/app.js" integrity="sha384-YjWrVacJzR1QmM/it71Rw8wKbWgkq2x9GcI0fF95ED0ntcEW88sm+4TFXAA90IrZ" crossorigin="anonymous"></script>
<script defer="" src="/boot.js" integrity="sha384-faMimiIBzyqWV/0Sz8gXyLSivDCGriFnQ/N0BwQtlzLorOUbxjSE+Ql02LGldJ14" crossorigin="anonymous"></script>
</head>
<body><header class="site-header"><a href="/" class="brand"><img src="/logo.svg" alt="" width="32" height="32"><span>Concierge</span></a><nav class="site-nav"><a href="/features" class="nav-link">Features</a><a href="/pricing" class="nav-link">Pricing</a><a href="/auth/login" class="btn btn-primary">Sign in</a></nav></header><main class="site-main"><div class="landing"><section class="hero"><h1>Answer every customer, even when you're closed.</h1><p class="hero-sub">Concierge replies to your WhatsApp messages in your own voice, and hands the conversation to you the moment it matters — a price, a promise, anything it shouldn't decide alone.</p><div class="hero-actions"><a href="/auth/login" class="btn btn-primary btn-lg">Get started</a><a href="/pricing" class="btn btn-secondary btn-lg">See pricing</a></div></section><section class="how-it-works"><h2 class="section-title">How it works</h2><ul class="steps"><li class="step"><h3>Connect your WhatsApp number</h3><p>Through Meta's own signup flow. No new number, no app to install.</p></li><li class="step"><h3>Describe your business once</h3><p>Pick a voice, tell it what you do and when you're open. It writes the prompt for you.</p></li><li class="step"><h3>It answers; you get the hard ones</h3><p>Anything about money or a commitment is never sent on its own — Concierge holds the conversation and emails you.</p></li></ul></section></div></main><footer class="site-footer"><ul class="footer-links"><li><a href="/features">Features</a></li><li><a href="/pricing">Pricing</a></li><li><a href="/terms">Terms</a></li><li><a href="/privacy">Privacy</a></li><li><a href="https://ananthb.github.io/concierge/">Docs</a></li></ul><p class="footer-note">Message bodies pass through but are never stored. Only channel, direction, sender, recipient and timestamp are kept.</p></footer></body></html>
2 changes: 1 addition & 1 deletion public/pricing.html
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
<link rel="stylesheet" href="/css/layout.css" integrity="sha384-T9CS4wzZOLS7ir8UQ0Ncd7BtTCD/6evKMxLvAVypuPWjtSsdqpc1hsKFMFsnQoLL" crossorigin="anonymous">
<link rel="stylesheet" href="/css/components.css" integrity="sha384-yC/QTZKApI25/gdkkM6YnHtL3S9ChV/tdjQiHn5ngdSIGjqvslzzGLzT3aBWhq0F" crossorigin="anonymous">
<link rel="stylesheet" href="/css/responsive.css" integrity="sha384-uClijP3+k5Ah5Ah32u3Vc4EcPhRGVN5eJjIcpeJkJzM9h9lRWDpwdCY5OBQpJfQR" crossorigin="anonymous">
<script defer="" src="/app.js" integrity="sha384-FtlH8XZDN++jZlRTs44cFGjdMLXvu5KjxXwgEMbi/3GvKEMTsBrI47dHzxT5o+W3" crossorigin="anonymous"></script>
<script defer="" src="/app.js" integrity="sha384-YjWrVacJzR1QmM/it71Rw8wKbWgkq2x9GcI0fF95ED0ntcEW88sm+4TFXAA90IrZ" crossorigin="anonymous"></script>
<script defer="" src="/boot.js" integrity="sha384-faMimiIBzyqWV/0Sz8gXyLSivDCGriFnQ/N0BwQtlzLorOUbxjSE+Ql02LGldJ14" crossorigin="anonymous"></script>
</head>
<body><header class="site-header"><a href="/" class="brand"><img src="/logo.svg" alt="" width="32" height="32"><span>Concierge</span></a><nav class="site-nav"><a href="/features" class="nav-link">Features</a><a href="/pricing" class="nav-link">Pricing</a><a href="/auth/login" class="btn btn-primary">Sign in</a></nav></header><main class="site-main"><div class="page page-pricing"><h1>Pay for replies, nothing else</h1><p class="page-sub">No subscription, no per-seat fee, no minimum. Buy a pile of replies and use them whenever.</p><section class="price-cards"><div class="error banner" role="alert">Couldn't reach the server. Check your connection and try again.</div></section><section class="price-notes"><h2>What counts as a reply</h2><ul><li>An AI-written answer costs one reply.</li><li>A fixed message you wrote costs nothing, however many go out.</li><li>A message Concierge decides not to answer — because it's about money, or it's been handed to you — costs nothing.</li><li>If the AI fails mid-reply, the credit goes back.</li></ul><h2>Buying</h2><p>Any quantity, upwards of the minimum shown above. Credits you buy don't expire.</p></section><div class="page-cta"><a href="/auth/login" class="btn btn-primary btn-lg">Get started</a></div></div></main><footer class="site-footer"><ul class="footer-links"><li><a href="/features">Features</a></li><li><a href="/pricing">Pricing</a></li><li><a href="/terms">Terms</a></li><li><a href="/privacy">Privacy</a></li><li><a href="https://ananthb.github.io/concierge/">Docs</a></li></ul><p class="footer-note">Message bodies pass through but are never stored. Only channel, direction, sender, recipient and timestamp are kept.</p></footer></body></html>
2 changes: 1 addition & 1 deletion public/privacy.html
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
<link rel="stylesheet" href="/css/layout.css" integrity="sha384-T9CS4wzZOLS7ir8UQ0Ncd7BtTCD/6evKMxLvAVypuPWjtSsdqpc1hsKFMFsnQoLL" crossorigin="anonymous">
<link rel="stylesheet" href="/css/components.css" integrity="sha384-yC/QTZKApI25/gdkkM6YnHtL3S9ChV/tdjQiHn5ngdSIGjqvslzzGLzT3aBWhq0F" crossorigin="anonymous">
<link rel="stylesheet" href="/css/responsive.css" integrity="sha384-uClijP3+k5Ah5Ah32u3Vc4EcPhRGVN5eJjIcpeJkJzM9h9lRWDpwdCY5OBQpJfQR" crossorigin="anonymous">
<script defer="" src="/app.js" integrity="sha384-FtlH8XZDN++jZlRTs44cFGjdMLXvu5KjxXwgEMbi/3GvKEMTsBrI47dHzxT5o+W3" crossorigin="anonymous"></script>
<script defer="" src="/app.js" integrity="sha384-YjWrVacJzR1QmM/it71Rw8wKbWgkq2x9GcI0fF95ED0ntcEW88sm+4TFXAA90IrZ" crossorigin="anonymous"></script>
<script defer="" src="/boot.js" integrity="sha384-faMimiIBzyqWV/0Sz8gXyLSivDCGriFnQ/N0BwQtlzLorOUbxjSE+Ql02LGldJ14" crossorigin="anonymous"></script>
</head>
<body><header class="site-header"><a href="/" class="brand"><img src="/logo.svg" alt="" width="32" height="32"><span>Concierge</span></a><nav class="site-nav"><a href="/features" class="nav-link">Features</a><a href="/pricing" class="nav-link">Pricing</a><a href="/auth/login" class="btn btn-primary">Sign in</a></nav></header><main class="site-main"><div class="page page-legal"><h1>Privacy Policy</h1><p class="muted">Last updated 26 September 2026.</p><div><section><h2>The short version</h2><p>We do not store the contents of your customers' messages. They pass through our worker, are sent to an AI model to generate a reply, and are not written to any database we keep.</p></section><section><h2>What we store</h2><p>For every message: the channel, whether it was inbound or outbound, the sender, the recipient, a timestamp, and what Concierge did with it. Not the text.</p><p>For your account: your email address and name from Google, your business details as you entered them, your prompt configuration, and your credit balance and payment records.</p></section><section><h2>Who else sees it</h2><p>Meta, because that is where WhatsApp messages come from and go to.</p><p>Cloudflare, who host the service and run the AI model that writes replies. Message text is sent to that model to produce a reply and is not used to train it.</p><p>Razorpay, for payments. We never see or store your card details.</p><p>Google, if you sign in with them — they tell us your email address and name.</p></section><section><h2>How long</h2><p>Message metadata and account records are kept while your account is open. Payment records are kept as long as tax and accounting law requires, which is longer.</p></section><section><h2>Your data</h2><p>You can ask us to delete your account and the metadata attached to it. Payment records survive with your account identifier removed.</p><p>There is also a data-deletion endpoint at /data-deletion, which exists because Meta requires one.</p></section><section><h2>Contact</h2><p>Questions about any of this: support@calculon.tech.</p></section></div></div></main><footer class="site-footer"><ul class="footer-links"><li><a href="/features">Features</a></li><li><a href="/pricing">Pricing</a></li><li><a href="/terms">Terms</a></li><li><a href="/privacy">Privacy</a></li><li><a href="https://ananthb.github.io/concierge/">Docs</a></li></ul><p class="footer-note">Message bodies pass through but are never stored. Only channel, direction, sender, recipient and timestamp are kept.</p></footer></body></html>
2 changes: 1 addition & 1 deletion public/terms.html
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
<link rel="stylesheet" href="/css/layout.css" integrity="sha384-T9CS4wzZOLS7ir8UQ0Ncd7BtTCD/6evKMxLvAVypuPWjtSsdqpc1hsKFMFsnQoLL" crossorigin="anonymous">
<link rel="stylesheet" href="/css/components.css" integrity="sha384-yC/QTZKApI25/gdkkM6YnHtL3S9ChV/tdjQiHn5ngdSIGjqvslzzGLzT3aBWhq0F" crossorigin="anonymous">
<link rel="stylesheet" href="/css/responsive.css" integrity="sha384-uClijP3+k5Ah5Ah32u3Vc4EcPhRGVN5eJjIcpeJkJzM9h9lRWDpwdCY5OBQpJfQR" crossorigin="anonymous">
<script defer="" src="/app.js" integrity="sha384-FtlH8XZDN++jZlRTs44cFGjdMLXvu5KjxXwgEMbi/3GvKEMTsBrI47dHzxT5o+W3" crossorigin="anonymous"></script>
<script defer="" src="/app.js" integrity="sha384-YjWrVacJzR1QmM/it71Rw8wKbWgkq2x9GcI0fF95ED0ntcEW88sm+4TFXAA90IrZ" crossorigin="anonymous"></script>
<script defer="" src="/boot.js" integrity="sha384-faMimiIBzyqWV/0Sz8gXyLSivDCGriFnQ/N0BwQtlzLorOUbxjSE+Ql02LGldJ14" crossorigin="anonymous"></script>
</head>
<body><header class="site-header"><a href="/" class="brand"><img src="/logo.svg" alt="" width="32" height="32"><span>Concierge</span></a><nav class="site-nav"><a href="/features" class="nav-link">Features</a><a href="/pricing" class="nav-link">Pricing</a><a href="/auth/login" class="btn btn-primary">Sign in</a></nav></header><main class="site-main"><div class="page page-legal"><h1>Terms of Service</h1><p class="muted">Last updated 26 September 2026.</p><div><section><h2>The service</h2><p>Concierge replies to messages sent to a WhatsApp business number you connect. You keep the number and the WhatsApp Business account; we act on your instructions.</p><p>Replies are generated by a language model working from a prompt you configure. We do not guarantee that any particular reply is accurate, appropriate or complete. You are responsible for what your business says to its customers, including what Concierge says on your behalf.</p></section><section><h2>What you must not do</h2><p>Do not use Concierge to send unsolicited bulk messages, to impersonate anyone, or for anything unlawful. Do not configure a prompt intended to deceive customers about whether they are talking to an automated system.</p><p>We may suspend an account that breaks these rules, or that Meta requires us to suspend.</p></section><section><h2>Replies and payment</h2><p>AI replies are prepaid. A fixed message you wrote costs nothing. A reply that fails, or one Concierge withholds, is not charged.</p><p>Credits you buy do not expire. Credits we grant may carry an expiry, which is shown on your billing page.</p><p>At sign-up we charge a small amount to verify your payment method and refund it immediately.</p></section><section><h2>Availability</h2><p>We aim to keep Concierge running but offer no uptime guarantee. Meta's WhatsApp API, our AI provider and our hosting are all outside our control, and an outage in any of them will stop replies going out.</p></section><section><h2>Liability</h2><p>To the extent the law allows, our liability to you is limited to the amount you paid us in the three months before the claim. We are not liable for lost business, lost customers or lost data.</p></section><section><h2>Ending it</h2><p>You can stop using Concierge at any time and ask us to delete your account. Unused credits are not refundable except where the law requires it. We may end your account with reasonable notice, or immediately if you break these terms.</p></section><section><h2>Changes</h2><p>We may change these terms. Continuing to use Concierge after a change means you accept it.</p></section></div></div></main><footer class="site-footer"><ul class="footer-links"><li><a href="/features">Features</a></li><li><a href="/pricing">Pricing</a></li><li><a href="/terms">Terms</a></li><li><a href="/privacy">Privacy</a></li><li><a href="https://ananthb.github.io/concierge/">Docs</a></li></ul><p class="footer-note">Message bodies pass through but are never stored. Only channel, direction, sender, recipient and timestamp are kept.</p></footer></body></html>
11 changes: 3 additions & 8 deletions src/api/wizard.rs
Original file line number Diff line number Diff line change
Expand Up @@ -238,15 +238,10 @@ pub async fn handle(mut req: Request, env: Env, rest: &[&str]) -> Result<Respons
}

// Seal the wizard.
//
// Billing is deactivated (dormant), so there is no card-verification
// gate here any more. The only requirement is a connected channel.
(Method::Post, ["complete"]) => {
let tenant = get_tenant(&db, &tenant_id).await?.unwrap_or_default();
if tenant.plan.is_metered() && tenant.verified_at.is_none() {
return super::error(
"unverified",
"Verify your account before finishing setup.",
402,
);
}
let has_wa = !list_whatsapp_accounts(&kv, &tenant_id).await?.is_empty();
let has_email = !list_email_accounts(&kv, &tenant_id).await?.is_empty();
if !has_wa && !has_email {
Expand Down
Loading
Loading