Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions bun.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

14 changes: 14 additions & 0 deletions docs/install/reference/breaking-changes.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,20 @@ icon: "hammer"

### What has changed?

#### Instagram for Business no longer reports success when a reel fails to publish

Publishing a reel uploads the video to Instagram and then waits for Instagram to finish processing it before the post goes live. The piece used to give up after about a hundred seconds and return the boolean `false`, which the flow recorded as a **successful** step. Anything downstream carried on with `false` where a media id was expected, and nothing in the run said the post had not been published.

Upload Reel now throws when Instagram reports an error, and waits up to five minutes, which is the window Instagram's own guidance describes. Upload Photo waits for the same signal instead of publishing blind, so a container Instagram rejects now surfaces as a failed step rather than a confusing error from the next call.

The piece also moves from Graph API v17.0, which expired on 12 September 2025, to v23.0.

#### What you need to do

If a flow branches on the output of Upload Reel being `false`, that branch will no longer be taken — the step fails instead. Handle the failure with the step's error handling options, or with a branch on the run status.

Reconnect your Instagram connections. The piece now requests the permissions needed for comments, insights, content management, direct messages and Page metadata, and a token issued before this release does not carry them. Steps that were saved earlier will also ask you to select the Page again, because messaging needs the Facebook Page id that the Page dropdown now stores.

#### The AI provider config endpoint is removed

