Skip to content

Releases

Dinghy 0.33.0

Choose a tag to compare

@github-actions github-actions released this 14 Nov 12:12
094724e

Changed

  • The operator app shows a skeleton in the shape of each screen while it loads, in place of the boat and "Loading...": the sidebar and the Inbox on start, the conversation, Contacts, Analytics, Visitors and the settings pages. It fades in only after 0.3 s, so a quick load shows nothing. While the Inbox's first list loads, it no longer says "Nothing is waiting for you."

Image: ghcr.io/dinghy-chat/dinghy:0.33.0

Dinghy 0.32.0

Choose a tag to compare

@github-actions github-actions released this 14 Nov 11:48
6aa8d34

Changed

  • A visitor's reply by email from an address other than their email now names that address: Replied by email from ada@example.org. API: messages with via: "email" have fromAddress, the address or null, in place of fromOtherAddress. Replies stored before this change read as from the visitor's email.

Image: ghcr.io/dinghy-chat/dinghy:0.32.0

Dinghy 0.31.1

Choose a tag to compare

@github-actions github-actions released this 14 Nov 09:45
7ba039f

Fixed

  • 0.31.0 has no image, because a test of the two-step removal request raced on the release's runner. This release holds the same changes as 0.31.0.

Image: ghcr.io/dinghy-chat/dinghy:0.31.1

Dinghy 0.30.1

Choose a tag to compare

@github-actions github-actions released this 13 Nov 23:08
a95fdaf

Fixed

  • 0.30.0 has no image, because the release's upgrade check still sent images to the route that 0.29.0 renamed. This release holds the same changes as 0.30.0.

Image: ghcr.io/dinghy-chat/dinghy:0.30.1

Dinghy 0.29.0

Choose a tag to compare

@github-actions github-actions released this 13 Nov 22:10
30ceb91

Upgrading

  • Behind your own reverse proxy, accept request bodies of at least 11 MB, such as client_max_body_size 11m; in nginx, because a file in the chat can be 10 MB. The bundled Caddy needs no change.
  • To turn on replies by email behind your own reverse proxy, accept 26 MB on /v1/inbound-email, such as client_max_body_size 26m; in an nginx location = /v1/inbound-email block, because an email can be 25 MB. A smaller limit makes the forwarder reject large emails for good. Self-hosting has the block.
  • Existing workspaces keep visitors' files off, so no visitor starts sending files after the upgrade. Turn them on in Settings > Appearance with Visitors can send files.
  • IMAGES_ORIGIN is now UPLOADS_ORIGIN; the old name still works until the next release.
  • If you set UPLOADS_ORIGIN and your site has a Content Security Policy, allow that origin in connect-src as well as img-src, because the chat fetches a file to save it.
  • API: the upload routes POST /v1/widget/{key}/images and POST .../conversations/{conversationId}/images are now .../uploads, with no alias, because they take files as well as images.
  • API: in a workspace's settings, usage.imageBytes is now usage.uploadBytes, and deletion.images is now deletion.uploads, as both count files too.
  • API: a message's image and imageRemoved are now one attachment, in every answer, stream event and webhook: { type: "image", url, width, height }, { type: "file", url, name, size }, { type, removed: true } once Dinghy Cloud deleted it after 12 months, or null. kind stays as it was.

