{"slug": "ai-in-practice-building-a-dynamic-line-group-buying-bot-with-edit-and-unsend", "title": "[AI in Practice] Building a Dynamic LINE Group Buying Bot with Edit and Unsend Webhooks", "summary": "LINE Taiwan Developer Relations Team Lead Evan Lin built a LINE group buying bot demo that leverages the Messaging API's Edit and Unsend webhook events to synchronize backend order data with user edits. The bot, named 'Shapeshifting Store Manager - Dan Dan', handles scenarios where users edit or unsend messages after placing orders, ensuring the recorded quantities and amounts match the latest intent. The sample code is available on GitHub.", "body_md": "Author: Evan Lin, LINE Taiwan Developer Relations Team Lead\n\nOn August 20, 2026, LINE announced the open free trial of the \"Edit Message\" feature in LINE Labs.\n\nFor general users, this is a very intuitive feature: if you find a typo, a wrong date, or want to adjust your tone after sending a message, you no longer need to go through the \"Unsend, retype, resend\" process. You can simply edit the original text message.\n\nBut when I saw this feature, the first thing I thought of was something else:\n\nIf a user changes a message that has already been processed by a LINE Bot, does the Bot know?\n\nThe answer is: Yes. The LINE Messaging API provides the `Edit event`\n\n; and when a user unsends a message, there is also an `Unsend event`\n\nthat can be received.\n\nIn this article, I want to share how I turned these two types of webhook events into a LINE Bot Demo with a story, as well as several easily overlooked but very important details during implementation.\n\nThe complete sample code is available on GitHub:\n\n[https://github.com/kkdai/linebot-edit-unsend](https://github.com/kkdai/linebot-edit-unsend)\n\nTo try out message editing in LINE Labs currently, you need to meet the following conditions:\n\nIn standard one-on-one and group chats, text messages can be edited within 15 minutes of being sent; for Keep Notes, it's within 6 days. Photos, videos, voice messages, files, and stickers cannot be edited at this time. After modification, the chat room will display \"Edited,\" but it does not provide a way to view or restore old versions.\n\nThere is also a limitation directly related to Bot development: currently, one-on-one chats with LINE Official Accounts do not support message editing. Therefore, to test the Messaging API's Edit event, the Bot must be added to a group chat.\n\nFor features and activation methods, please refer to the [LINE Taiwan Newsroom Announcement](https://www.linecorp.com/tw/pr/news/2026/0820/).\n\nIn the past, the common process for a Bot receiving a text message was:\n\nIf a message cannot be modified after being sent, this model is very simple. But when messages can be edited, the original text is not necessarily the user's final intent.\n\nFor example, a user sends:\n\n```\nPearl Milk Tea / Half Sugar / Less Ice / 1\n```\n\nThe Bot has already recorded it as one drink costing 60 TWD. A few seconds later, the user directly edits the original message:\n\n```\nPearl Milk Tea / Micro Sugar / No Ice / 2\n```\n\nIf the Bot does not handle the Edit event, the chat room sees two cups, but the backend still records one cup. The world on the screen and the world inside the Bot become disconnected.\n\nThis is the most practical value of the Edit event: it's not just a notification that \"the text has changed,\" but an opportunity for the service to synchronize with the user's latest intent.\n\nTo demonstrate both edit and unsend simultaneously, I set the Bot as \"Shapeshifting Store Manager - Dan Dan,\" responsible for saving the office from low energy at 3:00 PM.\n\nThe Demo flow is as follows:\n\n`Start Group Buy 50 Lan 15:20 Deadline`\n\n.`Pearl Milk Tea / Half Sugar / Less Ice / 1`\n\n.`Pearl Milk Tea / Micro Sugar / No Ice / 2`\n\n.`Current Orders`\n\nto view the latest summary via Flex Message.This scenario is perfect for a Demo because everyone knows the two most common things in group buying: changing your mind after ordering, and suddenly not wanting to drink after ordering.\n\nMore importantly, the audience can directly see from the quantity and amount whether the webhook actually affected the backend state, rather than the Bot just replying \"Edit received.\"\n\nThe `type`\n\nof the Edit event is `messageEdited`\n\n, which carries the edited text, timestamp, reply token, and message ID:\n\n```\n{\n  \"type\": \"messageEdited\",\n  \"replyToken\": \"950e63e8f46542ab89f645b4c2a1180a\",\n  \"message\": {\n    \"type\": \"text\",\n    \"id\": \"610830548529053697\",\n    \"text\": \"Pearl Milk Tea / Micro Sugar / No Ice / 2\"\n  },\n  \"timestamp\": 1776914799524,\n  \"source\": {\n    \"type\": \"group\",\n    \"groupId\": \"Ca56f94637c...\",\n    \"userId\": \"U4af4980629...\"\n  }\n}\n```\n\nThe most critical design point is: the `message.id`\n\nin the edit event is the same as the message ID of the original message event.\n\nTherefore, we can directly use the message ID as the order ID:\n\n```\ncase webhook.MessageEvent:\n    h.handleMessage(e)\ncase webhook.MessageEditedEvent:\n    h.handleEdit(e)\ncase webhook.UnsendEvent:\n    h.handleUnsend(e)\n```\n\nAdd an order when the original message event is received; when `MessageEditedEvent`\n\nis received, use the same message ID to find the order and overwrite the content.\n\nAdditionally, the Edit event has its own reply token, which is different from the original message event's reply token, so the Bot can directly reply \"Order successfully transformed\" to this specific edit.\n\nIf your Bot also uses the Mark as Read API, note that the Edit event does not carry a `markAsReadToken`\n\n. You cannot simply apply the standard message event processing flow. These field differences are well-suited for a full test using webhook fixtures after upgrading the SDK.\n\nFor complete fields, please refer to [Messaging API: Edit event](https://developers.line.biz/en/reference/messaging-api/#edit-event).\n\nThis is the most easily overlooked part of this implementation.\n\nA user might edit the same message multiple times in quick succession, and multiple `messageEdited`\n\nwebhooks are not guaranteed to arrive in the order they were edited. Official documentation recommends using the largest timestamp to represent the latest state.\n\nTherefore, you cannot simply adopt the \"last received webhook\"; you should adopt the \"webhook with the latest timestamp\":\n\n```\nif timestamp <= current.UpdatedAt {\n    return summarize(buy), errStaleEdit\n}\n\ncurrent.UpdatedAt = timestamp\nbuy.Orders[messageID] = current\n```\n\nAssuming the second edit arrives first and the first edit arrives later, this logic prevents old content from overwriting new content.\n\nAt the same time, the LINE Platform might resend webhooks, so the example also uses `webhookEventId`\n\nfor deduplication. These two mechanisms handle different problems:\n\n`webhookEventId`\n\n: Prevents the same event from being processed twice.`timestamp`\n\n: Prevents different edit events from updating data in the wrong order.Both need to be handled.\n\nUsers don't necessarily just change the quantity. They might also change the original order to something like:\n\n```\nI suddenly don't want to drink anymore\n```\n\nIn this case, the new message can no longer pass the order format validation. If the backend continues to keep the old order, it will still cause state inconsistency.\n\nThe approach in this Demo is: clear the old order content, mark this record as invalid and temporarily remove it from the total, and then ask the user to continue editing to fix it.\n\n```\nparsed, err := parseOrder(text)\nif err != nil {\n    current.Item = \"\"\n    current.Sugar = \"\"\n    current.Ice = \"\"\n    current.Quantity = 0\n    current.UnitPrice = 0\n    current.Valid = false\n    current.UpdatedAt = timestamp\n    buy.Orders[messageID] = current\n    return summarize(buy), err\n}\n```\n\nActual products can adopt different strategies, such as putting it into a \"waiting for manual confirmation\" state; the key is not to silently continue using old content that no longer exists in the chat room.\n\nThe content of the Unsend event is simpler than the Edit event:\n\n```\n{\n  \"type\": \"unsend\",\n  \"source\": {\n    \"type\": \"group\",\n    \"groupId\": \"Ca56f94637c...\",\n    \"userId\": \"U4af4980629...\"\n  },\n  \"unsend\": {\n    \"messageId\": \"610830548529053697\"\n  }\n}\n```\n\nIt only tells us which message ID was unsent; it does not re-attach the message content, nor does it have a reply token.\n\nSince we already use the message ID as the order ID, deletion is straightforward:\n\n```\ndelete(buy.Orders, messageID)\n```\n\nBut what's truly important here is not `delete()`\n\n, but how to respect the user's intent of \"I want to take back this content.\"\n\nOfficial documentation specifically reminds service providers that after receiving an Unsend event, they should handle it carefully so that the target message cannot be seen or used in the future. Therefore, this Demo will not quote the unsent item, sweetness, or other original text in the Bot's reply, but will only say:\n\n```\n💨 An order has been safely withdrawn, and the saved content has been deleted.\n```\n\nSince the Unsend event has no reply token, if you want to proactively notify the group, you can only use a push message. This means it will count towards message usage, so quota and cost should be considered when designing a formal service.\n\nFor full details, please refer to [Messaging API: Unsend event](https://developers.line.biz/en/reference/messaging-api/#unsend-event).\n\nWhile plain text can show results, to make the Demo understandable at a glance, I highly recommend using Flex Message to display the current order.\n\nThe card this time includes:\n\nAfter every addition, edit, or unsend, the Flex Message is regenerated from the current state. This way, the quantity and amount before and after an edit change immediately, and the line item disappears after an unsend.\n\nTo avoid the Flex Message becoming too large, the example displays a maximum of eight orders, with the rest summarized. Formal products can switch to carousels, LIFF pages, or add pagination queries as needed.\n\nThis example uses:\n\n```\ngithub.com/line/line-bot-sdk-go/v8 v8.22.0\nGo 1.25\n```\n\nOlder versions of the SDK may already have `UnsendEvent`\n\n, but they might not include the new `MessageEditedEvent`\n\ntype. After upgrading the SDK, you also need to check the required Go version; when upgrading from the old example this time, the Go toolchain and CI workflow both needed adjustment.\n\nThis is a common but easily missed upgrade issue: being able to compile locally doesn't mean Cloud Build or GitHub Actions are still using the same version.\n\nThis Demo is deployed on Google Cloud Run. The channel secret and channel access token are injected via Secret Manager and are not committed to the repository.\n\nTo keep the example simple, current orders are stored in the program's memory. This brings two limitations:\n\nTherefore, the Demo environment sets the maximum instances to 1 to avoid webhooks for the same group buy being split. But this is only suitable for demonstration, not a complete solution for a production environment.\n\nFormal services should use Redis, Firestore, or other shared storage, and further handle:\n\nEspecially for unsend, if message content has already been sent to search indices, analysis platforms, or other downstream services, deleting only the main database is incomplete. Data flow design should know where content went from the start to be able to truly complete a deletion.\n\nFirst, please enable webhooks in the LINE Developers Console and allow the Bot to join group chats. Point the webhook URL to the `/callback`\n\nof your deployed service.\n\nThen, enter the following in the group:\n\n```\nStart Group Buy 50 Lan 15:20 Deadline\nPearl Milk Tea / Half Sugar / Less Ice / 1\nCurrent Orders\n```\n\nThen directly edit the second message:\n\n```\nPearl Milk Tea / Micro Sugar / No Ice / 2\n```\n\nYou will see the Bot reply \"Order successfully transformed,\" and the quantity and total amount in the Flex Message will also update.\n\nFinally, unsend that order message, and the Bot will delete the content and show a new order summary.\n\nThe menu and prices in the example are fixed Demo data to make amount changes clearly visible. For complete startup instructions, environment variables, and test commands, please refer to the README in the repository:\n\n[https://github.com/kkdai/linebot-edit-unsend](https://github.com/kkdai/linebot-edit-unsend)\n\nGroup buying is just one easy-to-understand story. The same event model can be extended to:\n\nAs long as a Bot has ever converted \"a message\" into some kind of system state, it's worth re-checking: when the message is edited or unsent, should that state also change?\n\nMessage editing looks like an improvement to the chat interface, but from a Bot developer's perspective, it actually changes the lifecycle of an event.\n\nA message is no longer just the moment it is \"sent.\" It might be updated, or it might be withdrawn. Backend services need to understand these events, maintain the correct order, and respect the user's latest intent.\n\nThis time, I used an afternoon tea group buy story to string together Edit event, Unsend event, Flex Message, and Cloud Run. I hope this small example helps everyone master the new features faster and start thinking about how their own LINE Bots should respond after a message is changed.\n\nFeel free to refer to the code, and I look forward to seeing everyone create more interesting applications:\n\n[https://github.com/kkdai/linebot-edit-unsend](https://github.com/kkdai/linebot-edit-unsend)", "url": "https://wpnews.pro/news/ai-in-practice-building-a-dynamic-line-group-buying-bot-with-edit-and-unsend", "canonical_source": "https://dev.to/gde/ai-in-practice-building-a-dynamic-line-group-buying-bot-with-edit-and-unsend-webhooks-1clh", "published_at": "2026-09-03 04:27:12+00:00", "updated_at": "2026-09-03 04:53:51.294821+00:00", "lang": "en", "topics": ["developer-tools", "ai-products"], "entities": ["LINE", "Evan Lin", "LINE Taiwan Developer Relations Team", "LINE Messaging API", "LINE Labs", "GitHub"], "alternates": {"html": "https://wpnews.pro/news/ai-in-practice-building-a-dynamic-line-group-buying-bot-with-edit-and-unsend", "markdown": "https://wpnews.pro/news/ai-in-practice-building-a-dynamic-line-group-buying-bot-with-edit-and-unsend.md", "text": "https://wpnews.pro/news/ai-in-practice-building-a-dynamic-line-group-buying-bot-with-edit-and-unsend.txt", "jsonld": "https://wpnews.pro/news/ai-in-practice-building-a-dynamic-line-group-buying-bot-with-edit-and-unsend.jsonld"}}