6. Messaging
Messaging is the single largest subsystem on Ditto Up. Everything you'd expect from a modern messenger is in here — direct messages, group chats, voice messages, translation, scheduling, templates, archives, AI screening — plus a few things you wouldn't, like a reputation tier that controls how many strangers you can DM in a day.
In this section
- 6.1 Inbox basics
- 6.2 Starting a new conversation
- 6.3 Sending and receiving messages
- 6.4 Voice messages and on-demand transcription
- 6.5 Editing, deleting, pinning and starring messages
- 6.6 Reactions and forwarding
- 6.7 Translation (per-conversation and per-message)
- 6.8 Scheduled messages
- 6.9 Group conversations
- 6.10 Archiving and exporting
- 6.11 Search inside messages
- 6.12 Templates and labels
- 6.13 AI screening & reputation tiers
- 6.14 Worthwhile signals
- 6.15 Commitments (action items)
- 6.16 AI summaries and reply suggestions
- 6.17 Online status, unread count, mentions and analytics
- 6.18 SMS and email reply paths
- 6.19 Moderation queue (held messages)
- 6.20 Privacy settings for messaging
6.1 Inbox basics
Open the inbox at /messages/. Template inbox.html renders a two-pane layout:
- Left — list of all conversations (1:1 and group), sorted by most-recent-message first. Each row shows the other party's name (or group name), avatar, last-message preview, time, unread badge.
- Right — the open conversation, or a "Pick a conversation" placeholder if you haven't selected one yet.
Above the list there are filter tabs: All, Unread, Pinned, Starred, Archived, Labels, Scheduled. Each tab is a stable URL so you can bookmark them.
The unread count badge that appears in the site's top-right corner comes from /messages/unread-count/ — polled every 30 seconds (or pushed via WebSocket when WS_ENABLED is on).
6.2 Starting a new conversation
From the inbox top-right, click + New message (or hit the keyboard shortcut shown in the menu). The modal at /messages/compose/ (compose_modal.html) opens.
- Type the recipient's name or email in the search field — autocompletion is powered by
/search-users/. - Select one or more recipients (selecting more than one creates a group conversation, see 6.9).
- Optionally pick a template from the dropdown — see 6.12 Templates and labels.
- Type your first message.
- Click Send.
The conversation is created and you are dropped into it.
Stranger-DM rules. Whether you can message a person you've never spoken to before depends on (a) their privacy setting and (b) your reputation tier. See 6.13 AI screening & reputation tiers.
6.3 Sending and receiving messages
Inside an open conversation (/messages/<conversation_id>/, template conversation.html):
- The scrollback loads the last 50 messages on open and lazy-loads older ones as you scroll up.
- The composer at the bottom supports text, paste-image, attachments (drag-drop), and emoji picker.
- Each message you receive is auto-marked read after it's been visible on screen for ~1 second; the system POSTs to
/messages/<conversation_id>/read/. - The Send button uses
/messages/<conversation_id>/reply/under the hood. Optimistic rendering puts the message in the thread immediately; if the server rejects it you see a red banner and a Retry button. - An "is typing…" indicator appears for the other party when they're composing (only when WebSockets are enabled).
You can quote-reply to a specific message by hovering it and clicking the speech-bubble icon.
6.4 Voice messages and on-demand transcription
To send a voice message:
- In the composer, hold or click the microphone icon.
- Speak — the recorder shows a live waveform.
- Release / click again to stop. Preview the audio.
- Send. The audio is uploaded to S3 (the same multimedia bucket used for video) and posted via
/messages/<conversation_id>/voice/.
Voice messages display as a small audio player with a Transcribe button. Clicking transcribe calls /messages/message/<message_id>/transcribe/, runs AWS Transcribe, and shows the text below the player. Transcription is on-demand to keep costs down.
Voice messages count against the same daily message limit (see 6.13) as text messages.
6.5 Editing, deleting, pinning and starring messages
Hover any message you sent to reveal a small action menu:
| Action | URL | Notes |
|---|---|---|
| Edit | /messages/message/<message_id>/edit/ |
Available for ~5 minutes after sending. Edited messages show a small (edited) label. |
| Delete for me | /messages/message/<message_id>/delete-for-me/ |
Hides the message from your view only; everyone else still sees it. |
| Delete for all | /messages/message/<message_id>/delete-for-all/ |
Removes the message from everyone's view. Sender or admin only. |
| Pin | /messages/message/<message_id>/pin/ |
Pins to the top of the conversation. Anyone in the conversation sees pinned messages. |
| Star | /messages/message/<message_id>/star/ |
Personal bookmark. Only you see your starred set. |
Two dedicated lookup pages let you find what you've pinned or starred:
/messages/<conversation_id>/pinned/— every pinned message in this conversation./messages/starred/— every starred message you've ever made, across all conversations.
For received messages, only Star, React, Forward and Delete for me are available.
6.6 Reactions and forwarding
6.6.1 Reactions
Hover a message → click the smile icon to drop a quick emoji reaction. Available reactions:
- 👍 like
- ❤️ love
- 😂 laugh
- 😢 cry
- 😡 angry
- 🎉 celebrate
Backend: POST to /messages/message/<message_id>/react/ — same endpoint toggles on/off. List who reacted via /messages/message/<message_id>/reactions/.
6.6.2 Forwarding
Click Forward in the message menu. A modal (forward_modal.html) lets you pick one or more conversations and adds an optional comment. Backend: POST to /messages/message/<message_id>/forward/.
The forwarded message is shown in the destination thread with a Forwarded from <name> label so the new recipient knows where it came from.
6.7 Translation (per-conversation and per-message)
Ditto Up is multilingual. You can translate either a whole conversation or one message at a time.
| Action | URL | Notes |
|---|---|---|
| Translate this conversation | /messages/<conversation_id>/translate/ |
Adds a translated rendition under each existing message. The translation language is your account's preferred language (Account → Language) or the browser default. Toggle off to revert. |
| Translate just this message | /messages/message/<message_id>/translate-one/ |
Inline translation under the single message. |
Translations are cached server-side, so re-opening the same conversation tomorrow doesn't re-charge for the same content.
6.8 Scheduled messages
To send a message later — useful for time-zone-respect:
- Compose your message normally.
- Click the clock icon to the right of the Send button.
- Pick a date and time.
- Click Schedule.
| Action | URL |
|---|---|
| Schedule a message | /messages/<conversation_id>/schedule/ |
| List scheduled messages in this conversation | /messages/<conversation_id>/scheduled/ |
| Cancel a scheduled message | /messages/message/<message_id>/cancel-scheduled/ |
Scheduled messages stay in your Scheduled tab and convert to real messages when their send time arrives. If you delete the conversation before the send time, the schedule is also cancelled.
6.9 Group conversations
You can have any number of people in a single conversation.
| Action | URL |
|---|---|
| Create a group | /messages/groups/create/ (create_group_modal.html) |
| Add a member | /messages/groups/<conversation_id>/add-member/ |
| Remove a member | /messages/groups/<conversation_id>/remove-member/ |
| Leave the group | /messages/groups/<conversation_id>/leave/ |
| Rename the group | /messages/groups/<conversation_id>/rename/ |
Group rules:
- The creator is automatically the admin. Admins can rename the group, add/remove anyone, and delete-for-all.
- Non-admins can leave at any time and remove themselves.
- A group with one remaining member is auto-archived.
- Visibility-toggle is available on individual messages — you can post a message that's visible to admins only via
/messages/message/<message_id>/toggle-visibility/.
6.10 Archiving and exporting
Archived conversations move out of the main inbox but remain searchable.
| Action | URL |
|---|---|
| Archive a conversation | /messages/<conversation_id>/archive/ |
| Unarchive | /messages/<conversation_id>/unarchive/ |
| List archived | /messages/archived/ |
| Export as PDF / text | /messages/<conversation_id>/export/ |
Export produces a single PDF with the full thread, sender, timestamp and any voice-message transcripts. Useful for HR records, dispute archiving, or feeding into your own ATS.
6.11 Search inside messages
Three search levels exist — each one a separate URL:
| URL | Scope |
|---|---|
/messages/search/?q=<keyword> |
Across every conversation you have. |
/messages/search/<conversation_id>/?q=<keyword> |
Inside a single conversation. |
/messages/search/advanced/ |
Advanced filters: date range, sender, has-attachment, voice-only, scheduled, screening status. |
Results highlight the matched terms and link straight to the message in context.
6.12 Templates and labels
For recruiters or anyone sending the same type of message often, the platform has templates and labels.
6.12.1 Templates (canned replies)
| Action | URL |
|---|---|
| List templates | /messages/templates/ |
| Create | /messages/templates/create/ |
| Update | /messages/templates/<template_id>/update/ |
| Delete | /messages/templates/<template_id>/delete/ |
| Insert into composer | /messages/templates/<template_id>/use/ |
Templates support placeholders like {{first_name}}, {{job_title}}, {{business_name}}. When you use a template, the placeholders are auto-filled from the recipient's profile and the open job posting (if any).
6.12.2 Labels (conversation tags)
Labels are coloured tags you stick on conversations to organise your inbox.
| Action | URL |
|---|---|
| List labels | /messages/labels/ |
| Create | /messages/labels/create/ |
| Update | /messages/labels/<label_id>/update/ |
| Delete | /messages/labels/<label_id>/delete/ |
| Get a conversation's labels | /messages/<conversation_id>/labels/ |
| Assign a label | /messages/<conversation_id>/labels/assign/ |
| Remove a label | /messages/<conversation_id>/labels/remove/ |
Labels show up as small coloured dots in the inbox list, and you can filter by label using the Labels tab at the top of the inbox.
6.13 AI screening & reputation tiers
To stop spam and harassment, every message between users who have never spoken before is run through a lightweight AI screening before delivery.
The screening has four possible outcomes:
| Outcome | Effect |
|---|---|
| Pass | Message delivered immediately. |
| Hold for clarification | The message is held; the sender sees "Reviewed by Ditto Up — please add context." The recipient sees nothing yet. |
| Warn for mismatch | Message delivered but flagged. Common for "off-topic for the recipient's profile" messages — e.g. a recruiter pitching a role outside the candidate's stated terms. |
| Block | Message rejected (policy violation). |
The recipient can override holds by visiting their Moderation queue at /moderation/ (flagged_messages.html) and clicking Approve or Approve all. Endpoints: /moderation/action/, /moderation/approve-all-held/. The sender can also resolve a hold from their side via /messages/message/<message_id>/resolve-screening/ if they've been asked for clarification.
6.13.1 Reputation tiers
In addition to AI screening, the platform tiers users on a reputation scale to gate stranger-DM volume. View your current tier on /reputation/ (reputation_dashboard.html).
| Tier | Daily new-stranger DM cap | How to reach it |
|---|---|---|
| New | 3 / day | Default for new accounts. |
| Established | 10 / day | Verified email + complete profile + 7 days on the platform without flags. |
| Trusted | 30 / day | 30 days on the platform + at least one worthwhile signal received from a recipient (see 6.14). |
| Unlimited | No cap | Manually granted (sales reps, support staff, premium recruiter plan). |
Replies inside an existing conversation are unlimited at every tier.
6.14 Worthwhile signals
Recipients can mark a received message as Worthwhile or Not Worthwhile. This is most often used by candidates after a recruiter pitch:
| Action | URL | Effect |
|---|---|---|
| Mark worthwhile | /messages/message/<message_id>/worthwhile/ |
Boosts the sender's reputation. |
| Mark not worthwhile | /messages/message/<message_id>/not-worthwhile/ |
Lowers reputation; repeated flags can demote tier. |
| One-click via email | /messages/message/<message_id>/worthwhile-email/ |
Used in notification emails so recipients can flag without opening the site. |
The signal feeds back into the reputation tier (6.13) and into the AI screening model.
6.15 Commitments (action items)
Ditto Up auto-detects action items mentioned in conversations — for example "I'll send you the contract by Friday" — and surfaces them as commitments.
| URL | Purpose |
|---|---|
/messages/<conversation_id>/commitments/ |
Every commitment found in this conversation. |
/messages/<conversation_id>/commitments/create/ |
Manually add a commitment (free text + due date). |
/messages/commitment/<commitment_id>/complete/ |
Mark complete (a check appears next to the source message). |
/messages/commitment/<commitment_id>/dismiss/ |
Hide / mark not-actually-a-commitment. |
Commitments roll up into the recipient's daily digest if any are coming due in the next 48 hours.
6.16 AI summaries and reply suggestions
For long threads:
/messages/<conversation_id>/summary/— generates a 3–5 bullet summary of the conversation so far. The output is cached until a new message lands./messages/<conversation_id>/suggest-reply/— suggests three short replies to the most-recent inbound message.
Both are opt-in and use the same Claude pipeline as VBQ scoring.
6.17 Online status, unread count, mentions and analytics
| Endpoint | What it returns |
|---|---|
/api/online-status/ |
Online / away / offline status for a list of user IDs. |
/messages/unread-count/ |
Total unread messages across all conversations (used for the top-bar badge). |
/mentions/ |
Every place someone has @-mentioned you in a message or post. |
/messages/analytics/ |
(Recruiter view) Aggregate delivery / open / response / worthwhile rates for messages you've sent. |
/messages/message/<message_id>/info/ |
Deliveries, reads, reactions for a single message. |
/messages/message/<message_id>/interaction/ |
Quick state check — "has the recipient replied? has the message been read?" |
6.18 SMS and email reply paths
You can reply to a Ditto Up conversation from outside the site via two paths:
| Path | How it works |
|---|---|
| Email reply | When the platform emails you about a new message, you can reply to that email. The system has a unique reply-to address per conversation; the body of your reply is parsed (signatures stripped) and posted as your next message. The endpoint that ingests this is /forward-email/. |
| SMS reply | If you have an active Twilio integration enabled (set on your account), each new-message email also fires an SMS. Replying to that SMS goes through the Twilio webhook at /sms/ and posts as your reply. |
Both paths are subject to the same screening and reputation rules as in-site messages.
6.19 Moderation queue (held messages)
Every recipient has a moderation queue for held inbound messages.
| URL | Purpose |
|---|---|
/moderation/ |
List of held messages awaiting your decision (template flagged_messages.html). |
/moderation/action/ |
POST: approve or dismiss a single held message. |
/moderation/approve-all-held/ |
POST: approve everything currently held. |
You can also configure auto-approval rules in /settings/: e.g. "auto-approve messages from anyone whose published VBQ score for my main term is Advanced or higher."
6.20 Privacy settings for messaging
In /candidate-settings and /settings/ you can choose:
| Setting | Options |
|---|---|
| Who can DM me | Anyone / Anyone with a complete profile / Anyone whose reputation is Established+ / Only people I follow / Nobody. |
| Hold messages from people without a VBQ video | On / off. |
| Auto-block from a specific user | See Section 9 — Account & privacy. |
| Email me when a message lands | Per-tier filters. |
The setting "Only people I follow" combined with the screener gives the most aggressive filtering: every other DM is held until you explicitly approve.
Next: 7. Community — posts, reactions, comments, follows and notifications.