Skip to content

Commit c1b84e4

Browse files
authored
feat(linq): audit fixes + native auto-registering webhook trigger (#5301)
* feat(linq): audit fixes + native auto-registering webhook trigger Tools/block audit (validated against the live Linq partner API + OpenAPI spec): - create_chat: read the sent message from top-level response.message (was always null) - get_message/edit_message: expose canonical deliveryStatus; mark is_delivered/is_read deprecated - send_message: fall back to from_handle.service/preferred_service for service output - list_phone_numbers: migrate deprecated health_status to reputation; add forwardingNumber; guard JSON parse - check_imessage/check_rcs: guard response.json() parse - mark_chat_read: note 1:1-only / group no-op behavior Native webhook trigger (auto register + deregister): - 6 triggers (message received/delivered/failed/read, reaction added, all-events) - Standard Webhooks signature verification (HMAC-SHA256, whsec_ secret) - createSubscription/deleteSubscription manage the Linq subscription lifecycle - event_id idempotency; full 27-value WebhookEventType enum for all-events - regenerated docs * refactor(linq): drop phantom create_chat response path, complete deliveryStatus enum doc Final validation against the raw Linq OpenAPI spec confirmed the sent message is at chat.message (CreateChatResult exposes only chat), so the data.message fallback was dead code. Also list all 7 DeliveryStatus values in the send_message output description. * fix(linq): namespace trigger credential keys, handle edit_message 204, nullable forwardingNumber Review + final pre-merge audit fixes: - Trigger apiKey/phoneNumbers subblocks collided with the block's tool apiKey state key — rename to triggerApiKey/triggerPhoneNumbers (per the namespacing rule from #2133) and read them in the webhook handler - edit_message: the API returns 204 No Content when editing an already-deleted message; guard the empty body instead of throwing on response.json() - list_phone_numbers: mark forwardingNumber output nullable (returns null) - check_rcs: tighten address hint (RCS is phone-only, not email) - regenerated docs * fix(linq): read triggerApiKey in webhook deleteSubscription deleteSubscription still read config.apiKey after the credential rename, so undeploy would skip the DELETE and orphan the Linq subscription. Match createSubscription's triggerApiKey key. * fix(linq): mark list_phone_numbers healthStatus output nullable healthStatus returns null when Linq omits reputation/health_status; declare nullable: true to match the runtime value (same as forwardingNumber). * fix(linq): mark nullable list_webhook_subscriptions item fields phoneNumbers/createdAt/updatedAt are null-coerced by mapWebhookSubscription; declare nullable: true on the array-item schema to match runtime (consistent with the top-level webhook outputs' optional flags).
1 parent 604d03e commit c1b84e4

23 files changed

Lines changed: 960 additions & 47 deletions

apps/docs/content/docs/en/integrations/linq.mdx

Lines changed: 157 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ Check whether an address (phone number or email) supports RCS
8888
| Parameter | Type | Required | Description |
8989
| --------- | ---- | -------- | ----------- |
9090
| `apiKey` | string | Yes | Linq API key |
91-
| `address` | string | Yes | Phone number \(E.164 format\) or email address to check |
91+
| `address` | string | Yes | Phone number \(E.164 format\) to check |
9292
| `from` | string | No | Sender phone number to check from \(defaults to an available number\) |
9393

9494
#### Output
@@ -277,8 +277,9 @@ Edit the text of a sent message (up to 5 times, within 15 minutes of sending; iM
277277
| `id` | string | Message ID |
278278
| `chatId` | string | ID of the chat the message belongs to |
279279
| `isFromMe` | boolean | Whether the message was sent by you |
280-
| `isDelivered` | boolean | Whether the message was delivered |
281-
| `isRead` | boolean | Whether the message was read |
280+
| `deliveryStatus` | string | Delivery status \(pending, queued, sent, delivered, received, read, failed\) |
281+
| `isDelivered` | boolean | Whether the message was delivered \(deprecated; use deliveryStatus\) |
282+
| `isRead` | boolean | Whether the message was read \(deprecated; use deliveryStatus\) |
282283
| `service` | string | Delivery service \(iMessage, SMS, RCS\) |
283284
| `createdAt` | string | ISO 8601 creation timestamp |
284285
| `updatedAt` | string | ISO 8601 update timestamp |
@@ -374,8 +375,9 @@ Retrieve a single message by ID, including parts, reactions, and delivery status
374375
| `id` | string | Message ID |
375376
| `chatId` | string | ID of the chat the message belongs to |
376377
| `isFromMe` | boolean | Whether the message was sent by you |
377-
| `isDelivered` | boolean | Whether the message was delivered |
378-
| `isRead` | boolean | Whether the message was read |
378+
| `deliveryStatus` | string | Delivery status \(pending, queued, sent, delivered, received, read, failed\) |
379+
| `isDelivered` | boolean | Whether the message was delivered \(deprecated; use deliveryStatus\) |
380+
| `isRead` | boolean | Whether the message was read \(deprecated; use deliveryStatus\) |
379381
| `service` | string | Delivery service \(iMessage, SMS, RCS\) |
380382
| `createdAt` | string | ISO 8601 creation timestamp |
381383
| `updatedAt` | string | ISO 8601 update timestamp |
@@ -483,7 +485,8 @@ List all phone numbers assigned to your partner account, with line health
483485
| `phoneNumbers` | array | Phone numbers assigned to the account |
484486
|`id` | string | Phone number ID |
485487
|`phoneNumber` | string | Phone number in E.164 format |
486-
|`healthStatus` | json | Line health status \(status, doc_url\) |
488+
|`forwardingNumber` | string | Forwarding number in E.164 format, or null |
489+
|`healthStatus` | json | Line reputation/health status \(status, doc_url\) |
487490

488491
### `linq_list_thread`
489492

@@ -548,7 +551,7 @@ List all webhook subscriptions on your account
548551

549552
### `linq_mark_chat_read`
550553

551-
Mark all messages in a chat as read
554+
Mark messages in a chat as read (only applies to 1:1 iMessage/RCS; no effect on group chats)
552555

553556
#### Input
554557

@@ -633,7 +636,7 @@ Send a message to an existing chat, with optional media, link, effect, or reply
633636
| --------- | ---- | ----------- |
634637
| `chatId` | string | ID of the chat the message was sent to |
635638
| `messageId` | string | ID of the sent message |
636-
| `deliveryStatus` | string | Delivery status \(pending, queued, sent, delivered, failed\) |
639+
| `deliveryStatus` | string | Delivery status \(pending, queued, sent, delivered, received, read, failed\) |
637640
| `sentAt` | string | ISO 8601 timestamp the message was sent |
638641
| `service` | string | Delivery service \(iMessage, SMS, RCS\) |
639642
| `message` | json | The full sent message object with parts |
@@ -785,3 +788,149 @@ Update a webhook subscription (target URL, events, phone filter, or active state
785788
| `updatedAt` | string | ISO 8601 update timestamp |
786789

787790

791+
792+
## Triggers
793+
794+
A **Trigger** is a block that starts a workflow when an event happens in this service.
795+
796+
### Linq Message Delivered
797+
798+
Trigger workflow when a message is delivered
799+
800+
#### Configuration
801+
802+
| Parameter | Type | Required | Description |
803+
| --------- | ---- | -------- | ----------- |
804+
| `triggerApiKey` | string | Yes | Required to create the webhook subscription in Linq. |
805+
| `triggerPhoneNumbers` | string | No | Comma-separated E.164 numbers to restrict which numbers deliver events. |
806+
807+
#### Output
808+
809+
| Parameter | Type | Description |
810+
| --------- | ---- | ----------- |
811+
| `eventType` | string | Event type \(e.g. message.received, message.delivered, reaction.added\) |
812+
| `eventId` | string | Unique event identifier used for deduplication |
813+
| `createdAt` | string | ISO 8601 timestamp of when the event occurred |
814+
| `webhookVersion` | string | Payload schema version of the delivered event |
815+
| `data` | json | Full event payload \(shape varies by event type — message, reaction, chat, etc.\) |
816+
817+
818+
---
819+
820+
### Linq Message Failed
821+
822+
Trigger workflow when a message fails to deliver
823+
824+
#### Configuration
825+
826+
| Parameter | Type | Required | Description |
827+
| --------- | ---- | -------- | ----------- |
828+
| `triggerApiKey` | string | Yes | Required to create the webhook subscription in Linq. |
829+
| `triggerPhoneNumbers` | string | No | Comma-separated E.164 numbers to restrict which numbers deliver events. |
830+
831+
#### Output
832+
833+
| Parameter | Type | Description |
834+
| --------- | ---- | ----------- |
835+
| `eventType` | string | Event type \(e.g. message.received, message.delivered, reaction.added\) |
836+
| `eventId` | string | Unique event identifier used for deduplication |
837+
| `createdAt` | string | ISO 8601 timestamp of when the event occurred |
838+
| `webhookVersion` | string | Payload schema version of the delivered event |
839+
| `data` | json | Full event payload \(shape varies by event type — message, reaction, chat, etc.\) |
840+
841+
842+
---
843+
844+
### Linq Message Read
845+
846+
Trigger workflow when a message is read
847+
848+
#### Configuration
849+
850+
| Parameter | Type | Required | Description |
851+
| --------- | ---- | -------- | ----------- |
852+
| `triggerApiKey` | string | Yes | Required to create the webhook subscription in Linq. |
853+
| `triggerPhoneNumbers` | string | No | Comma-separated E.164 numbers to restrict which numbers deliver events. |
854+
855+
#### Output
856+
857+
| Parameter | Type | Description |
858+
| --------- | ---- | ----------- |
859+
| `eventType` | string | Event type \(e.g. message.received, message.delivered, reaction.added\) |
860+
| `eventId` | string | Unique event identifier used for deduplication |
861+
| `createdAt` | string | ISO 8601 timestamp of when the event occurred |
862+
| `webhookVersion` | string | Payload schema version of the delivered event |
863+
| `data` | json | Full event payload \(shape varies by event type — message, reaction, chat, etc.\) |
864+
865+
866+
---
867+
868+
### Linq Message Received
869+
870+
Trigger workflow when an inbound message is received
871+
872+
#### Configuration
873+
874+
| Parameter | Type | Required | Description |
875+
| --------- | ---- | -------- | ----------- |
876+
| `triggerApiKey` | string | Yes | Required to create the webhook subscription in Linq. |
877+
| `triggerPhoneNumbers` | string | No | Comma-separated E.164 numbers to restrict which numbers deliver events. |
878+
879+
#### Output
880+
881+
| Parameter | Type | Description |
882+
| --------- | ---- | ----------- |
883+
| `eventType` | string | Event type \(e.g. message.received, message.delivered, reaction.added\) |
884+
| `eventId` | string | Unique event identifier used for deduplication |
885+
| `createdAt` | string | ISO 8601 timestamp of when the event occurred |
886+
| `webhookVersion` | string | Payload schema version of the delivered event |
887+
| `data` | json | Full event payload \(shape varies by event type — message, reaction, chat, etc.\) |
888+
889+
890+
---
891+
892+
### Linq Reaction Added
893+
894+
Trigger workflow when a reaction is added to a message
895+
896+
#### Configuration
897+
898+
| Parameter | Type | Required | Description |
899+
| --------- | ---- | -------- | ----------- |
900+
| `triggerApiKey` | string | Yes | Required to create the webhook subscription in Linq. |
901+
| `triggerPhoneNumbers` | string | No | Comma-separated E.164 numbers to restrict which numbers deliver events. |
902+
903+
#### Output
904+
905+
| Parameter | Type | Description |
906+
| --------- | ---- | ----------- |
907+
| `eventType` | string | Event type \(e.g. message.received, message.delivered, reaction.added\) |
908+
| `eventId` | string | Unique event identifier used for deduplication |
909+
| `createdAt` | string | ISO 8601 timestamp of when the event occurred |
910+
| `webhookVersion` | string | Payload schema version of the delivered event |
911+
| `data` | json | Full event payload \(shape varies by event type — message, reaction, chat, etc.\) |
912+
913+
914+
---
915+
916+
### Linq Webhook (All Events)
917+
918+
Trigger on any Linq webhook event (messages, reactions, chats, and more)
919+
920+
#### Configuration
921+
922+
| Parameter | Type | Required | Description |
923+
| --------- | ---- | -------- | ----------- |
924+
| `triggerApiKey` | string | Yes | Required to create the webhook subscription in Linq. |
925+
| `triggerPhoneNumbers` | string | No | Comma-separated E.164 numbers to restrict which numbers deliver events. |
926+
927+
#### Output
928+
929+
| Parameter | Type | Description |
930+
| --------- | ---- | ----------- |
931+
| `eventType` | string | Event type \(e.g. message.received, message.delivered, reaction.added\) |
932+
| `eventId` | string | Unique event identifier used for deduplication |
933+
| `createdAt` | string | ISO 8601 timestamp of when the event occurred |
934+
| `webhookVersion` | string | Payload schema version of the delivered event |
935+
| `data` | json | Full event payload \(shape varies by event type — message, reaction, chat, etc.\) |
936+

apps/sim/blocks/blocks/linq.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { LinqIcon } from '@/components/icons'
22
import type { BlockConfig, BlockMeta } from '@/blocks/types'
33
import { AuthMode, IntegrationType } from '@/blocks/types'
44
import { normalizeFileInput } from '@/blocks/utils'
5+
import { getTrigger } from '@/triggers'
56

67
const CHAT_ID_OPS = [
78
'get_chat',
@@ -559,6 +560,12 @@ export const LinqBlock: BlockConfig = {
559560
condition: { field: 'operation', value: [...PAGINATION_OPS] },
560561
mode: 'advanced',
561562
},
563+
...getTrigger('linq_message_received').subBlocks,
564+
...getTrigger('linq_message_delivered').subBlocks,
565+
...getTrigger('linq_message_failed').subBlocks,
566+
...getTrigger('linq_message_read').subBlocks,
567+
...getTrigger('linq_reaction_added').subBlocks,
568+
...getTrigger('linq_webhook').subBlocks,
562569
],
563570

564571
tools: {
@@ -863,6 +870,18 @@ export const LinqBlock: BlockConfig = {
863870
events: { type: 'json', description: 'Available webhook event types' },
864871
docUrl: { type: 'string', description: 'Documentation URL' },
865872
},
873+
874+
triggers: {
875+
enabled: true,
876+
available: [
877+
'linq_message_received',
878+
'linq_message_delivered',
879+
'linq_message_failed',
880+
'linq_message_read',
881+
'linq_reaction_added',
882+
'linq_webhook',
883+
],
884+
},
866885
}
867886

868887
export const LinqBlockMeta = {

apps/sim/lib/integrations/integrations.json

Lines changed: 37 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10003,7 +10003,7 @@
1000310003
},
1000410004
{
1000510005
"name": "Mark Chat as Read",
10006-
"description": "Mark all messages in a chat as read"
10006+
"description": "Mark messages in a chat as read (only applies to 1:1 iMessage/RCS; no effect on group chats)"
1000710007
},
1000810008
{
1000910009
"name": "Leave Chat",
@@ -10119,8 +10119,39 @@
1011910119
}
1012010120
],
1012110121
"operationCount": 34,
10122-
"triggers": [],
10123-
"triggerCount": 0,
10122+
"triggers": [
10123+
{
10124+
"id": "linq_message_received",
10125+
"name": "Linq Message Received",
10126+
"description": "Trigger workflow when an inbound message is received"
10127+
},
10128+
{
10129+
"id": "linq_message_delivered",
10130+
"name": "Linq Message Delivered",
10131+
"description": "Trigger workflow when a message is delivered"
10132+
},
10133+
{
10134+
"id": "linq_message_failed",
10135+
"name": "Linq Message Failed",
10136+
"description": "Trigger workflow when a message fails to deliver"
10137+
},
10138+
{
10139+
"id": "linq_message_read",
10140+
"name": "Linq Message Read",
10141+
"description": "Trigger workflow when a message is read"
10142+
},
10143+
{
10144+
"id": "linq_reaction_added",
10145+
"name": "Linq Reaction Added",
10146+
"description": "Trigger workflow when a reaction is added to a message"
10147+
},
10148+
{
10149+
"id": "linq_webhook",
10150+
"name": "Linq Webhook (All Events)",
10151+
"description": "Trigger on any Linq webhook event (messages, reactions, chats, and more)"
10152+
}
10153+
],
10154+
"triggerCount": 6,
1012410155
"authType": "api-key",
1012510156
"category": "tools",
1012610157
"integrationType": "communication",
@@ -15649,11 +15680,11 @@
1564915680
"landingContent": {
1565015681
"install": {
1565115682
"heading": "Add Sim to your Slack workspace",
15652-
"intro": "Sim connects to Slack through Slack’s official OAuth flow. The “Add to Slack” button lives inside your Sim account (after sign-in) — connect from there and the Sim bot is installed in your Slack workspace. The steps below show exactly how to reach it.",
15683+
"intro": "Sim connects to Slack through Slack’s official OAuth flow. The “Add to Slack” button lives inside your Sim account (after sign-in). Connect from there and the Sim bot is installed in your Slack workspace. The steps below show exactly how to reach it.",
1565315684
"steps": [
1565415685
{
1565515686
"title": "Create your free Sim account",
15656-
"body": "Sign up at sim.ai — no credit card required."
15687+
"body": "Sign up at sim.ai. No credit card required."
1565715688
},
1565815689
{
1565915690
"title": "Add a Slack block",
@@ -15673,7 +15704,7 @@
1567315704
"body": "Sim requests only the Slack permissions its actions and triggers need, and never shows private channel names or messages to people who are not members of those channels in Slack.",
1567415705
"href": "/privacy"
1567515706
},
15676-
"aiDisclaimer": "Sim agents use AI models to generate messages and responses sent to Slack. AI-generated content can be inaccurate or incomplete review automated outputs before relying on them, especially for important communications."
15707+
"aiDisclaimer": "Sim agents use AI models to generate messages and responses sent to Slack. AI-generated content can be inaccurate or incomplete, so review automated outputs before relying on them, especially for important communications."
1567715708
}
1567815709
},
1567915710
{

0 commit comments

Comments
 (0)