From 513a81915b4703e21edba0c7d6af41d012a49422 Mon Sep 17 00:00:00 2001 From: Adam Kinniburgh Date: Sat, 31 Oct 2026 17:22:49 +0000 Subject: [PATCH] Add Resend plugin --- .github/CODEOWNERS | 1 + plugins/Resend/v1/configValidation.json | 11 + .../relate-broadcast-to-segment.json | 20 + .../relate-inbox-to-domain.json | 20 + plugins/Resend/v1/custom_types.json | 44 + plugins/Resend/v1/dataStreams/apiKeys.json | 48 + plugins/Resend/v1/dataStreams/apiLogs.json | 97 ++ .../Resend/v1/dataStreams/automationRuns.json | 140 +++ .../Resend/v1/dataStreams/automations.json | 83 ++ .../v1/dataStreams/broadcastClickedLinks.json | 83 ++ .../v1/dataStreams/broadcastRecipients.json | 134 +++ plugins/Resend/v1/dataStreams/broadcasts.json | 117 +++ .../Resend/v1/dataStreams/contactImports.json | 132 +++ plugins/Resend/v1/dataStreams/contacts.json | 93 ++ .../Resend/v1/dataStreams/domainRecords.json | 84 ++ plugins/Resend/v1/dataStreams/domains.json | 97 ++ .../Resend/v1/dataStreams/emailMetrics.json | 277 ++++++ .../Resend/v1/dataStreams/inboxDetails.json | 101 ++ plugins/Resend/v1/dataStreams/inboxes.json | 98 ++ .../Resend/v1/dataStreams/receivedEmails.json | 95 ++ .../scripts/broadcastRecipients.js | 10 + .../v1/dataStreams/scripts/broadcasts.js | 6 + .../v1/dataStreams/scripts/emailMetrics.js | 1 + .../Resend/v1/dataStreams/scripts/emails.js | 27 + plugins/Resend/v1/dataStreams/scripts/list.js | 6 + .../v1/dataStreams/scripts/listInTimeframe.js | 15 + plugins/Resend/v1/dataStreams/segments.json | 45 + plugins/Resend/v1/dataStreams/sentEmails.json | 127 +++ .../Resend/v1/dataStreams/suppressions.json | 107 +++ plugins/Resend/v1/dataStreams/templates.json | 88 ++ plugins/Resend/v1/dataStreams/topics.json | 71 ++ .../Resend/v1/dataStreams/webhookEvents.json | 91 ++ plugins/Resend/v1/dataStreams/webhooks.json | 72 ++ .../v1/defaultContent/automation.dash.json | 418 +++++++++ .../v1/defaultContent/broadcast.dash.json | 545 +++++++++++ .../Resend/v1/defaultContent/domain.dash.json | 579 ++++++++++++ .../Resend/v1/defaultContent/inbox.dash.json | 201 ++++ .../Resend/v1/defaultContent/manifest.json | 32 + .../v1/defaultContent/overview.dash.json | 876 ++++++++++++++++++ plugins/Resend/v1/defaultContent/scopes.json | 104 +++ .../v1/defaultContent/segment.dash.json | 405 ++++++++ .../v1/defaultContent/webhook.dash.json | 391 ++++++++ plugins/Resend/v1/docs/README.md | 84 ++ plugins/Resend/v1/icon.svg | 6 + .../Resend/v1/indexDefinitions/default.json | 129 +++ plugins/Resend/v1/metadata.json | 63 ++ plugins/Resend/v1/ui.json | 10 + 47 files changed, 6284 insertions(+) create mode 100644 plugins/Resend/v1/configValidation.json create mode 100644 plugins/Resend/v1/correlationRules/relate-broadcast-to-segment.json create mode 100644 plugins/Resend/v1/correlationRules/relate-inbox-to-domain.json create mode 100644 plugins/Resend/v1/custom_types.json create mode 100644 plugins/Resend/v1/dataStreams/apiKeys.json create mode 100644 plugins/Resend/v1/dataStreams/apiLogs.json create mode 100644 plugins/Resend/v1/dataStreams/automationRuns.json create mode 100644 plugins/Resend/v1/dataStreams/automations.json create mode 100644 plugins/Resend/v1/dataStreams/broadcastClickedLinks.json create mode 100644 plugins/Resend/v1/dataStreams/broadcastRecipients.json create mode 100644 plugins/Resend/v1/dataStreams/broadcasts.json create mode 100644 plugins/Resend/v1/dataStreams/contactImports.json create mode 100644 plugins/Resend/v1/dataStreams/contacts.json create mode 100644 plugins/Resend/v1/dataStreams/domainRecords.json create mode 100644 plugins/Resend/v1/dataStreams/domains.json create mode 100644 plugins/Resend/v1/dataStreams/emailMetrics.json create mode 100644 plugins/Resend/v1/dataStreams/inboxDetails.json create mode 100644 plugins/Resend/v1/dataStreams/inboxes.json create mode 100644 plugins/Resend/v1/dataStreams/receivedEmails.json create mode 100644 plugins/Resend/v1/dataStreams/scripts/broadcastRecipients.js create mode 100644 plugins/Resend/v1/dataStreams/scripts/broadcasts.js create mode 100644 plugins/Resend/v1/dataStreams/scripts/emailMetrics.js create mode 100644 plugins/Resend/v1/dataStreams/scripts/emails.js create mode 100644 plugins/Resend/v1/dataStreams/scripts/list.js create mode 100644 plugins/Resend/v1/dataStreams/scripts/listInTimeframe.js create mode 100644 plugins/Resend/v1/dataStreams/segments.json create mode 100644 plugins/Resend/v1/dataStreams/sentEmails.json create mode 100644 plugins/Resend/v1/dataStreams/suppressions.json create mode 100644 plugins/Resend/v1/dataStreams/templates.json create mode 100644 plugins/Resend/v1/dataStreams/topics.json create mode 100644 plugins/Resend/v1/dataStreams/webhookEvents.json create mode 100644 plugins/Resend/v1/dataStreams/webhooks.json create mode 100644 plugins/Resend/v1/defaultContent/automation.dash.json create mode 100644 plugins/Resend/v1/defaultContent/broadcast.dash.json create mode 100644 plugins/Resend/v1/defaultContent/domain.dash.json create mode 100644 plugins/Resend/v1/defaultContent/inbox.dash.json create mode 100644 plugins/Resend/v1/defaultContent/manifest.json create mode 100644 plugins/Resend/v1/defaultContent/overview.dash.json create mode 100644 plugins/Resend/v1/defaultContent/scopes.json create mode 100644 plugins/Resend/v1/defaultContent/segment.dash.json create mode 100644 plugins/Resend/v1/defaultContent/webhook.dash.json create mode 100644 plugins/Resend/v1/docs/README.md create mode 100644 plugins/Resend/v1/icon.svg create mode 100644 plugins/Resend/v1/indexDefinitions/default.json create mode 100644 plugins/Resend/v1/metadata.json create mode 100644 plugins/Resend/v1/ui.json diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index fbfec164..d310f8e2 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -41,3 +41,4 @@ /plugins/Zscaler/ @shawn149 /plugins/Healthchecks.io/ @adamkinniburgh /plugins/MyEnergi/ @adamkinniburgh +/plugins/Resend/ @adamkinniburgh diff --git a/plugins/Resend/v1/configValidation.json b/plugins/Resend/v1/configValidation.json new file mode 100644 index 00000000..30769fed --- /dev/null +++ b/plugins/Resend/v1/configValidation.json @@ -0,0 +1,11 @@ +{ + "steps": [ + { + "displayName": "Authenticate", + "dataStream": { "name": "apiKeys" }, + "required": true, + "error": "Could not authenticate. Check the API key is valid and has Full access permission — a Sending access key can't read account data.", + "success": "Connected successfully." + } + ] +} diff --git a/plugins/Resend/v1/correlationRules/relate-broadcast-to-segment.json b/plugins/Resend/v1/correlationRules/relate-broadcast-to-segment.json new file mode 100644 index 00000000..92637390 --- /dev/null +++ b/plugins/Resend/v1/correlationRules/relate-broadcast-to-segment.json @@ -0,0 +1,20 @@ +{ + "displayName": "Broadcast is sent to Segment", + "source": { + "types": "Broadcast" + }, + "target": { + "types": "Segment" + }, + "conditions": [ + { + "sourceProperty": "segment_id", + "operator": "equals", + "targetProperty": "rawId" + } + ], + "labels": { + "forward": "is sent to", + "reverse": "receives" + } +} diff --git a/plugins/Resend/v1/correlationRules/relate-inbox-to-domain.json b/plugins/Resend/v1/correlationRules/relate-inbox-to-domain.json new file mode 100644 index 00000000..7602f1e5 --- /dev/null +++ b/plugins/Resend/v1/correlationRules/relate-inbox-to-domain.json @@ -0,0 +1,20 @@ +{ + "displayName": "Inbox receives on Domain", + "source": { + "types": "Inbox" + }, + "target": { + "types": "Domain" + }, + "conditions": [ + { + "sourceProperty": "domain", + "operator": "equals", + "targetProperty": "name" + } + ], + "labels": { + "forward": "receives on", + "reverse": "hosts" + } +} diff --git a/plugins/Resend/v1/custom_types.json b/plugins/Resend/v1/custom_types.json new file mode 100644 index 00000000..66876bfb --- /dev/null +++ b/plugins/Resend/v1/custom_types.json @@ -0,0 +1,44 @@ +[ + { + "name": "Domain", + "sourceType": "Domain", + "icon": "globe", + "singular": "Domain", + "plural": "Domains" + }, + { + "name": "Segment", + "sourceType": "Segment", + "icon": "users", + "singular": "Segment", + "plural": "Segments" + }, + { + "name": "Broadcast", + "sourceType": "Broadcast", + "icon": "paper-plane", + "singular": "Broadcast", + "plural": "Broadcasts" + }, + { + "name": "Webhook", + "sourceType": "Webhook", + "icon": "webhook", + "singular": "Webhook", + "plural": "Webhooks" + }, + { + "name": "Automation", + "sourceType": "Automation", + "icon": "diagram-project", + "singular": "Automation", + "plural": "Automations" + }, + { + "name": "Inbox", + "sourceType": "Inbox", + "icon": "inbox", + "singular": "Inbox", + "plural": "Inboxes" + } +] diff --git a/plugins/Resend/v1/dataStreams/apiKeys.json b/plugins/Resend/v1/dataStreams/apiKeys.json new file mode 100644 index 00000000..a515e250 --- /dev/null +++ b/plugins/Resend/v1/dataStreams/apiKeys.json @@ -0,0 +1,48 @@ +{ + "name": "apiKeys", + "displayName": "API Keys", + "description": "API keys on the Resend team, with when each was created and last used", + "tags": [ + "Security" + ], + "baseDataSourceName": "httpRequestUnscoped", + "config": { + "httpMethod": "get", + "endpointPath": "api-keys", + "pathToData": "data" + }, + "matches": "none", + "metadata": [ + { + "name": "id", + "displayName": "ID", + "shape": "string", + "visible": false + }, + { + "name": "name", + "displayName": "Name", + "shape": "string", + "role": "label" + }, + { + "name": "created_at", + "displayName": "Created", + "shape": "date" + }, + { + "name": "last_used_at", + "displayName": "Last Used", + "shape": "date", + "role": "timestamp" + }, + { + "name": "days_since_used", + "displayName": "Days Since Used", + "computed": true, + "valueExpression": "{{ $['last_used_at'] ? Math.floor((Date.now() - new Date($['last_used_at']).getTime()) / 86400000) : null }}", + "shape": "number" + } + ], + "timeframes": false +} diff --git a/plugins/Resend/v1/dataStreams/apiLogs.json b/plugins/Resend/v1/dataStreams/apiLogs.json new file mode 100644 index 00000000..28e73cc1 --- /dev/null +++ b/plugins/Resend/v1/dataStreams/apiLogs.json @@ -0,0 +1,97 @@ +{ + "name": "apiLogs", + "displayName": "API Logs", + "description": "API requests made to the Resend team, newest first", + "tags": [ + "Logs" + ], + "baseDataSourceName": "httpRequestUnscoped", + "config": { + "httpMethod": "get", + "endpointPath": "logs", + "paging": { + "mode": "token", + "pageSize": { + "realm": "queryArg", + "path": "limit", + "value": "100" + }, + "in": { + "realm": "payload", + "path": "next_cursor" + }, + "out": { + "realm": "queryArg", + "path": "after" + } + }, + "postRequestScript": "listInTimeframe.js" + }, + "matches": "none", + "metadata": [ + { + "name": "id", + "displayName": "ID", + "shape": "string", + "visible": false + }, + { + "name": "created_at", + "displayName": "Time", + "shape": "date", + "role": "timestamp" + }, + { + "name": "method", + "displayName": "Method", + "shape": "string" + }, + { + "name": "endpoint", + "displayName": "Endpoint", + "shape": "string", + "role": "label" + }, + { + "name": "response_status", + "displayName": "Response Status", + "shape": "number" + }, + { + "name": "outcome", + "displayName": "Outcome", + "computed": true, + "valueExpression": "{{ $['response_status'] >= 200 && $['response_status'] < 300 ? 'success' : ($['response_status'] == 429 || $['response_status'] >= 500 ? 'error' : 'warning') }}", + "shape": [ + "state", + { + "map": { + "success": [ + "success" + ], + "warning": [ + "warning" + ], + "error": [ + "error" + ] + } + } + ] + }, + { + "name": "user_agent", + "displayName": "User Agent", + "shape": "string" + }, + { + "pattern": ".*" + } + ], + "timeframes": [ + "last1hour", + "last12hours", + "last24hours", + "last7days" + ] +} diff --git a/plugins/Resend/v1/dataStreams/automationRuns.json b/plugins/Resend/v1/dataStreams/automationRuns.json new file mode 100644 index 00000000..ccb95049 --- /dev/null +++ b/plugins/Resend/v1/dataStreams/automationRuns.json @@ -0,0 +1,140 @@ +{ + "name": "automationRuns", + "displayName": "Automation Runs", + "description": "Runs of an automation, with status and duration", + "tags": [ + "Email", + "Automations" + ], + "baseDataSourceName": "httpRequestScopedSingle", + "config": { + "httpMethod": "get", + "endpointPath": "automations/{{object.rawId}}/runs", + "getArgs": [ + { + "key": "status", + "value": "{{ status && status.length ? [].concat(status).join(',') : null }}" + } + ], + "paging": { + "mode": "token", + "pageSize": { + "realm": "queryArg", + "path": "limit", + "value": "100" + }, + "in": { + "realm": "payload", + "path": "next_cursor" + }, + "out": { + "realm": "queryArg", + "path": "after" + } + }, + "postRequestScript": "list.js" + }, + "matches": { + "sourceType": { + "type": "oneOf", + "values": [ + "Automation" + ] + } + }, + "ui": [ + { + "type": "choiceChips", + "name": "status", + "label": "Status", + "isMulti": true, + "options": [ + { + "value": "running", + "label": "Running" + }, + { + "value": "completed", + "label": "Completed" + }, + { + "value": "failed", + "label": "Failed" + }, + { + "value": "cancelled", + "label": "Cancelled" + } + ] + } + ], + "defaultShaping": { + "sort": { + "by": [ + [ + "created_at", + "desc" + ] + ] + } + }, + "metadata": [ + { + "name": "id", + "displayName": "ID", + "shape": "string", + "visible": false + }, + { + "name": "status", + "displayName": "Status", + "shape": [ + "state", + { + "map": { + "success": [ + "completed" + ], + "error": [ + "failed" + ], + "warning": [ + "cancelled" + ], + "unknown": [ + "running" + ] + } + } + ], + "role": "label" + }, + { + "name": "started_at", + "displayName": "Started", + "shape": "date" + }, + { + "name": "completed_at", + "displayName": "Completed", + "shape": "date" + }, + { + "name": "created_at", + "displayName": "Created", + "shape": "date", + "role": "timestamp" + }, + { + "name": "duration", + "displayName": "Duration", + "computed": true, + "valueExpression": "{{ $['completed_at'] && $['started_at'] ? Math.round((new Date($['completed_at']) - new Date($['started_at'])) / 1000) : null }}", + "shape": "seconds" + }, + { + "pattern": ".*" + } + ], + "timeframes": false +} diff --git a/plugins/Resend/v1/dataStreams/automations.json b/plugins/Resend/v1/dataStreams/automations.json new file mode 100644 index 00000000..16330957 --- /dev/null +++ b/plugins/Resend/v1/dataStreams/automations.json @@ -0,0 +1,83 @@ +{ + "name": "automations", + "displayName": "Automations", + "description": "Event-triggered email automations on the Resend team, with status", + "tags": [ + "Email", + "Automations" + ], + "baseDataSourceName": "httpRequestUnscoped", + "config": { + "httpMethod": "get", + "endpointPath": "automations", + "paging": { + "mode": "token", + "pageSize": { + "realm": "queryArg", + "path": "limit", + "value": "100" + }, + "in": { + "realm": "payload", + "path": "next_cursor" + }, + "out": { + "realm": "queryArg", + "path": "after" + } + }, + "postRequestScript": "list.js" + }, + "matches": "none", + "metadata": [ + { + "name": "id", + "displayName": "ID", + "shape": "string", + "visible": false + }, + { + "name": "name", + "displayName": "Automation", + "shape": "string", + "role": "label" + }, + { + "name": "status", + "displayName": "Status", + "shape": [ + "state", + { + "map": { + "success": [ + "enabled" + ], + "warning": [ + "disabled" + ] + } + } + ] + }, + { + "name": "created_at", + "displayName": "Created", + "shape": "date" + }, + { + "name": "updated_at", + "displayName": "Updated", + "shape": "date", + "role": "timestamp" + }, + { + "sourceId": "id", + "sourceType": "Automation", + "name": "name" + }, + { + "pattern": ".*" + } + ], + "timeframes": false +} diff --git a/plugins/Resend/v1/dataStreams/broadcastClickedLinks.json b/plugins/Resend/v1/dataStreams/broadcastClickedLinks.json new file mode 100644 index 00000000..aed77d08 --- /dev/null +++ b/plugins/Resend/v1/dataStreams/broadcastClickedLinks.json @@ -0,0 +1,83 @@ +{ + "name": "broadcastClickedLinks", + "displayName": "Broadcast Clicked Links", + "description": "Links clicked in a broadcast, ranked by total clicks", + "tags": [ + "Email", + "Broadcasts" + ], + "baseDataSourceName": "httpRequestScopedSingle", + "config": { + "httpMethod": "get", + "endpointPath": "broadcasts/{{object.rawId}}/clicked-links", + "paging": { + "mode": "token", + "pageSize": { + "realm": "queryArg", + "path": "limit", + "value": "100" + }, + "in": { + "realm": "payload", + "path": "next_cursor" + }, + "out": { + "realm": "queryArg", + "path": "after" + } + }, + "postRequestScript": "list.js" + }, + "matches": { + "sourceType": { + "type": "oneOf", + "values": [ + "Broadcast" + ] + } + }, + "defaultShaping": { + "sort": { + "by": [ + [ + "clicks", + "desc" + ] + ] + } + }, + "metadata": [ + { + "name": "id", + "displayName": "ID", + "shape": "string", + "visible": false + }, + { + "name": "url", + "displayName": "Link", + "shape": [ + "url", + { + "label": "{{column.url}}" + } + ], + "role": "label" + }, + { + "name": "clicks", + "displayName": "Clicks", + "shape": "number", + "role": "value" + }, + { + "name": "unique_clicks", + "displayName": "Unique Clicks", + "shape": "number" + }, + { + "pattern": ".*" + } + ], + "timeframes": false +} diff --git a/plugins/Resend/v1/dataStreams/broadcastRecipients.json b/plugins/Resend/v1/dataStreams/broadcastRecipients.json new file mode 100644 index 00000000..e292ee37 --- /dev/null +++ b/plugins/Resend/v1/dataStreams/broadcastRecipients.json @@ -0,0 +1,134 @@ +{ + "name": "broadcastRecipients", + "displayName": "Broadcast Recipients", + "description": "Recipients of a broadcast who triggered the chosen event", + "tags": [ + "Email", + "Broadcasts" + ], + "baseDataSourceName": "httpRequestScopedSingle", + "config": { + "httpMethod": "get", + "endpointPath": "broadcasts/{{object.rawId}}/recipients", + "getArgs": [ + { + "key": "type", + "value": "{{ event }}" + } + ], + "paging": { + "mode": "token", + "pageSize": { + "realm": "queryArg", + "path": "limit", + "value": "100" + }, + "in": { + "realm": "payload", + "path": "next_cursor" + }, + "out": { + "realm": "queryArg", + "path": "after" + } + }, + "postRequestScript": "broadcastRecipients.js" + }, + "matches": { + "sourceType": { + "type": "oneOf", + "values": [ + "Broadcast" + ] + } + }, + "ui": [ + { + "type": "choiceChips", + "name": "event", + "label": "Event", + "defaultValue": "delivered", + "validation": { + "required": true + }, + "options": [ + { + "value": "sent", + "label": "Sent" + }, + { + "value": "delivered", + "label": "Delivered" + }, + { + "value": "opened", + "label": "Opened" + }, + { + "value": "clicked", + "label": "Clicked" + }, + { + "value": "bounced", + "label": "Bounced" + }, + { + "value": "complained", + "label": "Complained" + }, + { + "value": "unsubscribed", + "label": "Unsubscribed" + }, + { + "value": "suppressed", + "label": "Suppressed" + } + ] + } + ], + "metadata": [ + { + "name": "id", + "displayName": "ID", + "shape": "string", + "visible": false + }, + { + "name": "contact_id", + "displayName": "Contact ID", + "shape": "string", + "visible": false + }, + { + "name": "email", + "displayName": "Email", + "shape": "string", + "role": "label" + }, + { + "name": "count", + "displayName": "Count", + "shape": "number" + }, + { + "name": "bounce_type", + "displayName": "Bounce Type", + "shape": "string" + }, + { + "name": "clicked_links", + "displayName": "Clicked Links", + "shape": [ + "array", + { + "itemShape": "url" + } + ] + }, + { + "pattern": ".*" + } + ], + "timeframes": false +} diff --git a/plugins/Resend/v1/dataStreams/broadcasts.json b/plugins/Resend/v1/dataStreams/broadcasts.json new file mode 100644 index 00000000..0be35b2e --- /dev/null +++ b/plugins/Resend/v1/dataStreams/broadcasts.json @@ -0,0 +1,117 @@ +{ + "name": "broadcasts", + "displayName": "Broadcasts", + "description": "Broadcasts (marketing campaigns) on the Resend team, with status and send times, optionally limited to segments", + "tags": [ + "Email", + "Broadcasts" + ], + "baseDataSourceName": "httpRequestUnscoped", + "config": { + "httpMethod": "get", + "endpointPath": "broadcasts", + "postRequestScript": "broadcasts.js" + }, + "matches": "none", + "ui": [ + { + "type": "objects", + "name": "segment", + "label": "Segment(s)", + "matches": { + "sourceType": { + "type": "oneOf", + "values": [ + "Segment" + ] + } + } + } + ], + "metadata": [ + { + "name": "id", + "displayName": "ID", + "shape": "string", + "visible": false + }, + { + "name": "name", + "displayName": "Broadcast", + "shape": "string", + "role": "label" + }, + { + "name": "status", + "displayName": "Status", + "shape": [ + "state", + { + "map": { + "success": [ + "sent" + ], + "warning": [ + "canceled", + "cancelled" + ], + "error": [ + "failed" + ], + "unknown": [ + "draft", + "scheduled", + "queued", + "sending" + ] + } + } + ] + }, + { + "name": "segment_id", + "displayName": "Segment ID", + "shape": "string", + "visible": false, + "sourceType": "Segment" + }, + { + "name": "segment", + "displayName": "Segment", + "sourceId": "segment_id", + "objectPropertyPath": "name", + "valueExpression": "{{ typeof $['segment_id'] === 'string' ? object.name : '' }}" + }, + { + "name": "created_at", + "displayName": "Created", + "shape": "date" + }, + { + "name": "scheduled_at", + "displayName": "Scheduled", + "shape": "date" + }, + { + "name": "sent_at", + "displayName": "Sent", + "shape": "date", + "role": "timestamp" + }, + { + "sourceId": "id", + "sourceType": "Broadcast", + "name": "name" + }, + { + "name": "audience_id", + "displayName": "Audience ID", + "shape": "string", + "visible": false + }, + { + "pattern": ".*" + } + ], + "timeframes": false +} diff --git a/plugins/Resend/v1/dataStreams/contactImports.json b/plugins/Resend/v1/dataStreams/contactImports.json new file mode 100644 index 00000000..a7c44bb8 --- /dev/null +++ b/plugins/Resend/v1/dataStreams/contactImports.json @@ -0,0 +1,132 @@ +{ + "name": "contactImports", + "displayName": "Contact Imports", + "description": "Bulk contact import jobs with their status and row counts", + "tags": [ + "Contacts" + ], + "baseDataSourceName": "httpRequestUnscoped", + "config": { + "httpMethod": "get", + "endpointPath": "contacts/imports", + "getArgs": [ + { + "key": "status", + "value": "{{ status || null }}" + } + ], + "paging": { + "mode": "token", + "pageSize": { + "realm": "queryArg", + "path": "limit", + "value": "100" + }, + "in": { + "realm": "payload", + "path": "next_cursor" + }, + "out": { + "realm": "queryArg", + "path": "after" + } + }, + "postRequestScript": "list.js", + "expandInnerObjects": true + }, + "matches": "none", + "ui": [ + { + "type": "choiceChips", + "name": "status", + "label": "Status", + "options": [ + { + "value": "queued", + "label": "Queued" + }, + { + "value": "in_progress", + "label": "In progress" + }, + { + "value": "completed", + "label": "Completed" + }, + { + "value": "failed", + "label": "Failed" + } + ] + } + ], + "metadata": [ + { + "name": "id", + "displayName": "ID", + "shape": "string", + "role": "label" + }, + { + "name": "status", + "displayName": "Status", + "shape": [ + "state", + { + "map": { + "success": [ + "completed" + ], + "error": [ + "failed" + ], + "unknown": [ + "queued", + "in_progress" + ] + } + } + ] + }, + { + "name": "created_at", + "displayName": "Created", + "shape": "date", + "role": "timestamp" + }, + { + "name": "completed_at", + "displayName": "Completed", + "shape": "date" + }, + { + "name": "counts.total", + "displayName": "Total Rows", + "shape": "number" + }, + { + "name": "counts.created", + "displayName": "Contacts Created", + "shape": "number" + }, + { + "name": "counts.updated", + "displayName": "Contacts Updated", + "shape": "number" + }, + { + "name": "counts.skipped", + "displayName": "Rows Skipped", + "shape": "number" + }, + { + "name": "counts.failed", + "displayName": "Rows Failed", + "shape": "number" + }, + { + "pattern": ".*" + } + ], + "timeframes": false +} diff --git a/plugins/Resend/v1/dataStreams/contacts.json b/plugins/Resend/v1/dataStreams/contacts.json new file mode 100644 index 00000000..e3081653 --- /dev/null +++ b/plugins/Resend/v1/dataStreams/contacts.json @@ -0,0 +1,93 @@ +{ + "name": "contacts", + "displayName": "Contacts", + "description": "Contacts on the Resend team, optionally limited to one segment", + "tags": [ + "Email", + "Contacts" + ], + "baseDataSourceName": "httpRequestUnscoped", + "config": { + "httpMethod": "get", + "endpointPath": "contacts", + "getArgs": [ + { + "key": "segment_id", + "value": "{{ segment && segment.length ? segment[0].rawId : null }}" + } + ], + "pathToData": "data" + }, + "matches": "none", + "ui": [ + { + "type": "objects", + "name": "segment", + "label": "Segment", + "matches": { + "sourceType": { + "type": "oneOf", + "values": [ + "Segment" + ] + } + } + } + ], + "metadata": [ + { + "name": "id", + "displayName": "ID", + "shape": "string", + "visible": false + }, + { + "name": "email", + "displayName": "Email", + "shape": "string" + }, + { + "name": "first_name", + "displayName": "First Name", + "shape": "string", + "visible": false + }, + { + "name": "last_name", + "displayName": "Last Name", + "shape": "string", + "visible": false + }, + { + "name": "name", + "displayName": "Name", + "computed": true, + "valueExpression": "{{ [$['first_name'], $['last_name']].filter((v) => typeof v === 'string' && v.trim()).join(' ') || $['email'] }}", + "shape": "string", + "role": "label" + }, + { + "name": "unsubscribed", + "displayName": "Unsubscribed", + "shape": "boolean" + }, + { + "name": "is_unsubscribed", + "displayName": "Unsubscribed (0/1)", + "computed": true, + "valueExpression": "{{ $['unsubscribed'] === true ? 1 : 0 }}", + "shape": "number", + "visible": false + }, + { + "name": "created_at", + "displayName": "Created", + "shape": "date", + "role": "timestamp" + }, + { + "pattern": ".*" + } + ], + "timeframes": false +} diff --git a/plugins/Resend/v1/dataStreams/domainRecords.json b/plugins/Resend/v1/dataStreams/domainRecords.json new file mode 100644 index 00000000..8c28deda --- /dev/null +++ b/plugins/Resend/v1/dataStreams/domainRecords.json @@ -0,0 +1,84 @@ +{ + "name": "domainRecords", + "displayName": "Domain Records", + "description": "DNS records needed to verify and configure a sending domain, one row per record", + "tags": [ + "Email", + "Domains" + ], + "baseDataSourceName": "httpRequestScopedSingle", + "config": { + "httpMethod": "get", + "endpointPath": "domains/{{object.rawId}}", + "pathToData": "records" + }, + "matches": { + "sourceType": { + "type": "oneOf", + "values": [ + "Domain" + ] + } + }, + "metadata": [ + { + "name": "record", + "displayName": "Record", + "shape": "string", + "role": "label" + }, + { + "name": "name", + "displayName": "Host", + "shape": "string" + }, + { + "name": "type", + "displayName": "Type", + "shape": "string" + }, + { + "name": "value", + "displayName": "Value", + "shape": "string" + }, + { + "name": "ttl", + "displayName": "TTL", + "shape": "string" + }, + { + "name": "priority", + "displayName": "Priority", + "shape": "number" + }, + { + "name": "status", + "displayName": "Status", + "shape": [ + "state", + { + "map": { + "success": [ + "verified" + ], + "warning": [ + "pending", + "temporary_failure" + ], + "error": [ + "failed" + ], + "unknown": [ + "not_started" + ] + } + } + ] + }, + { + "pattern": ".*" + } + ], + "timeframes": false +} diff --git a/plugins/Resend/v1/dataStreams/domains.json b/plugins/Resend/v1/dataStreams/domains.json new file mode 100644 index 00000000..68732355 --- /dev/null +++ b/plugins/Resend/v1/dataStreams/domains.json @@ -0,0 +1,97 @@ +{ + "name": "domains", + "displayName": "Domains", + "description": "Sending and receiving domains on the Resend team, with verification status and tracking settings", + "tags": [ + "Email", + "Domains" + ], + "baseDataSourceName": "httpRequestUnscoped", + "config": { + "httpMethod": "get", + "endpointPath": "domains", + "pathToData": "data", + "expandInnerObjects": true + }, + "matches": "none", + "metadata": [ + { + "name": "id", + "displayName": "ID", + "shape": "string", + "visible": false + }, + { + "name": "name", + "displayName": "Domain", + "shape": "string", + "role": "label" + }, + { + "name": "status", + "displayName": "Status", + "shape": [ + "state", + { + "map": { + "success": [ + "verified" + ], + "warning": [ + "pending", + "partially_verified", + "temporary_failure" + ], + "error": [ + "failed", + "partially_failed" + ], + "unknown": [ + "not_started" + ] + } + } + ] + }, + { + "name": "region", + "displayName": "Region", + "shape": "string" + }, + { + "name": "capabilities.sending", + "displayName": "Sending", + "shape": "string" + }, + { + "name": "capabilities.receiving", + "displayName": "Receiving", + "shape": "string" + }, + { + "name": "open_tracking", + "displayName": "Open Tracking", + "shape": "boolean" + }, + { + "name": "click_tracking", + "displayName": "Click Tracking", + "shape": "boolean" + }, + { + "name": "created_at", + "displayName": "Created", + "shape": "date", + "role": "timestamp" + }, + { + "sourceId": "id", + "sourceType": "Domain", + "name": "name" + }, + { + "pattern": ".*" + } + ], + "timeframes": false +} diff --git a/plugins/Resend/v1/dataStreams/emailMetrics.json b/plugins/Resend/v1/dataStreams/emailMetrics.json new file mode 100644 index 00000000..d5ddb497 --- /dev/null +++ b/plugins/Resend/v1/dataStreams/emailMetrics.json @@ -0,0 +1,277 @@ +{ + "name": "emailMetrics", + "displayName": "Email Metrics", + "description": "Email sending, delivery and engagement metrics, optionally by period, domain or broadcast", + "tags": [ + "Email", + "Metrics" + ], + "baseDataSourceName": "httpRequestUnscoped", + "config": { + "httpMethod": "get", + "endpointPath": "emails/metrics", + "getArgs": [ + { + "key": "start_date", + "value": "{{timeframe.start}}" + }, + { + "key": "end_date", + "value": "{{timeframe.end}}" + }, + { + "key": "granularity", + "value": "{{ (typeof granularity !== 'undefined' && granularity && granularity !== 'total') ? granularity : null }}" + }, + { + "key": "dimensions", + "value": "{{ [(typeof granularity === 'undefined' || !granularity || granularity !== 'total') ? 'period' : null, (typeof breakdown !== 'undefined' && breakdown) ? breakdown : null].filter(Boolean).join(',') || null }}" + }, + { + "key": "domain_id", + "value": "{{ (typeof domain !== 'undefined' && domain && domain.length) ? domain.flatMap((o) => o.rawId).join(',') : null }}" + }, + { + "key": "broadcast_id", + "value": "{{ (typeof broadcast !== 'undefined' && broadcast && broadcast.length) ? broadcast.flatMap((o) => o.rawId).join(',') : null }}" + } + ], + "postRequestScript": "emailMetrics.js" + }, + "matches": "none", + "ui": [ + { + "type": "objects", + "name": "domain", + "label": "Domain(s)", + "matches": { + "sourceType": { + "type": "oneOf", + "values": [ + "Domain" + ] + } + } + }, + { + "type": "objects", + "name": "broadcast", + "label": "Broadcast(s)", + "matches": { + "sourceType": { + "type": "oneOf", + "values": [ + "Broadcast" + ] + } + } + }, + { + "type": "choiceChips", + "name": "granularity", + "label": "Granularity", + "defaultValue": "daily", + "options": [ + { + "value": "hourly", + "label": "Hourly" + }, + { + "value": "daily", + "label": "Daily" + }, + { + "value": "weekly", + "label": "Weekly" + }, + { + "value": "monthly", + "label": "Monthly" + }, + { + "value": "total", + "label": "Total" + } + ] + }, + { + "type": "choiceChips", + "name": "breakdown", + "label": "Break down by", + "options": [ + { + "value": "domain", + "label": "Domain" + }, + { + "value": "broadcast", + "label": "Broadcast" + } + ] + } + ], + "metadata": [ + { + "name": "period", + "displayName": "Period", + "shape": "date", + "role": "timestamp" + }, + { + "name": "domain_id", + "displayName": "Domain ID", + "shape": "string", + "visible": false + }, + { + "name": "domain_name", + "displayName": "Domain", + "shape": "string", + "role": "none" + }, + { + "sourceId": "domain_id", + "sourceType": "Domain", + "name": "domain_name" + }, + { + "name": "broadcast_id", + "displayName": "Broadcast ID", + "shape": "string", + "visible": false + }, + { + "name": "broadcast_name", + "displayName": "Broadcast", + "shape": "string", + "role": "none" + }, + { + "sourceId": "broadcast_id", + "sourceType": "Broadcast", + "name": "broadcast_name" + }, + { + "name": "sent", + "displayName": "Sent", + "shape": "number" + }, + { + "name": "received", + "displayName": "Received", + "shape": "number" + }, + { + "name": "delivered", + "displayName": "Delivered", + "shape": "number" + }, + { + "name": "complained", + "displayName": "Complained", + "shape": "number" + }, + { + "name": "suppressed", + "displayName": "Suppressed", + "shape": "number" + }, + { + "name": "bounced", + "displayName": "Bounced", + "shape": "number" + }, + { + "name": "bounced_transient", + "displayName": "Bounced (Transient)", + "shape": "number" + }, + { + "name": "bounced_permanent", + "displayName": "Bounced (Permanent)", + "shape": "number" + }, + { + "name": "bounced_undetermined", + "displayName": "Bounced (Undetermined)", + "shape": "number" + }, + { + "name": "opened", + "displayName": "Opened", + "shape": "number" + }, + { + "name": "unique_opened", + "displayName": "Unique Opened", + "shape": "number" + }, + { + "name": "clicked", + "displayName": "Clicked", + "shape": "number" + }, + { + "name": "unique_clicked", + "displayName": "Unique Clicked", + "shape": "number" + }, + { + "name": "unsubscribed", + "displayName": "Unsubscribed", + "shape": "number" + }, + { + "name": "delivery_delayed", + "displayName": "Delivery Delayed", + "shape": "number" + }, + { + "name": "failed", + "displayName": "Failed", + "shape": "number" + }, + { + "name": "delivery_rate", + "displayName": "Delivery Rate", + "shape": "percent" + }, + { + "name": "open_rate", + "displayName": "Open Rate", + "shape": "percent" + }, + { + "name": "click_rate", + "displayName": "Click Rate", + "shape": "percent" + }, + { + "name": "bounce_rate", + "displayName": "Bounce Rate", + "shape": "percent" + }, + { + "name": "complaint_rate", + "displayName": "Complaint Rate", + "shape": "percent" + }, + { + "name": "unsubscribe_rate", + "displayName": "Unsubscribe Rate", + "shape": "percent" + }, + { + "pattern": ".*" + } + ], + "timeframes": [ + "last1hour", + "last12hours", + "last24hours", + "last7days", + "last30days", + "thisMonth", + "lastMonth" + ] +} diff --git a/plugins/Resend/v1/dataStreams/inboxDetails.json b/plugins/Resend/v1/dataStreams/inboxDetails.json new file mode 100644 index 00000000..3c1e57c1 --- /dev/null +++ b/plugins/Resend/v1/dataStreams/inboxDetails.json @@ -0,0 +1,101 @@ +{ + "name": "inboxDetails", + "displayName": "Inbox Details", + "description": "An inbox's unread thread and draft counts, when it last received mail, and where it forwards to", + "tags": [ + "Email", + "Inboxes" + ], + "baseDataSourceName": "httpRequestScopedSingle", + "config": { + "httpMethod": "get", + "endpointPath": "inboxes/{{object.rawId}}" + }, + "matches": { + "sourceType": { + "type": "oneOf", + "values": [ + "Inbox" + ] + } + }, + "metadata": [ + { + "name": "id", + "displayName": "ID", + "shape": "string", + "visible": false + }, + { + "name": "email_address", + "displayName": "Address", + "shape": "string", + "role": "label" + }, + { + "name": "from_name", + "displayName": "From Name", + "shape": "string" + }, + { + "name": "domain_id", + "displayName": "Domain ID", + "shape": "string", + "visible": false + }, + { + "name": "domain", + "displayName": "Domain", + "computed": true, + "valueExpression": "{{ typeof $['email_address'] === 'string' ? $['email_address'].split('@').pop().toLowerCase() : null }}", + "shape": "string" + }, + { + "name": "unread", + "displayName": "Unread Threads", + "shape": "number", + "role": "value" + }, + { + "name": "drafts", + "displayName": "Drafts", + "shape": "number" + }, + { + "name": "last_received", + "displayName": "Last Received", + "shape": "date", + "role": "timestamp" + }, + { + "name": "receiving_address", + "displayName": "Forwards To", + "shape": "string" + }, + { + "name": "created_at", + "displayName": "Created", + "shape": "date" + }, + { + "name": "updated_at", + "displayName": "Updated", + "shape": "date" + }, + { + "sourceId": "domain_id", + "sourceType": "Domain", + "name": "domain" + }, + { + "name": "object", + "displayName": "Object", + "shape": "string", + "visible": false + }, + { + "pattern": ".*" + } + ], + "timeframes": false +} diff --git a/plugins/Resend/v1/dataStreams/inboxes.json b/plugins/Resend/v1/dataStreams/inboxes.json new file mode 100644 index 00000000..e69d67b1 --- /dev/null +++ b/plugins/Resend/v1/dataStreams/inboxes.json @@ -0,0 +1,98 @@ +{ + "name": "inboxes", + "displayName": "Inboxes", + "description": "Resend inboxes, with unread thread count and when mail was last received", + "tags": [ + "Email", + "Inboxes" + ], + "baseDataSourceName": "httpRequestUnscoped", + "config": { + "httpMethod": "get", + "endpointPath": "inboxes", + "paging": { + "mode": "token", + "pageSize": { + "realm": "queryArg", + "path": "limit", + "value": "100" + }, + "in": { + "realm": "payload", + "path": "next_cursor" + }, + "out": { + "realm": "queryArg", + "path": "after" + } + }, + "postRequestScript": "list.js" + }, + "matches": "none", + "metadata": [ + { + "name": "id", + "displayName": "ID", + "shape": "string", + "visible": false + }, + { + "name": "name", + "displayName": "Name", + "shape": "string", + "visible": false + }, + { + "name": "email_address", + "displayName": "Address", + "shape": "string" + }, + { + "name": "domain", + "displayName": "Domain", + "computed": true, + "valueExpression": "{{ typeof $['email_address'] === 'string' ? $['email_address'].split('@').pop().toLowerCase() : null }}", + "shape": "string" + }, + { + "name": "inbox_name", + "displayName": "Inbox", + "computed": true, + "valueExpression": "{{ typeof $['name'] === 'string' && $['name'].trim() ? $['name'] : $['email_address'] }}", + "shape": "string", + "role": "label" + }, + { + "name": "from_name", + "displayName": "From Name", + "shape": "string" + }, + { + "name": "unread", + "displayName": "Unread Threads", + "shape": "number", + "role": "value" + }, + { + "name": "last_received", + "displayName": "Last Received", + "shape": "date", + "role": "timestamp" + }, + { + "name": "created_at", + "displayName": "Created", + "shape": "date" + }, + { + "sourceId": "id", + "sourceType": "Inbox", + "name": "inbox_name", + "valueExpression": "{{ typeof $['name'] === 'string' && $['name'].trim() ? $['name'] : $['email_address'] }}" + }, + { + "pattern": ".*" + } + ], + "timeframes": false +} diff --git a/plugins/Resend/v1/dataStreams/receivedEmails.json b/plugins/Resend/v1/dataStreams/receivedEmails.json new file mode 100644 index 00000000..cc2a77a3 --- /dev/null +++ b/plugins/Resend/v1/dataStreams/receivedEmails.json @@ -0,0 +1,95 @@ +{ + "name": "receivedEmails", + "displayName": "Received Emails", + "description": "Inbound emails received on receiving-enabled domains", + "tags": [ + "Email" + ], + "baseDataSourceName": "httpRequestUnscoped", + "config": { + "httpMethod": "get", + "endpointPath": "emails/receiving", + "paging": { + "mode": "token", + "pageSize": { + "realm": "queryArg", + "path": "limit", + "value": "100" + }, + "in": { + "realm": "payload", + "path": "next_cursor" + }, + "out": { + "realm": "queryArg", + "path": "after" + } + }, + "postRequestScript": "emails.js" + }, + "matches": "none", + "ui": [ + { + "type": "objects", + "name": "domain", + "label": "Domain(s)", + "matches": { + "sourceType": { + "type": "oneOf", + "values": [ + "Domain" + ] + } + } + } + ], + "metadata": [ + { + "name": "id", + "displayName": "ID", + "shape": "string", + "visible": false + }, + { + "name": "subject", + "displayName": "Subject", + "shape": "string", + "role": "label" + }, + { + "name": "from", + "displayName": "From", + "shape": "string" + }, + { + "name": "to", + "displayName": "To", + "shape": [ + "array", + { + "itemShape": "string" + } + ] + }, + { + "name": "attachment_count", + "displayName": "Attachments", + "shape": "number" + }, + { + "name": "created_at", + "displayName": "Received", + "shape": "date", + "role": "timestamp" + }, + { + "pattern": ".*" + } + ], + "timeframes": [ + "last1hour", + "last12hours", + "last24hours", + "last7days" + ] +} diff --git a/plugins/Resend/v1/dataStreams/scripts/broadcastRecipients.js b/plugins/Resend/v1/dataStreams/scripts/broadcastRecipients.js new file mode 100644 index 00000000..c8c746ba --- /dev/null +++ b/plugins/Resend/v1/dataStreams/scripts/broadcastRecipients.js @@ -0,0 +1,10 @@ +// Broadcast recipients page by cursor like every Resend list (see list.js). Clicked +// recipients carry clicked_links as [{ url, clicks }]; flatten those to plain URL +// strings so the column can be a real list. +const rows = (data && data.data) || []; + +result = rows.map((row) => ({ + ...row, + clicked_links: Array.isArray(row.clicked_links) ? row.clicked_links.map((l) => l.url) : row.clicked_links +})); +pagingContext = data && data.has_more && rows.length ? rows[rows.length - 1].id : undefined; diff --git a/plugins/Resend/v1/dataStreams/scripts/broadcasts.js b/plugins/Resend/v1/dataStreams/scripts/broadcasts.js new file mode 100644 index 00000000..3329ee81 --- /dev/null +++ b/plugins/Resend/v1/dataStreams/scripts/broadcasts.js @@ -0,0 +1,6 @@ +// Resend can't filter broadcasts by segment, so an optional segment selection is applied here. +const unwrap = (v) => (Array.isArray(v) ? v[0] : v); +const segmentIds = new Set(((context.config && context.config.segment) || []).map((o) => unwrap(o.rawId)).filter(Boolean)); +const rows = (data && data.data) || []; + +result = segmentIds.size ? rows.filter((row) => segmentIds.has(row.segment_id)) : rows; diff --git a/plugins/Resend/v1/dataStreams/scripts/emailMetrics.js b/plugins/Resend/v1/dataStreams/scripts/emailMetrics.js new file mode 100644 index 00000000..9fbd7885 --- /dev/null +++ b/plugins/Resend/v1/dataStreams/scripts/emailMetrics.js @@ -0,0 +1 @@ +result = data.data || [data.totals]; diff --git a/plugins/Resend/v1/dataStreams/scripts/emails.js b/plugins/Resend/v1/dataStreams/scripts/emails.js new file mode 100644 index 00000000..b10d10ba --- /dev/null +++ b/plugins/Resend/v1/dataStreams/scripts/emails.js @@ -0,0 +1,27 @@ +// Newest-first Resend email lists with no date or domain filter: keep rows inside the +// timeframe, stop paging once a page reaches rows older than its start, and optionally +// keep only emails sent from or to a selected domain. +const rows = (data && data.data) || []; +const start = new Date(context.timeframe.start).getTime(); +const end = new Date(context.timeframe.end).getTime(); +const createdAt = (row) => new Date(row.created_at).getTime(); + +const unwrap = (v) => (Array.isArray(v) ? v[0] : v); +const wanted = new Set( + ((context.config && context.config.domain) || []).map((o) => String(unwrap(o.name) || '').toLowerCase()).filter(Boolean) +); +// Addresses look like "Name " or "user@example.com" +const domainOf = (address) => String(address || '').split('@').pop().replace('>', '').trim().toLowerCase(); +const matchesDomain = (row) => !wanted.size || [row.from].concat(row.to || []).some((a) => wanted.has(domainOf(a))); + +result = rows + .filter((row) => createdAt(row) >= start && createdAt(row) <= end && matchesDomain(row)) + // Only received emails list their attachments + .map((row) => (Array.isArray(row.attachments) ? { ...row, attachment_count: row.attachments.length } : row)); + +// Every timeframe offered ends now, so time since its end is time spent paging. Stop +// early on busy accounts, returning the newest emails rather than timing out. +const pagingBudgetMs = 20000; +const last = rows[rows.length - 1]; +const morePages = data && data.has_more && last && createdAt(last) >= start; +pagingContext = morePages && Date.now() - end < pagingBudgetMs ? last.id : undefined; diff --git a/plugins/Resend/v1/dataStreams/scripts/list.js b/plugins/Resend/v1/dataStreams/scripts/list.js new file mode 100644 index 00000000..ca2283cf --- /dev/null +++ b/plugins/Resend/v1/dataStreams/scripts/list.js @@ -0,0 +1,6 @@ +// Resend lists return { has_more, data } and page by cursor: the next page starts +// after the last row's id. That id isn't at a fixed payload path, so it's set here. +const rows = (data && data.data) || []; + +result = rows; +pagingContext = data && data.has_more && rows.length ? rows[rows.length - 1].id : undefined; diff --git a/plugins/Resend/v1/dataStreams/scripts/listInTimeframe.js b/plugins/Resend/v1/dataStreams/scripts/listInTimeframe.js new file mode 100644 index 00000000..91d03c20 --- /dev/null +++ b/plugins/Resend/v1/dataStreams/scripts/listInTimeframe.js @@ -0,0 +1,15 @@ +// For newest-first Resend lists that have no date filter: keep the rows inside the +// timeframe, and stop paging once a page reaches rows older than its start. +const rows = (data && data.data) || []; +const start = new Date(context.timeframe.start).getTime(); +const end = new Date(context.timeframe.end).getTime(); +const createdAt = (row) => new Date(row.created_at).getTime(); + +result = rows.filter((row) => createdAt(row) >= start && createdAt(row) <= end); + +// Every timeframe offered ends now, so time since its end is time spent paging. Stop +// early on busy accounts, returning the newest rows rather than timing out. +const pagingBudgetMs = 20000; +const last = rows[rows.length - 1]; +const morePages = data && data.has_more && last && createdAt(last) >= start; +pagingContext = morePages && Date.now() - end < pagingBudgetMs ? last.id : undefined; diff --git a/plugins/Resend/v1/dataStreams/segments.json b/plugins/Resend/v1/dataStreams/segments.json new file mode 100644 index 00000000..d294bf2e --- /dev/null +++ b/plugins/Resend/v1/dataStreams/segments.json @@ -0,0 +1,45 @@ +{ + "name": "segments", + "displayName": "Segments", + "description": "Contact segments on the Resend team", + "tags": [ + "Email", + "Contacts" + ], + "baseDataSourceName": "httpRequestUnscoped", + "config": { + "httpMethod": "get", + "endpointPath": "segments", + "pathToData": "data" + }, + "matches": "none", + "metadata": [ + { + "name": "id", + "displayName": "ID", + "shape": "string", + "visible": false + }, + { + "name": "name", + "displayName": "Segment", + "shape": "string", + "role": "label" + }, + { + "name": "created_at", + "displayName": "Created", + "shape": "date", + "role": "timestamp" + }, + { + "sourceId": "id", + "sourceType": "Segment", + "name": "name" + }, + { + "pattern": ".*" + } + ], + "timeframes": false +} diff --git a/plugins/Resend/v1/dataStreams/sentEmails.json b/plugins/Resend/v1/dataStreams/sentEmails.json new file mode 100644 index 00000000..e07fb273 --- /dev/null +++ b/plugins/Resend/v1/dataStreams/sentEmails.json @@ -0,0 +1,127 @@ +{ + "name": "sentEmails", + "displayName": "Sent Emails", + "description": "Emails sent from the team, with latest delivery status", + "tags": [ + "Email", + "Deliverability" + ], + "baseDataSourceName": "httpRequestUnscoped", + "config": { + "httpMethod": "get", + "endpointPath": "emails", + "paging": { + "mode": "token", + "pageSize": { + "realm": "queryArg", + "path": "limit", + "value": "100" + }, + "in": { + "realm": "payload", + "path": "next_cursor" + }, + "out": { + "realm": "queryArg", + "path": "after" + } + }, + "postRequestScript": "emails.js" + }, + "matches": "none", + "ui": [ + { + "type": "objects", + "name": "domain", + "label": "Domain(s)", + "matches": { + "sourceType": { + "type": "oneOf", + "values": [ + "Domain" + ] + } + } + } + ], + "metadata": [ + { + "name": "id", + "displayName": "ID", + "shape": "string", + "visible": false + }, + { + "name": "subject", + "displayName": "Subject", + "shape": "string", + "role": "label" + }, + { + "name": "from", + "displayName": "From", + "shape": "string" + }, + { + "name": "to", + "displayName": "To", + "shape": [ + "array", + { + "itemShape": "string" + } + ] + }, + { + "name": "last_event", + "displayName": "Status", + "shape": [ + "state", + { + "map": { + "success": [ + "delivered", + "opened", + "clicked" + ], + "warning": [ + "delivery_delayed", + "canceled" + ], + "error": [ + "bounced", + "complained", + "failed", + "suppressed" + ], + "unknown": [ + "scheduled", + "queued", + "sent" + ] + } + } + ] + }, + { + "name": "created_at", + "displayName": "Sent", + "shape": "date", + "role": "timestamp" + }, + { + "name": "scheduled_at", + "displayName": "Scheduled", + "shape": "date" + }, + { + "pattern": ".*" + } + ], + "timeframes": [ + "last1hour", + "last12hours", + "last24hours", + "last7days" + ] +} diff --git a/plugins/Resend/v1/dataStreams/suppressions.json b/plugins/Resend/v1/dataStreams/suppressions.json new file mode 100644 index 00000000..864013e4 --- /dev/null +++ b/plugins/Resend/v1/dataStreams/suppressions.json @@ -0,0 +1,107 @@ +{ + "name": "suppressions", + "displayName": "Suppressions", + "description": "Addresses suppressed from receiving mail after a bounce, complaint or manual addition", + "tags": [ + "Email", + "Deliverability" + ], + "baseDataSourceName": "httpRequestUnscoped", + "config": { + "httpMethod": "get", + "endpointPath": "suppressions", + "getArgs": [ + { + "key": "origin", + "value": "{{ origin || null }}" + } + ], + "paging": { + "mode": "token", + "pageSize": { + "realm": "queryArg", + "path": "limit", + "value": "100" + }, + "in": { + "realm": "payload", + "path": "next_cursor" + }, + "out": { + "realm": "queryArg", + "path": "after" + } + }, + "postRequestScript": "list.js" + }, + "matches": "none", + "ui": [ + { + "type": "choiceChips", + "name": "origin", + "label": "Origin", + "options": [ + { + "value": "bounce", + "label": "Bounce" + }, + { + "value": "complaint", + "label": "Complaint" + }, + { + "value": "manual", + "label": "Manual" + } + ] + } + ], + "metadata": [ + { + "name": "id", + "displayName": "ID", + "shape": "string", + "visible": false + }, + { + "name": "email", + "displayName": "Email", + "shape": "string", + "role": "label" + }, + { + "name": "origin", + "displayName": "Origin", + "shape": [ + "state", + { + "map": { + "warning": [ + "bounce", + "complaint" + ], + "unknown": [ + "manual" + ] + } + } + ] + }, + { + "name": "source_id", + "displayName": "Source Email ID", + "shape": "string", + "visible": false + }, + { + "name": "created_at", + "displayName": "Created", + "shape": "date", + "role": "timestamp" + }, + { + "pattern": ".*" + } + ], + "timeframes": false +} diff --git a/plugins/Resend/v1/dataStreams/templates.json b/plugins/Resend/v1/dataStreams/templates.json new file mode 100644 index 00000000..db8a68c9 --- /dev/null +++ b/plugins/Resend/v1/dataStreams/templates.json @@ -0,0 +1,88 @@ +{ + "name": "templates", + "displayName": "Templates", + "description": "Email templates on the Resend team, with publish status", + "tags": [ + "Email", + "Templates" + ], + "baseDataSourceName": "httpRequestUnscoped", + "config": { + "httpMethod": "get", + "endpointPath": "templates", + "paging": { + "mode": "token", + "pageSize": { + "realm": "queryArg", + "path": "limit", + "value": "100" + }, + "in": { + "realm": "payload", + "path": "next_cursor" + }, + "out": { + "realm": "queryArg", + "path": "after" + } + }, + "postRequestScript": "list.js" + }, + "matches": "none", + "metadata": [ + { + "name": "id", + "displayName": "ID", + "shape": "string", + "visible": false + }, + { + "name": "name", + "displayName": "Template", + "shape": "string", + "role": "label" + }, + { + "name": "alias", + "displayName": "Alias", + "shape": "string" + }, + { + "name": "status", + "displayName": "Status", + "shape": [ + "state", + { + "map": { + "success": [ + "published" + ], + "unknown": [ + "draft" + ] + } + } + ] + }, + { + "name": "published_at", + "displayName": "Published", + "shape": "date" + }, + { + "name": "created_at", + "displayName": "Created", + "shape": "date" + }, + { + "name": "updated_at", + "displayName": "Updated", + "shape": "date", + "role": "timestamp" + }, + { + "pattern": ".*" + } + ], + "timeframes": false +} diff --git a/plugins/Resend/v1/dataStreams/topics.json b/plugins/Resend/v1/dataStreams/topics.json new file mode 100644 index 00000000..aea77531 --- /dev/null +++ b/plugins/Resend/v1/dataStreams/topics.json @@ -0,0 +1,71 @@ +{ + "name": "topics", + "displayName": "Topics", + "description": "Subscription topics contacts can opt in to or out of", + "tags": [ + "Email", + "Contacts" + ], + "baseDataSourceName": "httpRequestUnscoped", + "config": { + "httpMethod": "get", + "endpointPath": "topics", + "paging": { + "mode": "token", + "pageSize": { + "realm": "queryArg", + "path": "limit", + "value": "100" + }, + "in": { + "realm": "payload", + "path": "next_cursor" + }, + "out": { + "realm": "queryArg", + "path": "after" + } + }, + "postRequestScript": "list.js" + }, + "matches": "none", + "metadata": [ + { + "name": "id", + "displayName": "ID", + "shape": "string", + "visible": false + }, + { + "name": "name", + "displayName": "Topic", + "shape": "string", + "role": "label" + }, + { + "name": "description", + "displayName": "Description", + "shape": "string" + }, + { + "name": "default_subscription", + "displayName": "Default Subscription", + "shape": "string" + }, + { + "name": "visibility", + "displayName": "Visibility", + "shape": "string" + }, + { + "name": "created_at", + "displayName": "Created", + "shape": "date", + "role": "timestamp" + }, + { + "pattern": ".*" + } + ], + "timeframes": false +} diff --git a/plugins/Resend/v1/dataStreams/webhookEvents.json b/plugins/Resend/v1/dataStreams/webhookEvents.json new file mode 100644 index 00000000..99600897 --- /dev/null +++ b/plugins/Resend/v1/dataStreams/webhookEvents.json @@ -0,0 +1,91 @@ +{ + "name": "webhookEvents", + "displayName": "Webhook Events", + "description": "Events delivered to a webhook, with delivery status", + "tags": [ + "Email", + "Webhooks" + ], + "baseDataSourceName": "httpRequestScopedSingle", + "config": { + "httpMethod": "get", + "endpointPath": "webhooks/{{object.rawId}}/events", + "paging": { + "mode": "token", + "pageSize": { + "realm": "queryArg", + "path": "limit", + "value": "100" + }, + "in": { + "realm": "payload", + "path": "next_cursor" + }, + "out": { + "realm": "queryArg", + "path": "after" + } + }, + "postRequestScript": "listInTimeframe.js" + }, + "matches": { + "sourceType": { + "type": "oneOf", + "values": [ + "Webhook" + ] + } + }, + "metadata": [ + { + "name": "id", + "displayName": "ID", + "shape": "string", + "visible": false + }, + { + "name": "type", + "displayName": "Event", + "shape": "string", + "role": "label" + }, + { + "name": "status", + "displayName": "Delivery", + "shape": [ + "state", + { + "map": { + "success": [ + "success" + ], + "error": [ + "failed" + ], + "warning": [ + "attempting" + ], + "unknown": [ + "pending" + ] + } + } + ] + }, + { + "name": "created_at", + "displayName": "Created", + "shape": "date", + "role": "timestamp" + }, + { + "pattern": ".*" + } + ], + "timeframes": [ + "last1hour", + "last12hours", + "last24hours", + "last7days" + ] +} diff --git a/plugins/Resend/v1/dataStreams/webhooks.json b/plugins/Resend/v1/dataStreams/webhooks.json new file mode 100644 index 00000000..989ab327 --- /dev/null +++ b/plugins/Resend/v1/dataStreams/webhooks.json @@ -0,0 +1,72 @@ +{ + "name": "webhooks", + "displayName": "Webhooks", + "description": "Webhook endpoints on the Resend team, with status and subscribed event types", + "tags": [ + "Email", + "Webhooks" + ], + "baseDataSourceName": "httpRequestUnscoped", + "config": { + "httpMethod": "get", + "endpointPath": "webhooks", + "pathToData": "data" + }, + "matches": "none", + "metadata": [ + { + "name": "id", + "displayName": "ID", + "shape": "string", + "visible": false + }, + { + "name": "endpoint", + "displayName": "Endpoint", + "shape": "string", + "role": "label" + }, + { + "name": "status", + "displayName": "Status", + "shape": [ + "state", + { + "map": { + "success": [ + "enabled" + ], + "warning": [ + "disabled" + ] + } + } + ] + }, + { + "name": "events", + "displayName": "Events", + "shape": [ + "array", + { + "itemShape": "string" + } + ] + }, + { + "name": "created_at", + "displayName": "Created", + "shape": "date", + "role": "timestamp" + }, + { + "sourceId": "id", + "sourceType": "Webhook", + "name": "endpoint" + }, + { + "pattern": ".*" + } + ], + "timeframes": false +} diff --git a/plugins/Resend/v1/defaultContent/automation.dash.json b/plugins/Resend/v1/defaultContent/automation.dash.json new file mode 100644 index 00000000..7b7d8476 --- /dev/null +++ b/plugins/Resend/v1/defaultContent/automation.dash.json @@ -0,0 +1,418 @@ +{ + "name": "Automation", + "schemaVersion": "1.5", + "timeframe": "none", + "variables": [ + "{{variables.[Resend Automation]}}" + ], + "dashboard": { + "_type": "layout/grid", + "columns": 4, + "version": 1, + "contents": [ + { + "i": "153afe3f-2b4b-4206-877d-21f1e93c369a", + "x": 0, + "y": 0, + "w": 1, + "h": 3, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Automation Details", + "description": "Status and history of this automation", + "activePluginConfigIds": [ + "{{configId}}" + ], + "timeframe": "none", + "dataStream": { + "id": "datastream-properties", + "name": "properties", + "pluginConfigId": "{{configId}}" + }, + "scope": { + "scope": "{{scopes.[Resend Automations]}}", + "workspace": "{{workspaceId}}", + "variable": "{{variables.[Resend Automation]}}" + }, + "variables": [ + "{{variables.[Resend Automation]}}" + ], + "vizSpec": { + "version": "1.0", + "visualization": { + "chartType": "richTable", + "encoding": { + "columns": [ + { + "field": "status", + "title": "Status", + "renderer": { + "type": "status" + } + }, + { + "field": "created_at", + "title": "Created", + "renderer": { + "type": "date", + "relative": true + } + }, + { + "field": "updated_at", + "title": "Updated", + "renderer": { + "type": "date", + "relative": true + } + } + ] + }, + "style": { + "richTableCellPadding": 6, + "richTableTranspose": true + } + } + } + } + }, + { + "i": "9575180e-7641-4615-a2a9-f31a07d3302b", + "x": 1, + "y": 0, + "w": 1, + "h": 3, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Run Status", + "description": "Runs of this automation by status", + "activePluginConfigIds": [ + "{{configId}}" + ], + "timeframe": "none", + "dataStream": { + "id": "{{dataStreams.[automationRuns]}}", + "name": "automationRuns", + "pluginConfigId": "{{configId}}" + }, + "scope": { + "scope": "{{scopes.[Resend Automations]}}", + "workspace": "{{workspaceId}}", + "variable": "{{variables.[Resend Automation]}}" + }, + "variables": [ + "{{variables.[Resend Automation]}}" + ], + "vizSpec": { + "version": "1.0", + "visualization": { + "chartType": "pie", + "encoding": { + "label": { + "field": "status[Expanded].rawState", + "type": "nominal" + }, + "value": { + "field": "created_at", + "type": "nominal", + "aggregate": "count" + } + }, + "style": { + "pieInnerRadius": 60, + "pieDonutCenterValue": "total", + "labelFontSize": 16, + "mappedColors": { + "completed": "#22c55e", + "failed": "#ef4444", + "cancelled": "#f59e0b", + "running": "#3b82f6" + } + } + } + } + } + }, + { + "i": "8604da28-6747-4a88-babd-316caa488b0a", + "x": 2, + "y": 0, + "w": 2, + "h": 3, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Run Duration", + "description": "Average duration of this automation's runs per day", + "activePluginConfigIds": [ + "{{configId}}" + ], + "timeframe": "none", + "dataStream": { + "id": "{{dataStreams.[automationRuns]}}", + "name": "automationRuns", + "pluginConfigId": "{{configId}}", + "group": { + "by": [ + [ + "started_at", + "byDay" + ] + ], + "aggregate": [ + { + "type": "mean", + "names": [ + "duration" + ] + } + ] + }, + "sort": { + "by": [ + [ + "started_at_byDay", + "asc" + ] + ] + } + }, + "scope": { + "scope": "{{scopes.[Resend Automations]}}", + "workspace": "{{workspaceId}}", + "variable": "{{variables.[Resend Automation]}}" + }, + "variables": [ + "{{variables.[Resend Automation]}}" + ], + "vizSpec": { + "version": "1.0", + "visualization": { + "chartType": "line", + "encoding": { + "x": { + "field": "started_at_byDay", + "type": "temporal", + "title": "Day" + }, + "y": { + "field": "duration_mean", + "type": "quantitative", + "aggregate": "avg", + "title": "Seconds" + } + }, + "style": { + "cartesianAreaGradient": true, + "cartesianSmooth": true, + "cartesianLineWidth": 2, + "colors": [ + "#8b5cf6" + ] + } + } + } + } + }, + { + "i": "5877e135-a3cc-4c61-aedb-33f900a05c20", + "x": 0, + "y": 3, + "w": 2, + "h": 3, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Failed Runs", + "description": "Runs of this automation that failed, newest first", + "activePluginConfigIds": [ + "{{configId}}" + ], + "timeframe": "none", + "dataStream": { + "id": "{{dataStreams.[automationRuns]}}", + "name": "automationRuns", + "pluginConfigId": "{{configId}}", + "filter": { + "multiOperation": "and", + "filters": [ + { + "column": "status", + "operation": "equals", + "value": "error" + } + ] + }, + "sort": { + "by": [ + [ + "created_at", + "desc" + ] + ] + } + }, + "scope": { + "scope": "{{scopes.[Resend Automations]}}", + "workspace": "{{workspaceId}}", + "variable": "{{variables.[Resend Automation]}}" + }, + "variables": [ + "{{variables.[Resend Automation]}}" + ], + "vizSpec": { + "version": "1.0", + "visualization": { + "chartType": "richTable", + "encoding": { + "columns": [ + { + "field": "status[Expanded].rawState", + "title": "Status", + "renderer": { + "type": "badge", + "colors": { + "completed": "#22c55e", + "failed": "#ef4444", + "cancelled": "#f59e0b", + "running": "#3b82f6" + } + } + }, + { + "field": "started_at", + "title": "Started", + "renderer": { + "type": "date", + "relative": true + } + }, + { + "field": "completed_at", + "title": "Completed", + "renderer": { + "type": "date" + } + }, + { + "field": "duration", + "title": "Duration", + "renderer": { + "type": "number", + "decimals": 1, + "suffix": " s" + } + } + ] + }, + "style": { + "richTableCellPadding": 6 + } + } + } + } + }, + { + "i": "f569fabe-815b-462c-96b1-407d93c38c15", + "x": 2, + "y": 3, + "w": 2, + "h": 3, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Recent Runs", + "description": "Latest runs of this automation", + "activePluginConfigIds": [ + "{{configId}}" + ], + "timeframe": "none", + "dataStream": { + "id": "{{dataStreams.[automationRuns]}}", + "name": "automationRuns", + "pluginConfigId": "{{configId}}", + "sort": { + "by": [ + [ + "created_at", + "desc" + ] + ], + "top": 20 + } + }, + "scope": { + "scope": "{{scopes.[Resend Automations]}}", + "workspace": "{{workspaceId}}", + "variable": "{{variables.[Resend Automation]}}" + }, + "variables": [ + "{{variables.[Resend Automation]}}" + ], + "vizSpec": { + "version": "1.0", + "visualization": { + "chartType": "richTable", + "encoding": { + "columns": [ + { + "field": "status[Expanded].rawState", + "title": "Status", + "renderer": { + "type": "badge", + "colors": { + "completed": "#22c55e", + "failed": "#ef4444", + "cancelled": "#f59e0b", + "running": "#3b82f6" + } + } + }, + { + "field": "started_at", + "title": "Started", + "renderer": { + "type": "date", + "relative": true + } + }, + { + "field": "completed_at", + "title": "Completed", + "renderer": { + "type": "date" + } + }, + { + "field": "duration", + "title": "Duration", + "renderer": { + "type": "number", + "decimals": 1, + "suffix": " s" + } + } + ] + }, + "style": { + "richTableCellPadding": 6 + } + } + } + } + } + ] + } +} diff --git a/plugins/Resend/v1/defaultContent/broadcast.dash.json b/plugins/Resend/v1/defaultContent/broadcast.dash.json new file mode 100644 index 00000000..ee6ea501 --- /dev/null +++ b/plugins/Resend/v1/defaultContent/broadcast.dash.json @@ -0,0 +1,545 @@ +{ + "name": "Broadcast", + "schemaVersion": "1.5", + "timeframe": "last30days", + "variables": [ + "{{variables.[Resend Broadcast]}}" + ], + "dashboard": { + "_type": "layout/grid", + "columns": 4, + "version": 1, + "contents": [ + { + "i": "5fb0e68c-1877-4d45-99b5-aa3ade34106b", + "x": 0, + "y": 0, + "w": 1, + "h": 2, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Status", + "description": "Whether this broadcast has been sent", + "activePluginConfigIds": [ + "{{configId}}" + ], + "timeframe": "none", + "dataStream": { + "id": "datastream-properties", + "name": "properties", + "pluginConfigId": "{{configId}}" + }, + "scope": { + "scope": "{{scopes.[Resend Broadcasts]}}", + "workspace": "{{workspaceId}}", + "variable": "{{variables.[Resend Broadcast]}}" + }, + "variables": [ + "{{variables.[Resend Broadcast]}}" + ], + "vizSpec": { + "version": "1.0", + "visualization": { + "chartType": "statusGrid", + "encoding": { + "label": { + "field": "name", + "type": "nominal" + }, + "status": { + "field": "status", + "type": "nominal" + } + }, + "style": { + "labelFontSize": 16, + "secondaryFontSize": 12, + "statusGridColumnsPerRow": 1, + "statusGridLabelWrap": true + } + } + } + } + }, + { + "i": "1bad4c53-18d5-4bb1-bbf5-63c2ed193f91", + "x": 1, + "y": 0, + "w": 3, + "h": 2, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Performance", + "description": "Delivery and engagement for this broadcast", + "activePluginConfigIds": [ + "{{configId}}" + ], + "dataStream": { + "id": "{{dataStreams.[emailMetrics]}}", + "name": "emailMetrics", + "pluginConfigId": "{{configId}}", + "dataSourceConfig": { + "broadcast": { + "scope": "{{scopes.[Resend Broadcasts]}}", + "workspace": "{{workspaceId}}", + "variable": "{{variables.[Resend Broadcast]}}" + }, + "granularity": "total" + } + }, + "variables": [ + "{{variables.[Resend Broadcast]}}" + ], + "vizSpec": { + "version": "1.0", + "visualization": { + "chartType": "scalar", + "encoding": { + "values": [ + { + "field": "delivered", + "type": "quantitative", + "aggregate": "sum", + "title": "Delivered" + }, + { + "field": "open_rate", + "type": "quantitative", + "aggregate": "avg", + "title": "Open rate" + }, + { + "field": "click_rate", + "type": "quantitative", + "aggregate": "avg", + "title": "Click rate" + }, + { + "field": "bounce_rate", + "type": "quantitative", + "aggregate": "avg", + "title": "Bounce rate" + }, + { + "field": "unsubscribe_rate", + "type": "quantitative", + "aggregate": "avg", + "title": "Unsubscribe rate" + } + ] + }, + "style": { + "scalarColumns": 5 + } + } + } + } + }, + { + "i": "39164362-a408-4d36-8808-cc39095a557d", + "x": 0, + "y": 2, + "w": 1, + "h": 3, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Broadcast Details", + "description": "When this broadcast was created, scheduled and sent", + "activePluginConfigIds": [ + "{{configId}}" + ], + "timeframe": "none", + "dataStream": { + "id": "datastream-properties", + "name": "properties", + "pluginConfigId": "{{configId}}" + }, + "scope": { + "scope": "{{scopes.[Resend Broadcasts]}}", + "workspace": "{{workspaceId}}", + "variable": "{{variables.[Resend Broadcast]}}" + }, + "variables": [ + "{{variables.[Resend Broadcast]}}" + ], + "vizSpec": { + "version": "1.0", + "visualization": { + "chartType": "richTable", + "encoding": { + "columns": [ + { + "field": "sent_at", + "title": "Sent", + "renderer": { + "type": "date", + "relative": true + } + }, + { + "field": "scheduled_at", + "title": "Scheduled", + "renderer": { + "type": "date", + "relative": true + } + }, + { + "field": "created_at", + "title": "Created", + "renderer": { + "type": "date", + "relative": true + } + } + ] + }, + "style": { + "richTableCellPadding": 6, + "richTableTranspose": true + } + } + } + } + }, + { + "i": "6546b96c-dbf4-4705-9ef9-211e2ca0f72e", + "x": 1, + "y": 2, + "w": 1, + "h": 3, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Engagement Funnel", + "description": "How many emails from this broadcast were delivered, opened and clicked", + "activePluginConfigIds": [ + "{{configId}}" + ], + "dataStream": { + "id": "{{dataStreams.[emailMetrics]}}", + "name": "emailMetrics", + "pluginConfigId": "{{configId}}", + "dataSourceConfig": { + "broadcast": { + "scope": "{{scopes.[Resend Broadcasts]}}", + "workspace": "{{workspaceId}}", + "variable": "{{variables.[Resend Broadcast]}}" + }, + "granularity": "total" + }, + "sql": "SELECT \"stage\", \"emails\" FROM (SELECT 1 AS \"ord\", 'Sent' AS \"stage\", CAST(COALESCE(\"sent\", 0) AS DOUBLE) AS \"emails\" FROM \"dataset\" UNION ALL SELECT 2, 'Delivered', CAST(COALESCE(\"delivered\", 0) AS DOUBLE) FROM \"dataset\" UNION ALL SELECT 3, 'Opened', CAST(COALESCE(\"unique_opened\", 0) AS DOUBLE) FROM \"dataset\" UNION ALL SELECT 4, 'Clicked', CAST(COALESCE(\"unique_clicked\", 0) AS DOUBLE) FROM \"dataset\") ORDER BY \"ord\"" + }, + "variables": [ + "{{variables.[Resend Broadcast]}}" + ], + "vizSpec": { + "version": "1.0", + "visualization": { + "chartType": "funnel", + "encoding": { + "label": { + "field": "stage", + "type": "nominal" + }, + "value": { + "field": "emails", + "type": "quantitative", + "aggregate": "sum" + } + }, + "style": { + "funnelSort": "none", + "colors": [ + "#6366f1", + "#8b5cf6", + "#14b8a6", + "#22c55e" + ], + "labelFormat": "{{{{raw}}}}{{name}} · {{value}}{{{{/raw}}}}", + "funnelLabelPosition": "inside", + "labelColor": "#ffffff" + } + } + } + } + }, + { + "i": "2566c64c-5041-4003-a279-a63555682bf6", + "x": 2, + "y": 2, + "w": 2, + "h": 3, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Top Clicked Links", + "description": "Links in this broadcast with the most clicks", + "activePluginConfigIds": [ + "{{configId}}" + ], + "timeframe": "none", + "dataStream": { + "id": "{{dataStreams.[broadcastClickedLinks]}}", + "name": "broadcastClickedLinks", + "pluginConfigId": "{{configId}}", + "sort": { + "by": [ + [ + "clicks", + "desc" + ] + ], + "top": 10 + } + }, + "scope": { + "scope": "{{scopes.[Resend Broadcasts]}}", + "workspace": "{{workspaceId}}", + "variable": "{{variables.[Resend Broadcast]}}" + }, + "variables": [ + "{{variables.[Resend Broadcast]}}" + ], + "vizSpec": { + "version": "1.0", + "visualization": { + "chartType": "bar", + "encoding": { + "x": { + "field": "url", + "type": "nominal", + "title": "Link" + }, + "y": { + "field": "clicks", + "type": "quantitative", + "aggregate": "sum", + "title": "Clicks" + } + }, + "style": { + "cartesianHorizontal": true, + "cartesianBarBorderRadius": 4, + "showLabels": true, + "colors": [ + "#6366f1" + ], + "cartesianShowHorizontalGridLines": false + } + } + } + } + }, + { + "i": "2be9a06a-a7fd-4868-ba96-3ea63fbdd17c", + "x": 0, + "y": 5, + "w": 2, + "h": 3, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Clicked", + "description": "Recipients who clicked a link, and the links they clicked", + "activePluginConfigIds": [ + "{{configId}}" + ], + "timeframe": "none", + "dataStream": { + "id": "{{dataStreams.[broadcastRecipients]}}", + "name": "broadcastRecipients", + "pluginConfigId": "{{configId}}", + "dataSourceConfig": { + "event": "clicked" + }, + "sort": { + "by": [ + [ + "count", + "desc" + ] + ] + } + }, + "scope": { + "scope": "{{scopes.[Resend Broadcasts]}}", + "workspace": "{{workspaceId}}", + "variable": "{{variables.[Resend Broadcast]}}" + }, + "variables": [ + "{{variables.[Resend Broadcast]}}" + ], + "vizSpec": { + "version": "1.0", + "visualization": { + "chartType": "richTable", + "encoding": { + "columns": [ + { + "field": "email", + "title": "Recipient", + "renderer": { + "type": "text" + } + }, + { + "field": "count", + "title": "Clicks", + "renderer": { + "type": "bar", + "color": "#6366f1" + } + }, + { + "field": "clicked_links", + "title": "Links", + "renderer": { + "type": "tag-list" + } + } + ] + }, + "style": { + "richTableCellPadding": 6 + } + } + } + } + }, + { + "i": "f8faec25-40d3-4e3e-938a-28991a696695", + "x": 2, + "y": 5, + "w": 1, + "h": 3, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Bounced", + "description": "Recipients whose email from this broadcast bounced", + "activePluginConfigIds": [ + "{{configId}}" + ], + "timeframe": "none", + "dataStream": { + "id": "{{dataStreams.[broadcastRecipients]}}", + "name": "broadcastRecipients", + "pluginConfigId": "{{configId}}", + "dataSourceConfig": { + "event": "bounced" + } + }, + "scope": { + "scope": "{{scopes.[Resend Broadcasts]}}", + "workspace": "{{workspaceId}}", + "variable": "{{variables.[Resend Broadcast]}}" + }, + "variables": [ + "{{variables.[Resend Broadcast]}}" + ], + "vizSpec": { + "version": "1.0", + "visualization": { + "chartType": "richTable", + "encoding": { + "columns": [ + { + "field": "email", + "title": "Recipient", + "renderer": { + "type": "text" + } + }, + { + "field": "bounce_type", + "title": "Bounce type", + "renderer": { + "type": "badge", + "defaultColor": "#d97706" + } + } + ] + }, + "style": { + "richTableCellPadding": 6 + } + } + } + } + }, + { + "i": "ea43bd45-56f9-4aa2-9073-3f9a9d445d49", + "x": 3, + "y": 5, + "w": 1, + "h": 3, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Unsubscribed", + "description": "Recipients who unsubscribed from this broadcast", + "activePluginConfigIds": [ + "{{configId}}" + ], + "timeframe": "none", + "dataStream": { + "id": "{{dataStreams.[broadcastRecipients]}}", + "name": "broadcastRecipients", + "pluginConfigId": "{{configId}}", + "dataSourceConfig": { + "event": "unsubscribed" + } + }, + "scope": { + "scope": "{{scopes.[Resend Broadcasts]}}", + "workspace": "{{workspaceId}}", + "variable": "{{variables.[Resend Broadcast]}}" + }, + "variables": [ + "{{variables.[Resend Broadcast]}}" + ], + "vizSpec": { + "version": "1.0", + "visualization": { + "chartType": "richTable", + "encoding": { + "columns": [ + { + "field": "email", + "title": "Recipient", + "renderer": { + "type": "text" + } + } + ] + }, + "style": { + "richTableCellPadding": 6 + } + } + } + } + } + ] + } +} diff --git a/plugins/Resend/v1/defaultContent/domain.dash.json b/plugins/Resend/v1/defaultContent/domain.dash.json new file mode 100644 index 00000000..63649076 --- /dev/null +++ b/plugins/Resend/v1/defaultContent/domain.dash.json @@ -0,0 +1,579 @@ +{ + "name": "Domain", + "schemaVersion": "1.5", + "timeframe": "last7days", + "variables": [ + "{{variables.[Resend Domain]}}" + ], + "dashboard": { + "_type": "layout/grid", + "columns": 4, + "version": 1, + "contents": [ + { + "i": "ae84c913-781e-4e00-86a4-0eca0ba95df7", + "x": 0, + "y": 0, + "w": 1, + "h": 2, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Status", + "description": "Verification status and region of this domain", + "activePluginConfigIds": [ + "{{configId}}" + ], + "timeframe": "none", + "dataStream": { + "id": "datastream-properties", + "name": "properties", + "pluginConfigId": "{{configId}}" + }, + "scope": { + "scope": "{{scopes.[Resend Domains]}}", + "workspace": "{{workspaceId}}", + "variable": "{{variables.[Resend Domain]}}" + }, + "variables": [ + "{{variables.[Resend Domain]}}" + ], + "vizSpec": { + "version": "1.0", + "visualization": { + "chartType": "statusGrid", + "encoding": { + "label": { + "field": "name", + "type": "nominal" + }, + "status": { + "field": "status", + "type": "nominal" + } + }, + "style": { + "labelFontSize": 16, + "secondaryFontSize": 12, + "statusGridColumnsPerRow": 1, + "statusGridLabelWrap": true, + "statusGridSecondaryTemplate": "{{{{raw}}}}{{region}}{{{{/raw}}}}" + } + } + } + } + }, + { + "i": "400c4838-5741-482c-8294-3d0333792d82", + "x": 1, + "y": 0, + "w": 3, + "h": 2, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Sending", + "description": "Emails from this domain sent, delivered, bounced and marked as spam", + "activePluginConfigIds": [ + "{{configId}}" + ], + "dataStream": { + "id": "{{dataStreams.[emailMetrics]}}", + "name": "emailMetrics", + "pluginConfigId": "{{configId}}", + "dataSourceConfig": { + "domain": { + "scope": "{{scopes.[Resend Domains]}}", + "workspace": "{{workspaceId}}", + "variable": "{{variables.[Resend Domain]}}" + }, + "granularity": "total" + } + }, + "variables": [ + "{{variables.[Resend Domain]}}" + ], + "vizSpec": { + "version": "1.0", + "visualization": { + "chartType": "scalar", + "encoding": { + "values": [ + { + "field": "sent", + "type": "quantitative", + "aggregate": "sum", + "title": "Sent" + }, + { + "field": "delivered", + "type": "quantitative", + "aggregate": "sum", + "title": "Delivered" + }, + { + "field": "bounced", + "type": "quantitative", + "aggregate": "sum", + "title": "Bounced" + }, + { + "field": "complained", + "type": "quantitative", + "aggregate": "sum", + "title": "Complained" + } + ] + }, + "style": { + "scalarColumns": 4 + } + } + } + } + }, + { + "i": "1dc7673c-1cf7-4400-9283-b879027c2450", + "x": 0, + "y": 2, + "w": 3, + "h": 3, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Email Volume", + "description": "Emails from this domain sent, delivered and bounced per day", + "activePluginConfigIds": [ + "{{configId}}" + ], + "dataStream": { + "id": "{{dataStreams.[emailMetrics]}}", + "name": "emailMetrics", + "pluginConfigId": "{{configId}}", + "dataSourceConfig": { + "domain": { + "scope": "{{scopes.[Resend Domains]}}", + "workspace": "{{workspaceId}}", + "variable": "{{variables.[Resend Domain]}}" + }, + "granularity": "daily" + }, + "sql": "SELECT \"period\", \"metric\", \"emails\" FROM (SELECT \"period\", 1 AS \"ord\", 'Sent' AS \"metric\", CAST(COALESCE(\"sent\", 0) AS DOUBLE) AS \"emails\" FROM \"dataset\" UNION ALL SELECT \"period\", 2, 'Delivered', CAST(COALESCE(\"delivered\", 0) AS DOUBLE) FROM \"dataset\" UNION ALL SELECT \"period\", 3, 'Bounced', CAST(COALESCE(\"bounced\", 0) AS DOUBLE) FROM \"dataset\") ORDER BY \"period\", \"ord\"" + }, + "variables": [ + "{{variables.[Resend Domain]}}" + ], + "vizSpec": { + "version": "1.0", + "visualization": { + "chartType": "line", + "encoding": { + "x": { + "field": "period", + "type": "temporal", + "title": "Day" + }, + "y": { + "field": "emails", + "type": "quantitative", + "aggregate": "sum", + "title": "Emails" + }, + "series": { + "field": "metric", + "type": "nominal" + } + }, + "style": { + "cartesianAreaGradient": true, + "cartesianSymbol": "none", + "cartesianLineWidth": 2, + "cartesianShowVerticalGridLines": false, + "legendPosition": "bottom", + "mappedColors": { + "Sent": "#6366f1", + "Delivered": "#22c55e", + "Bounced": "#ef4444" + } + } + } + } + } + }, + { + "i": "3b5901a1-2aff-4977-bb51-48931dce1a42", + "x": 3, + "y": 2, + "w": 1, + "h": 3, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Engagement Funnel", + "description": "How many emails from this domain were delivered, opened and clicked", + "activePluginConfigIds": [ + "{{configId}}" + ], + "dataStream": { + "id": "{{dataStreams.[emailMetrics]}}", + "name": "emailMetrics", + "pluginConfigId": "{{configId}}", + "dataSourceConfig": { + "domain": { + "scope": "{{scopes.[Resend Domains]}}", + "workspace": "{{workspaceId}}", + "variable": "{{variables.[Resend Domain]}}" + }, + "granularity": "total" + }, + "sql": "SELECT \"stage\", \"emails\" FROM (SELECT 1 AS \"ord\", 'Sent' AS \"stage\", CAST(COALESCE(\"sent\", 0) AS DOUBLE) AS \"emails\" FROM \"dataset\" UNION ALL SELECT 2, 'Delivered', CAST(COALESCE(\"delivered\", 0) AS DOUBLE) FROM \"dataset\" UNION ALL SELECT 3, 'Opened', CAST(COALESCE(\"unique_opened\", 0) AS DOUBLE) FROM \"dataset\" UNION ALL SELECT 4, 'Clicked', CAST(COALESCE(\"unique_clicked\", 0) AS DOUBLE) FROM \"dataset\") ORDER BY \"ord\"" + }, + "variables": [ + "{{variables.[Resend Domain]}}" + ], + "vizSpec": { + "version": "1.0", + "visualization": { + "chartType": "funnel", + "encoding": { + "label": { + "field": "stage", + "type": "nominal" + }, + "value": { + "field": "emails", + "type": "quantitative", + "aggregate": "sum" + } + }, + "style": { + "funnelSort": "none", + "colors": [ + "#6366f1", + "#8b5cf6", + "#14b8a6", + "#22c55e" + ], + "labelFormat": "{{{{raw}}}}{{name}} · {{value}}{{{{/raw}}}}", + "funnelLabelPosition": "inside", + "labelColor": "#ffffff" + } + } + } + } + }, + { + "i": "057f9cc2-235f-4cdf-90ea-1f8ebea769b6", + "x": 0, + "y": 5, + "w": 1, + "h": 3, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Domain Details", + "description": "Capabilities and tracking settings of this domain", + "activePluginConfigIds": [ + "{{configId}}" + ], + "timeframe": "none", + "dataStream": { + "id": "datastream-properties", + "name": "properties", + "pluginConfigId": "{{configId}}" + }, + "scope": { + "scope": "{{scopes.[Resend Domains]}}", + "workspace": "{{workspaceId}}", + "variable": "{{variables.[Resend Domain]}}" + }, + "variables": [ + "{{variables.[Resend Domain]}}" + ], + "vizSpec": { + "version": "1.0", + "visualization": { + "chartType": "richTable", + "encoding": { + "columns": [ + { + "field": "region", + "title": "Region", + "renderer": { + "type": "text" + } + }, + { + "field": "sending", + "title": "Sending", + "renderer": { + "type": "badge", + "colors": { + "enabled": "#16a34a", + "disabled": "#64748b" + } + } + }, + { + "field": "receiving", + "title": "Receiving", + "renderer": { + "type": "badge", + "colors": { + "enabled": "#16a34a", + "disabled": "#64748b" + } + } + }, + { + "field": "open_tracking", + "title": "Open tracking", + "renderer": { + "type": "boolean" + } + }, + { + "field": "click_tracking", + "title": "Click tracking", + "renderer": { + "type": "boolean" + } + }, + { + "field": "created_at", + "title": "Created", + "renderer": { + "type": "date", + "relative": true + } + } + ] + }, + "style": { + "richTableCellPadding": 6, + "richTableTranspose": true + } + } + } + } + }, + { + "i": "4e8093f2-32d1-47d3-947b-4b5d64843e4a", + "x": 1, + "y": 5, + "w": 3, + "h": 3, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "DNS Records", + "description": "DNS records that verify and configure this domain", + "activePluginConfigIds": [ + "{{configId}}" + ], + "timeframe": "none", + "dataStream": { + "id": "{{dataStreams.[domainRecords]}}", + "name": "domainRecords", + "pluginConfigId": "{{configId}}" + }, + "scope": { + "scope": "{{scopes.[Resend Domains]}}", + "workspace": "{{workspaceId}}", + "variable": "{{variables.[Resend Domain]}}" + }, + "variables": [ + "{{variables.[Resend Domain]}}" + ], + "vizSpec": { + "version": "1.0", + "visualization": { + "chartType": "richTable", + "encoding": { + "columns": [ + { + "field": "status[Expanded].rawState", + "title": "Status", + "renderer": { + "type": "badge", + "colors": { + "verified": "#16a34a", + "pending": "#d97706", + "temporary_failure": "#d97706", + "failed": "#dc2626", + "not_started": "#64748b" + } + } + }, + { + "field": "record", + "title": "Record", + "renderer": { + "type": "text" + } + }, + { + "field": "type", + "title": "Type", + "renderer": { + "type": "badge", + "defaultColor": "#6366f1" + } + }, + { + "field": "name", + "title": "Name", + "renderer": { + "type": "text" + } + }, + { + "field": "value", + "title": "Value", + "renderer": { + "type": "text" + } + }, + { + "field": "priority", + "title": "Priority", + "renderer": { + "type": "number", + "decimals": 0 + } + }, + { + "field": "ttl", + "title": "TTL", + "renderer": { + "type": "text" + } + } + ] + }, + "style": { + "richTableCellPadding": 6 + } + } + } + } + }, + { + "i": "53bad7fa-553f-4c34-9dbb-46d6f363c88b", + "x": 0, + "y": 8, + "w": 4, + "h": 3, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Recent Emails", + "description": "Emails sent from this domain, newest first", + "activePluginConfigIds": [ + "{{configId}}" + ], + "dataStream": { + "id": "{{dataStreams.[sentEmails]}}", + "name": "sentEmails", + "pluginConfigId": "{{configId}}", + "dataSourceConfig": { + "domain": { + "scope": "{{scopes.[Resend Domains]}}", + "workspace": "{{workspaceId}}", + "variable": "{{variables.[Resend Domain]}}" + } + }, + "sort": { + "by": [ + [ + "created_at", + "desc" + ] + ] + } + }, + "variables": [ + "{{variables.[Resend Domain]}}" + ], + "vizSpec": { + "version": "1.0", + "visualization": { + "chartType": "richTable", + "encoding": { + "columns": [ + { + "field": "last_event[Expanded].rawState", + "title": "Last event", + "renderer": { + "type": "badge", + "colors": { + "delivered": "#16a34a", + "opened": "#16a34a", + "clicked": "#16a34a", + "sent": "#3b82f6", + "queued": "#3b82f6", + "scheduled": "#3b82f6", + "delivery_delayed": "#d97706", + "canceled": "#64748b", + "bounced": "#dc2626", + "complained": "#dc2626", + "failed": "#dc2626", + "suppressed": "#dc2626" + } + } + }, + { + "field": "subject", + "title": "Subject", + "renderer": { + "type": "text" + } + }, + { + "field": "to", + "title": "To", + "renderer": { + "type": "tag-list" + } + }, + { + "field": "from", + "title": "From", + "renderer": { + "type": "text" + } + }, + { + "field": "created_at", + "title": "Sent", + "renderer": { + "type": "date", + "relative": true + } + } + ] + }, + "style": { + "richTableCellPadding": 6 + } + } + } + } + } + ] + } +} diff --git a/plugins/Resend/v1/defaultContent/inbox.dash.json b/plugins/Resend/v1/defaultContent/inbox.dash.json new file mode 100644 index 00000000..ddaa36db --- /dev/null +++ b/plugins/Resend/v1/defaultContent/inbox.dash.json @@ -0,0 +1,201 @@ +{ + "name": "Inbox", + "schemaVersion": "1.5", + "timeframe": "none", + "variables": [ + "{{variables.[Resend Inbox]}}" + ], + "dashboard": { + "_type": "layout/grid", + "columns": 4, + "version": 1, + "contents": [ + { + "i": "57239d4c-c51d-479d-80b9-1eb3fbe45481", + "x": 0, + "y": 0, + "w": 1, + "h": 3, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Unread Threads", + "description": "Threads in this inbox with unread email", + "activePluginConfigIds": [ + "{{configId}}" + ], + "timeframe": "none", + "dataStream": { + "id": "{{dataStreams.[inboxDetails]}}", + "name": "inboxDetails", + "pluginConfigId": "{{configId}}" + }, + "scope": { + "scope": "{{scopes.[Resend Inboxes]}}", + "workspace": "{{workspaceId}}", + "variable": "{{variables.[Resend Inbox]}}" + }, + "variables": [ + "{{variables.[Resend Inbox]}}" + ], + "vizSpec": { + "version": "1.0", + "visualization": { + "chartType": "scalar", + "encoding": { + "values": [ + { + "field": "unread", + "type": "quantitative", + "aggregate": "sum", + "title": "Unread threads" + } + ] + } + } + } + } + }, + { + "i": "4d8eec31-7ace-4496-a2eb-1f5ffc45a296", + "x": 1, + "y": 0, + "w": 1, + "h": 3, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Drafts", + "description": "Unsent drafts in this inbox", + "activePluginConfigIds": [ + "{{configId}}" + ], + "timeframe": "none", + "dataStream": { + "id": "{{dataStreams.[inboxDetails]}}", + "name": "inboxDetails", + "pluginConfigId": "{{configId}}" + }, + "scope": { + "scope": "{{scopes.[Resend Inboxes]}}", + "workspace": "{{workspaceId}}", + "variable": "{{variables.[Resend Inbox]}}" + }, + "variables": [ + "{{variables.[Resend Inbox]}}" + ], + "vizSpec": { + "version": "1.0", + "visualization": { + "chartType": "scalar", + "encoding": { + "values": [ + { + "field": "drafts", + "type": "quantitative", + "aggregate": "sum", + "title": "Drafts" + } + ] + } + } + } + } + }, + { + "i": "01a37cb5-f9f7-4253-b1a6-97a3c14431f9", + "x": 2, + "y": 0, + "w": 2, + "h": 3, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Inbox Details", + "description": "Address, sender name and domain of this inbox. Click to open the domain", + "activePluginConfigIds": [ + "{{configId}}" + ], + "timeframe": "none", + "dataStream": { + "id": "{{dataStreams.[inboxDetails]}}", + "name": "inboxDetails", + "pluginConfigId": "{{configId}}" + }, + "scope": { + "scope": "{{scopes.[Resend Inboxes]}}", + "workspace": "{{workspaceId}}", + "variable": "{{variables.[Resend Inbox]}}" + }, + "variables": [ + "{{variables.[Resend Inbox]}}" + ], + "vizSpec": { + "version": "1.0", + "visualization": { + "chartType": "richTable", + "encoding": { + "columns": [ + { + "field": "email_address", + "title": "Address", + "renderer": { + "type": "text" + } + }, + { + "field": "from_name", + "title": "From name", + "renderer": { + "type": "text" + } + }, + { + "field": "domain", + "title": "Domain", + "renderer": { + "type": "text" + } + }, + { + "field": "receiving_address", + "title": "Forwards to", + "renderer": { + "type": "text" + } + }, + { + "field": "last_received", + "title": "Last received", + "renderer": { + "type": "date", + "relative": true + } + }, + { + "field": "created_at", + "title": "Created", + "renderer": { + "type": "date", + "relative": true + } + } + ] + }, + "style": { + "richTableTranspose": true, + "richTableCellPadding": 6 + } + } + } + } + } + ] + } +} diff --git a/plugins/Resend/v1/defaultContent/manifest.json b/plugins/Resend/v1/defaultContent/manifest.json new file mode 100644 index 00000000..eaab760b --- /dev/null +++ b/plugins/Resend/v1/defaultContent/manifest.json @@ -0,0 +1,32 @@ +{ + "items": [ + { + "name": "overview", + "type": "dashboard" + }, + { + "name": "domain", + "type": "dashboard" + }, + { + "name": "broadcast", + "type": "dashboard" + }, + { + "name": "segment", + "type": "dashboard" + }, + { + "name": "webhook", + "type": "dashboard" + }, + { + "name": "automation", + "type": "dashboard" + }, + { + "name": "inbox", + "type": "dashboard" + } + ] +} diff --git a/plugins/Resend/v1/defaultContent/overview.dash.json b/plugins/Resend/v1/defaultContent/overview.dash.json new file mode 100644 index 00000000..f9060a7d --- /dev/null +++ b/plugins/Resend/v1/defaultContent/overview.dash.json @@ -0,0 +1,876 @@ +{ + "name": "Overview", + "schemaVersion": "1.5", + "timeframe": "last7days", + "dashboard": { + "_type": "layout/grid", + "columns": 4, + "version": 1, + "contents": [ + { + "i": "100a5567-7128-461a-9f23-2e293c0e5804", + "x": 0, + "y": 0, + "w": 2, + "h": 2, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Sending", + "description": "Emails sent, delivered, bounced and marked as spam across all domains", + "activePluginConfigIds": [ + "{{configId}}" + ], + "dataStream": { + "id": "{{dataStreams.[emailMetrics]}}", + "name": "emailMetrics", + "pluginConfigId": "{{configId}}", + "dataSourceConfig": { + "granularity": "total" + } + }, + "vizSpec": { + "version": "1.0", + "visualization": { + "chartType": "scalar", + "encoding": { + "values": [ + { + "field": "sent", + "type": "quantitative", + "aggregate": "sum", + "title": "Sent" + }, + { + "field": "delivered", + "type": "quantitative", + "aggregate": "sum", + "title": "Delivered" + }, + { + "field": "bounced", + "type": "quantitative", + "aggregate": "sum", + "title": "Bounced" + }, + { + "field": "complained", + "type": "quantitative", + "aggregate": "sum", + "title": "Complained" + } + ] + }, + "style": { + "scalarColumns": 4 + } + } + } + } + }, + { + "i": "7f35a943-2bf7-4e31-968f-dfb14127064a", + "x": 2, + "y": 0, + "w": 2, + "h": 2, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Engagement", + "description": "Delivery, open and click rates across all domains", + "activePluginConfigIds": [ + "{{configId}}" + ], + "dataStream": { + "id": "{{dataStreams.[emailMetrics]}}", + "name": "emailMetrics", + "pluginConfigId": "{{configId}}", + "dataSourceConfig": { + "granularity": "total" + }, + "sql": "SELECT \"metric\", \"rate\" FROM (SELECT 1 AS \"ord\", 'Delivered' AS \"metric\", CAST(COALESCE(\"delivery_rate\", 0) AS DOUBLE) AS \"rate\" FROM \"dataset\" UNION ALL SELECT 2, 'Opened', CAST(COALESCE(\"open_rate\", 0) AS DOUBLE) FROM \"dataset\" UNION ALL SELECT 3, 'Clicked', CAST(COALESCE(\"click_rate\", 0) AS DOUBLE) FROM \"dataset\") ORDER BY \"ord\"" + }, + "vizSpec": { + "version": "1.0", + "visualization": { + "chartType": "ringGrid", + "encoding": { + "value": { + "field": "rate", + "type": "quantitative" + }, + "label": { + "field": "metric", + "type": "nominal" + } + }, + "style": { + "ringGridTrackWidth": 10, + "ringGridThresholds": [ + { + "to": 1, + "color": "#6366f1" + } + ] + } + } + } + } + }, + { + "i": "ce90174d-7b54-47ab-8313-429f54056429", + "x": 0, + "y": 2, + "w": 3, + "h": 3, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Email Volume", + "description": "Emails sent, delivered and bounced per day", + "activePluginConfigIds": [ + "{{configId}}" + ], + "dataStream": { + "id": "{{dataStreams.[emailMetrics]}}", + "name": "emailMetrics", + "pluginConfigId": "{{configId}}", + "dataSourceConfig": { + "granularity": "daily" + }, + "sql": "SELECT \"period\", \"metric\", \"emails\" FROM (SELECT \"period\", 1 AS \"ord\", 'Sent' AS \"metric\", CAST(COALESCE(\"sent\", 0) AS DOUBLE) AS \"emails\" FROM \"dataset\" UNION ALL SELECT \"period\", 2, 'Delivered', CAST(COALESCE(\"delivered\", 0) AS DOUBLE) FROM \"dataset\" UNION ALL SELECT \"period\", 3, 'Bounced', CAST(COALESCE(\"bounced\", 0) AS DOUBLE) FROM \"dataset\") ORDER BY \"period\", \"ord\"" + }, + "vizSpec": { + "version": "1.0", + "visualization": { + "chartType": "line", + "encoding": { + "x": { + "field": "period", + "type": "temporal", + "title": "Day" + }, + "y": { + "field": "emails", + "type": "quantitative", + "aggregate": "sum", + "title": "Emails" + }, + "series": { + "field": "metric", + "type": "nominal" + } + }, + "style": { + "cartesianAreaGradient": true, + "cartesianSymbol": "none", + "cartesianLineWidth": 2, + "cartesianShowVerticalGridLines": false, + "legendPosition": "bottom", + "mappedColors": { + "Sent": "#6366f1", + "Delivered": "#22c55e", + "Bounced": "#ef4444" + } + } + } + } + } + }, + { + "i": "063cdb95-1ef0-4db6-9755-d23e0ea23389", + "x": 3, + "y": 2, + "w": 1, + "h": 3, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Engagement Funnel", + "description": "How many sent emails were delivered, opened and clicked", + "activePluginConfigIds": [ + "{{configId}}" + ], + "dataStream": { + "id": "{{dataStreams.[emailMetrics]}}", + "name": "emailMetrics", + "pluginConfigId": "{{configId}}", + "dataSourceConfig": { + "granularity": "total" + }, + "sql": "SELECT \"stage\", \"emails\" FROM (SELECT 1 AS \"ord\", 'Sent' AS \"stage\", CAST(COALESCE(\"sent\", 0) AS DOUBLE) AS \"emails\" FROM \"dataset\" UNION ALL SELECT 2, 'Delivered', CAST(COALESCE(\"delivered\", 0) AS DOUBLE) FROM \"dataset\" UNION ALL SELECT 3, 'Opened', CAST(COALESCE(\"unique_opened\", 0) AS DOUBLE) FROM \"dataset\" UNION ALL SELECT 4, 'Clicked', CAST(COALESCE(\"unique_clicked\", 0) AS DOUBLE) FROM \"dataset\") ORDER BY \"ord\"" + }, + "vizSpec": { + "version": "1.0", + "visualization": { + "chartType": "funnel", + "encoding": { + "label": { + "field": "stage", + "type": "nominal" + }, + "value": { + "field": "emails", + "type": "quantitative", + "aggregate": "sum" + } + }, + "style": { + "funnelSort": "none", + "colors": [ + "#6366f1", + "#8b5cf6", + "#14b8a6", + "#22c55e" + ], + "labelFormat": "{{{{raw}}}}{{name}} · {{value}}{{{{/raw}}}}", + "funnelLabelPosition": "inside", + "labelColor": "#ffffff" + } + } + } + } + }, + { + "i": "b6ad77ae-bbb4-45e2-b38d-3f57b6641c96", + "x": 0, + "y": 5, + "w": 2, + "h": 3, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Domain Health", + "description": "Verification status of each domain. Click one to open it", + "activePluginConfigIds": [ + "{{configId}}" + ], + "timeframe": "none", + "dataStream": { + "id": "{{dataStreams.[domains]}}", + "name": "domains", + "pluginConfigId": "{{configId}}", + "sort": { + "by": [ + [ + "name", + "asc" + ] + ] + } + }, + "vizSpec": { + "version": "1.0", + "visualization": { + "chartType": "statusGrid", + "encoding": { + "label": { + "field": "name", + "type": "nominal" + }, + "status": { + "field": "status", + "type": "nominal" + } + }, + "style": { + "statusGridSecondaryTemplate": "{{{{raw}}}}{{region}}{{{{/raw}}}}", + "statusGridLabelWrap": true, + "statusGridBlockGap": 4 + } + } + } + } + }, + { + "i": "f176d7af-6e8e-4d19-baf9-77be9279fa84", + "x": 2, + "y": 5, + "w": 1, + "h": 3, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Webhook Health", + "description": "Whether each webhook endpoint is enabled. Click one to open it", + "activePluginConfigIds": [ + "{{configId}}" + ], + "timeframe": "none", + "dataStream": { + "id": "{{dataStreams.[webhooks]}}", + "name": "webhooks", + "pluginConfigId": "{{configId}}", + "sort": { + "by": [ + [ + "endpoint", + "asc" + ] + ] + } + }, + "vizSpec": { + "version": "1.0", + "visualization": { + "chartType": "statusGrid", + "encoding": { + "label": { + "field": "endpoint", + "type": "nominal" + }, + "status": { + "field": "status", + "type": "nominal" + } + }, + "style": { + "statusGridColumnsPerRow": 1, + "statusGridLabelWrap": true, + "statusGridBlockGap": 4 + } + } + } + } + }, + { + "i": "cb40ec72-0af3-41bc-8f02-c0930b73baf8", + "x": 3, + "y": 5, + "w": 1, + "h": 3, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Automations", + "description": "Whether each automation is enabled. Click one to open it", + "activePluginConfigIds": [ + "{{configId}}" + ], + "timeframe": "none", + "dataStream": { + "id": "{{dataStreams.[automations]}}", + "name": "automations", + "pluginConfigId": "{{configId}}", + "sort": { + "by": [ + [ + "name", + "asc" + ] + ] + } + }, + "vizSpec": { + "version": "1.0", + "visualization": { + "chartType": "statusGrid", + "encoding": { + "label": { + "field": "name", + "type": "nominal" + }, + "status": { + "field": "status", + "type": "nominal" + } + }, + "style": { + "statusGridColumnsPerRow": 2, + "statusGridLabelWrap": true, + "statusGridBlockGap": 4 + } + } + } + } + }, + { + "i": "06707398-ae33-4c4a-95cf-7f6db771a706", + "x": 0, + "y": 8, + "w": 2, + "h": 3, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Sending by Domain", + "description": "Emails sent per day from each domain. Click one to open it", + "activePluginConfigIds": [ + "{{configId}}" + ], + "dataStream": { + "id": "{{dataStreams.[emailMetrics]}}", + "name": "emailMetrics", + "pluginConfigId": "{{configId}}", + "dataSourceConfig": { + "granularity": "daily", + "breakdown": "domain" + } + }, + "vizSpec": { + "version": "1.0", + "visualization": { + "chartType": "sparkline", + "encoding": { + "time": { + "field": "period", + "type": "temporal" + }, + "value": { + "field": "sent", + "type": "quantitative" + }, + "label": { + "field": "domain_name", + "type": "nominal" + } + }, + "style": { + "sparklineValue": "sum", + "sparklineSortBy": "value", + "sparklineSortDirection": "desc", + "sparklineLimit": 8, + "cartesianSmooth": true, + "sparklineColor": "#6366f1" + } + } + } + } + }, + { + "i": "b63c42c6-3a16-4d8d-9399-7b62629314ce", + "x": 2, + "y": 8, + "w": 2, + "h": 3, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "API Traffic", + "description": "API requests per day by outcome", + "timeframe": "last7days", + "dataStream": { + "id": "datastream-sql", + "dataSourceConfig": { + "version": "2.0", + "sql": "SELECT STRFTIME(DATE_TRUNC('day', \"created_at\"), '%Y-%m-%dT%H:%M:%SZ') AS \"day\", CASE \"outcome\" WHEN 'success' THEN 'Success' WHEN 'warning' THEN 'Client error' ELSE 'Rate limited or server error' END AS \"outcome\", CAST(COUNT(*) AS DOUBLE) AS \"requests\" FROM \"logs\" GROUP BY ALL ORDER BY \"day\"", + "tables": [ + { + "tableName": "logs", + "config": { + "dataStream": { + "id": "{{dataStreams.[apiLogs]}}", + "name": "apiLogs" + }, + "activePluginConfigIds": [ + "{{configId}}" + ], + "timeframe": "last7days" + } + } + ] + } + }, + "vizSpec": { + "version": "1.0", + "visualization": { + "chartType": "bar", + "encoding": { + "x": { + "field": "day", + "type": "temporal", + "title": "Day" + }, + "y": { + "field": "requests", + "type": "quantitative", + "aggregate": "sum", + "title": "Requests" + }, + "series": { + "field": "outcome", + "type": "nominal" + } + }, + "style": { + "cartesianStack": "on", + "cartesianBarBorderRadius": 3, + "legendPosition": "bottom", + "mappedColors": { + "Success": "#22c55e", + "Client error": "#f59e0b", + "Rate limited or server error": "#ef4444" + } + } + } + } + } + }, + { + "i": "dabd695a-85c6-46c1-ae07-021e7b82e48c", + "x": 0, + "y": 11, + "w": 2, + "h": 3, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Recent API Errors", + "description": "API requests that did not succeed, newest first", + "activePluginConfigIds": [ + "{{configId}}" + ], + "dataStream": { + "id": "{{dataStreams.[apiLogs]}}", + "name": "apiLogs", + "pluginConfigId": "{{configId}}", + "filter": { + "multiOperation": "and", + "filters": [ + { + "column": "outcome", + "operation": "notequals", + "value": "success" + } + ] + }, + "sort": { + "by": [ + [ + "created_at", + "desc" + ] + ] + } + }, + "vizSpec": { + "version": "1.0", + "visualization": { + "chartType": "richTable", + "encoding": { + "columns": [ + { + "field": "response_status", + "title": "Status", + "renderer": { + "type": "number", + "decimals": 0, + "thresholds": [ + { + "to": 428, + "color": "#f59e0b" + }, + { + "to": 429, + "color": "#ef4444" + }, + { + "to": 499, + "color": "#f59e0b" + }, + { + "to": 1000, + "color": "#ef4444" + } + ] + } + }, + { + "field": "created_at", + "title": "When", + "renderer": { + "type": "date", + "relative": true + } + }, + { + "field": "method", + "title": "Method", + "renderer": { + "type": "badge", + "defaultColor": "#64748b" + }, + "width": 90 + }, + { + "field": "endpoint", + "title": "Endpoint", + "renderer": { + "type": "text" + } + } + ] + }, + "style": { + "richTableCellPadding": 6 + } + } + } + } + }, + { + "i": "c41ed89e-f123-47ec-b078-e7d70f1fdc5d", + "x": 2, + "y": 11, + "w": 1, + "h": 3, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Suppressions by Origin", + "description": "Addresses Resend will not send to, by what caused the suppression", + "timeframe": "none", + "dataStream": { + "id": "datastream-sql", + "dataSourceConfig": { + "version": "2.0", + "sql": "SELECT CASE \"origin[Expanded].rawState\" WHEN 'bounce' THEN 'Bounce' WHEN 'complaint' THEN 'Complaint' ELSE 'Manual' END AS \"origin\", CAST(COUNT(*) AS DOUBLE) AS \"addresses\" FROM \"suppressions\" GROUP BY ALL", + "tables": [ + { + "tableName": "suppressions", + "config": { + "dataStream": { + "id": "{{dataStreams.[suppressions]}}", + "name": "suppressions" + }, + "activePluginConfigIds": [ + "{{configId}}" + ], + "timeframe": "none" + } + } + ] + } + }, + "vizSpec": { + "version": "1.0", + "visualization": { + "chartType": "pie", + "encoding": { + "label": { + "field": "origin", + "type": "nominal" + }, + "value": { + "field": "addresses", + "type": "quantitative", + "aggregate": "sum" + } + }, + "style": { + "pieInnerRadius": 60, + "pieDonutCenterValue": "total", + "labelFontSize": 16, + "mappedColors": { + "Bounce": "#f59e0b", + "Complaint": "#ef4444", + "Manual": "#64748b" + } + } + } + } + } + }, + { + "i": "fd1b87c5-9667-4470-9dd0-61a5dfeb3449", + "x": 3, + "y": 11, + "w": 1, + "h": 3, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "API Keys", + "description": "API keys and how long since each was last used", + "activePluginConfigIds": [ + "{{configId}}" + ], + "timeframe": "none", + "dataStream": { + "id": "{{dataStreams.[apiKeys]}}", + "name": "apiKeys", + "pluginConfigId": "{{configId}}", + "sort": { + "by": [ + [ + "last_used_at", + "desc" + ] + ] + } + }, + "vizSpec": { + "version": "1.0", + "visualization": { + "chartType": "richTable", + "encoding": { + "columns": [ + { + "field": "name", + "title": "Key", + "renderer": { + "type": "text" + } + }, + { + "field": "last_used_at", + "title": "Last used", + "renderer": { + "type": "date", + "relative": true + } + } + ] + }, + "style": { + "richTableCellPadding": 6, + "richTableRowHighlight": { + "field": "days_since_used", + "thresholds": [ + { + "to": 30, + "color": "rgba(0,0,0,0)" + }, + { + "to": 90, + "color": "rgba(245,158,11,0.12)" + }, + { + "to": 100000, + "color": "rgba(239,68,68,0.14)" + } + ] + } + } + } + } + } + }, + { + "i": "e807bb60-464c-42e4-8690-799a28885ea2", + "x": 0, + "y": 14, + "w": 4, + "h": 3, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Broadcasts", + "description": "Broadcasts on the team, newest first. Click one to open it", + "activePluginConfigIds": [ + "{{configId}}" + ], + "timeframe": "none", + "dataStream": { + "id": "{{dataStreams.[broadcasts]}}", + "name": "broadcasts", + "pluginConfigId": "{{configId}}", + "sort": { + "by": [ + [ + "created_at", + "desc" + ] + ] + } + }, + "vizSpec": { + "version": "1.0", + "visualization": { + "chartType": "richTable", + "encoding": { + "columns": [ + { + "field": "name", + "title": "Broadcast", + "renderer": { + "type": "text" + } + }, + { + "field": "status[Expanded].rawState", + "title": "Status", + "renderer": { + "type": "badge", + "colors": { + "sent": "#16a34a", + "draft": "#64748b", + "scheduled": "#3b82f6", + "queued": "#3b82f6", + "sending": "#3b82f6", + "canceled": "#d97706", + "cancelled": "#d97706", + "failed": "#dc2626" + } + } + }, + { + "field": "segment", + "title": "Segment", + "renderer": { + "type": "text" + } + }, + { + "field": "sent_at", + "title": "Sent", + "renderer": { + "type": "date", + "relative": true + } + }, + { + "field": "scheduled_at", + "title": "Scheduled", + "renderer": { + "type": "date", + "relative": true + } + }, + { + "field": "created_at", + "title": "Created", + "renderer": { + "type": "date", + "relative": true + } + } + ] + }, + "style": { + "richTableCellPadding": 6 + } + } + } + } + } + ] + } +} diff --git a/plugins/Resend/v1/defaultContent/scopes.json b/plugins/Resend/v1/defaultContent/scopes.json new file mode 100644 index 00000000..a56703b8 --- /dev/null +++ b/plugins/Resend/v1/defaultContent/scopes.json @@ -0,0 +1,104 @@ +[ + { + "name": "Resend Domains", + "matches": { + "sourceType": { + "type": "oneOf", + "values": [ + "Domain" + ] + } + }, + "variable": { + "name": "Resend Domain", + "allowMultipleSelection": false, + "default": "none", + "type": "object" + } + }, + { + "name": "Resend Broadcasts", + "matches": { + "sourceType": { + "type": "oneOf", + "values": [ + "Broadcast" + ] + } + }, + "variable": { + "name": "Resend Broadcast", + "allowMultipleSelection": false, + "default": "none", + "type": "object" + } + }, + { + "name": "Resend Segments", + "matches": { + "sourceType": { + "type": "oneOf", + "values": [ + "Segment" + ] + } + }, + "variable": { + "name": "Resend Segment", + "allowMultipleSelection": false, + "default": "none", + "type": "object" + } + }, + { + "name": "Resend Webhooks", + "matches": { + "sourceType": { + "type": "oneOf", + "values": [ + "Webhook" + ] + } + }, + "variable": { + "name": "Resend Webhook", + "allowMultipleSelection": false, + "default": "none", + "type": "object" + } + }, + { + "name": "Resend Automations", + "matches": { + "sourceType": { + "type": "oneOf", + "values": [ + "Automation" + ] + } + }, + "variable": { + "name": "Resend Automation", + "allowMultipleSelection": false, + "default": "none", + "type": "object" + } + }, + { + "name": "Resend Inboxes", + "matches": { + "sourceType": { + "type": "oneOf", + "values": [ + "Inbox" + ] + } + }, + "variable": { + "name": "Resend Inbox", + "allowMultipleSelection": false, + "default": "none", + "type": "object" + } + } +] diff --git a/plugins/Resend/v1/defaultContent/segment.dash.json b/plugins/Resend/v1/defaultContent/segment.dash.json new file mode 100644 index 00000000..6ef08199 --- /dev/null +++ b/plugins/Resend/v1/defaultContent/segment.dash.json @@ -0,0 +1,405 @@ +{ + "name": "Segment", + "schemaVersion": "1.5", + "timeframe": "none", + "variables": [ + "{{variables.[Resend Segment]}}" + ], + "dashboard": { + "_type": "layout/grid", + "columns": 4, + "version": 1, + "contents": [ + { + "i": "8d39d680-bc4c-47b9-bf25-5a69c0d55d58", + "x": 0, + "y": 0, + "w": 2, + "h": 2, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Segment Details", + "description": "Name and creation date of this segment", + "activePluginConfigIds": [ + "{{configId}}" + ], + "timeframe": "none", + "dataStream": { + "id": "datastream-properties", + "name": "properties", + "pluginConfigId": "{{configId}}" + }, + "scope": { + "scope": "{{scopes.[Resend Segments]}}", + "workspace": "{{workspaceId}}", + "variable": "{{variables.[Resend Segment]}}" + }, + "variables": [ + "{{variables.[Resend Segment]}}" + ], + "vizSpec": { + "version": "1.0", + "visualization": { + "chartType": "richTable", + "encoding": { + "columns": [ + { + "field": "name", + "title": "Segment", + "renderer": { + "type": "text" + } + }, + { + "field": "created_at", + "title": "Created", + "renderer": { + "type": "date", + "relative": true + } + } + ] + }, + "style": { + "richTableCellPadding": 6, + "richTableTranspose": true + } + } + } + } + }, + { + "i": "c32cb0cf-16da-4520-af86-6bd1d9ad6118", + "x": 2, + "y": 0, + "w": 1, + "h": 2, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Contacts", + "description": "Contacts in this segment", + "activePluginConfigIds": [ + "{{configId}}" + ], + "timeframe": "none", + "dataStream": { + "id": "{{dataStreams.[contacts]}}", + "name": "contacts", + "pluginConfigId": "{{configId}}", + "dataSourceConfig": { + "segment": { + "scope": "{{scopes.[Resend Segments]}}", + "workspace": "{{workspaceId}}", + "variable": "{{variables.[Resend Segment]}}" + } + }, + "group": { + "by": [], + "aggregate": [ + { + "type": "count" + } + ] + } + }, + "variables": [ + "{{variables.[Resend Segment]}}" + ], + "vizSpec": { + "version": "1.0", + "visualization": { + "chartType": "scalar", + "encoding": { + "values": [ + { + "field": "count", + "type": "quantitative", + "aggregate": "sum", + "title": "Contacts" + } + ] + } + } + } + } + }, + { + "i": "cd70bafa-8b52-47c2-9f73-c0ff632e41bd", + "x": 3, + "y": 0, + "w": 1, + "h": 2, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Unsubscribed", + "description": "Contacts in this segment who unsubscribed from all emails, out of all its contacts", + "activePluginConfigIds": [ + "{{configId}}" + ], + "timeframe": "none", + "dataStream": { + "id": "{{dataStreams.[contacts]}}", + "name": "contacts", + "pluginConfigId": "{{configId}}", + "dataSourceConfig": { + "segment": { + "scope": "{{scopes.[Resend Segments]}}", + "workspace": "{{workspaceId}}", + "variable": "{{variables.[Resend Segment]}}" + } + }, + "group": { + "by": [], + "aggregate": [ + { + "type": "count" + }, + { + "type": "sum", + "names": [ + "is_unsubscribed" + ] + } + ] + } + }, + "variables": [ + "{{variables.[Resend Segment]}}" + ], + "vizSpec": { + "version": "1.0", + "visualization": { + "chartType": "ringGrid", + "encoding": { + "value": { + "field": "is_unsubscribed_sum", + "type": "quantitative", + "title": "Unsubscribed" + }, + "max": { + "field": "count", + "type": "quantitative", + "title": "Contacts" + } + }, + "style": { + "ringGridThresholds": [ + { + "to": 0.02, + "color": "#22c55e" + }, + { + "to": 0.05, + "color": "#f59e0b" + }, + { + "to": 1, + "color": "#ef4444" + } + ] + } + } + } + } + }, + { + "i": "cb33a637-bc30-473b-a27f-3594840fe1dd", + "x": 0, + "y": 2, + "w": 2, + "h": 4, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Contacts", + "description": "Contacts in this segment, newest first", + "activePluginConfigIds": [ + "{{configId}}" + ], + "timeframe": "none", + "dataStream": { + "id": "{{dataStreams.[contacts]}}", + "name": "contacts", + "pluginConfigId": "{{configId}}", + "dataSourceConfig": { + "segment": { + "scope": "{{scopes.[Resend Segments]}}", + "workspace": "{{workspaceId}}", + "variable": "{{variables.[Resend Segment]}}" + } + }, + "sort": { + "by": [ + [ + "created_at", + "desc" + ] + ] + } + }, + "variables": [ + "{{variables.[Resend Segment]}}" + ], + "vizSpec": { + "version": "1.0", + "visualization": { + "chartType": "richTable", + "encoding": { + "columns": [ + { + "field": "name", + "title": "Name", + "renderer": { + "type": "text" + } + }, + { + "field": "email", + "title": "Email", + "renderer": { + "type": "text" + } + }, + { + "field": "unsubscribed", + "title": "Unsubscribed", + "renderer": { + "type": "boolean", + "trueColor": "#d97706", + "falseColor": "#64748b" + } + }, + { + "field": "created_at", + "title": "Added", + "renderer": { + "type": "date", + "relative": true + } + } + ] + }, + "style": { + "richTableCellPadding": 6 + } + } + } + } + }, + { + "i": "1265bc04-1c75-4db6-bb0f-ee3acc040f88", + "x": 2, + "y": 2, + "w": 2, + "h": 4, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Broadcasts", + "description": "Broadcasts sent to this segment, newest first. Click one to open it", + "activePluginConfigIds": [ + "{{configId}}" + ], + "timeframe": "none", + "dataStream": { + "id": "{{dataStreams.[broadcasts]}}", + "name": "broadcasts", + "pluginConfigId": "{{configId}}", + "dataSourceConfig": { + "segment": { + "scope": "{{scopes.[Resend Segments]}}", + "workspace": "{{workspaceId}}", + "variable": "{{variables.[Resend Segment]}}" + } + }, + "sort": { + "by": [ + [ + "created_at", + "desc" + ] + ] + } + }, + "variables": [ + "{{variables.[Resend Segment]}}" + ], + "vizSpec": { + "version": "1.0", + "visualization": { + "chartType": "richTable", + "encoding": { + "columns": [ + { + "field": "name", + "title": "Broadcast", + "renderer": { + "type": "text" + } + }, + { + "field": "status[Expanded].rawState", + "title": "Status", + "renderer": { + "type": "badge", + "colors": { + "sent": "#16a34a", + "draft": "#64748b", + "scheduled": "#3b82f6", + "queued": "#3b82f6", + "sending": "#3b82f6", + "canceled": "#d97706", + "cancelled": "#d97706", + "failed": "#dc2626" + } + } + }, + { + "field": "sent_at", + "title": "Sent", + "renderer": { + "type": "date", + "relative": true + } + }, + { + "field": "scheduled_at", + "title": "Scheduled", + "renderer": { + "type": "date", + "relative": true + } + }, + { + "field": "created_at", + "title": "Created", + "renderer": { + "type": "date", + "relative": true + } + } + ] + }, + "style": { + "richTableCellPadding": 6 + } + } + } + } + } + ] + } +} diff --git a/plugins/Resend/v1/defaultContent/webhook.dash.json b/plugins/Resend/v1/defaultContent/webhook.dash.json new file mode 100644 index 00000000..3579be74 --- /dev/null +++ b/plugins/Resend/v1/defaultContent/webhook.dash.json @@ -0,0 +1,391 @@ +{ + "name": "Webhook", + "schemaVersion": "1.5", + "timeframe": "last7days", + "variables": [ + "{{variables.[Resend Webhook]}}" + ], + "dashboard": { + "_type": "layout/grid", + "columns": 4, + "version": 1, + "contents": [ + { + "i": "8153ecc9-e1dc-42f5-9a38-5000cb852047", + "x": 0, + "y": 0, + "w": 1, + "h": 3, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Webhook Details", + "description": "Status and subscribed events of this endpoint", + "activePluginConfigIds": [ + "{{configId}}" + ], + "timeframe": "none", + "dataStream": { + "id": "datastream-properties", + "name": "properties", + "pluginConfigId": "{{configId}}" + }, + "scope": { + "scope": "{{scopes.[Resend Webhooks]}}", + "workspace": "{{workspaceId}}", + "variable": "{{variables.[Resend Webhook]}}" + }, + "variables": [ + "{{variables.[Resend Webhook]}}" + ], + "vizSpec": { + "version": "1.0", + "visualization": { + "chartType": "richTable", + "encoding": { + "columns": [ + { + "field": "status", + "title": "Status", + "renderer": { + "type": "status" + } + }, + { + "field": "events", + "title": "Events", + "renderer": { + "type": "tag-list" + } + }, + { + "field": "created_at", + "title": "Created", + "renderer": { + "type": "date", + "relative": true + } + } + ] + }, + "style": { + "richTableCellPadding": 6, + "richTableTranspose": true, + "richTableWrapText": true + } + } + } + } + }, + { + "i": "dca34504-e220-4abe-83ee-1c98cb2648fb", + "x": 1, + "y": 0, + "w": 1, + "h": 3, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Delivery Status", + "description": "Events sent to this endpoint by delivery status", + "activePluginConfigIds": [ + "{{configId}}" + ], + "dataStream": { + "id": "{{dataStreams.[webhookEvents]}}", + "name": "webhookEvents", + "pluginConfigId": "{{configId}}" + }, + "scope": { + "scope": "{{scopes.[Resend Webhooks]}}", + "workspace": "{{workspaceId}}", + "variable": "{{variables.[Resend Webhook]}}" + }, + "variables": [ + "{{variables.[Resend Webhook]}}" + ], + "vizSpec": { + "version": "1.0", + "visualization": { + "chartType": "pie", + "encoding": { + "label": { + "field": "status[Expanded].rawState", + "type": "nominal" + }, + "value": { + "field": "type", + "type": "nominal", + "aggregate": "count" + } + }, + "style": { + "pieInnerRadius": 60, + "pieDonutCenterValue": "total", + "labelFontSize": 16, + "mappedColors": { + "success": "#22c55e", + "failed": "#ef4444", + "attempting": "#f59e0b", + "pending": "#64748b", + "error": "#ef4444", + "warning": "#f59e0b", + "unknown": "#64748b" + } + } + } + } + } + }, + { + "i": "50a9cf0f-5dc1-4202-bd5b-be76cba76186", + "x": 2, + "y": 0, + "w": 2, + "h": 3, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Deliveries Over Time", + "description": "Events sent to this endpoint per day, by delivery status", + "activePluginConfigIds": [ + "{{configId}}" + ], + "dataStream": { + "id": "{{dataStreams.[webhookEvents]}}", + "name": "webhookEvents", + "pluginConfigId": "{{configId}}", + "group": { + "by": [ + [ + "created_at", + "byDay" + ], + [ + "status", + "uniqueValues" + ] + ], + "aggregate": [ + { + "type": "count" + } + ] + }, + "sort": { + "by": [ + [ + "created_at_byDay", + "asc" + ] + ] + } + }, + "scope": { + "scope": "{{scopes.[Resend Webhooks]}}", + "workspace": "{{workspaceId}}", + "variable": "{{variables.[Resend Webhook]}}" + }, + "variables": [ + "{{variables.[Resend Webhook]}}" + ], + "vizSpec": { + "version": "1.0", + "visualization": { + "chartType": "bar", + "encoding": { + "x": { + "field": "created_at_byDay", + "type": "temporal", + "title": "Day" + }, + "y": { + "field": "count", + "type": "quantitative", + "aggregate": "sum", + "title": "Events" + }, + "series": { + "field": "status_uniqueValues", + "type": "nominal" + } + }, + "style": { + "cartesianStack": "on", + "cartesianBarBorderRadius": 3, + "legendPosition": "bottom", + "mappedColors": { + "success": "#22c55e", + "failed": "#ef4444", + "attempting": "#f59e0b", + "pending": "#64748b", + "error": "#ef4444", + "warning": "#f59e0b", + "unknown": "#64748b" + } + } + } + } + } + }, + { + "i": "e733a50b-a453-4ad9-a284-53584191da19", + "x": 0, + "y": 3, + "w": 2, + "h": 3, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Failed Deliveries", + "description": "Events this endpoint has not accepted (failed or still retrying), newest first", + "activePluginConfigIds": [ + "{{configId}}" + ], + "dataStream": { + "id": "{{dataStreams.[webhookEvents]}}", + "name": "webhookEvents", + "pluginConfigId": "{{configId}}", + "filter": { + "multiOperation": "and", + "filters": [ + { + "column": "status", + "operation": "notequals", + "value": "success" + } + ] + }, + "sort": { + "by": [ + [ + "created_at", + "desc" + ] + ] + } + }, + "scope": { + "scope": "{{scopes.[Resend Webhooks]}}", + "workspace": "{{workspaceId}}", + "variable": "{{variables.[Resend Webhook]}}" + }, + "variables": [ + "{{variables.[Resend Webhook]}}" + ], + "vizSpec": { + "version": "1.0", + "visualization": { + "chartType": "richTable", + "encoding": { + "columns": [ + { + "field": "created_at", + "title": "When", + "renderer": { + "type": "date", + "relative": true + } + }, + { + "field": "type", + "title": "Event", + "renderer": { + "type": "text" + } + }, + { + "field": "status[Expanded].rawState", + "title": "Status", + "renderer": { + "type": "badge", + "colors": { + "success": "#22c55e", + "failed": "#ef4444", + "attempting": "#f59e0b", + "pending": "#64748b", + "error": "#ef4444", + "warning": "#f59e0b", + "unknown": "#64748b" + } + } + } + ] + }, + "style": { + "richTableCellPadding": 6 + } + } + } + } + }, + { + "i": "deb90499-cca6-4370-8b85-2925d503114f", + "x": 2, + "y": 3, + "w": 2, + "h": 3, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Events by Type", + "description": "Events sent to this endpoint by event type", + "activePluginConfigIds": [ + "{{configId}}" + ], + "dataStream": { + "id": "{{dataStreams.[webhookEvents]}}", + "name": "webhookEvents", + "pluginConfigId": "{{configId}}" + }, + "scope": { + "scope": "{{scopes.[Resend Webhooks]}}", + "workspace": "{{workspaceId}}", + "variable": "{{variables.[Resend Webhook]}}" + }, + "variables": [ + "{{variables.[Resend Webhook]}}" + ], + "vizSpec": { + "version": "1.0", + "visualization": { + "chartType": "bar", + "encoding": { + "x": { + "field": "type", + "type": "nominal", + "title": "Event" + }, + "y": { + "field": "type", + "type": "nominal", + "aggregate": "count", + "title": "Events" + } + }, + "style": { + "cartesianHorizontal": true, + "cartesianBarBorderRadius": 4, + "showLabels": true, + "colors": [ + "#6366f1" + ], + "cartesianShowHorizontalGridLines": false + } + } + } + } + } + ] + } +} diff --git a/plugins/Resend/v1/docs/README.md b/plugins/Resend/v1/docs/README.md new file mode 100644 index 00000000..63605182 --- /dev/null +++ b/plugins/Resend/v1/docs/README.md @@ -0,0 +1,84 @@ +Monitor your [Resend](https://resend.com) team in SquaredUp — email delivery and engagement, sending domain health, broadcasts, automations, webhook delivery, suppressions and API activity — via the [Resend API](https://resend.com/docs/api-reference/introduction). + +> ⚠️ This plugin is read-only — it never sends email or changes anything in Resend. + +## Setup + +You will need a Resend **API key** with **Full access**. + +1. Sign in to the [Resend dashboard](https://resend.com/login). +2. Go to [**API Keys**](https://resend.com/api-keys) and click **Create API Key**. +3. Give the key a name and set **Permission** to **Full access**. A **Sending access** key can only send email, so it can't read domains, metrics, broadcasts or anything else this plugin shows. +4. Copy the key (it starts with `re_`) and paste it into the **API key** field. + +## Configuration fields + +| Field | What it is | Where to find it | Required | +| ----------- | ------------------------------------------------------------------ | ---------------------------------------------------------- | -------- | +| **API key** | Authenticates every request via the `Authorization: Bearer` header. | Resend dashboard → [**API Keys**](https://resend.com/api-keys). | Yes | + +On save, the plugin validates the key by listing your team's API keys; an invalid, revoked or **Sending access** key fails setup with an authentication error. + +## What this plugin monitors + +- **Email delivery and engagement** — sent, delivered, bounced, complained, opened and clicked counts and rates over time, for the whole team or per domain or broadcast. +- **Sending domain health** — verification status of each domain and of every DNS record it needs (SPF, DKIM, MX, tracking). +- **Recent activity** — the latest sent and received emails with their delivery status, and API requests with their response status. +- **Broadcasts** — campaign status, recipients by event (delivered, opened, bounced…) and the links people clicked. +- **Automations and webhooks** — automation run outcomes and durations, and whether each webhook event was delivered to your endpoint. +- **Audience and deliverability** — contacts (optionally by segment), suppressed addresses and contact import jobs. +- **Account hygiene** — API keys and how long since each was last used. + +The out-of-the-box dashboards include an account-wide **Overview** plus a perspective for each **Domain**, **Broadcast**, **Segment**, **Webhook**, **Automation** and **Inbox**. + +## Data streams + +- **Email Metrics** — sending, delivery and engagement counts and rates, by hourly/daily/weekly/monthly period or as totals, optionally filtered to or broken down by domain or broadcast; account-wide. +- **Sent Emails** — emails sent by the team with their latest delivery status, optionally filtered by domain; account-wide. +- **Received Emails** — inbound emails received on receiving-enabled domains, optionally filtered by domain; account-wide. +- **API Logs** — API requests made with the team's keys, with response status and outcome; account-wide. +- **Domains** — sending domains with verification status, region and tracking settings; account-wide. +- **Domain Records** — the DNS records a domain needs, with the verification status of each; per domain. +- **Broadcasts** — broadcasts with status, target segment and send times, optionally limited to segments; account-wide. +- **Broadcast Recipients** — recipients of a broadcast who triggered the chosen event (delivered, opened, clicked, bounced…); per broadcast. +- **Broadcast Clicked Links** — links clicked in a broadcast, with total and unique clicks; per broadcast. +- **Segments** — contact segments; account-wide. +- **Contacts** — contacts with subscription status, optionally limited to one segment; account-wide. +- **Suppressions** — addresses suppressed after a bounce, complaint or manual addition, optionally filtered by origin; account-wide. +- **Contact Imports** — bulk contact import jobs with status and created/updated/skipped/failed counts; account-wide. +- **Automations** — automations with their enabled status; account-wide. +- **Automation Runs** — runs of an automation with status and duration, optionally filtered by status; per automation. +- **Webhooks** — webhook endpoints with status and subscribed event types; account-wide. +- **Webhook Events** — events sent to a webhook, with delivery status; per webhook. +- **Templates** — email templates with publish status; account-wide. +- **Topics** — subscription topics; account-wide. +- **Inboxes** — inboxes with unread thread count and when mail was last received; account-wide. +- **Inbox Details** — an inbox's unread threads, drafts, last received mail and forwarding address; per inbox. +- **API Keys** — API keys with when each was created and last used; account-wide. + +## What gets indexed + +| Object type | API source | Represents | +| -------------- | --------------------- | ---------------------------------------------- | +| **Domain** | `GET /domains` | A sending or receiving domain. | +| **Segment** | `GET /segments` | A contact segment. | +| **Broadcast** | `GET /broadcasts` | A marketing campaign. | +| **Webhook** | `GET /webhooks` | A webhook endpoint. | +| **Automation** | `GET /automations` | An event-triggered email automation. | +| **Inbox** | `GET /inboxes` | A Resend inbox. | + +**Relationships:** each Broadcast is related to the Segment it's sent to, and each Inbox to the Domain its address is on. Email Metrics rows drill down to their Domain or Broadcast. + +Contacts are deliberately **not** indexed — a marketing list can hold far more contacts than an import can carry, and they're personal data. Use the **Contacts** data stream instead. + +## Known limitations + +- **Metrics follow your plan's data retention** — Email Metrics only goes back as far as your Resend plan retains data. A range that starts earlier is shortened to the oldest retained day, and a range that ends before it (such as **Last month** on a short-retention plan) fails with a "data retention window" error. Metrics filtered to a broadcast are the exception — Resend keeps those beyond the retention window. +- **Recent-activity streams cover up to 7 days** — Sent Emails, Received Emails, API Logs and Webhook Events have no date filter in the API, so the plugin reads newest-first until it reaches the start of the timeframe. On very busy teams it stops after about 20 seconds of paging and returns the newest rows it has. Use **Email Metrics** for longer trends and totals. +- **Contacts filter by one segment** — the API accepts a single segment, so if several are selected only the first is used. Very large contact lists may exceed the platform's response size limit. +- **Automation Runs ignore the timeframe** — Resend doesn't document the order runs are returned in, so all runs are returned and sorted newest first. +- **Open and click metrics need tracking** — opens and clicks are only recorded for domains with open and click tracking turned on in Resend. +- **Cached responses** — Resend caches metrics and broadcast recipient and clicked-link responses for up to 15 minutes. +- **Rate limit** — Resend allows 10 requests per second per team, shared by every API key, so busy dashboards can compete with your own application's API calls. +- **Segment contact counts** — Resend's segment metrics API is in private beta and isn't used; count contacts per segment with the **Contacts** data stream instead. +- **Read-only** — the plugin never sends email or creates, changes or deletes anything in Resend. diff --git a/plugins/Resend/v1/icon.svg b/plugins/Resend/v1/icon.svg new file mode 100644 index 00000000..ca11cc6c --- /dev/null +++ b/plugins/Resend/v1/icon.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/plugins/Resend/v1/indexDefinitions/default.json b/plugins/Resend/v1/indexDefinitions/default.json new file mode 100644 index 00000000..782708a2 --- /dev/null +++ b/plugins/Resend/v1/indexDefinitions/default.json @@ -0,0 +1,129 @@ +{ + "steps": [ + { + "name": "domains", + "dataStream": { + "name": "domains" + }, + "timeframe": "none", + "objectMapping": { + "id": "id", + "name": "name", + "type": { + "value": "Domain" + }, + "properties": [ + "status", + "region", + { + "sending": "capabilities.sending" + }, + { + "receiving": "capabilities.receiving" + }, + "open_tracking", + "click_tracking", + "created_at" + ] + } + }, + { + "name": "segments", + "dataStream": { + "name": "segments" + }, + "timeframe": "none", + "objectMapping": { + "id": "id", + "name": "name", + "type": { + "value": "Segment" + }, + "properties": [ + "created_at" + ] + } + }, + { + "name": "broadcasts", + "dataStream": { + "name": "broadcasts" + }, + "timeframe": "none", + "objectMapping": { + "id": "id", + "name": "name", + "type": { + "value": "Broadcast" + }, + "properties": [ + "status", + "segment_id", + "created_at", + "scheduled_at", + "sent_at" + ] + } + }, + { + "name": "webhooks", + "dataStream": { + "name": "webhooks" + }, + "timeframe": "none", + "objectMapping": { + "id": "id", + "name": "endpoint", + "type": { + "value": "Webhook" + }, + "properties": [ + "status", + "events", + "created_at" + ] + } + }, + { + "name": "automations", + "dataStream": { + "name": "automations" + }, + "timeframe": "none", + "objectMapping": { + "id": "id", + "name": "name", + "type": { + "value": "Automation" + }, + "properties": [ + "status", + "created_at", + "updated_at" + ] + }, + "optional": true + }, + { + "name": "inboxes", + "dataStream": { + "name": "inboxes" + }, + "timeframe": "none", + "objectMapping": { + "id": "id", + "name": "inbox_name", + "type": { + "value": "Inbox" + }, + "properties": [ + "email_address", + "domain", + "from_name", + "created_at" + ] + }, + "optional": true + } + ] +} diff --git a/plugins/Resend/v1/metadata.json b/plugins/Resend/v1/metadata.json new file mode 100644 index 00000000..b1a6579b --- /dev/null +++ b/plugins/Resend/v1/metadata.json @@ -0,0 +1,63 @@ +{ + "name": "resend", + "displayName": "Resend", + "version": "1.0.0", + "author": { + "name": "@adamkinniburgh", + "type": "community" + }, + "description": "Monitor Resend email delivery, sending domains, broadcasts, automations and webhooks.", + "category": "Utility", + "type": "hybrid", + "schemaVersion": "2.1", + "importNotSupported": false, + "restrictedToPlatforms": [], + "keywords": [ + "email", + "resend", + "transactional email", + "deliverability", + "domains", + "broadcasts", + "webhooks", + "automations" + ], + "objectTypes": [ + "Domain", + "Segment", + "Broadcast", + "Webhook", + "Automation", + "Inbox" + ], + "links": [ + { + "category": "documentation", + "url": "https://github.com/squaredup/plugins/blob/main/plugins/Resend/v1/docs/README.md", + "label": "Help adding this plugin" + }, + { + "category": "source", + "url": "https://github.com/squaredup/plugins/tree/main/plugins/Resend/v1", + "label": "Repository" + } + ], + "base": { + "plugin": "WebAPI", + "majorVersion": "1", + "config": { + "baseUrl": "https://api.resend.com", + "authMode": "none", + "headers": [ + { + "key": "Authorization", + "value": "Bearer {{apiKey}}" + }, + { + "key": "User-Agent", + "value": "SquaredUp-ResendPlugin/1.0" + } + ] + } + } +} diff --git a/plugins/Resend/v1/ui.json b/plugins/Resend/v1/ui.json new file mode 100644 index 00000000..fd371aa8 --- /dev/null +++ b/plugins/Resend/v1/ui.json @@ -0,0 +1,10 @@ +[ + { + "type": "password", + "name": "apiKey", + "label": "API key", + "placeholder": "re_xxxxxxxxx", + "help": "Create a key with **Full access** permission in [Resend → API Keys](https://resend.com/api-keys).", + "validation": { "required": true } + } +]