---
title: Omnichannel (messaging & lead capture)
description: One inbox for all your customer channels — Tilda web forms, Moldcell PBX calls, Viber, WhatsApp, Facebook/Instagram, comments — with replies to a specific message, file attachments, reactions, Instagram stories, ice breakers and menus, automatic keyword replies, lead capture, routing rules, compliant campaigns and three reports. Off by default; turn it on from the Config Center.
product: TSync Intelligence 11.3.6
language: en
canonical: https://docs.tsync.pro/modules/tsync-omnichannel/
source: https://docs.tsync.pro/llms.txt
---

# 📨 Omnichannel — messaging & lead capture

**Omnichannel** brings all the ways customers reach you into one place inside TSync: web-form leads,
phone calls, and messaging apps (Viber, WhatsApp, Facebook Messenger, Instagram Direct). Every message
becomes a conversation attached to a lead, and every web-form or ad submission becomes a lead
automatically — deduplicated, so you never get two records for the same person.

It is **off by default**. Nothing changes until an administrator turns it on.

## Turning it on

Open **Config Center → Omnichannel → Settings** and choose the **mode**:

- **Off** — completely inert. Only this Settings page exists; nothing is shown, sent or received.
- **Shadow** — the console + menus appear so you can set things up safely; channels aren't live yet.
- **On** — fully active. Channels send and receive.

On the same page you set the **default country dial code** (for Moldova use **373**, for Romania **40**).
This makes local numbers like `069 123 456` resolve to the full international number automatically.

**Meta API version (Graph).** The same page carries the version used by every call to Meta — Messenger,
WhatsApp, Instagram, Lead Ads and reading your own posts. It is a field (form `vNN.N`, for example `v23.0`)
rather than something buried in a release, because Meta retires a version roughly two years after it appears
and the calls then start failing without anything having changed on your side. **There are two independent
versions**, and this is only one of them: each webhook field carries its own version, set in the Meta app
console, which decides the shape of what Meta *sends* you. They can differ with no error at all — align both.

## The right-click quick-action menu

Once the module is on, **right-click any phone number** anywhere in TSync — in a lead, a list, or a form
field — and a small menu appears with:

- **WhatsApp · Viber · Telegram** — opens a chat with that number in your app,
- **Call · SMS** — starts a call or a text,
- **Copy number**.

