Importing data from Canny to Cycle has never been easier! This guide will walk you through using the Cycle API to seamlessly transfer your valuable customer feedback from Canny into your Cycle inbox alongside your features from your Canny roadmap.
For more details and to access the script, check out the code repository. 🚀
What this script does
Fetches each Canny post from all your boards.
Retrieves votes and comments for each post.
Creates a document with the desired type (from config) for each post in Cycle.
Links feedback to each vote for the created document.
Tags all imported documents with an imported property, allowing you to filter or bulk delete these documents if needed.
How to use the script
Configure Your Data:
Fill your data in ./config.ts.
Run the Import Script:
Execute the command npm run import:canny.
Code overview
Let’s try to understand how the script works and why it’s done that way:
Cycle core logic
From your Cycle workspace, you get a unique slug, the script will proceed as follows
Retrieve the “productId” from the slug (”product” is the old name for “workspace”)
Create the import attribute (”attribute” is the tech name for “properties”) in order to let you filter on imported docs in views
Link the attributes to doc type in order to assign the attribute value to imported docs
Fetch Canny data and create docs out of it
Fetching Canny
The script defines a fetchCanny function to retrieve data from Canny's API. It handles pagination to ensure all posts, comments, and votes are fetched.
The script creates documents in Cycle for each post, along with linked feedback for each vote:
const createDocAndFeedback = async (workspaceId: string, post: Post, comments: Comment[], votes: Vote[], importAttributeData: any, docTypeToImport: any, insight: any) => {
constcreatedDoc=awaitcreateDoc({ workspaceId, attributes: [importAttributeData], contentJSON:getJSONDocContent({ post, comments:getFormattedComments(comments) }), doctypeId:docTypeToImport.id, title:post.title, });if (createdDoc) {for (constvoteof votes) {constvoteFeedback=awaitcreateFeedback({ workspaceId, attributes: [importAttributeData], content:`Vote for ${post.title}`, sourceUrl:post.url, title:`Vote from ${vote.voter?.name ||'unknown'} on ${post.title}`, customerEmail:vote.voter.email, });awaitcreateDoc({ doctypeId:insight.id, workspaceId, attributes: [importAttributeData], contentJSON:'', title:`Vote from ${vote.voter?.name ||'unknown'} on ${post.title}`, customerId:voteFeedback.customer.id, docSourceId:voteFeedback.id, parentId:createdDoc.id, }); } }};
As you can see, the way to have feedback connected to features in Cycle is using Insight concept which is a certain type of doc plus a link between feedback and feature. The link between a feedback and an insight is made via a doc containing a “docSourceId”. Then to link the insight to the feature, use a the “parentId” field.
So you always need to have the parent before the child in any relation import in Cycle.
The right order to proceed is then:
Create the feature (Well the name is up to you since it’s fully customisable)
Create the feedback
Create the link between both (insight)
Hope this will help you to build crazy integrations with our API.
For more details and to access the script, check out the code repository. 🚀