LoopMessage New gen API update;#20459
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
|
Thank you so much for submitting this! We've added it to our backlog to review, and our team has been notified. |
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughAdds multiple new LoopMessage actions and a new HTTP inbound source, refactors send actions to explicit run methods and props, changes app auth/endpoint/request logic and adds API methods, updates constants/utils, adjusts an existing source and package metadata. Changes
Sequence Diagram(s)sequenceDiagram
participant Action as Action Runtime
participant App as loopmessage.app
participant API as External API
participant Source as HTTP Source
Note right of Action: send/sendReaction/sendTyping/checkStatus
Action->>App: call sendMessage/sendReaction/sendTyping/getMessageStatus(step, data)
App->>API: HTTP request to /integrations/pipedream/...
API-->>App: HTTP response (status, message_id, ...)
App-->>Action: return response
Action->>Action: step.export("$summary", getSummary(response))
Action-->>Caller: return response
Note over Source,App: webhook registration flow
Source->>App: updatePipedreamWebhook(endpoint) / deactivatePipedreamWebhook(endpoint)
App->>API: POST/DELETE webhook endpoint
API-->>App: confirmation
App-->>Source: return
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes 🚥 Pre-merge checks | ✅ 1 | ❌ 2❌ Failed checks (2 inconclusive)
✅ Passed checks (1 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 10
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In
`@components/loopmessage/actions/check_message_status/check_message_status.mjs`:
- Around line 10-14: The annotations object in check_message_status.mjs
incorrectly sets readOnlyHint to false; update the annotations block (the
annotations object that contains destructiveHint, openWorldHint, readOnlyHint)
so that readOnlyHint: true to accurately indicate this action is read-only (it
only performs a GET to retrieve message status).
In `@components/loopmessage/actions/send-reaction/send-reaction.mjs`:
- Around line 37-39: The getSummary function currently declares an unused
parameter `response`; remove the unused parameter by changing the signature to
getSummary() and update any local callers in this module that pass an argument
to call getSummary() without arguments (search for references to getSummary in
send-reaction.mjs); alternatively, if you prefer a more informative message, use
properties from the `response` object inside getSummary (e.g., response.status
or response.data) and include them in the returned string — but be sure to
update all call sites accordingly to pass the response when chosen.
In `@components/loopmessage/actions/send-text-message/send-text-message.mjs`:
- Around line 48-53: The attachments prop is defined inline in
send-text-message.mjs while other props use propDefinition from the app; move
the attachments definition into loopmessage.app.mjs as a reusable prop (e.g.,
export a propDefinition named attachments) and update send-text-message.mjs to
reference it via propDefinition: [app].attachments so other actions can reuse
the same schema and descriptions.
In `@components/loopmessage/actions/send-voice-message/send-voice-message.mjs`:
- Line 7: The description string in send-voice-message.mjs contains a typo:
update the exported description property (the "description" field in the
module/object for send-voice-message) from "Send s voice memo. Supports only in:
iMessage, RCS, WhatsApp." to "Send a voice memo. Supports only in: iMessage,
RCS, WhatsApp." so the wording is correct.
In `@components/loopmessage/actions/typing-indicator/typing_indicator.mjs`:
- Around line 4-43: The file uses 4-space indentation; update the export
object's formatting to use the project's 2-space style for consistency by
reformatting the default exported object (including keys: key, name,
description, type, version, annotations, props, methods, getSummary, and the
async run method) and nested blocks (the props.messageId.propDefinition,
methods.getSummary, and run destructuring and await call) to 2-space indentation
throughout so it matches other action files like send-reaction and
send-text-message.
- Around line 25-27: getSummary currently has no parameter but is invoked with a
response elsewhere and its returned string lacks the trailing period; update the
getSummary signature to accept the response parameter (e.g.,
getSummary(response)) and change the returned text to "Request accepted." so it
matches other actions like send-reaction.mjs and avoids a signature mismatch
when called with a response.
In `@components/loopmessage/loopmessage.app.mjs`:
- Line 71: The description string for the field named description contains a
stray Cyrillic "о" at the end; remove that trailing Cyrillic character so the
value ends with the intended punctuation/English text (update the description
property in loopmessage.app.mjs where the description string is defined).
- Line 58: The description string currently contains a duplicated "Optional.
Optional."; update the description for the relevant parameter/object in
loopmessage.app.mjs (the property named description) to remove the duplicate so
it reads e.g. "Optional. Add effect to your message. For iMessage only."
ensuring only a single "Optional." appears.
- Around line 107-115: The makeRequest method has inconsistent leading spaces;
reformat its declaration and body to use 2-space indentation like the rest of
the file: align the parameter destructuring (step = this, path, headers, ...args
= {}) and the return axios call so properties (headers:
this.getHeaders(headers), url: this.getUrl(path), ...args) are indented two
spaces per level; ensure references to makeRequest, axios, this.getHeaders and
this.getUrl remain unchanged but have consistent 2-space indentation.
In `@components/loopmessage/sources/new-alert-received/new-alert-received.mjs`:
- Around line 18-24: The fallback id using Date.now().toString() in the
this.$emit call (id: body.webhook_id || body.message_id ||
Date.now().toString()) is unsafe for dedupe="unique"; confirm whether
LoopMessage always supplies webhook_id or message_id, and if not replace the
Date.now() fallback with a robust deterministic fallback (e.g., compute a hash
of the entire body payload or JSON.stringify(body) to derive the id) so retries
and concurrent events produce stable, unique ids for deduplication.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: a3739481-e1d1-4537-b37a-1271d6bf60fa
📒 Files selected for processing (10)
components/loopmessage/actions/check_message_status/check_message_status.mjscomponents/loopmessage/actions/send-reaction/send-reaction.mjscomponents/loopmessage/actions/send-text-message/send-text-message.mjscomponents/loopmessage/actions/send-voice-message/send-voice-message.mjscomponents/loopmessage/actions/typing-indicator/typing_indicator.mjscomponents/loopmessage/common/constants.mjscomponents/loopmessage/common/utils.mjscomponents/loopmessage/loopmessage.app.mjscomponents/loopmessage/package.jsoncomponents/loopmessage/sources/new-alert-received/new-alert-received.mjs
💤 Files with no reviewable changes (1)
- components/loopmessage/common/utils.mjs
There was a problem hiding this comment.
Actionable comments posted: 4
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@components/loopmessage/actions/send-reaction/send-reaction.mjs`:
- Around line 37-38: The getSummary(response) method currently returns a
template string that mistakenly includes a duplicated "ID" token; update
getSummary to return something like `Request accepted. Message ID:
\`${response.message_id}\`` (i.e., remove the extra "ID" before the backticked
message_id) so the step output reads "Message ID: <id>" instead of "Message ID:
ID <id>".
In `@components/loopmessage/actions/send-voice-message/send-voice-message.mjs`:
- Around line 23-28: The action currently defines a senderName prop that ends up
serialized (via utils.keysToSnakeCase) as sender_name and is sent in voice
requests, but the voice API ignores/renamed this field; remove the senderName
prop (and the duplicate occurrence at the later block) from the action's props
so it is not included in the request payload, or if you must keep it for UI
only, ensure it is not part of the object passed into utils.keysToSnakeCase/send
payload; update the send-voice-message action to reference no senderName
property (check the senderName prop definitions and any usages in
sendVoiceMessage/sendVoiceRequest) and remove those keys from the payload
construction.
In `@components/loopmessage/loopmessage.app.mjs`:
- Around line 68-72: The description for the passthrough config option
incorrectly says "store with the checkout" and is misleading; update the
passthrough property's description (property name: passthrough) to refer to
storing arbitrary metadata with the outbound message (or webhook payload)
instead of a checkout, e.g., "Optional. An arbitrary string of metadata to
attach to the outbound message; it will be included with all webhooks for this
message. Max length: 1000 characters." Make this change in the passthrough
object so the copy accurately reflects message metadata usage.
- Around line 7-21: The auth schema nests api_key under auth.props.auth.props,
but getHeaders() reads this.$auth.api_key; remove the extra nesting so the
config exposes api_key at auth.props.api_key (i.e., eliminate the inner "auth"
object) so that this.$auth.api_key resolves correctly; update the auth
declaration where api_key is defined and verify getHeaders() continues to
reference this.$auth.api_key.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: 5814de26-9021-4464-aa8f-0d6053b31ef5
📒 Files selected for processing (6)
components/loopmessage/actions/check_message_status/check_message_status.mjscomponents/loopmessage/actions/send-reaction/send-reaction.mjscomponents/loopmessage/actions/send-voice-message/send-voice-message.mjscomponents/loopmessage/actions/typing-indicator/typing_indicator.mjscomponents/loopmessage/loopmessage.app.mjscomponents/loopmessage/sources/new-inbound-message/new-inbound-message.mjs
There was a problem hiding this comment.
Actionable comments posted: 1
♻️ Duplicate comments (3)
components/loopmessage/loopmessage.app.mjs (2)
69-73:⚠️ Potential issue | 🟡 MinorFix the passthrough description copy.
The description still says “store with the checkout”, which misdescribes what this field does.
Suggested fix
passthrough: { type: "string", label: "Passthrough", - description: "Optional. A string of metadata you wish to store with the checkout. Will be sent alongside all webhooks associated with the outbound message. Max length: 1000 characters.", + description: "Optional. A string of metadata you wish to store with the outbound message. It will be sent alongside all related webhooks. Max length: 1000 characters.", optional: true, },🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@components/loopmessage/loopmessage.app.mjs` around lines 69 - 73, Update the passthrough field description to remove the incorrect reference to "checkout" and accurately describe its purpose: state that it's optional metadata stored with the outbound message and sent with all webhooks for that message, and keep the Max length: 1000 characters; modify the description string on the passthrough property (the passthrough schema entry) accordingly.
7-21:⚠️ Potential issue | 🔴 CriticalFlatten the custom auth schema or fix the access path.
Line 104 reads
this.$auth.api_key, but the schema nestsapi_keyunderauth.props.auth.props. As written, theAuthorizationheader will beundefined, so outbound requests will fail authentication.Suggested fix
auth: { type: "custom", props: { - auth: { - type: "custom", - props: { - api_key: { - type: "string", - label: "Authorization", - secret: true, - }, - }, - }, + api_key: { + type: "string", + label: "Authorization", + secret: true, + }, }, },#!/bin/bash # Compare the declared auth shape with the runtime access path. sed -n '7,21p' components/loopmessage/loopmessage.app.mjs sed -n '101,105p' components/loopmessage/loopmessage.app.mjs # Check whether any other app file nests a second custom-auth block under app auth. rg -nUP --glob '*.app.mjs' 'auth:\s*\{\s*type:\s*"custom",\s*props:\s*\{\s*auth:\s*\{\s*type:\s*"custom"' componentsExpected: the first two snippets show the mismatch directly, and the repo search should ideally only hit this file.
Also applies to: 101-105
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@components/loopmessage/loopmessage.app.mjs` around lines 7 - 21, The auth schema currently nests api_key under auth.props.auth.props but the code reads this.$auth.api_key; fix by flattening the declared auth schema to expose api_key at this.$auth.api_key or update runtime access to match the nested shape (e.g., read this.$auth.auth.api_key); update the object under auth in the top-level app definition (the block with type:"custom" and props:{ auth: { type:"custom", props:{ api_key:... }}}) so api_key is directly under props, or change uses of this.$auth.api_key in the file to the nested path (this.$auth.auth.api_key) wherever referenced.components/loopmessage/sources/new-inbound-message/new-inbound-message.mjs (1)
9-9:⚠️ Potential issue | 🟠 MajorMake the emitted event metadata deterministic.
With
dedupe: "unique",idbecomesundefinedwhen bothwebhook_idandmessage_idare absent, andDate.parse(body.created_at)returnsNaNfor malformed timestamps. That makes deduplication and event ordering unstable.Suggested fix
+import { createHash } from "node:crypto"; import app from "../../loopmessage.app.mjs"; @@ async run({ body }) { + const parsedCreatedAt = body.created_at + ? Date.parse(body.created_at) + : NaN; + const eventId = body.webhook_id + ?? body.message_id + ?? createHash("sha256") + .update(JSON.stringify(body)) + .digest("hex"); + this.$emit(body, { - id: body.webhook_id || body.message_id, + id: eventId, summary: body.contact ? `New inbound message from ${body.contact}` : "New inbound received", - ts: body.created_at - ? Date.parse(body.created_at) - : Date.now(), + ts: Number.isNaN(parsedCreatedAt) + ? Date.now() + : parsedCreatedAt, });Also applies to: 25-33
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@components/loopmessage/sources/new-inbound-message/new-inbound-message.mjs` at line 9, The emitted event metadata is non-deterministic: when dedupe is "unique" the id can be undefined if webhook_id and message_id are missing and Date.parse(body.created_at) can be NaN for malformed timestamps; update the metadata construction (the places using dedupe: "unique", the id field and Date.parse(body.created_at) — also adjust the same logic around the 25-33 range) to produce deterministic values by: 1) setting id to a stable fallback when webhook_id and message_id are absent (e.g., derive a deterministic hash from the payload/headers or a canonical string of body fields), and 2) normalizing the timestamp so Date.parse(body.created_at) is validated and falls back to a safe numeric value (e.g., Date.now() or 0) when parsing fails; apply these fixes to all occurrences that build the emitted event metadata.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@components/loopmessage/loopmessage.app.mjs`:
- Around line 165-168: The getMessageStatus function interpolates raw messageId
into the URL path, which can break the route if messageId contains characters
like /, ?, or #; fix by URL-encoding messageId before building the path (e.g.,
call encodeURIComponent on messageId) so the path passed to makeRequest remains
valid and safe when constructing
`/integrations/pipedream/message/status/{encodedId}/`; update getMessageStatus
to use the encoded variable when calling this.makeRequest.
---
Duplicate comments:
In `@components/loopmessage/loopmessage.app.mjs`:
- Around line 69-73: Update the passthrough field description to remove the
incorrect reference to "checkout" and accurately describe its purpose: state
that it's optional metadata stored with the outbound message and sent with all
webhooks for that message, and keep the Max length: 1000 characters; modify the
description string on the passthrough property (the passthrough schema entry)
accordingly.
- Around line 7-21: The auth schema currently nests api_key under
auth.props.auth.props but the code reads this.$auth.api_key; fix by flattening
the declared auth schema to expose api_key at this.$auth.api_key or update
runtime access to match the nested shape (e.g., read this.$auth.auth.api_key);
update the object under auth in the top-level app definition (the block with
type:"custom" and props:{ auth: { type:"custom", props:{ api_key:... }}}) so
api_key is directly under props, or change uses of this.$auth.api_key in the
file to the nested path (this.$auth.auth.api_key) wherever referenced.
In `@components/loopmessage/sources/new-inbound-message/new-inbound-message.mjs`:
- Line 9: The emitted event metadata is non-deterministic: when dedupe is
"unique" the id can be undefined if webhook_id and message_id are missing and
Date.parse(body.created_at) can be NaN for malformed timestamps; update the
metadata construction (the places using dedupe: "unique", the id field and
Date.parse(body.created_at) — also adjust the same logic around the 25-33 range)
to produce deterministic values by: 1) setting id to a stable fallback when
webhook_id and message_id are absent (e.g., derive a deterministic hash from the
payload/headers or a canonical string of body fields), and 2) normalizing the
timestamp so Date.parse(body.created_at) is validated and falls back to a safe
numeric value (e.g., Date.now() or 0) when parsing fails; apply these fixes to
all occurrences that build the emitted event metadata.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: 851bd036-16b9-43e0-9b13-ea509b67e762
📒 Files selected for processing (2)
components/loopmessage/loopmessage.app.mjscomponents/loopmessage/sources/new-inbound-message/new-inbound-message.mjs
There was a problem hiding this comment.
Actionable comments posted: 1
♻️ Duplicate comments (1)
components/loopmessage/loopmessage.app.mjs (1)
160-164:⚠️ Potential issue | 🟡 MinorEncode
messageIdbefore building the status URL.Line 163 still interpolates raw
messageIdinto the path. If the ID contains/,?, or#, the request can hit the wrong route or fail unexpectedly.Suggested fix
getMessageStatus(messageId, args = {}) { + const encodedMessageId = encodeURIComponent(messageId); return this.makeRequest({ method: "get", - path: `/integrations/pipedream/message/status/${messageId}/`, + path: `/integrations/pipedream/message/status/${encodedMessageId}/`, ...args, }); },🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@components/loopmessage/loopmessage.app.mjs` around lines 160 - 164, getMessageStatus currently interpolates raw messageId into the request path which can break routes if the ID contains reserved characters; update the path construction in getMessageStatus to encode the ID (use encodeURIComponent(messageId)) before interpolation so the URL is safe, i.e., replace direct ${messageId} usage in the path argument of makeRequest with an encoded version to ensure correct routing and avoid request failures.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@components/loopmessage/loopmessage.app.mjs`:
- Around line 71-75: The shared prop definition for mediaUrl wrongly marks the
audio URL optional; remove the `optional: true` property from the mediaUrl
shared prop in loopmessage.app.mjs so the field becomes required, and verify
that the action reusing this prop (send-voice-message.mjs) does not override it
back to optional (ensure send-voice-message uses the shared mediaUrl prop
as-is). This ensures send-voice-message.mjs cannot be configured without a media
URL.
---
Duplicate comments:
In `@components/loopmessage/loopmessage.app.mjs`:
- Around line 160-164: getMessageStatus currently interpolates raw messageId
into the request path which can break routes if the ID contains reserved
characters; update the path construction in getMessageStatus to encode the ID
(use encodeURIComponent(messageId)) before interpolation so the URL is safe,
i.e., replace direct ${messageId} usage in the path argument of makeRequest with
an encoded version to ensure correct routing and avoid request failures.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: 548ae231-d851-496d-8b13-a6268e7b6469
📒 Files selected for processing (5)
components/loopmessage/actions/send-reaction/send-reaction.mjscomponents/loopmessage/actions/send-text-message/send-text-message.mjscomponents/loopmessage/actions/send-voice-message/send-voice-message.mjscomponents/loopmessage/common/constants.mjscomponents/loopmessage/loopmessage.app.mjs
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@components/loopmessage/README.md`:
- Line 3: Change the product name usage in the README from "The LoopMessage" to
"LoopMessage": locate the sentence that currently starts with "The LoopMessage
is an omnichannel..." and remove the leading article so it reads "LoopMessage is
an omnichannel..."; also scan the same README for any other occurrences of "The
LoopMessage" and replace them with "LoopMessage" for consistency.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: 2530b7b6-89b1-492b-af35-18cabb9a1259
📒 Files selected for processing (1)
components/loopmessage/README.md
michelle0927
left a comment
There was a problem hiding this comment.
Thank you for your contribution! I left a few comments. Also, you'll need to update the package.json version to "0.3.0" and remove the blank line at the beginning of utils.mjs.
| type: "string", | ||
| label: "Message ID", | ||
| description: "Outbound message ID.", | ||
| }, |
There was a problem hiding this comment.
Props should display a list of async options if possible. Could the message history endpoint be used to retrieve message IDs?
There was a problem hiding this comment.
We plan to add it in a future update.
| label: "Recipient", | ||
| description: "The recipient of the message. This can be a phone number or email address.", | ||
| label: "Contact", | ||
| description: "Recipient phone number or email address.", |
There was a problem hiding this comment.
Could async options for contact be retrieved through get audience list endpoint?
There was a problem hiding this comment.
We plan to add it in a future update.
| label: "Reply To ID", | ||
| description: "Optional. Reply to a message with a specific ID", | ||
| optional: true, | ||
| }, |
There was a problem hiding this comment.
Could replyToId use async options?
There was a problem hiding this comment.
We plan to add it in a future update.
| export default { | ||
| key: "loopmessage-check-message-status", | ||
| name: "Check Message Status", | ||
| description: "Action to get the current outbound message status. Possible values: processing, failed, delivered.", |
There was a problem hiding this comment.
Sorry, I missed this earlier. All actions should have doc links to the relevant documentation. Applies to all actions.
| description: "Action to get the current outbound message status. Possible values: processing, failed, delivered.", | |
| description: "Action to get the current outbound message status. Possible values: processing, failed, delivered. [See the documentation](<url here>)", |
There was a problem hiding this comment.
@michelle0927 The problem is that the documentation URLs may change over time. If we provide a link there and the page is moved, it could confuse users.
|
@Deliany, It sounds like perhaps this PR is premature? I'm getting 404 errors for endpoint that contain "/integrations/pipedream". If the API is still being developed, we should wait until it's ready before developing and releasing components for it. |
|
@michelle0927 |
There was a problem hiding this comment.
These are the results I'm getting with the api key provided by the LoopMessage team.
For send-reaction, I'm getting undefined in the summary:
For send-voice-message, I'm getting undefined in the summary:
---------------
For check-message-status, I'm geting a 404 error:
---------------
For typing-indicator, I'm getting undefined in the summary:
---------------
For send-text-message, I'm getting undefined in the summary:
For new-inbound-message, I'm unable to deploy the source:
|
@michelle0927 we made some changes. |
michelle0927
left a comment
There was a problem hiding this comment.
Thanks @Deliany. I'm able to login and send & receive messages. Using the new api key, I'm still seeing the same results from the actions. I am able to deploy the source, new-inbound-message, but it's not receiveing any events and there's nothing under Webhooks history in the LoopMessage UI.
|
@michelle0927 Thank you for the details! |
|
@Deliany Thanks, the webhook is working! However, I'm seeing a couple issues with these actions: |
|
@michelle0927 thank you for noticing. Our engineers have already made some changes, and now it should work for you. |
michelle0927
left a comment
There was a problem hiding this comment.
@Deliany Thank you for your work on this. This is ready for release!


WHY
LoopMessage released a new generation API update that required significant changes.
Summary by CodeRabbit
New Features
Improvements
Chores
Documentation