Right-click a **document** (where it's tagged for sharing) and you'll get **Share (system)** — which uses
your device's native share sheet (great on a phone, where it can attach the actual file) — plus per-app
"copy the link & open" options.

You choose which apps appear, in **Settings → Quick actions**.

## The floating contact widget

While the right-click menu is a tool for *your team*, the **floating contact widget** is an always-visible
button for your *visitors and clients* to reach you. It's a small round button pinned in a corner of the page;
clicking it opens a fan of channel icons. It's the modern successor to the old support-contact widget.

Turn it on in **Config Center → Omnichannel → Settings → Contact widget**:

- **Enable widget**, and choose where it shows — the **client portal**, the **admin area**, or both.
- Set the **button position** (which corner of the screen) and its **colour**.
- Fill in the channels you offer — **WhatsApp, Viber, Telegram, Messenger, phone, e-mail**. A channel button
  appears only when you fill in its value, so you show exactly the ways you want to be reached.

A visitor sees the round button, clicks it, and picks a channel: WhatsApp / Viber / Telegram / Messenger open a
chat with you, **phone** starts a call, and **e-mail** opens a new message. It all happens on the visitor's own
device — nothing is sent until they choose to contact you.

## Connecting a channel (Accounts)

Go to **Omnichannel → Accounts** and click **New**. Pick the channel and fill in its details — each channel
gives you a **secure webhook URL** to paste into the provider:

- **Tilda** — for website forms. Paste the URL into *Tilda → Site Settings → Forms → Webhook*. Every
  submission arrives in the inbox as a **thread**; **no lead is created by default** — see below.
  **After connecting the webhook, republish the pages in Tilda:** the list of destinations is written into
  the already-published pages, so otherwise the forms keep sending only where they sent before.
- **Viber** — paste your Viber Bot **auth token**; register the URL with Viber (`set_webhook`).
- **WhatsApp** — Cloud API: enter the phone-number id, access token, app secret and a verify token; paste
  the URL + verify token into the Meta app webhook.
- **Meta Lead Ads** (Facebook/Instagram ads) — enter the page id, page access token, app secret and verify
  token; subscribe the app's *leadgen* field.
- **Messenger / Instagram Direct** — same Meta page fields. **Two subscriptions are required, and
  missing the second one is the usual reason nothing arrives:** subscribe the **Page** to the app *and*
  subscribe the **app** itself to the `messages` field (Meta app → Webhooks). The "Configure webhooks" screen
  verifies your callback but can leave the field list empty — if `messages` is not in it, Meta sends nothing at
  all. For **comments** on posts and ads, also subscribe the `instagram` object to `comments` and the Page to
  `feed`.

Each account also lets you set the **default lead status, source and assignee** for leads it creates.

## The inbox

**Omnichannel → Inbox** is one list of all your conversations across every channel: who it's from, the last
message, an unread badge, and a link to the lead. Filter by **All / Unread**, by channel, or search by name,
phone or email.

Open a conversation to see the full **message timeline** (incoming and outgoing bubbles). For channels that
support replies (Viber, WhatsApp, Messenger/Instagram) there's a **reply box** — you can type a message or
pick a **canned response** and send it straight from TSync. Each lead's card also shows a compact
**Omnichannel timeline** of that person's messages.

**Pictures, voice notes and files arrive too.** A photo, sticker, GIF, voice note or document sent by a
customer is downloaded and kept with the conversation, so you see the image in the timeline and play the
voice note without leaving TSync — on Messenger, Instagram, Viber and WhatsApp alike. The sender's name and
profile picture are shown wherever the channel gives them; when it doesn't, the conversation is labelled
with its channel and the tail of the sender's id — `Messenger · #a1b2c3` — so two strangers never look
like the same person.

**Which ad paid for the conversation.** If somebody wrote to you by clicking a Facebook or Instagram
"send message" ad, the inbox shows the source as **Messenger (ad)** or **Instagram (ad)**, and inside the
conversation a **From ad** chip names the ad itself. That is how you tell paid traffic from organic
without opening Ads Manager. Non-ad referrals (a short link, the website chat plugin) are recorded too,
but deliberately not labelled as ads.

**A reaction is an answer.** When somebody puts a 👍 or a ❤️ on one of your messages, the conversation shows
it **on that message**, not as a new line — so a thumbs-up on a quote is visible instead of reading as silence.
If they remove the reaction it disappears here too. Only reactions on messages the inbox already holds are
shown; nothing is invented for a conversation that started before the channel was connected.

**Instagram stories.** A reply to one of your stories is labelled **Replied to your story**, with a link to
**open the story** it answers, and somebody who puts your business in their own story arrives as **Mentioned
you in a story** — they showed you to their own followers. Those two are the most valuable events in an
Instagram inbox, and they used to look like any other picture.

**When a reply can't be sent, you're told why.** Before your message leaves, TSync checks that the person
hasn't opted out and that the channel still allows a message: on Messenger and Instagram the free window is
**24 hours** from their last message to you. Past it, a reply *you typed* still goes out under Meta's
allowance for human answers (up to 7 days). Past that, or on a channel with no such allowance (a free-form
WhatsApp message), the send stops with a plain reason instead of a provider error code — use an approved
template instead.

**Deleting a conversation.** Spam, a mis-routed message or your own test rows can be removed: open the
conversation and press **Delete conversation**. The messages and their files are gone for good. **The
contact and any lead stay** — a conversation is evidence, a lead is a commercial record, and removing the
first must never quietly take the second. The confirmation says exactly that before you press it.

## Replying with tappable buttons

Under the reply box there is a collapsed **Quick replies (optional)** panel. Open it, press **+** for each
answer you want to offer and type a short label (up to 20 characters). Send as usual, and those labels appear
under your message as buttons the customer can tap instead of typing.

It is worth doing whenever the answer is one of a few known options — *Yes / No / Call me back*. A tap cannot
be misspelled, and it tells you something a typed word cannot: a tapped answer is marked **tapped** in the
conversation, so the *"Yes"* somebody chose never looks like the *"Yes"* they happened to type.

Leave the panel closed and your reply goes out exactly as before. An empty row is ignored.

**Not every channel carries everything, and you are told when something was left out.** WhatsApp allows at
most three buttons; Instagram shows them only in the phone app; Telegram will not accept two kinds of keyboard
at once. TSync fits the message to the channel and then reports the difference — *"2 element(s) were not
supported on this channel and were left out"*. This matters because a message can be sent **successfully** and
still arrive without its buttons, which is precisely the case you would otherwise never discover. A label that
is too long is shortened rather than dropped: a shorter button still works, while a missing one breaks the
answer that referred to it.

You can also send **a picture or a file with no text at all** — a photo without a caption is a normal thing to
send, and it now goes through.

## Which message a reply refers to

When somebody answers one specific message — using the reply gesture in the Messenger app, say — the
conversation shows **In reply to** with a quote of the original above their words. On a thread with several
open questions that is the only way to tell which one was answered: the link cannot be reconstructed
afterwards from the timing or the order of the messages.

If the original is older than the part of the conversation on screen, it says **In reply to an earlier
message** rather than showing nothing — so a reply always looks like a reply, even when the quote itself
cannot be resolved. A message that arrives while you are watching the conversation shows its quote straight
away; you no longer have to reload the page to see what it answered.

### Answering one specific message

You can do the same from your side. Point at the customer's message and press the reply arrow beside the
bubble — **Reply to this message**. A **Replying to** strip appears above the reply box with the text you are
answering; write your answer and send as usual, and the customer sees it attached to that message, exactly as
if you had used the reply gesture in their own app. Press **✕** on the strip to go back to answering the
conversation as a whole.

Use it whenever a thread has drifted across several topics: *"yes, that one is in stock"* means nothing three
questions later.

**Messenger, Instagram, WhatsApp and Telegram** carry it. On **Viber** the send is refused with a plain
reason, rather than arriving detached from the message it was meant to answer.

**One small thing that went away with it:** sending a reply no longer raises the browser's *"Leave site?
Changes that you made may not be saved"* dialog. It used to appear on every single send, and pressing Cancel
threw the message away.

## Sending a file from your computer

Under the reply box there is **Attach a file**. Choose an image, video, audio clip, PDF or Office document up
to **25 MB**, add a message if you want, and send it with the reply.

**The file is never published.** It is uploaded from your server straight to the platform, so there is no
public web address anybody with the link could open. That distinction matters the day you send a signed
contract instead of a product photo. What may be sent is a fixed list of file types, and the type is read from
the **content** of the file, not from its name.

Today only **Messenger and Instagram** can send a file from your computer. On **Telegram, WhatsApp and Viber**
the send is refused with a sentence saying so — instead of sending your text and quietly losing the file.

If a file fails a check, nothing is sent at all: you fix it and press Send again. And if your server refuses an
upload before TSync ever sees it (a common limit is well under 25 MB), you are told that too — *"nothing
happened"* is the worst possible answer to pressing Send with a 30 MB file.

## When a message becomes a lead

**By default, it does not.** A message, a comment or a submitted form arrives in the inbox as a **thread**, and
a lead is created only once **you** decide it is a real customer — with the **Create lead** button on the thread.

The reason is simple: not every message is a qualified request. A phone number left in a "call me back" bar, a
question under a comment, a "hello" on Messenger — these are conversations, not customers. If each one became a
lead automatically, the pipeline would fill with records nobody vetted, and your sales figures would stop
meaning anything.

Nothing is lost: the thread is kept in full, with the messages, the phone number and whatever was filled in. You
can search it, reply from it, and promote it at any point later.

If qualification happens **before** things reach the system in your setup — a bot that only forwards
pre-filtered requests, say — tick the box on that account under **Omnichannel → Accounts**:

- **Create a lead for every form submission** (Tilda),
- **Create a lead for every chat** (Messenger, Instagram, Viber, WhatsApp),
- **Create a lead for every commenter** (Facebook/Instagram comments).

Each channel has its own switch, so you can leave comments as a journal while turning every form into a lead.

## Phone calls in the inbox

If you use the **Moldcell PBX connector**, calls show up here too: each call becomes an entry on the
contact's conversation with the direction, duration, status and — when there's one — an inline **audio
player** for the recording. An incoming call from an unknown number can open a new lead automatically.

## Comments on Facebook and Instagram posts

Comments under your posts and ads land in the inbox as conversations, just like direct messages — but they
get there by **being fetched**, not by Meta pushing them to you. Meta does not deliver comment notifications
until your app has passed Advanced Access review, while *reading* comments works from day one. So TSync
fetches them.

A Scheduler job, **Comment poll (Facebook / Instagram)** (`omni.comment_poll`, every 15 minutes by
default), looks at your recent posts and files any new comment. It is bounded on purpose, so one busy page
can never crowd out the others: the **10 most recent posts** per account, published within the last **30
days**, and at most **300 comments per run**. Your own replies are recognised and never filed as customer
messages, and a comment already in the inbox is never filed twice — so it makes no difference if the job
runs again.

You'll find it in the [Scheduler](../scheduler.md) (**Scheduler** in the sidebar), where you can also run
it by hand with **Run now**. It needs the `pages_read_user_content` permission on your Meta app. If comment webhooks are approved for you later, they
take over automatically and this job stays on as a safety net for the delivery gaps webhooks quietly have.

### Answering a commenter privately

A public comment can be turned into a private conversation. Open the comment in the inbox and use **Private
reply**: the person receives your message in Messenger or Instagram Direct, and the thread continues normally
if they answer. Meta allows **exactly one** private reply per comment, within **7 days** — the confirmation
says so, because it cannot be undone or repeated.

It can also happen by itself. On **Omnichannel → Keyword replies**, tick **Answer new comments privately,
automatically**. From there:

- a comment that matches one of your **keyword rules** gets **that rule's answer** — somebody who writes
  *"price"* receives the price list, somebody who writes *"demo"* receives the booking link;
- the **Automatic private reply** text below is what everybody else receives;
- leave that text **empty** and only the people whose comment matched a rule are answered. That is the
  quieter setting, and usually the right one: a private message to someone who merely wrote *"nice"* is
  unsolicited in a way an answer to *"price"* is not.

It is off until you switch it on, one reply per commenter is enforced, only comments that came *in* are
answered, and a failure here never costs you the comment itself.

## Entry points — links that remember where they came from

**Omnichannel → Entry points** builds a link (and a QR code) that starts a conversation with you, and tags it
with the campaign it came from. Put the same tag on every place you publish the link — a poster, an ad, a
newsletter — and the inbox will tell you which one people actually used.

You choose where the link opens, the page/account/number it opens, and a campaign tag. Nothing is saved: the
page just builds the link, and you copy it or print the QR code.

**The four channels do not behave the same, and the screen says so on every link:**

| Opens in | The tag | Worth knowing |
|---|---|---|
| Messenger | travels invisibly | — |
| Instagram | travels invisibly | **only on a phone** — on a computer the tag is lost |
| WhatsApp | **there is no hidden tag** | it goes into the message the person sends, so they can see and edit it |
| Telegram | travels invisibly | accepts fewer characters, so a long tag may be shortened |

If your tag has to be changed to fit a channel — spaces become hyphens, unusual characters are removed — the
page tells you the new tag. That matters: once a link is on a poster, a tag that quietly changed shape no
longer matches the one you are looking for in reports.

## Field mapping (optional)

**Omnichannel → Field mapping** lets you decide exactly how a form's fields become lead data. As leads
arrive, TSync **learns** the field names a form sends; open the profile and map each one to a **lead field**
(name, email, phone, company, …) or a **lead custom field**. Anything you don't map still lands in the lead
notes. This is optional — without it, TSync detects name/phone/email automatically.

## Routing rules (optional)

**Omnichannel → Routing rules** send incoming leads to the right place automatically. A rule matches by
**channel + form / UTM source / UTM campaign / ad id** and then sets the lead's **status (pipeline), source,
assignee and tags**. The highest-priority active rule wins; a rule with no conditions is a catch-all. Rules
override the account defaults — e.g. *"leads from the Google campaign → status Hot, assigned to Ana,
tag `google`."*

## Canned responses

**Omnichannel → Canned responses** is your library of reusable quick replies (title + message, optionally
scoped to one channel). They appear in a dropdown above the reply box so your team answers fast and
consistently.

## Automatic replies (keyword replies)

**Omnichannel → Keyword replies** lets the inbox answer by itself. Someone writes *"price"* at 22:40 and
gets your answer straight away, instead of the next morning when a colleague opens the inbox.

It is deliberately simple: **one incoming message, one prepared answer.** No branching bot conversations —
just the questions you already answer ten times a day.

**A rule** is a set of keywords plus the answer to send:

- **Match** — *is exactly · contains · contains the whole word · starts with · is a thumbs-up · contains
  none of*.
- **Keywords** — up to ten, separated by commas. Accents are ignored, so `pret` also matches `preț` and you
  never have to type both spellings of a word. (Russian is matched letter for letter, where accents carry
  meaning.)
- **Answer with** — either text you type, or one of your saved **canned responses**.
- **Channel** — one channel, or all of them.
- **Priority** — the lowest number is checked first. **The first rule that matches replies, and only one
  reply is ever sent.**

**A default reply** at the bottom of the same page answers anything no rule matched — useful for *"thank
you, we've received your message and answer between 9:00 and 18:00."* Switch it off, or leave its text
empty, and nothing is sent.

### The safety rails

These are not settings you have to find — they are how the feature behaves:

- **It is off until you switch it on.** Upgrading never makes your install start talking to customers on its
  own. When automatic replying is off, the rules page says so at the top instead of quietly doing nothing.
- **It never answers twice in a row.** Five messages in a row get one answer. You set the wait between two
  automatic replies to the same person — 5 minutes by default, anywhere from 1 to 240.
- **It only ever answers an incoming message.** An outgoing message never triggers a reply, so two systems
  can't talk each other into a loop. A public comment is answered only through **Answer new comments
  privately** above — a separate switch, off by default — and never with a public reply under the post.
- **It stays inside the messaging rules.** An automatic reply goes out inside the ordinary 24-hour window,
  which is open by definition since it answers a message that has just arrived. TSync never uses Meta's
  "human agent" allowance for automation — that one is reserved for text a person typed.
- **An automatic reply is marked as automatic** in the conversation, so you can always tell it from an
  answer your team wrote.

A rule with no keywords never matches anything — including under *contains none of*, where it would
otherwise answer every message ever sent.

## Ice breakers & menu (Messenger and Instagram)

**Omnichannel → Ice breakers & menu** sets what a person sees **before they type anything**.

- **Ice breakers** — up to **four** questions offered on a conversation with no history: *"What are your
  prices?"*, *"Where are you?"*, *"I want an offer"*. Somebody who has never written to you gets a choice
  instead of an empty box, which is the difference between a conversation and a page they close.
- **Persistent menu** — up to **three** entries always available under the composer. Each one either **sends
  a message** or **opens a link** (https only).

A tap arrives in the inbox as a normal message whose text is the question the customer saw, so your **keyword
rules** answer it exactly as they answer a typed question — and you read the same words they pressed.

**This is set per Page.** A company with two Pages publishes different questions on each; there is no single
global list.

**Saving and publishing are two different things.** **Save** keeps a draft — nothing changes for your
customers. **Publish to the Page** is what puts it in front of them. Under the form, **What Meta actually
holds** is read back from Meta after each publish, so you see what is live rather than what you typed. If
Meta cannot be reached, that section says *unknown* rather than showing an empty list.

## Reports — measuring the inbox

Three Omnichannel reports register themselves in **Reports** (the platform reports console), so they inherit
everything reports there can do: filters, columns, **export to XLSX/CSV**, charts, and e-mail **schedules**.

| Report | Answers |
|---|---|
| **Omnichannel — conversation volume** | new conversations and messages per day and channel, with the share sent **automatically** shown separately from what a person typed |
| **Omnichannel — time to first answer** | per conversation: when the customer first wrote, when somebody answered, and how long they waited |
| **Omnichannel — where conversations came from** | conversations grouped by the ref tag or ad that started them, and how many became leads |

Two things are deliberate and worth knowing when you read the numbers. **An unanswered conversation has no
duration at all** — not a zero, which would read as "answered instantly" and quietly improve your average.
And a conversation that carries **no attribution** is counted as such, rather than dropped from the report:
otherwise the entry-point table would flatter whichever campaign happened to be tagged.

The reports appear only while Omnichannel is on, and they respect the same permission as the inbox.

## WhatsApp templates — the only way to write first

On a real WhatsApp number you cannot simply start a conversation. Free text needs a customer who wrote to you
**in the last 24 hours**; outside that window WhatsApp accepts nothing but a **template** Meta has reviewed and
approved. So "which templates do I have, and are they approved" is not a detail — it is the complete list of
openings your business has.

**Omnichannel → WhatsApp templates** answers it. The list shows each template with its **language**, its real
**status at Meta** — *APPROVED · PENDING · REJECTED* — and, when it was refused, **Meta's own reason**, which
is the one sentence that tells you what to change.

### Creating one

**New template** builds all three languages from a single form:

- **Name** — lowercase letters, digits and underscores. One name carries all three language versions.
- **Category** — *UTILITY* means "about an existing request or order". Meta re-classifies what you submit and
  refuses a mismatch, so keep the wording transactional. A body containing something like `nr. {{2}}` can be
  read as an authentication code and refused before you ever use it.
- **Message** — write it with `{{1}}`, `{{2}}` where the values go. A variable may not open or close the
  message.
- **Sample values** — one per variable. Meta reviews the **rendered** message, not the skeleton. **Invent
  them**: they travel to Meta, so never use a real customer's name or number.

**A language left empty is simply not created**, and each language gets its **own verdict** — Romanian
approved while English is refused for wording is an ordinary outcome, and a single joint "done" would hide it.
Answers usually come back within minutes.

> Doing this here rather than in Meta's console is deliberate: that editor auto-closes braces (typing `{{1}}`
> produces `{{1}}1}}`), its example inputs look like their own labels so sample text lands inside the message,
> and the wizard has to be walked once per language.