Added

  • Saved replies: the team writes common answers once in Settings > Saved replies, and anyone inserts one by typing / in the reply or note box, then changes it before sending. Every member can add, change and delete them, up to 200 per workspace. MCP has list_saved_replies, so a bot can answer in the team's words. API: GET .../saved-replies, and POST, PATCH and DELETE with a session.
  • Files in the chat: visitors and operators send a PDF, TXT, CSV, LOG, JSON, ZIP, Word, Excel, PowerPoint, OpenDocument text or spreadsheet, MP4 or MOV file of up to 10 MB beside images. The chat shows its name and size. A file only downloads and never opens in the page. Visitors can send files in Settings > Appearance is on for new workspaces and off for existing ones, and operators can always send files. Exports carry files. On Dinghy Cloud, files count toward the 1 GB of storage and go after 12 months, as images do. Dinghy does not scan files for viruses. API: the upload routes take the file's name as filename in the query and answer kind: "file" with attachment: { type: "file", url, name, size }, a conversation's lastMessage has its kind, a contact's conversations have lastKind, and the widget setting is visitorFiles.
  • Mentions in notes: type @ in a note to pick a teammate. Each member a note mentions gets an alert with its text, by push and by email as their alert settings say, and the conversation is unread for them. An edit alerts only the members it newly mentions. A pick mentions the member picked, even when two members share a name. API: mentions on a note, and on MCP's add_note.
  • Business hours: Settings > Business hours sets a time zone and up to 3 ranges a day. Outside them the widget shows the away message and "We're back Monday at 9:00 AM" on the visitor's own clock, with the date when that is more than 6 days away, even when a member is online, and asks for an email: before the first message, or after it for a visitor who has chatted. Visitors can still write. The widget gets the hours with its appearance. API: GET and PUT .../business-hours.
  • The widget speaks English, French and Spanish. Language in Settings > Appearance picks one, or Visitor's language, the first of the browser's languages that the widget speaks. New workspaces start with the visitor's language, and existing ones keep English. data-locale and Dinghy.configure({ locale }) win over the setting. The chat loads French or Spanish as a small file of its own, so the loader carries no translation. The team's own texts stay as written. The visitor's reply emails, transcripts and chat page follow the widget's language, with dates in that language, and the visitor panel shows it. API: the widget setting language, and locale in the widget's context report and on a conversation's visitor.
  • Visitors reply by email: with INBOUND_EMAIL_DOMAIN and INBOUND_EMAIL_SECRET set, each reply email and transcript has its own reply address, and the visitor's answer goes into that conversation, reopening it if closed, as their message. Dinghy keeps the text above the quote of its email, and up to 5 attachments with the chat's checks and limits. The team sees Replied by email under it, or Replied by email from another address. Automatic answers are dropped, and an address takes 20 emails an hour and lasts 90 days after its last use. Self-hosting has a Cloudflare Email Worker for it. Off by default. API: mail reaches the new POST /v1/inbound-email as raw MIME, signed with the secret, and such messages have via: "email" and fromOtherAddress.
  • Two-step sign-in for operators. Profile turns it on with a QR code for an authenticator app and shows 10 single-use recovery codes once. It also shows how many are left, and makes a new set with a code from the app. Each new sign-in then asks for a 6-digit code after the email link, and Settings > Team shows who has it on. A server's admin turns it off for an operator who lost their authenticator app with chat two-factor-reset <email>. With a mail server, the account gets an email when it is turned on or off, by the operator or the admin, when a recovery code is used, and when too many wrong codes lock it. API: /v1/operator/two-factor/setup, enable, check, recovery-codes and disable, and twoFactor and recoveryCodesLeft in /v1/operator/me.
  • Removal of two-step sign-in for an operator who lost their authenticator app and their recovery codes. Lost your authenticator app and your recovery codes? on the code screen, within 15 minutes of an email link, asks to remove it after 72 hours, only when someone else can stop the request: another owner or admin of one of the account's workspaces, or another unexpired session that passed the check. The worker emails the account a cancel link, pushes to its devices and emails the owners and admins, and tries a failed send again. The 72 hours count from that warning, and the request never completes before it. It emails the account again 24 hours before the end. Every session that passed the check shows a banner with Cancel the request at once, and Settings > Team marks Removal requested. A correct code also cancels it. A cancel signs out the sessions that never passed the check, blocks new requests for 7 days and tells the account to secure its email. With nobody to stop it, the screen points to a manual review: Dinghy Cloud's address, or the server's admin. Owners and admins can also reset a lower-ranked teammate who is in no other workspace from Settings > Team. After a removal or a reset, the next sign-in asks to turn it on again. API: POST /v1/operator/two-factor/removal, .../removal/cancel, /v1/account/two-factor-removal/cancel with the link's token, .../two-factor/skip-prompt and POST .../members/{operatorId}/two-factor-reset, removalCompletesAt and asksTwoFactorAgain in me, and twoFactor: "removal_requested" and canResetTwoFactor on members. A team.changed event now also makes the operator app read me again.
  • Recovery codes have 16 letters in 4 groups, 80 random bits in place of 50. Codes made before keep working.
  • Labels: put colored labels on conversations, such as billing or bugs, with L or the tag button in a conversation. Anyone on the team adds one by typing its name, up to 100 per workspace, and owners and admins rename, recolor and delete them in Settings > Labels. The Inbox filter lists the conversations with one label, rows and the conversation header show them as pills, and Analytics counts the period's conversations by label. MCP has list_labels, label_conversation and unlabel_conversation, and its list_conversations takes a label. API: GET .../labels, PUT and DELETE .../conversations/{conversationId}/labels/{labelId}, which change one label at a time, and the conversation.labeled webhook for each change, with label: { id, name, color } and action, added or removed.
  • Bulk actions in the Inbox: choose conversations with the box on each row, X, Shift for a range, or a long press on a phone, and the box beside the count chooses every row. Then close, assign, label, mark read or, for owners and admins, delete them at once. Each changes as it would alone, with its own event and webhook, and a read-only or suspended workspace can still mark them read. API: POST .../conversations/bulk for up to 100 conversations, with a result for each.