`GET /v1/ai-providers/:provider/config` returned a provider's decrypted credentials so that an AI step running inside the engine could build the model itself and call the provider directly. AI steps no longer work that way: the worker resolves the credentials and makes the call, which is what lets Activepieces charge managed AI on what the call actually cost. Nothing in the product calls the endpoint any more, and it now returns `404`.
Expand Down
2 changes: 1 addition & 1 deletion packages/pieces/community/asana/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@activepieces/piece-asana",
"version": "0.5.0",
"version": "0.6.0",
"main": "./dist/src/index.js",
"types": "./dist/src/index.d.ts",
"scripts": {
Expand Down
411 changes: 410 additions & 1 deletion packages/pieces/community/asana/src/i18n/translation.json

Large diffs are not rendered by default.

133 changes: 131 additions & 2 deletions packages/pieces/community/asana/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,152 @@
import { createCustomApiCallAction } from '@activepieces/pieces-common';
import {
OAuth2PropertyValue,
PieceAuth,
createPiece,
} from '@activepieces/pieces-framework';
import { PieceCategory } from '@activepieces/pieces-framework';
import { asanaCreateTaskAction } from './lib/actions/create-task';
import { asanaGetCurrentUserAction } from './lib/actions/ai/get-current-user';
import { asanaListWorkspacesAction } from './lib/actions/ai/list-workspaces';
import { asanaListTeamsAction } from './lib/actions/ai/list-teams';
import { asanaSearchWorkspaceObjectsAction } from './lib/actions/ai/search-workspace-objects';
import { asanaCreateProjectTaskAction } from './lib/actions/ai/create-project-task';
import { asanaGetTaskAction } from './lib/actions/ai/get-task';
import { asanaUpdateTaskAction } from './lib/actions/ai/update-task';
import { asanaDeleteTaskAction } from './lib/actions/ai/delete-task';
import { asanaDuplicateTaskAction } from './lib/actions/ai/duplicate-task';
import { asanaGetJobAction } from './lib/actions/ai/get-job';
import { asanaListAssignedTasksAction } from './lib/actions/ai/list-assigned-tasks';
import { asanaListProjectTasksAction } from './lib/actions/ai/list-project-tasks';
import { asanaListSubtasksAction } from './lib/actions/ai/list-subtasks';
import { asanaCreateSubtaskAction } from './lib/actions/ai/create-subtask';
import { asanaSetTaskParentAction } from './lib/actions/ai/set-task-parent';
import { asanaListTaskProjectsAction } from './lib/actions/ai/list-task-projects';
import { asanaAddTaskToProjectAction } from './lib/actions/ai/add-task-to-project';
import { asanaRemoveTaskFromProjectAction } from './lib/actions/ai/remove-task-from-project';
import { asanaAddTaskFollowersAction } from './lib/actions/ai/add-task-followers';
import { asanaRemoveTaskFollowersAction } from './lib/actions/ai/remove-task-followers';
import { asanaAddTaskCommentAction } from './lib/actions/ai/add-task-comment';
import { asanaListTaskStoriesAction } from './lib/actions/ai/list-task-stories';
import { asanaGetStoryAction } from './lib/actions/ai/get-story';
import { asanaUpdateCommentAction } from './lib/actions/ai/update-comment';
import { asanaDeleteCommentAction } from './lib/actions/ai/delete-comment';
import { asanaCreateProjectAction } from './lib/actions/ai/create-project';
import { asanaGetProjectAction } from './lib/actions/ai/get-project';
import { asanaListProjectsAction } from './lib/actions/ai/list-projects';
import { asanaUpdateProjectAction } from './lib/actions/ai/update-project';
import { asanaDeleteProjectAction } from './lib/actions/ai/delete-project';
import { asanaDuplicateProjectAction } from './lib/actions/ai/duplicate-project';
import { asanaGetProjectTaskCountsAction } from './lib/actions/ai/get-project-task-counts';
import { asanaAddProjectMembersAction } from './lib/actions/ai/add-project-members';
import { asanaRemoveProjectMembersAction } from './lib/actions/ai/remove-project-members';
import { asanaAddProjectFollowersAction } from './lib/actions/ai/add-project-followers';
import { asanaRemoveProjectFollowersAction } from './lib/actions/ai/remove-project-followers';
import { asanaCreateSectionAction } from './lib/actions/ai/create-section';
import { asanaGetSectionAction } from './lib/actions/ai/get-section';
import { asanaListSectionsAction } from './lib/actions/ai/list-sections';
import { asanaUpdateSectionAction } from './lib/actions/ai/update-section';
import { asanaDeleteSectionAction } from './lib/actions/ai/delete-section';
import { asanaMoveSectionAction } from './lib/actions/ai/move-section';
import { asanaMoveTaskToSectionAction } from './lib/actions/ai/move-task-to-section';
import { asanaListSectionTasksAction } from './lib/actions/ai/list-section-tasks';
import { asanaCreateMembershipAction } from './lib/actions/ai/create-membership';
import { asanaGetMembershipAction } from './lib/actions/ai/get-membership';
import { asanaListMembershipsAction } from './lib/actions/ai/list-memberships';
import { asanaDeleteMembershipAction } from './lib/actions/ai/delete-membership';
import { asanaCreateStatusUpdateAction } from './lib/actions/ai/create-status-update';
import { asanaGetStatusUpdateAction } from './lib/actions/ai/get-status-update';
import { asanaListStatusUpdatesAction } from './lib/actions/ai/list-status-updates';
import { asanaDeleteStatusUpdateAction } from './lib/actions/ai/delete-status-update';
import { asanaCreateProjectBriefAction } from './lib/actions/ai/create-project-brief';
import { asanaGetProjectBriefAction } from './lib/actions/ai/get-project-brief';
import { asanaUpdateProjectBriefAction } from './lib/actions/ai/update-project-brief';
import { asanaDeleteProjectBriefAction } from './lib/actions/ai/delete-project-brief';
import { asanaCreateTagAction } from './lib/actions/ai/create-tag';
import { asanaGetTagAction } from './lib/actions/ai/get-tag';
import { asanaListTagsAction } from './lib/actions/ai/list-tags';
import { asanaUpdateTagAction } from './lib/actions/ai/update-tag';
import { asanaDeleteTagAction } from './lib/actions/ai/delete-tag';
import { asanaListTaskTagsAction } from './lib/actions/ai/list-task-tags';
import { asanaAddTagToTaskAction } from './lib/actions/ai/add-tag-to-task';
import { asanaRemoveTagFromTaskAction } from './lib/actions/ai/remove-tag-from-task';
import { asanaListTagTasksAction } from './lib/actions/ai/list-tag-tasks';
import { asanaAuth } from './lib/auth';

export const asana = createPiece({
displayName: 'Asana',
description: "Work management platform designed to help teams organize, track, and manage their work.",
minimumSupportedRelease: '0.30.0',
minimumSupportedRelease: '0.88.2',
logoUrl: 'https://cdn.activepieces.com/pieces/asana.png',
categories: [PieceCategory.PRODUCTIVITY],
authors: ["ShayPunter","kishanprmr","MoShizzle","khaledmashaly","abuaboud"],
auth: asanaAuth,
actions: [
asanaCreateTaskAction,
asanaGetCurrentUserAction,
asanaListWorkspacesAction,
asanaListTeamsAction,
asanaSearchWorkspaceObjectsAction,
asanaCreateProjectTaskAction,
asanaGetTaskAction,
asanaUpdateTaskAction,
asanaDeleteTaskAction,
asanaDuplicateTaskAction,
asanaGetJobAction,
asanaListAssignedTasksAction,
asanaListProjectTasksAction,
asanaListSubtasksAction,
asanaCreateSubtaskAction,
asanaSetTaskParentAction,
asanaListTaskProjectsAction,
asanaAddTaskToProjectAction,
asanaRemoveTaskFromProjectAction,
asanaAddTaskFollowersAction,
asanaRemoveTaskFollowersAction,
asanaAddTaskCommentAction,
asanaListTaskStoriesAction,
asanaGetStoryAction,
asanaUpdateCommentAction,
asanaDeleteCommentAction,
asanaCreateProjectAction,
asanaGetProjectAction,
asanaListProjectsAction,
asanaUpdateProjectAction,
asanaDeleteProjectAction,
asanaDuplicateProjectAction,
asanaGetProjectTaskCountsAction,
asanaAddProjectMembersAction,
asanaRemoveProjectMembersAction,
asanaAddProjectFollowersAction,
asanaRemoveProjectFollowersAction,
asanaCreateSectionAction,
asanaGetSectionAction,
asanaListSectionsAction,
asanaUpdateSectionAction,
asanaDeleteSectionAction,
asanaMoveSectionAction,
asanaMoveTaskToSectionAction,
asanaListSectionTasksAction,
asanaCreateMembershipAction,
asanaGetMembershipAction,
asanaListMembershipsAction,
asanaDeleteMembershipAction,
asanaCreateStatusUpdateAction,
asanaGetStatusUpdateAction,
asanaListStatusUpdatesAction,
asanaDeleteStatusUpdateAction,
asanaCreateProjectBriefAction,
asanaGetProjectBriefAction,
asanaUpdateProjectBriefAction,
asanaDeleteProjectBriefAction,
asanaCreateTagAction,
asanaGetTagAction,
asanaListTagsAction,
asanaUpdateTagAction,
asanaDeleteTagAction,
asanaListTaskTagsAction,
asanaAddTagToTaskAction,
asanaRemoveTagFromTaskAction,
asanaListTagTasksAction,
createCustomApiCallAction({
baseUrl: () => `https://app.asana.com/api/1.0`,
auth: asanaAuth,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { createAction, Property } from '@activepieces/pieces-framework';
import { HttpMethod } from '@activepieces/pieces-common';
import { asanaAuth } from '../../auth';
import { ASANA_FIELDS, AsanaRecord, asanaClient, asanaUtils } from '../../common/client';
import { asanaProjectOutputSchema } from '../../output-schemas';

export const asanaAddProjectFollowersAction = createAction({
auth: asanaAuth,
name: 'add_project_followers',
classification: 'WRITE',
displayName: 'Add Project Followers',
description: 'Subscribe users to "task added" notifications of an Asana project.',
audience: 'ai',
aiMetadata: {
description:
'Adds users as followers of a project so they are notified when tasks are added, and returns the updated project. Users who are not yet members also become members. Existing followers are unaffected, so repeating the call converges and is safe to retry.',
idempotent: true,
},
outputSchema: asanaProjectOutputSchema,
props: {
project: Property.ShortText({
displayName: 'Project GID',
description: 'Gid of the project. Obtain it from List Projects or Search Workspace Objects.',
required: true,
}),
followers: Property.Array({
displayName: 'Followers',
description: 'Users to add, one per item: "me", an email address or a user gid.',
required: true,
}),
},
async run(context) {
const { project, followers } = context.propsValue;
const followerList = asanaUtils.toStringArray(followers);
if (followerList.length === 0) {
throw new Error('Followers must contain at least one user ("me", an email address or a user gid).');
}
return asanaClient.asanaData<AsanaRecord>({
auth: context.auth,
method: HttpMethod.POST,
path: `/projects/${asanaUtils.pathSegment(project)}/addFollowers`,
operation: 'Add Project Followers',
query: { opt_fields: ASANA_FIELDS.project },
data: { followers: followerList.join(',') },
});
},
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { createAction, Property } from '@activepieces/pieces-framework';
import { HttpMethod } from '@activepieces/pieces-common';
import { asanaAuth } from '../../auth';
import { ASANA_FIELDS, AsanaRecord, asanaClient, asanaUtils } from '../../common/client';
import { asanaProjectOutputSchema } from '../../output-schemas';

export const asanaAddProjectMembersAction = createAction({
auth: asanaAuth,
name: 'add_project_members',
classification: 'WRITE',
displayName: 'Add Project Members',
description: 'Give users access to an Asana project.',
audience: 'ai',
aiMetadata: {
description:
'Adds users as members of a project with the project\'s default access level and returns the updated project; their notification settings may also make them followers. Use Create Membership to add a team or to choose the access level. Existing members are unaffected, so repeating the call converges and is safe to retry.',
idempotent: true,
},
outputSchema: asanaProjectOutputSchema,
props: {
project: Property.ShortText({
displayName: 'Project GID',
description: 'Gid of the project. Obtain it from List Projects or Search Workspace Objects.',
required: true,
}),
members: Property.Array({
displayName: 'Members',
description: 'Users to add, one per item: "me", an email address or a user gid.',
required: true,
}),
},
async run(context) {
const { project, members } = context.propsValue;
const memberList = asanaUtils.toStringArray(members);
if (memberList.length === 0) {
throw new Error('Members must contain at least one user ("me", an email address or a user gid).');
}
return asanaClient.asanaData<AsanaRecord>({
auth: context.auth,
method: HttpMethod.POST,
path: `/projects/${asanaUtils.pathSegment(project)}/addMembers`,
operation: 'Add Project Members',
query: { opt_fields: ASANA_FIELDS.project },
data: { members: memberList.join(',') },
});
},
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { createAction, Property } from '@activepieces/pieces-framework';
import { HttpMethod } from '@activepieces/pieces-common';
import { asanaAuth } from '../../auth';
import { asanaClient, asanaUtils } from '../../common/client';
import { asanaTaskTagChangeOutputSchema } from '../../output-schemas';

export const asanaAddTagToTaskAction = createAction({
auth: asanaAuth,
name: 'add_tag_to_task',
classification: 'WRITE',
displayName: 'Add Tag to Task',
description: 'Apply a tag to an Asana task.',
audience: 'ai',
aiMetadata: {
description:
'Applies an existing tag to a task, by tag gid (resolve a name with List Tags or Search Workspace Objects, or make a new tag with Create Tag). Applying a tag the task already has converges, so it is safe to retry.',
idempotent: true,
},
outputSchema: asanaTaskTagChangeOutputSchema,
props: {
task: Property.ShortText({
displayName: 'Task GID',
description: 'Gid of the task. Obtain it from List Project Tasks or Search Workspace Objects.',
required: true,
}),
tag: Property.ShortText({
displayName: 'Tag GID',
description: 'Gid of the tag to apply. Obtain it from List Tags or Search Workspace Objects (object type tag).',
required: true,
}),
},
async run(context) {
const { task, tag } = context.propsValue;
await asanaClient.asanaEmpty({
auth: context.auth,
method: HttpMethod.POST,
path: `/tasks/${asanaUtils.pathSegment(task)}/addTag`,
operation: 'Add Tag to Task',
data: { tag: tag.trim() },
});
return { success: true, task_gid: task.trim(), tag_gid: tag.trim() };
},
});
Loading
Loading