# Introduction

The Crowdin Apps SDK is a framework that allows you to build custom apps for Crowdin. It provides a set of tools and components that you can use to build your app and integrate it with Crowdin.

It is designed to help you create apps that can be easily installed and used by Crowdin users. It handles the communication between your app and Crowdin, so you can focus on building the functionality of your app.

The SDK automatically adds all the necessary endpoints for the App to work in the Crowdin environment. It can either extend your [Express](http://expressjs.com/) application or even create it for you.

## App Structure

Below is an example of a basic Crowdin App structure.

- index.js
  - logo.png
  - db
    - app.sqlite
  - package.json
  - package-lock.json
  - README.md
  - .env
  - .gitignore
In some cases, you may need to add additional files or folders to your app, depending on the functionality you want to implement.

## Methods

The SDK provides two main methods:

- `createApp` to create complete Express application for you.
- `addCrowdinEndpoints` to extend your Express application.

In both options you will need to provide the Crowdin App configuration object.

### `createApp`

The `createApp` method will create an Express application for you and add all necessary endpoints for the App to work in the Crowdin environment. It will also handle the communication between your app and Crowdin, so you can focus on building the functionality of your app.

Example:

```js title="index.js"
    import crowdinModule from '@crowdin/app-project-module';

    const configuration = {
      name: 'Sample App',
      identifier: 'sample-app',
      description: 'Sample App description',
      // ...
    };

    crowdinModule.createApp(configuration);
    ```
  ```ts title="index.ts"
    import crowdinModule from '@crowdin/app-project-module';
    import type { ClientConfig } from '@crowdin/app-project-module';

    const configuration: ClientConfig = {
      name: 'Sample App',
      identifier: 'sample-app',
      description: 'Sample App description',
      // ...
    };

    crowdinModule.createApp(configuration);
    ```
  ### `addCrowdinEndpoints`

Returns an object with some additional utility methods to help you add your own custom logic:

- `establishCrowdinConnection` - method that accepts a [JWT](https://jwt.io/) token, and an optional `moduleKey` where developer could specify which modules can access this route. The method returns a Crowdin client instance and the context. **Important:** Always provide the `moduleKey` parameter to protect your endpoints. See [Security Best Practices](/app-project-module/security/) for details.
- `encryptCrowdinConnection` - method to encrypt Crowdin connection (in order to pass it to external system, e.g. webhook transition).
- `decryptCrowdinConnection` - method to decrypt Crowdin connection.

Example:

```js title="index.js"
    import crowdinModule from '@crowdin/app-project-module';

    const app = crowdinModule.express();

    const configuration = {
      name: 'Sample App',
      identifier: 'sample-app',
      description: 'Sample App description',
      // ...
    };

    const crowdinApp = crowdinModule.addCrowdinEndpoints(app, configuration);

    app.listen(3000, () =>  console.log('Crowdin app started'));
    ```
  ```ts title="index.ts"
    import crowdinModule from '@crowdin/app-project-module';
    import type { ClientConfig } from '@crowdin/app-project-module';

    const app = crowdinModule.express();

    const configuration: ClientConfig = {
      name: 'Sample App',
      identifier: 'sample-app',
      description: 'Sample App description',
      // ...
    };

    const crowdinApp = crowdinModule.addCrowdinEndpoints(app, configuration);

    app.listen(3000, () =>  console.log('Crowdin app started'));
    ```
  The returned object returns all the [App Metadata](/app-project-module/database/#app-metadata) methods.

The `crowdinModule.express()` is a helper method that returns an instance of the Express application. You can use it to extend your existing Express application.

Example:

```js title="index.js"
app.get('/metadata', async (req, res) => {
  // Always specify moduleKey to restrict access to authorized modules
  const { context } = await crowdinApp.establishCrowdinConnection({
    jwtToken: req.query.jwt,
    moduleKey: ['my-metadata-module'] // Only this module can access this endpoint
  });
  const id = `${context.jwtPayload.context.organization_id}`;
  const metadata = await metadataStore.getMetadata(id) || {};

  res.status(200).send(metadata);
});
```

## Basic Configuration

The basic configuration for the Crowdin App includes the following parameters:

```js title="index.js"
    import crowdinModule from '@crowdin/app-project-module';

    const configuration = {
      name: 'Sample App',
      identifier: 'sample-app',
      description: 'Sample App description',
      baseUrl: process.env.BASE_URL || 'https://123.ngrok.io',
      port: Number(process.env.PORT) || 8080,
      clientId: process.env.CLIENT_ID || 'clientId',
      clientSecret: process.env.CLIENT_SECRET || 'clientSecret',
      dbFolder: import.meta.dirname,
      imagePath: import.meta.dirname + '/logo.png',
      detailPage: 'https://company.com/apps/sample',
      stringBasedAvailable: true
    };

    crowdinModule.createApp(configuration);
    ```
  ```ts title="index.ts"
    import crowdinModule from '@crowdin/app-project-module';
    import type { ClientConfig } from '@crowdin/app-project-module';

    const configuration: ClientConfig = {
      name: 'Sample App',
      identifier: 'sample-app',
      description: 'Sample App description',
      baseUrl: process.env.BASE_URL || 'https://123.ngrok.io',
      port: Number(process.env.PORT) || 8080,
      clientId: process.env.CLIENT_ID || 'clientId',
      clientSecret: process.env.CLIENT_SECRET || 'clientSecret',
      dbFolder: import.meta.dirname,
      imagePath: import.meta.dirname + '/logo.png',
      detailPage: 'https://company.com/apps/sample',
      stringBasedAvailable: true
    };

    crowdinModule.createApp(configuration);
    ```
  | Parameter<div class="w-40"/> | Description                                                                                             |
|------------------------------|---------------------------------------------------------------------------------------------------------|
| `name`                       | The human-readable name of the app.                                                                     |
| `identifier`                 | A unique key to identify the app. This identifier must be less than 255 characters.                     |
| `description`                | The human-readable description of what the app does. The description will be visible in the Crowdin UI. |
| `baseUrl`                    | The base URL of the app, which is used for all communications back to the app instance.                 |
| `port`                       | The port on which the app will listen for incoming requests.                                            |
| `clientId`                   | The client ID of the app.                                                                               |
| `clientSecret`               | The client secret of the app.                                                                           |
| `dbFolder`                   | The path to the folder where the app will store its data.                                               |
| `imagePath`                  | The path to the app logo that will be displayed in the Crowdin UI.                                      |
| `detailPage`                 | The URL to the app's detail page where users can learn more about the app.                              |
| `stringBasedAvailable`       | Flag to indicate if the app works with string-based projects. Default: `false`.                         |

**Note:** To get the `clientId` and `clientSecret`, you need to create a new [OAuth App](https://support.crowdin.com/account-settings/#oauth) in Crowdin.

### `onInstall` Hook

Define the `onInstall` function in the configuration object to be called when the app is installed by a user.

It accepts an object with the following parameters:

- `organization` - Organization ID of the user who installed the app.
- `userId` - User ID of the user who installed the app.
- `client` - Crowdin API client.

Example:

```js title="index.js"
    const configuration = {
      onInstall: async ({ organization, userId, client }) => {
        const promptRequest = {
          name: configuration.name,
          action: `custom:${configuration.identifier}`,
          config: {
            mode: 'advanced',
            prompt: ''
          }
        };

        if (client.organization) {
          await client.aiApi.addAiOrganizationPrompt(promptRequest);
        } else {
          await client.aiApi.addAiUserPrompt(userId, promptRequest);
        }
      }
    }
    ```
  ```ts title="index.ts"
    import type { OnInstallHandler } from '@crowdin/app-project-module';
    import type { AiModel } from '@crowdin/crowdin-api-client';

    const onInstall: OnInstallHandler = async ({ organization, userId, client }) => {
      const promptRequest: AiModel.AddAiPromptRequest = {
        name: configuration.name,
        action: `custom:${configuration.identifier}` as AiModel.Action,
        config: {
          mode: 'advanced',
          prompt: ''
        }
      };

      if (client.organization) {
        await client.aiApi.addAiOrganizationPrompt(promptRequest);
      } else {
        await client.aiApi.addAiUserPrompt(userId, promptRequest);
      }
    };

    const configuration = {
      name: 'Sample App',
      identifier: 'sample-app',
      // ...
      onInstall
    }
    ```
  ### `onUninstall` Hook

Define the `onUninstall` function in the configuration object to be called when the app is uninstalled by a user.

It accepts an object with an `organization` string and an `allCredentials` object which contains `settings` and `credentials` objects.

Example:

```js title="index.js"
    const configuration = {
      onUninstall: async ({ organization, allCredentials }) => {
        console.log('Uninstalling app for organization:', organization);
      }
    }
    ```
  ```ts title="index.ts"
    import type { OnUninstallHandler } from '@crowdin/app-project-module';

    const onUninstall: OnUninstallHandler = async ({ organization, allCredentials }) => {
      console.log('Uninstalling app for organization:', organization);
    };

    const configuration = {
      // ...
      onUninstall
    }
    ```
  ### Permissions

Set the `scopes` parameter in the configuration object to define the [API Scopes](https://support.crowdin.com/developer/understanding-scopes/) that the app will have access to:

```js title="index.js"
import crowdinModule from '@crowdin/app-project-module';

const configuration = {
  scopes: [
    crowdinModule.Scope.PROJECTS
  ]
}
```

It's also possible to define the default permissions for the app that will be pre-selected during the [Installation Process](https://support.crowdin.com/developer/crowdin-apps-installation/):

```js title="index.js"
import crowdinModule from '@crowdin/app-project-module';

const configuration = {
  defaultPermissions: {
    user: crowdinModule.UserPermissions.ALL_MEMBERS,
    project: crowdinModule.ProjectPermissions.RESTRICTED
  }
}
```

### Authentication

By default, the Crowdin Apps SDK uses the [OAuth 2.0](https://oauth.net/2/) protocol for authentication with the custom `crowdin_app` grant type.

It's possible to override the default authentication type:

```js
configuration.authenticationType = 'authorization_code';
```

#### Unauthorized Apps

Usually, Crowdin Apps require authorization to access the Crowdin API. However, in some cases, you may want to create an app that doesn't require authorization.

```js
configuration.authenticationType = 'none';
```

In this case, you can omit the `clientId` and `clientSecret` parameters.

Only a limited set of [modules](/app-project-module/modules/) are available for unauthorized apps:

- `customSpellchecker`
- `editorRightPanel`
- `projectMenu`
- `profileResourcesMenu`
- `profileSettingsMenu`
- `organizationMenu`
- `organizationSettingsMenu`
- `projectMenuCrowdsource`
- `projectTools`
- `projectReports`
- `modal`
- `contextMenu`

**Caution:** The Crowdin `client` and `context` will not be present in the execution context and callbacks for unauthorized apps.

#### Custom Crowdin API User-Agent

The SDK automatically sends the `User-Agent` header in requests to the Crowdin API. By default it looks like this: `app/Sample App node/v21.6.2 Darwin/darwin/23.4.0`.

It's possible to override the default `User-Agent`:

```js
configuration.crowdinApiUserAgent = 'Sample App v1.1.1';
```

## Useful Resources

- [App Functions](/app-project-module/reference/app-functions/) - OAuth and app token helpers, JWT validation, and Crowdin API utilities.
- [`@crowdin/crowdin-api-client-js`](https://github.com/crowdin/crowdin-api-client-js) - Crowdin API client for JavaScript.
- [Crowdin API Overview](https://support.crowdin.com/developer/api/) - Crowdin API documentation.