Security

  • With two-step sign-in on, a session that has not passed the check gets 401 two_factor_required everywhere but me, the check and sign-out, including the event stream at each wake, an upload's end and OAuth consent. 5 wrong codes in 15 minutes end the session, and the check answers 503 check_unavailable rather than skip that limit when Valkey does not answer. The TOTP secret is encrypted with a key derived from AUTH_SECRET, and the recovery codes are stored as keyed hashes, so after a change of AUTH_SECRET the check and the turn-off answer 500 two_factor_unreadable until the admin runs chat two-factor-reset. API keys are not affected. API: too_many_attempts, check_unavailable and two_factor_unreadable on `/v1/operator/two-factor...
Read more

Dinghy 0.28.0

Choose a tag to compare

@github-actions github-actions released this 13 Nov 14:26
7d94162

Changed

  • The keyboard shortcuts list is easier to scan: a close button, keys that read G then I, and the switch for single keys in its own panel. Documentation in Help shows that it opens a new tab.
  • In the sidebar, the Inbox and Visitors counts sit at the right edge. With the pointer on a view, its keys take the count's place.

Fixed

  • A visitor signed in with Dinghy.identify(async () => token) now gets a country and a time zone, and shows as known in Visitors. Before, the widget sent their beat without the token, so the contact had no country.

Image: ghcr.io/dinghy-chat/dinghy:0.28.0

Dinghy 0.27.0

Choose a tag to compare

@github-actions github-actions released this 13 Nov 13:54
f886118

Added

  • Keyboard shortcuts in the operator app: ? lists them, / searches, G then I, C, V, A or S opens a view, J and K move through the Inbox, R and N write a reply or a note, E closes the conversation and opens the next one, A and I assign, . opens the … menu, and P shows the visitor's panel. Esc leaves the reply box. The app shows each key where it applies, and a switch in the list turns the single keys off for this browser.
  • Help in the sidebar, with the documentation and the keyboard shortcuts.

Image: ghcr.io/dinghy-chat/dinghy:0.27.0

Dinghy 0.26.0

Choose a tag to compare

@github-actions github-actions released this 12 Nov 22:59
59facce

Changed

  • With a mouse, Snooze in the … menu opens its choices beside the menu on hover. On a phone it still opens them in place.

Fixed

  • The reply box keeps its height when you switch between Reply and Note, as Add note is now as tall as the send button.

Image: ghcr.io/dinghy-chat/dinghy:0.26.0

Dinghy 0.25.0

Choose a tag to compare

@github-actions github-actions released this 12 Nov 22:35
ea4807e

Changed

  • The … menu has one Snooze item, which opens its choices in place, with a row to go back. The right arrow opens it and the left arrow goes back.
  • A signed detail that links to a page ending in an ID, such as organization: https://example.com/admin/organizations/org_8f2k1, shows the ID and still opens the page. Its copy button copies the ID.
  • The conversation's side panel no longer shows the last page, which Pages visited already lists. A click on the visitor's email copies it.
  • Reply and Note above the reply box are a small switch with an icon each, and Note turns yellow when chosen.

Fixed

  • In dark mode, menus, popovers and modals stand out from the page, on a lighter card with a pale edge over a darker backdrop.

Image: ghcr.io/dinghy-chat/dinghy:0.25.0

Dinghy 0.24.0

Choose a tag to compare

@github-actions github-actions released this 12 Nov 21:39
3548e58