### Using an approved template

**Import the approved ones** copies what Meta has approved into your local library, so nobody has to retype a
name that must match exactly — a single typo answers "no template" at send time and explains nothing further.
Only APPROVED ones are imported (a pending one is a name that fails when used), and re-running refreshes rather
than duplicates.

From then on the template can be sent:

- **to one person, from the conversation.** When more than 24 hours have passed since their last message, the
  thread offers **Send an approved template**: pick one, fill in the values for `{{1}}`, `{{2}}`, send. Their
  reply reopens the free window. Without those values Meta delivers the placeholder literally and the customer
  reads *"Bună ziua, {{1}}"* — so they are asked for, not optional;
- **to a segment**, through **Campaigns** (the section below).

The control appears only when it is both **needed and possible** — the window is closed and at least one
approved template exists. While free text still works it is faster, free and reads like a person wrote it.

## Campaigns (compliant broadcast)

**Omnichannel → Campaigns** sends a message to a **segment** of your contacts on a messaging channel —
**controlled and compliant**:

- You pick the channel, the message (or a canned one), a segment (e.g. a lead status) and a **throttle**
  (messages per minute).
- **Build recipients** creates the list and checks each one: anyone who **opted out** is skipped; for a
  **free-form** message, only contacts inside the **24-hour window** (since their last message to you) are
  eligible; a **template** message needs an explicit opt-in.
