Skip to content
Open
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
8 changes: 5 additions & 3 deletions articles/Extensibility/api/Extensibility-API.v1.json
Original file line number Diff line number Diff line change
Expand Up @@ -1291,7 +1291,8 @@
"description",
"version",
"releaseNotes",
"descriptorVersion"
"descriptorVersion",
"vendor"
],
"properties": {
"name": {
Expand Down Expand Up @@ -1357,7 +1358,7 @@
"baseUrl": {
"type": "string",
"description": "The prefix for all calls to the app service.",
"example": "somehostname.net/service/v1"
"examples": ["https://somehostname.net/service/v1", "https://somehostname.net" ]
},
"regionalBaseUrls": {
"$ref": "#/components/schemas/RegionalBaseUrls"
Expand Down Expand Up @@ -2913,7 +2914,8 @@
],
"properties": {
"url": {
"type": "string"
"type": "string",
"examples": [ "/webhook-receiving-endpoint" ]
},
"eventTypes": {
"type": "array",
Expand Down
23 changes: 16 additions & 7 deletions articles/Extensibility/docs/development/App-Descriptor.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,12 @@ The descriptor model defines attributes that provide basic information like `nam

## Version

The `version` field is used by Trados to detect newer versions of the app's descriptor. You should increase the version every time you make a change in the descriptor, otherwise, your changes won't reach the registered instance from Trados.
The `version` field is used by Trados to detect newer versions of the app's descriptor.

The version is periodically checked by Trados by performing GET descriptor requests.
> [!WARNING]
> **You must increment the version every time you make changes to the descriptor.** If the version is not updated, Trados will not detect your changes—they will be silently ignored until the version number changes. This is a common gotcha that can cause confusion when changes don't take effect.

Trados periodically checks the descriptor by performing GET descriptor requests. When it detects a new version number, it will fetch the updated descriptor. If the version hasn't changed, Trados will assume the descriptor is unchanged and will not pick up any modifications you've made.

## Base URL

Expand All @@ -32,26 +35,30 @@ For example, if we have in the descriptor:
Trados will make scheduled GET requests to `https://foo.com/health` to check the health.

### Changing Base URL
For various reasons you might want to change the host of your App and that can be done from Trados management UI.

For various reasons you might want to change the host of your App and that can be done from Trados management UI.

When updating the host, you also have to update `baseUrl` to match the new host.

You must still support old host as all previous installs will be calling on the `baseUrl` at the installed version. In order to be able to decomission the old host, you must make sure all consumers updated their installs to latest version.

> [!WARNING]
> Because request authentication is based on Audience matching 'baseUrl', you must ensure that your authentication code can accept both old and new `baseUrl`. See [Request Authentication](Request-Authentication.md).
> Because request authentication is based on Audience matching 'baseUrl', you must ensure that your authentication code can accept both old and new `baseUrl`. See [Request Authentication](Request-Authentication.md).

## Standard Endpoints

An app must implement a set of standard endpoints that are defined in the descriptor schema under `standardEndpoints`. Not all endpoints are required, as you can see in the descriptor schema.
The `standardEndpoints` section is optional in the descriptor contract. However, it becomes required when the app defines `extensions`, since Trados needs to know how to interact with the app through those endpoints. If your app only registers `webhooks` and does not provide any extensions, you may omit this section entirely.

When present, not all endpoints within `standardEndpoints` are required — refer to the descriptor schema for which ones are mandatory.

All endpoint paths need to start with the leading character `/` and are relative to `baseUrl`.
All endpoint paths need to start with the leading character `/` and are relative to `baseUrl`.

Standard endpoints are defined under the `Standard` tag. The actual path should be replaced with the one you defined in the descriptor. The expected Request and Responses are defined and should be used as reference.
In the contract, standard endpoints are defined under the `Standard` tag. The actual path should be replaced with the one you defined in the descriptor. The expected Request and Responses are defined and should be used as reference.

### Lifecycle Endpoint

Additionally, in the `standardEnpoints` section we can find the lifecycle endpoint. This endpoint needs to handle different events sent by Trados (similar to webhooks). For instance, when the app is being installed on a certain account, Trados will send an `INSTALLED` event along with some data for that account. The app should react and save the received data.

- `appLifecycle` - is used for all types of events: `REGISTERED`, `UNREGISTERED`, `INSTALLED`, and `UNINSTALLED`. See the contract [here](../../api/Extensibility-API.v1-fv.html#/operations/Lifecycle).

> [!NOTE]
Expand All @@ -62,11 +69,13 @@ Additionally, in the `standardEnpoints` section we can find the lifecycle endpoi
The list of provided extensions is described under `extensions`. An app can provide none, one, or more extensions.

Any extension will have the generic set of properties:

- `extensionPointId` that specifies what extension point it extends. Can be only a value specified in the descriptor contract (ex: `lc.mtprovider`).
- `extensionPointVersion` defines the version of the extension point it extends. The allowed value is defined in the descriptor contract (ex: `1.0`).
- `name` is defined by the developer, to provide a friendly name for the extension. This is useful when the app provides multiple extensions.
- `description` will have a summary describing the extension.
- `configuration` defines the extension and the structure depends on the type of the extension that is implemented.

```json
{
...
Expand Down
88 changes: 81 additions & 7 deletions articles/Extensibility/docs/development/Webhooks.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Webhooks

Apps can specify a list of webhooks that will be registered automatically when the app is installed on an account. That allows the app to specify a list of webhooks and consume them, allowing for asynchronous scenarios where the app can wait for events instead of polling constantly to check for a particular event or state improving performance both for the app and for Trados.
Apps can specify a list of webhooks that will be registered automatically when the app is installed on an account. That allows the app to specify a list of webhooks and consume them, allowing for asynchronous scenarios where the app can wait for events instead of polling constantly to check for a particular event or state improving performance both for the app and for Trados.

## Constraints

Expand All @@ -11,13 +11,14 @@ Not all accounts have webhooks enabled. Installing of the app requiring webhooks

## Setup

The required list of webhooks must be specified in the descriptor in the `webhooks` property.
The required list of webhooks must be specified in the descriptor in the `webhooks` property.

`webhooks` is an array of URLs and corresponding event types. You can specify a single URL for all webhook event types, or one URL for each event type, or any combination. This is done for maximum flexibility so you can decide if you want to ingest all webhooks through a single endpoint or have multiple endpoints maybe by event type or category, etc.

`url` can be an absolute URL or a path relative to `basePath`.
`url` can be an absolute URL or a path relative to `basePath`.

Example of a `webhooks` property in the app descriptor:

```json
{
...
Expand All @@ -37,20 +38,93 @@ Example of a `webhooks` property in the app descriptor:
...
}
```
That example will subscribe to `PROJECT.TASK.ACCEPTED` and `PROJECT.TASK.CREATED` events and will receive these events on the `<basePath>/webhooks-endpoint` URL.

This example will subscribe to `PROJECT.TASK.ACCEPTED` and `PROJECT.TASK.CREATED` events and will receive these events on the `<basePath>/webhooks-endpoint` URL.

Alternatively, you can split the events across multiple endpoints:

```json
{
...
"webhooks": [
{
"url": "/project-webhooks-endpoint",
"eventTypes": [
{
"eventType": "PROJECT.CREATED"
},
{
"eventType": "PROJECT.STARTED"
}
]
},
{
"url": "/task-webhooks-endpoint",
"eventTypes": [
{
"eventType": "PROJECT.TASK.CREATED"
}
]
}
]
...
}
```

In this example, `PROJECT.CREATED` and `PROJECT.STARTED` events are routed to `/project-webhooks-endpoint`, while `PROJECT.TASK.CREATED` events are sent to a separate `/task-webhooks-endpoint`.

## Webhook-only app

If you only need to subscribe to webhooks and do not require other extensibility features, you can register a webhook-only app. This creates a minimal app that does not require any additional endpoints.

The following example shows a minimal descriptor that only requires a descriptor endpoint and a webhook receiver endpoint:

```json
{
"name": "my-extension",
"version": "1.0.0",
"description": "A sample extension",
"releaseNotes": "Initial release",
"descriptorVersion": "1.4",
"scopes": [ "TENANT_READ"],
"webhooks": [
{
"url": "/webhook-endpoint",
"eventTypes": [
{
"eventType": "PROJECT.CREATED"
},
{
"eventType": "PROJECT.STARTED"
},
{
"eventType": "PROJECT.TASK.CREATED"
}
]
}
],
"baseUrl": "https://your.site",
"vendor": {
"name": "Your Company",
"url": "https://your.site",
"email": "support@your.site"
}
}
```

When installed, this app will register the `/webhook-endpoint` URL to receive `PROJECT.CREATED`, `PROJECT.STARTED`, and `PROJECT.TASK.CREATED` events. No other endpoints are required.

## Webhook events and payloads

Webhooks for apps are sent in a batched format.
Webhooks for apps are sent in a batched format.

The webhook payload description can be found in our [Trados Cloud Platform API documentation](https://eu.cloud.trados.com/lc/api-docs/batched-webhooks).
The webhook payload description can be found in our [Trados Cloud Platform API documentation](https://eu.cloud.trados.com/lc/api-docs/batched-webhooks).

Webhooks are grouped in batches by callback URL, so it is likely that events from different tenants will be included in the same batch. It is the responsability of the app developer to handle the events from the batch accordingly to their `accountId` from the event body.

> [!NOTE]
> **Note:** For *Webhook Authenticity*, ignore the described behavior in the above link and only consider the following chapter about *Signature Validation*.


## Signature Validation

Unlike webhooks created in the UI through the Applications, webhooks that are declared in the descriptor are received using app signature. The endpoint that receives the webhook should treat these as signed with JWS, just as any other endpoint in the app. See [Request Authentication](Request-Authentication.md) page for more details.
Loading