Added

  • An MCP client such as Claude Code connects with OAuth in the browser: claude mcp add --transport http dinghy https://chat.example.com/v1/mcp, then an owner or admin approves it on one page and picks the workspace, the access (Chats or Full access) and the reply mode. The agent never sees a secret. A connected app shows in Settings > API & webhooks beside the keys, and can be disconnected there. OAuth is on when PUBLIC_URL is https or localhost. API: OAuth discovery at /.well-known/oauth-protected-resource and /.well-known/oauth-authorization-server, /oauth/authorize, /v1/oauth/token, /v1/oauth/register and /v1/oauth/revoke, with Client ID Metadata Documents and Dynamic Client Registration.
  • MCP speaks protocol 2026-07-28, which is stateless, beside the older versions.
  • Full access tools and routes, so a coding agent can install the chat: the install snippet for seven frameworks, allowed sites, an install check, identity code with no secret, the widget's look, webhook signature code, delivery replay, and test visitors. API: /install/snippet, /install/check, /install/identity, /sites, /test-visitors and .../deliveries/{deliveryId}/replay.
  • A key's reply mode: Send directly, or Drafts only, where each reply waits under the conversation as a pale suggested reply, for an operator to send, edit or discard. A draft becomes stale when the visitor writes or an operator replies after it, and the thread folds stale drafts into one card. API: replyMode on keys, a 202 { draft } answer to a key's reply, and /drafts routes.
  • Internal notes: choose Note above the reply box to write a message that only your team sees, never the visitor. API: "note": true on POST .../conversations/{conversationId}/messages, a message kind of note, and the MCP tool add_note. A note goes while bots are paused and while a person handles the conversation, and an operator's note neither takes the conversation from the bots nor makes drafts stale.
  • A bot hands a conversation to people with a reason, a summary and a last message to the visitor, and the team gets an alert. While a person handles it, a key that sends directly gets 409 human_handling, until an operator gives it back. The thread shows the handoff with the bot's note, the inbox row ends with a mark for who handles each conversation, and one dropdown picks the bot, the team or a member. API: POST .../handoff, POST .../give-back, which also clears the assignee, handler and bot on conversations, handoff on messages, and the conversation.handoff and conversation.given_back webhooks.
  • Owners and admins pause every bot with one switch: replies, drafts and typing from keys answer 409 bots_paused, and the banner says who paused them and when, with Resume bots. API: PUT .../bots-paused, and botsPaused, botsPausedBy and botsPausedAt on workspaces.
  • A bot shows " is typing" in the chat. API: POST .../typing, and a widget notice.
  • GET /v1/events streams the webhook events to an API key, for a bot with no public address, and the MCP tool wait_for_messages waits for new visitor messages.
  • MCP tools for keys: hand_off, list_members, get_contact, list_contacts, search_contacts, get_live_visitors, get_analytics, and format: "markdown" and replySeconds on get_conversation.
  • A failed webhook delivery shows the start of the receiver's answer. API: responseExcerpt and replayOf on deliveries.
  • Install has a "Set up with an agent" prompt to copy, for Claude Code, Cursor or another MCP client.
  • Docs: Use Dinghy with AI agents, with a block to paste into a repository's AGENTS.md, and every doc in one file at https://dinghy.chat/llms-full.txt.
  • Snooze: the … menu of a conversation snoozes it until the visitor replies, until tomorrow at 9:00 or until next Monday at 9:00. It stays open, shows Snoozed in the Inbox and does not count as waiting until then, and the visitor sees nothing. API: { "snooze": { "until" } } on PATCH .../conversations/{conversationId}, snooze on each conversation, a conversation.snoozed change, and the MCP tools snooze_conversation and unsnooze_conversation.

Changed

  • A new API key or connected app starts in Drafts only. Existing keys keep sending directly, so no bot changes how it behaves.
  • API keys can read the analytics, and Full access keys can read and change the widget's look.
  • Settings names the two access levels Chats and Full access. The API keeps conversations and setup.
  • Assigning a conversation in the operator app takes it from the bots, as a reply does. An API key's assign changes only the assignee.
  • Settings > API & webhooks shows how many bots are connected, with a big switch to pause them, and lists connected apps, API keys and webhooks apart.
  • close_conversation is marked destructive, so an MCP client asks the person first.
  • A visitor who writes after their conversation was closed reopens it, with its assignee, instead of starting a new one. Only a visitor's first message starts a conversation, and the conversation:started event and conversation.created webhook come only then.

Fixed

  • Text you select in the widget stays readable. A site that styled its own selection made the selected text in the chat invisible, because Chrome passes the page's selection style into the widget.
  • A visitor who left only their email shows it on one line at the top of the conversation's side panel, a little smaller when it is long, instead of split across two lines. Only a very long email wraps, after its @.

Image: ghcr.io/dinghy-chat/dinghy:0.24.0