diff --git a/components/codeqr/README.md b/components/codeqr/README.md new file mode 100644 index 0000000000000..aa4a86eaf204f --- /dev/null +++ b/components/codeqr/README.md @@ -0,0 +1,17 @@ +# Overview + +The CodeQR API enables you to create and manage dynamic QR Codes, shorten URLs, capture leads, and track engagement through powerful analytics. By integrating CodeQR with Pipedream, you can automate the generation of QR Codes and short links across a wide range of workflows — from marketing automation to customer support — and seamlessly connect these actions to over 2,500 apps. fileciteturn3file0 + +# Example Use Cases + +- **Dynamic QR Code Generation for Marketing Campaigns** + Automatically generate campaign-specific QR Codes when a new campaign is launched in your CRM or marketing tool. Store the QR code image in your cloud drive (e.g., Google Drive or Dropbox), and attach it to your newsletters or printed materials. + +- **URL Shortening with Pre-Redirect Lead Capture** + When a new record is added to Airtable or a Google Sheet with a destination URL, create a short link using CodeQR that optionally includes a lead capture page before redirection. Use this to qualify traffic before they reach your landing page. + +- **Automated Feedback Collection** + Trigger the creation of a QR code linked to a feedback form after each customer interaction in platforms like Zendesk, Intercom, or HubSpot. Easily track which customers scanned the code and filled the form, and centralize the responses. + +- **Analytics Monitoring and Reporting** + Use Pipedream workflows to pull scan and click analytics from CodeQR at regular intervals. Send the data to Slack, update a Google Sheet, or visualize trends on a custom dashboard. diff --git a/components/codeqr/actions/create-link/create-link.mjs b/components/codeqr/actions/create-link/create-link.mjs new file mode 100644 index 0000000000000..df048d68fa80e --- /dev/null +++ b/components/codeqr/actions/create-link/create-link.mjs @@ -0,0 +1,197 @@ +import codeqr from "../../codeqr.app.mjs"; + +export default { + key: "codeqr-create-link", + name: "Create a CodeQR Link", + description: + "Creates a short link in CodeQR using the CodeQR API. [See the documentation](https://codeqr.mintlify.app/api-reference/endpoint/create-a-link)", + version: "0.0.1", + type: "action", + props: { + codeqr, + url: { + type: "string", + label: "URL", + description: "The destination URL of the short link.", + }, + key: { + type: "string", + label: "Key", + description: + "The short link slug. If not provided, a random 7-character slug will be generated.", + optional: true, + }, + domain: { + type: "string", + label: "Domain", + description: + "The domain of the short link. If not provided, the default workspace domain will be used.", + optional: true, + }, + externalId: { + type: "string", + label: "External ID", + description: + "This is the ID of the link in your database. Must be prefixed with ext_.", + optional: true, + }, + password: { + type: "string", + label: "Password", + description: "The password required to access the destination URL.", + optional: true, + }, + flexible: { + type: "boolean", + label: "Flexible Link", + description: + "Whether this is a flexible link with dynamic destination setting.", + optional: true, + }, + title: { + type: "string", + label: "Title", + description: "The title displayed on the short link page.", + optional: true, + }, + description: { + type: "string", + label: "Description", + description: "A description displayed on the short link page.", + optional: true, + }, + image: { + type: "string", + label: "Image URL", + description: "URL of the image displayed on the short link page.", + optional: true, + }, + video: { + type: "string", + label: "Video URL", + description: "URL of the video displayed on the short link page.", + optional: true, + }, + proxy: { + type: "boolean", + label: "Proxy", + description: "Enable proxy settings.", + optional: true, + }, + rewrite: { + type: "boolean", + label: "Rewrite Link", + description: "Enable link rewriting.", + optional: true, + }, + ios: { + type: "string", + label: "iOS URL", + description: "The iOS destination URL for device-specific redirection.", + optional: true, + }, + android: { + type: "string", + label: "Android URL", + description: + "The Android destination URL for device-specific redirection.", + optional: true, + }, + doIndex: { + type: "boolean", + label: "Allow Indexing", + description: "Enable indexing of the short link.", + optional: true, + }, + comments: { + type: "string", + label: "Comments", + description: "Comments or notes about the short link.", + optional: true, + }, + expiresAt: { + type: "string", + label: "Expiration Date", + description: + "The date and time when the short link will expire (ISO 8601). E.g. `2025-06-13T05:31:56Z`", + optional: true, + }, + expiredUrl: { + type: "string", + label: "Expired Redirect URL", + description: "The URL to redirect to when the short link has expired.", + optional: true, + }, + geo: { + type: "object", + label: "Geo-Targeting", + description: + "Mapping of country codes to destination URLs (JSON format).", + optional: true, + }, + publicStats: { + type: "boolean", + label: "Public Stats", + description: "Whether the short link's stats are publicly accessible.", + optional: true, + }, + }, + + async run({ $ }) { + const { + url, + key, + domain, + externalId, + password, + flexible, + title, + description, + image, + video, + proxy, + rewrite, + ios, + android, + doIndex, + comments, + expiresAt, + expiredUrl, + publicStats, + } = this; + + const geo = typeof this.geo === "string" + ? JSON.parse(this.geo) + : this.geo; + + const payload = { + url, + }; + key && (payload.key = key); + domain && (payload.domain = domain); + externalId && (payload.externalId = externalId); + password && (payload.password = password); + flexible != null && (payload.flexible = flexible); + title && (payload.title = title); + description && (payload.description = description); + image && (payload.image = image); + video && (payload.video = video); + proxy != null && (payload.proxy = proxy); + rewrite != null && (payload.rewrite = rewrite); + ios && (payload.ios = ios); + android && (payload.android = android); + doIndex != null && (payload.doIndex = doIndex); + comments && (payload.comments = comments); + expiresAt && (payload.expiresAt = expiresAt); + expiredUrl && (payload.expiredUrl = expiredUrl); + geo && (payload.geo = geo); + publicStats != null && (payload.publicStats = publicStats); + + const response = await this.codeqr.createLink({ + $, + data: payload, + }); + response && $.export("$summary", "Link created successfully"); + return response; + }, +}; diff --git a/components/codeqr/actions/create-qrcode/create-qrcode.mjs b/components/codeqr/actions/create-qrcode/create-qrcode.mjs new file mode 100644 index 0000000000000..23533ebddf661 --- /dev/null +++ b/components/codeqr/actions/create-qrcode/create-qrcode.mjs @@ -0,0 +1,128 @@ +import codeqr from "../../codeqr.app.mjs"; + +export default { + key: "codeqr-create-qrcode", + name: "Create a QR Code", + description: + "Creates a new QR Code in CodeQR using the QR Codes API. [See the documentation](https://codeqr.mintlify.app/api-reference/endpoint/create-a-qrcode)", + version: "0.0.1", + type: "action", + props: { + codeqr, + type: { + type: "string", + label: "QR Code Type", + description: "Select the type of QR Code to generate.", + options: [ + "url", + "text", + ], + optional: false, + }, + static: { + type: "boolean", + label: "Static/Dynamic", + description: + "Yes = Static QR Code (fixed content); No = Dynamic QR Code (editable content).", + optional: false, + default: true, + }, + url: { + type: "string", + label: "URL", + description: "The destination URL of the QR Code.", + optional: true, + }, + text: { + type: "string", + label: "Text", + description: "Text content stored in the QR Code.", + optional: true, + }, + trackConversion: { + type: "boolean", + label: "Track Conversion", + description: + "Enable tracking of conversions for the QR Code. Only available for dynamic QR Codes.", + optional: true, + }, + title: { + type: "string", + label: "Title", + description: "Title associated with the QR Code.", + optional: true, + }, + bgColor: { + type: "string", + label: "Background Color", + description: "Background color of the QR Code.", + optional: true, + }, + fgColor: { + type: "string", + label: "Foreground Color", + description: "Foreground color of the QR Code.", + optional: true, + }, + showLogo: { + type: "boolean", + label: "Show Logo", + description: "Whether to display a logo in the QR Code.", + optional: true, + }, + src: { + type: "string", + label: "Logo URL", + description: + "URL of the logo to display in the QR Code (only if Show Logo is true).", + optional: true, + }, + comments: { + type: "string", + label: "Comments", + description: "Comments or notes about the QR Code.", + optional: true, + }, + expiresAt: { + type: "string", + label: "Expiration Date", + description: + "The date and time when the short link will expire (ISO 8601). Only available for dynamic QR Codes. E.g. `2025-06-13T05:31:56Z`", + optional: true, + }, + expiredUrl: { + type: "string", + label: "Expired Redirect URL", + description: + "The URL to redirect to when the short link has expired. Only available for dynamic QR Codes.", + optional: true, + }, + }, + async run({ $ }) { + const payload = {}; + for (const key of [ + "type", + "static", + "url", + "text", + "trackConversion", + "title", + "bgColor", + "fgColor", + "showLogo", + "src", + "comments", + "expiresAt", + "expiredUrl", + ]) { + if (this[key] != null) payload[key] = this[key]; + } + + const response = await this.codeqr.createQrcode({ + $, + data: payload, + }); + $.export("$summary", "QR Code created successfully."); + return response; + }, +}; diff --git a/components/codeqr/actions/delete-link/delete-link.mjs b/components/codeqr/actions/delete-link/delete-link.mjs new file mode 100644 index 0000000000000..d1c971f82fcfc --- /dev/null +++ b/components/codeqr/actions/delete-link/delete-link.mjs @@ -0,0 +1,51 @@ +import { ConfigurationError } from "@pipedream/platform"; +import codeqr from "../../codeqr.app.mjs"; + +export default { + key: "codeqr-delete-link", + name: "Delete a Link", + description: "Deletes a short link in CodeQR by linkId or externalId. [See the documentation](https://codeqr.mintlify.app/api-reference/endpoint/delete-a-link)", + version: "0.0.1", + type: "action", + props: { + codeqr, + linkId: { + propDefinition: [ + codeqr, + "linkId", + ], + description: "The unique ID of the link to delete.", + optional: true, + }, + externalId: { + type: "string", + label: "External ID", + description: + "This is the ID of the link in your database. Must be prefixed with ext_.", + optional: true, + }, + }, + async run({ $ }) { + const { + linkId, externalId, + } = this; + if (!linkId && !externalId) { + throw new ConfigurationError( + "Please provide either linkId or externalId to delete the link.", + ); + } + + // Determine identifier to use + const identifier = linkId || externalId; + + // Perform DELETE request to /links/{identifier} + await this.codeqr.deleteLink({ + $, + identifier, + }); + $.export("$summary", `Link deleted successfully (${identifier}).`); + return { + success: true, + }; + }, +}; diff --git a/components/codeqr/actions/delete-qrcode/delete-qrcode.mjs b/components/codeqr/actions/delete-qrcode/delete-qrcode.mjs new file mode 100644 index 0000000000000..ad1a28e7f1f0d --- /dev/null +++ b/components/codeqr/actions/delete-qrcode/delete-qrcode.mjs @@ -0,0 +1,48 @@ +import { ConfigurationError } from "@pipedream/platform"; +import codeqr from "../../codeqr.app.mjs"; + +export default { + key: "codeqr-delete-qrcode", + name: "Delete a QR Code", + description: + "Deletes a QR Code in CodeQR by qrcodeId or externalId. [See the documentation](https://codeqr.mintlify.app/api-reference/endpoint/delete-a-qrcode)", + version: "0.0.1", + type: "action", + props: { + codeqr, + qrcodeId: { + propDefinition: [ + codeqr, + "qrcodeId", + ], + description: "The unique ID of the QR Code to delete.", + optional: true, + }, + externalId: { + type: "string", + label: "External ID", + description: + "ID of the QR Code in your database. Must be prefixed with ext_.", + optional: true, + }, + }, + async run({ $ }) { + const { + qrcodeId, externalId, + } = this; + if (!qrcodeId && !externalId) { + throw new ConfigurationError( + "Please provide either qrcodeId or externalId to delete the QR Code.", + ); + } + const identifier = qrcodeId || externalId; + await this.codeqr.deleteQrcode({ + $, + identifier, + }); + $.export("$summary", `QR Code deleted successfully (${identifier}).`); + return { + success: true, + }; + }, +}; diff --git a/components/codeqr/actions/get-link-info/get-link-info.mjs b/components/codeqr/actions/get-link-info/get-link-info.mjs new file mode 100644 index 0000000000000..441756f3f6fd3 --- /dev/null +++ b/components/codeqr/actions/get-link-info/get-link-info.mjs @@ -0,0 +1,77 @@ +import { ConfigurationError } from "@pipedream/platform"; +import codeqr from "../../codeqr.app.mjs"; + +export default { + key: "codeqr-get-link-info", + name: "Get a Link Info", + description: + "Retrieves a short link from CodeQR by linkId, externalId, or domain/key via query string parameters. [See the documentation](https://codeqr.mintlify.app/api-reference/endpoint/retrieve-a-link)", + version: "0.0.1", + type: "action", + props: { + codeqr, + linkId: { + propDefinition: [ + codeqr, + "linkId", + ], + description: "The unique ID of the short link.", + optional: true, + }, + externalId: { + type: "string", + label: "External ID", + description: + "This is the ID of the link in your database. Must be prefixed with ext_.", + optional: true, + }, + domain: { + type: "string", + label: "Domain", + description: "The domain of the link to retrieve.", + optional: true, + }, + key: { + type: "string", + label: "Key", + description: + "The key of the link to retrieve. E.g., for codeqr.io/github, the key is github.", + optional: true, + }, + }, + + async run({ $ }) { + const { + linkId, externalId, domain, key, + } = this; + if (!linkId && !externalId && !(domain && key)) { + throw new ConfigurationError( + "Please provide linkId, externalId, or both domain and key.", + ); + } + + // Build query parameters + const params = {}; + linkId && (params.linkId = linkId); + externalId && (params.externalId = externalId); + domain && (params.domain = domain); + key && (params.key = key); + + // Make GET request to /links/info with query string + const response = await this.codeqr.getLinkInfo({ + $, + params, + }); + $.export( + "$summary", + `Link retrieved successfully${ + linkId + ? ` (ID: ${linkId})` + : externalId + ? ` (external ID: ${externalId})` + : ` (${domain}/${key})` + }.`, + ); + return response; + }, +}; diff --git a/components/codeqr/actions/get-qrcode-info/get-qrcode-info.mjs b/components/codeqr/actions/get-qrcode-info/get-qrcode-info.mjs new file mode 100644 index 0000000000000..a38c69842962d --- /dev/null +++ b/components/codeqr/actions/get-qrcode-info/get-qrcode-info.mjs @@ -0,0 +1,71 @@ +import { ConfigurationError } from "@pipedream/platform"; +import codeqr from "../../codeqr.app.mjs"; + +export default { + key: "codeqr-get-qrcode-info", + name: "Get QR Code Info", + description: + "Retrieves QR Code info by qrcodeId, externalId, domain, or key via query string. [See the documentation](https://codeqr.mintlify.app/api-reference/endpoint/retrieve-a-qrcode)", + version: "0.0.1", + type: "action", + props: { + codeqr, + qrcodeId: { + propDefinition: [ + codeqr, + "qrcodeId", + ], + description: "The unique ID of the QR Code.", + optional: true, + }, + externalId: { + type: "string", + label: "External ID", + description: + "ID of the QR Code in your database. Must be prefixed with ext_.", + optional: true, + }, + domain: { + type: "string", + label: "Domain", + description: "The domain of the QR Code to retrieve.", + optional: true, + }, + key: { + type: "string", + label: "Key", + description: "The key of the QR Code to retrieve.", + optional: true, + }, + }, + async run({ $ }) { + const { + qrcodeId, externalId, domain, key, + } = this; + if (!qrcodeId && !externalId && !(domain && key)) { + throw new ConfigurationError( + "Please provide qrcodeId, externalId, or both domain and key.", + ); + } + const params = {}; + qrcodeId && (params.qrcodeId = qrcodeId); + externalId && (params.externalId = externalId); + domain && (params.domain = domain); + key && (params.key = key); + const response = await this.codeqr.getQrcodeInfo({ + $, + params, + }); + $.export( + "$summary", + `QR Code info retrieved successfully${ + qrcodeId + ? ` (ID: ${qrcodeId})` + : externalId + ? ` (external ID: ${externalId})` + : ` (${domain}/${key})` + }.`, + ); + return response; + }, +}; diff --git a/components/codeqr/codeqr.app.mjs b/components/codeqr/codeqr.app.mjs index 0e92598ab4fdc..65ab6e2da849f 100644 --- a/components/codeqr/codeqr.app.mjs +++ b/components/codeqr/codeqr.app.mjs @@ -1,11 +1,121 @@ +import { axios } from "@pipedream/platform"; +import constants from "./common/constants.mjs"; + export default { type: "app", app: "codeqr", - propDefinitions: {}, + propDefinitions: { + linkId: { + type: "string", + label: "Link ID", + description: "The unique ID of a link", + async options({ page }) { + const links = await this.listLinks({ + params: { + page: page + 1, + }, + }); + return links?.map((link) => ({ + label: link.url, + value: link.id, + })); + }, + }, + qrcodeId: { + type: "string", + label: "QR Code ID", + description: "The unique ID of a QR Code", + async options({ page }) { + const qrCodes = await this.listQrCodes({ + params: { + page: page + 1, + }, + }); + return qrCodes?.map((qrCode) => ({ + label: qrCode.url, + value: qrCode.id, + })); + }, + }, + }, methods: { - // this.$auth contains connected account data - authKeys() { - console.log(Object.keys(this.$auth)); + getHeader() { + return { + "Content-Type": "application/json", + "Authorization": `Bearer ${this.$auth.oauth_access_token}`, + }; + }, + getUrl(path) { + const { BASE_URL } = constants; + return `${BASE_URL}${path}`; + }, + async makeRequest(args = {}) { + const { + $ = this, method = "get", path, ...opts + } = args; + const config = { + method, + url: this.getUrl(path), + headers: this.getHeader(), + ...opts, + }; + return axios($, config); + }, + async listLinks(opts = {}) { + return this.makeRequest({ + path: "/links", + ...opts, + }); + }, + async listQrCodes(opts = {}) { + return this.makeRequest({ + path: "/qrcodes", + ...opts, + }); + }, + async createLink(opts = {}) { + return this.makeRequest({ + method: "post", + path: "/links", + ...opts, + }); + }, + async getLinkInfo(opts = {}) { + return this.makeRequest({ + path: "/links/info", + ...opts, + }); + }, + async deleteLink({ + identifier, ...opts + }) { + return this.makeRequest({ + method: "delete", + path: `/links/${identifier}`, + ...opts, + }); + }, + async createQrcode(opts = {}) { + return this.makeRequest({ + method: "post", + path: "/qrcodes", + ...opts, + }); + }, + async getQrcodeInfo(opts = {}) { + return this.makeRequest({ + path: "/qrcodes/info", + ...opts, + }); + }, + async deleteQrcode({ + identifier, ...opts + }) { + return this.makeRequest({ + method: "delete", + path: `/qrcodes/${identifier}`, + ...opts, + }); }, }, }; diff --git a/components/codeqr/common/constants.mjs b/components/codeqr/common/constants.mjs new file mode 100644 index 0000000000000..2fa67e331174f --- /dev/null +++ b/components/codeqr/common/constants.mjs @@ -0,0 +1,5 @@ +const BASE_URL = "https://api.codeqr.io"; + +export default { + BASE_URL, +}; diff --git a/components/codeqr/package.json b/components/codeqr/package.json index 0bf13075ed95f..1975edf6ce8e4 100644 --- a/components/codeqr/package.json +++ b/components/codeqr/package.json @@ -1,6 +1,6 @@ { "name": "@pipedream/codeqr", - "version": "0.0.1", + "version": "0.1.2", "description": "Pipedream CodeQR Components", "main": "codeqr.app.mjs", "keywords": [ @@ -11,5 +11,8 @@ "author": "Pipedream (https://pipedream.com/)", "publishConfig": { "access": "public" + }, + "dependencies": { + "@pipedream/platform": "^3.0.0" } -} \ No newline at end of file +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 62e32450448a9..a8d7b7fc9cb32 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -2748,7 +2748,11 @@ importers: components/codeq_natural_language_processing_api: {} - components/codeqr: {} + components/codeqr: + dependencies: + '@pipedream/platform': + specifier: ^3.0.0 + version: 3.1.0 components/codereadr: dependencies: