Skip to content

Workflow Step Type

This module allows you to add custom workflow step.

index.js
const crowdinModule = require('@crowdin/app-project-module');
const app = crowdinModule.express();
const configuration = {
baseUrl: 'https://123.ngrok.io',
clientId: 'clientId',
clientSecret: 'clientSecret',
name: 'Sample App',
identifier: 'sample-app',
description: 'Sample App description',
dbFolder: __dirname,
imagePath: __dirname + '/' + 'logo.png',
workflowStepType: [
{
name: 'Translation Delay',
description: 'Delays strings until the file is fully translated',
boundaries: {
input: {
title: 'Strings',
ports: ['translated']
},
outputs: [
{
title: 'Strings',
port: 'translated'
}
],
},
settingsUiModule: {
formSchema: {
"title": "Workflow Step Settings",
"type": "object",
"required": ["delayHours"],
"properties": {
"delayHours": {
"type": "number",
"title": "Delay (hours)",
"default": 24
}
}
},
formUiSchema: {
"ui:submitButtonOptions": {
"submitText": "Save Settings"
}
}
},
onStepSettingsSave: async ({ organizationId, projectId, stepId, workflowId, settings, context, client }) => {
await crowdinApp.saveMetadata({
id: `form-data-${organizationId}-${projectId}-${stepId}`,
metadata: settings,
crowdinId: organizationId,
});
},
onDeleteStep: async ({ organizationId, projectId, stepId, workflowId, context, client }) => {
await crowdinApp.deleteMetadata(`form-data-${organizationId}-${projectId}-${stepId}`);
}
}
]
};
const crowdinApp = crowdinModule.addCrowdinEndpoints(app, configuration);
app.listen(3000, () => console.log('Crowdin app started'));
ParameterTypeRequiredDescription
namestringYesWorkflow step name.
descriptionstringNoWorkflow step description.
boundariesobjectYesDefines the input and output ports for the workflow step. See Boundaries.
editorModestringNoEditor mode for viewing strings. See Editor Mode.
settingsUiModuleobjectNoSettings UI module configuration. See Settings UI Module.
onStepSettingsSavefunctionYesCalled after saving workflow for custom data management. See details below.
onDeleteStepfunctionYesCalled after deleting a step for custom data management. See details below.

Defines the input and output ports for the workflow step. The boundaries parameter includes one input and up to two outputs.

The input and outputs can take the following port values:

  • false - Used for branching.
  • true - Used for branching.
  • untranslated - Represents an untranslated string.
  • translated - Represents a translated string.
  • approved - Represents an approved string.
  • skipped - Represents a string not sent to a vendor (applicable only for vendor-specific blocks).
  • all - Represents any output.
  • initial - Used for connecting to the start step.

Defines the editor mode for the workflow step. To allow view strings in the editor, this attribute should be defined. Read more about the editor modes.

Available values:

  • comfortable - Comfortable mode.
  • side-by-side - Side-by-side mode.
  • multilingual - Multilingual mode.

Object with the settings UI module configuration. This optional parameter allows you to define a UI for workflow step settings.

You can use either a low-code approach with formSchema and formUiSchema, or provide your own custom UI with uiPath and fileName. Read more in the User Interface documentation.

This optional function is called after saving the workflow for custom data management.

  • organizationId - Crowdin organization ID.
  • projectId - Crowdin project ID.
  • stepId - Workflow step ID.
  • workflowId - Workflow ID.
  • settings - formData object .
  • context - Context object.
  • client - Crowdin API client.

This optional function is called after saving the workflow if a step has been deleted, allowing for custom data management.

  • organizationId - Crowdin organization ID.
  • projectId - Crowdin project ID.
  • stepId - Workflow step ID.
  • workflowId - Workflow ID.
  • context - Context object.
  • client - Crowdin API client.

If you want to receive notifications when workflow step statuses are recalculated for strings, you need to declare a webhooks module and listen to the string.status_on_step.recalculation_triggered event.

index.js
webhooks: [
{
events: ['string.status_on_step.recalculation_triggered'],
callback({client, credentials, events}) {
console.log('String status on step event:', events);
}
}
]

Learn more about webhooks configuration.

Each event in the events array contains the following structure:

FieldTypeDescription
eventstringEvent name: string.status_on_step.recalculation_triggered
stringStatus.statusstringCurrent step status. See Status Values
stringStatus.outputstringOutput from the workflow step when the status is DONE
stringStatus.originEventstringAction that triggered the recalculation. See Origin Events
stringStatus.organizationIdstringCrowdin organization ID
stringStatus.translationobjectSource string details (id, key, text, file, project, etc.)
stringStatus.sourceLanguageobjectSource language information
stringStatus.affectedLanguageobjectTarget language affected by this step
stringStatus.workflowStepobjectWorkflow step that triggered the event
stringStatus.userobjectUser who triggered the action