- **Start** sends in throttled batches — automatically each minute (via the Scheduler job
  `omni.campaign_dispatch`) or immediately with **Send a batch now**.
- **Every recipient is checked again at the moment of sending**, not only when the list was built. The
  24-hour window is a moving deadline, so a campaign prepared today and sent tomorrow does not write to
  someone whose window closed in between — nor to anyone who opted out after the list was made.
- The **delivery report** shows total / pending / sent / **delivered / read** / failed / skipped (with the
  reason). Someone the rules excluded at send time is reported as **skipped**, never as failed: nothing was
  attempted, and **Retry failed** must not push them back into the queue.

> **Consent & opt-out.** An incoming message counts as opt-in; if someone replies **STOP** (or
> *unsubscribe / dezabonare / отписаться*) they are opted out and never messaged again. This keeps your
> outreach within WhatsApp/Viber rules.

## Event log — see what arrived, and recover what failed

**Omnichannel → Event log** lists every event a provider delivered to you: which channel, what type, whether it
was processed — and, when something went wrong, the exact error.

This page exists because of one hard fact: **a provider does not send a message twice.** To keep Facebook,
Viber or WhatsApp from retrying in a storm, TSync confirms receipt immediately. If the message then failed to be
filed (a temporary database problem, a mis-configured account), it used to be gone for good — and you would
never know a customer had written to you.

Now every event is stored, so you can bring it back:

1. Open **Omnichannel → Event log** and click the **Failed** filter (the Health page also warns you when
   something failed in the last 24 hours).
2. Click an event to read the error and see exactly what the provider sent.
3. Fix the cause, then press **Replay** — the message is processed again and appears in the inbox as normal.
   **Replay all** does the whole failed queue at once.

> **Replaying an already-processed event** would file the message a *second* time, so it is blocked by default.
> If you are sure the first attempt did not actually store anything, you can force it from the event page, with
> a confirmation.

Events received before you upgraded to this version were not stored and cannot be replayed — the page says so
rather than pretending otherwise.

**Keeping the list tidy.** A daily job deletes settled events (processed, skipped, rejected) older than the
retention window you set at the bottom of the page (90 days by default). **Failed and still-pending events are
never deleted automatically** — those are the ones that still need you.

## Security

Everything that comes in from a channel is treated as **untrusted data** — validated and mapped, never
executed. Each webhook is authenticated (a secret token in the URL, plus the provider's signature where one
exists) and is **idempotent**, so a provider retry never creates duplicates. All actions are audited.
