Voice and tone
How Oration sounds: professional, clear and friendly, and how the tone shifts from a greeting to an error.
Oration's voice#
One voice across the CRM, the Agents Platform, the Contact Center and Ticketing: professional, clear, friendly, operational and specific. The voice stays fixed; the tone moves with the moment.
Professional
Written for people doing their jobs, with money and customers on the line. Steady, never cute.
Sounds like “Payment run approved.”
Not: “Boom! Payments are on their way!”
Clear
Plain words a tired reader gets on the first pass. One idea per sentence, the important word first.
Sounds like “Couldn't send the remittance. Check the supplier's email address.”
Not: “Remittance dispatch encountered an unexpected delivery exception.”
Friendly
Warm through respect and help, not exclamation points. We greet people, thank them when they did us a favor, and never scold.
Sounds like “Good morning, Maya.”
Not: “Hey superstar, ready to crush it today?”
Operational
Copy is for doing the work. Say what happened, what it means and what to do next, in the order the reader needs it.
Sounds like “3 invoices failed matching. Review them before Thursday's run.”
Not: “Unlock the power of intelligent matching across your AP workflow.”
Specific
Names, amounts, dates and counts beat categories. Specific copy is shorter, because it doesn't need to explain itself.
Sounds like “Halcyon Packaging's W-9 expired on September 1.”
Not: “Some supplier documents may need attention.”
The voice comes from PRODUCT.md: professional, clear, friendly operational copy, such as dated summaries and direct task labels, and never hype on the surfaces where people work. It is the voice of a colleague who knows the books: they tell you what changed, show you the figure and leave the decision with you.
Principles#
Six habits that turn the voice into copy. Each one is shown as the draft that usually comes first and the line that ships.
1Lead with the outcome
Put what happened, or what the reader can do, first. Detail follows for those who want it.
Before: We have successfully completed processing your payment run request.
After: Payment run approved.
2Name the thing
Say which record, which person, which amount. A pronoun or a category makes the reader go and look.
Before: Item updated.
After: Northwind Freight's remittance email updated.
3Say what happens next
When an action starts something, say when it finishes or how the reader will know.
Before: Your import has been queued.
After: Importing 248 suppliers. You'll get a notification when it's done.
4Use the reader's words
Write in the reader's domain, not the system's. Suppliers, invoices and payment runs, not entities, objects and jobs.
Before: Entity sync failed for 3 objects of type vendor.
After: Couldn't update 3 suppliers.
5Stay calm as the stakes rise
The bigger the consequence, the plainer the words. Never raise the volume to make a point.
Before: WARNING!!! This action is PERMANENT and CANNOT be undone!
After: This can't be undone. Paid invoices stay in the audit log.
6Be friendly, not familiar
Courtesy without performance. No slang, no emoji, no in-jokes that don't translate.
Before: Hey there! Ready to rock your pipeline today?
After: Good morning, Maya. 4 decisions are waiting.
Tone by moment#
Tone rises and falls with the stakes. Light where nothing is at risk, plain and serious where money, data or a customer is.
Eight moments, live
Each moment rendered with the component that carries it. Step through them and try the actions.
Good morning, Maya
Monday, September 28. 4 decisions waiting and 3 meetings on your calendar.
import { ApprovalCard } from "@oration/canon/components/ai/approval-card";import { Button } from "@oration/canon/components/button";import { ConfirmDialog } from "@oration/canon/components/confirm-dialog";import { EmptyState, ErrorState } from "@oration/canon/components/data-state";import { Meter } from "@oration/canon/components/meter";import { SideTabs, sideTabId } from "@oration/canon/components/side-tabs";import { toast } from "@oration/canon/components/toast";import { CircleCheckIcon, FileTextIcon, GaugeIcon } from "lucide-react";import * as React from "react";export function ToneByMoment() { const panelId = React.useId(); const [moment, setMoment] = React.useState("greeting"); const [confirmOpen, setConfirmOpen] = React.useState(false); const moments = [ { id: "greeting", label: "Home greeting", meta: "Warm, brief" }, { id: "empty", label: "Empty state", meta: "Helpful" }, { id: "success", label: "Success", meta: "Quiet" }, { id: "error", label: "Error", meta: "Calm, plain" }, { id: "destructive", label: "Destructive", meta: "Serious" }, { id: "proposal", label: "AI proposal", meta: "Evidence first" }, { id: "limits", label: "Limits and billing", meta: "Neutral, exact" }, { id: "onboarding", label: "Onboarding", meta: "Encouraging" }, ]; return ( <div className="grid w-full max-w-3xl gap-4 text-left md:grid-cols-[12rem_minmax(0,1fr)]"> <SideTabs label="Moments" items={moments} value={moment} onValueChange={setMoment} panelId={panelId} orientation="vertical" /> <div id={panelId} role="tabpanel" aria-labelledby={sideTabId(moment)} className="flex min-h-72 min-w-0 items-center justify-center rounded-xl bg-card p-5 shadow-border" > {moment === "greeting" ? ( <div className="w-full"> <p className="text-2xl font-semibold tracking-[-0.02em] text-foreground"> Good morning, Maya </p> <p className="mt-1 text-sm text-muted-foreground"> Monday, September 28. 4 decisions waiting and 3 meetings on your calendar. </p> </div> ) : null} {moment === "empty" ? ( <EmptyState illustration="done" title="No invoices on hold" description="Invoices that fail matching wait here for someone to approve or reject the match." action={ <Button variant="outline" onClick={() => toast.add({ title: "Matching rules opened", }) } > View matching rules </Button> } /> ) : null} {moment === "success" ? ( <div className="flex w-full max-w-sm flex-col items-center gap-4"> <div className="flex w-full items-start gap-3 rounded-[10px] bg-muted/70 p-3"> <CircleCheckIcon aria-hidden="true" className="mt-0.5 size-4 shrink-0 text-success" /> <div className="flex flex-col gap-0.5"> <p className="text-13 font-medium text-foreground"> Payment run approved </p> <p className="text-13 text-muted-foreground"> 14 invoices for $86,412.50 go out Thursday, October 1. </p> </div> </div> <Button variant="outline" onClick={() => toast.add({ type: "success", title: "Payment run approved", description: "14 invoices for $86,412.50 go out Thursday, October 1.", }) } > Show the toast </Button> </div> ) : null} {moment === "error" ? ( <ErrorState title="Couldn't load payment runs" description="The request timed out. Nothing was changed. Try again in a moment." onRetry={() => toast.add({ title: "Loading payment runs again" }) } /> ) : null} {moment === "destructive" ? ( <div className="flex flex-col items-center gap-3 text-center"> <p className="max-w-xs text-13 text-muted-foreground"> The dialog names the supplier, says what goes and what stays, and repeats the action on its button. </p> <Button variant="destructive" onClick={() => setConfirmOpen(true)} > Delete supplier </Button> <ConfirmDialog open={confirmOpen} onOpenChange={setConfirmOpen} destructive title="Delete Halcyon Packaging?" description="This removes the supplier and takes its 3 open invoices out of upcoming payment runs. Paid invoices and remittance history stay in the audit log." confirmLabel="Delete supplier" onConfirm={() => toast.add({ title: "Halcyon Packaging deleted", description: "3 open invoices left the October 1 run.", }) } /> </div> ) : null} {moment === "proposal" ? ( <ApprovalCard className="w-full" icon={<FileTextIcon />} title="Hold INV-20431 until Halcyon sends a W-9" description="From Nora's call with Halcyon at 9:40 AM. The W-9 on file expired on September 1." fields={[ { label: "Invoice status", before: "Scheduled for Oct 1", after: "On hold", }, { label: "Supplier task", after: "Upload a current W-9", }, ]} footnote="Updates 1 invoice and emails Halcyon" approveLabel="Hold invoice" onApprove={() => toast.add({ type: "success", title: "INV-20431 on hold", description: "Halcyon was asked for a current W-9.", }) } onSkip={() => toast.add({ title: "Proposal skipped" })} /> ) : null} {moment === "limits" ? ( <div className="flex w-full max-w-sm flex-col gap-3"> <div className="flex items-center gap-2 text-sm font-medium text-foreground"> <GaugeIcon aria-hidden="true" className="size-4 text-muted-foreground" /> 9,200 of 10,000 credits used </div> <Meter value={9200} max={10000} label="Credits used" /> <p className="text-13 text-muted-foreground"> Enrichment pauses at 10,000 credits. Credits reset on October 1, or you can add more now. </p> <div> <Button variant="outline" onClick={() => toast.add({ title: "Billing opened", description: "Add credits from the usage tab.", }) } > Add credits </Button> </div> </div> ) : null} {moment === "onboarding" ? ( <div className="flex w-full max-w-sm flex-col gap-3"> <p className="text-xs text-muted-foreground tabular-nums"> 2 of 6 steps done </p> <p className="text-sm font-semibold text-foreground"> Build your first voice agent </p> <p className="text-13 text-muted-foreground"> Give it a voice and a procedure to follow, then call it from your browser before it takes a supplier call. </p> <div> <Button onClick={() => toast.add({ title: "New agent", description: "Name it and pick a voice to start.", }) } > Build an agent </Button> </div> </div> ) : null} </div> </div> );}| Moment | Tone | Ships | Never |
|---|---|---|---|
| Home greeting | Warm, brief, factual | Good morning, Maya with “Monday, September 28. 4 decisions waiting and 3 meetings on your calendar.” | Welcome back, superstar. Let's crush Monday! |
| Empty states | Helpful, pointing forward | No invoices on hold with “Invoices that fail matching wait here for someone to approve or reject the match.” and View matching rules | Nothing here yet! It's lonely in here. |
| Success | Quiet, past tense | Payment run approved with “14 invoices for $86,412.50 go out Thursday, October 1.” | Woohoo! You did it! |
| Errors | Calm, plain, no blame | Couldn't load payment runs with “The request timed out. Nothing was changed. Try again in a moment.” and Retry | Oops! Something went wrong. |
| Destructive confirmations | Serious, explicit | Delete Halcyon Packaging? with what goes, what stays, and Delete supplier | Are you sure? Yes or No. |
| AI proposals | Evidence first; the person decides | Hold INV-20431 until Halcyon sends a W-9 with “From Nora's call with Halcyon at 9:40 AM. The W-9 on file expired on September 1.” and Hold invoice | Our AI magically spotted a problem and fixed it for you! |
| Limits and billing | Neutral, exact, no pressure | 9,200 of 10,000 credits used with “Enrichment pauses at 10,000 credits. Credits reset on October 1, or you can add more now.” | You're almost out! Upgrade now to avoid disruption. |
| Onboarding | Encouraging, concrete | Build your first voice agent with “Give it a voice and a procedure to follow, then call it from your browser.” and Build an agent | Let's get you set up in no time! |
The tone dial in one line
The closer the copy sits to money, data loss, security or a customer, the less personality it carries. Greetings and empty states can be warm; destructive confirmations and billing are plain.
How AI speaks#
Agents propose, people decide. AI copy shows its evidence, states the change it wants, and leaves the decision with a person. It never claims a mind or a mood.
A proposal written well
The title is the action in plain words. The description is the evidence: where it came from and why. The fields show the change, and the button repeats the verb.
Move Orchard Street's payment to October 5
Orchard Street's AP lead asked on Nora's 10:15 AM call. Their bank account changes on October 2, so the October 1 run would pay the account that is closing.
- Payment date
- Before: Oct 1After: Oct 5
- Early-pay discount
- Before: 2% ($146.10)After: None, past the 10-day window
import { ApprovalCard } from "@oration/canon/components/ai/approval-card";import { toast } from "@oration/canon/components/toast";import { FileTextIcon } from "lucide-react";export function ProposalCopy() { return ( <ApprovalCard className="w-full max-w-lg text-left" icon={<FileTextIcon />} title="Move Orchard Street's payment to October 5" description="Orchard Street's AP lead asked on Nora's 10:15 AM call. Their bank account changes on October 2, so the October 1 run would pay the account that is closing." fields={[ { label: "Payment date", before: "Oct 1", after: "Oct 5" }, { label: "Early-pay discount", before: "2% ($146.10)", after: "None, past the 10-day window", }, ]} footnote="Updates 1 payment and notifies Orchard Street" approveLabel="Move payment" onApprove={() => toast.add({ type: "success", title: "Payment moved to October 5", description: "Orchard Street was notified.", }) } onEdit={() => toast.add({ title: "Editing the proposal" })} onSkip={() => toast.add({ title: "Proposal skipped" })} /> );}Agents propose, people decide
Evidence before opinion
Software, not a colleague
The Copilot reports, then offers
| Write | Instead of | Why |
|---|---|---|
| Nora proposes | Nora wants to, Nora would love to | A proposal is a request, not a wish |
| Drafted, found, matched, summarized | Thought about, figured out, realized | Describe the output, not an inner life |
| Transferred the call to Priya | Decided to hand you off | Say what happened and to whom |
| Couldn't find a W-9 for Halcyon | Nora is confused | Name what's missing |
| Draft reply, Summarize call | AI magic, Smart reply | The button names the action |
Hold INV-20431 until Halcyon sends a W-9
From Nora's call at 9:40 AM. The W-9 on file expired on September 1.
Your AI teammate is on it!
Nora noticed something fishy and magically took care of Halcyon's invoice for you.
Where to put AI in the interface is covered in AI assistance. The card itself is Approval card.
One voice across four apps#
Each app's readers bring their own vocabulary and pace. The voice holds; the register adjusts.
| App | Reader | Register | Sounds like |
|---|---|---|---|
| CRM | Revenue team, Maya | Pipeline language, amounts and dates up front | “Halcyon has been quiet for 17 days. $148,000 closes October 16.” |
| Agents Platform | Conversation designers and AI ops | Precise, technical terms explained in an Info tip | “End-to-end latency is 820 ms at p95, above the 800 ms target.” |
| Contact Center | Support agents under load | Short and scannable; drafts to customers are warmer | “Caller verified. Payment PMT-58213 went out September 25.” |
| Ticketing | Support team triaging | Terse, past tense, Linear-like | “Assigned to Wen. Moved to In progress.” |
- Copy that Cedarline sends to its own customers, such as a drafted supplier reply, is written in Cedarline's voice and signed by a person. Oration's voice is for the interface around it.
- Use the same word for the same thing in every app. A ticket is a ticket in the Contact Center too. See the glossary.
What we never say#
Words and habits that break the voice, why they do, and what to write instead.
| Never | Why | Write instead |
|---|---|---|
| Oops, whoops, uh oh | Turns a failure into a joke the reader didn't ask for. | Couldn't save the sequence. Try again. |
| Something went wrong (alone) | Says nothing about what failed or what to do. | Couldn't load invoices. Check your connection and try again. |
| Invalid, illegal, error 422 | Blames the reader in system language. | Enter an amount greater than $0.00. |
| Are you sure? | Asks for courage instead of naming the consequence. | Delete Halcyon Packaging? |
| Successfully | Every completed action succeeded; the word adds nothing. | Supplier added. |
| Simply, just, easy, quick | Makes anyone who finds it hard feel slow. | Upload a CSV of suppliers. |
| Seamless, revolutionary, supercharge, unlock, game-changing | Marketing heat on an operating surface. | Say what it does: Matches invoices to purchase orders. |
| AI-powered, magic, smart (as adjectives) | Labels the technology instead of the action. | Draft reply, Summarize call |
| Your AI teammate, she, he | Gives software a personhood it doesn't have. | Nora, the agent, it |
| Click here, learn more (alone) | Means nothing out of context or to a screen reader. | Read the billing docs |
| Act now, don't miss out | Manufactured urgency. | Credits reset on October 1. |
| We're sorry for any inconvenience | Ritual apology instead of help. | Fix it, or say how: Try again in a few minutes. |
| The user | Talks about the reader instead of to them. | You, or the role: the account owner |
| Exclamation points and emoji | Raise the volume and don't translate. None in the UI. | A period. |
Claims we never make
Interface copy never asserts anything about Oration that isn't confirmed: no customer names, testimonials, revenue figures or compliance claims. The trust row on log in and sign up is the only place compliance programs appear. See Brand identity.