Below is an example of string.status_on_step.recalculation_triggered event payload:

{
"event": "string.status_on_step.recalculation_triggered",
"stringStatus": {
"status": "NEED_PROCESS",
"output": "",
"originEvent": "string.added",
"organizationId": "200000001",
"translation": {
"id": 1000001,
15 collapsed lines
"identifier": "abc123def456",
"key": "welcome_message",
"text": "Welcome to our application!",
"type": "text",
"context": "homepage",
"maxLength": "0",
"isHidden": false,
"isDuplicate": false,
"masterStringId": null,
"revision": 1,
"hasPlurals": false,
"labelIds": [],
"url": "https://example.crowdin.com/editor/100/500/en-uk#1000001",
"createdAt": "2026-01-21T12:29:56+00:00",
"updatedAt": null,
"file": {
"id": 500,
13 collapsed lines
"name": "strings.xml",
"title": null,
"type": "android",
"path": "/strings.xml",
"status": "active",
"revision": "1",
"branch": {
"id": null
},
"directory": {
"id": null
},
"project": null
},
"project": {
"id": 100,
19 collapsed lines
"userId": 1,
"sourceLanguageId": "en",
"targetLanguageIds": ["uk", "de", "fr"],
"identifier": "abc123def456",
"name": "Example Project",
"createdAt": "2026-01-01T10:00:00+00:00",
"updatedAt": "2026-01-21T12:25:25+00:00",
"lastActivity": "2026-01-21T12:29:56+00:00",
"description": "Sample project description",
"url": "https://example.crowdin.com/u/projects/100",
"cname": null,
"languageAccessPolicy": null,
"visibility": null,
"publicDownloads": null,
"logo": "data:image/png;base64,iVBORw0KGg...",
"isExternal": false,
"externalType": null,
"hasCrowdsourcing": false,
"groupId": "10"
}
},
"sourceLanguage": {
"id": "en",
10 collapsed lines
"name": "English",
"editorCode": "en",
"twoLettersCode": "en",
"threeLettersCode": "eng",
"locale": "en-US",
"androidCode": "en-rUS",
"osxCode": "en.lproj",
"osxLocale": "en",
"textDirection": "ltr",
"dialectOf": null
},
"affectedLanguage": {
"id": "uk",
10 collapsed lines
"name": "Ukrainian",
"editorCode": "uk",
"twoLettersCode": "uk",
"threeLettersCode": "ukr",
"locale": "uk-UA",
"androidCode": "uk-rUA",
"osxCode": "uk.lproj",
"osxLocale": "uk",
"textDirection": "ltr",
"dialectOf": null
},
"workflowStep": {
"id": 1000,
7 collapsed lines
"title": "Translation Delay",
"type": "Application",
"languages": ["uk", "de", "fr"],
"applicationModule": {
"applicationIdentifier": "workflow-step-delay",
"moduleKey": "workflow-step-delay-translation_delay"
}
},
"user": {
"id": "1",
3 collapsed lines
"username": "john_doe",
"fullName": "John Doe",
"avatarUrl": "https://example.crowdin.com/avatar/1/small/avatar_default.png"
}
}
}

The stringStatus.status field indicates the current state of the string in the workflow step:

StatusDescription
NEED_PROCESSString needs to be handled by application
TODOString is awaiting action
DONEString was successfully handled
FAILEDStep failed to handle this string
INCOMPLETEString was previously available in the workflow step but is now missing

The stringStatus.originEvent field indicates what action triggered the workflow step recalculation. Possible values include:

String Events:

  • string.added - A new source string was added
  • string.updated - A source string was modified
  • string.deleted - A source string was deleted
  • string.restored - A deleted string was restored
  • string.triggered - A string was triggered in the workflow
  • string.triggered.after_file_update - A string was triggered after file update

Asset Events:

  • asset.added - A new asset was added
  • asset.triggered - An asset was triggered in the workflow
  • asset.triggered.after_file_update - An asset was triggered after file update

Duplicate String Events:

  • duplicate.string.triggered - A duplicate string was triggered in the workflow

Suggestion Events:

  • suggestion.added - A new translation suggestion was added
  • suggestion.updated - A translation suggestion was modified
  • suggestion.deleted - A translation suggestion was deleted
  • suggestion.restored - A deleted suggestion was restored
  • suggestion.approved - A suggestion was approved
  • suggestion.disapproved - A suggestion was disapproved
  • suggestion.voted - A vote was added to a suggestion
  • suggestion.voteCanceled - A vote was removed from a suggestion

File Events:

  • file.language_exclude - A language was excluded from a file
  • file.branch_protection.changed - Branch protection settings were changed