Workflow Step Type
This module allows you to add custom workflow step.
Sample
Section titled “Sample”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'));Configuration
Section titled “Configuration”| Parameter | Type | Required | Description |
|---|---|---|---|
name | string | Yes | Workflow step name. |
description | string | No | Workflow step description. |
boundaries | object | Yes | Defines the input and output ports for the workflow step. See Boundaries. |
editorMode | string | No | Editor mode for viewing strings. See Editor Mode. |
settingsUiModule | object | No | Settings UI module configuration. See Settings UI Module. |
onStepSettingsSave | function | Yes | Called after saving workflow for custom data management. See details below. |
onDeleteStep | function | Yes | Called after deleting a step for custom data management. See details below. |
Boundaries
Section titled “Boundaries”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.
Editor Mode
Section titled “Editor Mode”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.
Settings UI Module
Section titled “Settings UI Module”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.
onStepSettingsSave Function
Section titled “onStepSettingsSave Function”This optional function is called after saving the workflow for custom data management.
Parameters
Section titled “Parameters”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.
onDeleteStep Function
Section titled “onDeleteStep Function”This optional function is called after saving the workflow if a step has been deleted, allowing for custom data management.
Parameters
Section titled “Parameters”organizationId- Crowdin organization ID.projectId- Crowdin project ID.stepId- Workflow step ID.workflowId- Workflow ID.context- Context object.client- Crowdin API client.
Webhooks
Section titled “Webhooks”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.
Implementation Example
Section titled “Implementation Example”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.
Events Payload
Section titled “Events Payload”Payload Structure
Section titled “Payload Structure”Each event in the events array contains the following structure:
| Field | Type | Description |
|---|---|---|
event | string | Event name: string.status_on_step.recalculation_triggered |
stringStatus.status | string | Current step status. See Status Values |
stringStatus.output | string | Output from the workflow step when the status is DONE |
stringStatus.originEvent | string | Action that triggered the recalculation. See Origin Events |
stringStatus.organizationId | string | Crowdin organization ID |
stringStatus.translation | object | Source string details (id, key, text, file, project, etc.) |
stringStatus.sourceLanguage | object | Source language information |
stringStatus.affectedLanguage | object | Target language affected by this step |
stringStatus.workflowStep | object | Workflow step that triggered the event |
stringStatus.user | object | User who triggered the action |
Event Example
Section titled “Event Example”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" } }}Status Values
Section titled “Status Values”The stringStatus.status field indicates the current state of the string in the workflow step:
| Status | Description |
|---|---|
NEED_PROCESS | String needs to be handled by application |
TODO | String is awaiting action |
DONE | String was successfully handled |
FAILED | Step failed to handle this string |
INCOMPLETE | String was previously available in the workflow step but is now missing |
Origin Events
Section titled “Origin Events”The stringStatus.originEvent field indicates what action triggered the workflow step recalculation. Possible values include:
String Events:
string.added- A new source string was addedstring.updated- A source string was modifiedstring.deleted- A source string was deletedstring.restored- A deleted string was restoredstring.triggered- A string was triggered in the workflowstring.triggered.after_file_update- A string was triggered after file update
Asset Events:
asset.added- A new asset was addedasset.triggered- An asset was triggered in the workflowasset.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 addedsuggestion.updated- A translation suggestion was modifiedsuggestion.deleted- A translation suggestion was deletedsuggestion.restored- A deleted suggestion was restoredsuggestion.approved- A suggestion was approvedsuggestion.disapproved- A suggestion was disapprovedsuggestion.voted- A vote was added to a suggestionsuggestion.voteCanceled- A vote was removed from a suggestion
File Events:
file.language_exclude- A language was excluded from a filefile.branch_protection.changed- Branch protection settings were changed