# Welcome!

Here you'll find all the documentation you need to get up and running with the TextMaster API.

## Want to jump right in?

Feeling like an eager beaver? Jump in to the quick start docs and get making your first request:

{% content-ref url="/pages/kyUn6dlB1Mp8CUL1wh9o" %}
[Quick Start](/quick-start)
{% endcontent-ref %}

## Want to deep dive?

Dive a little deeper and get more details about available resources:

{% content-ref url="/pages/iQQLDpOFxLu35fhVd8pq" %}
[Resources in the REST API](/overview/resources-in-the-rest-api)
{% endcontent-ref %}

Or, start learning about OAuth Apps:

{% content-ref url="/pages/qYrz6Sqapsud2BoZSSbt" %}
[About OAuth Apps](/apps/about-oauth-apps)
{% endcontent-ref %}

Or, start exploring our API reference to get an idea of everything that's possible with the API:

{% content-ref url="/pages/2Z4rP31NK4SYpfSsPvfS" %}
[Reference](/reference/abilities)
{% endcontent-ref %}

Finally, don't forget to read about Webhooks to build or set up powerful integrations:

{% content-ref url="/pages/D7qSqAH0UxaINYqHIGBE" %}
[Webhooks & Events](/webhooks-and-events/webhooks)
{% endcontent-ref %}


# Quick Start

Learn the foundations for using the REST API, starting with authentication and some endpoint examples.

Let's walk through core API concepts as we tackle some everyday use cases.

## Overview

Most applications will use an existing wrapper library in the language of your choice, but it's important to familiarize yourself with the underlying API HTTP methods first.

There's no easier way to kick the tires than through [cURL](http://curl.haxx.se/).

### Hello World

Let's start by testing our setup. Open up a command prompt and enter the following command:

```shell
$ curl https://api.textmaster.com/ping

> {"message":"Textmaster API at your service"}%
```

### Authentication

To be honest, doing anything interesting with the TextMaster API requires [authentication](/overview/authentication).

#### Using a signature

The easiest way to authenticate with TextMaster API is by using the [signature authentication strategy](/overview/authentication#signature).

{% hint style="warning" %}
**Warning:** We strongly advise to use the signature strategy **only** for test purposes. Prefer using OAuth2 tokens for production use cases.
{% endhint %}

In the top-bar navigation of TextMaster's application, click on **API & Loop**

![](https://2001322700-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FiLJk7NFk70NqFY1GU7Oi%2Fuploads%2FVTbiBp7mMUZYzEbpMgx8%2Fcreating-oauth-app-step-1.png?alt=media\&token=1f9cc0d6-5bd5-4390-8ec1-34ae2ff3c77e)

In the left panel, **copy & paste your api key and secret**.

![](https://2001322700-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FiLJk7NFk70NqFY1GU7Oi%2Fuploads%2F77HIJmzcIIAYe3cz505t%2Fcreating-oauth-app-step-2.png?alt=media\&token=a5784a2c-045b-4b31-935d-7ef12d4a81d6)

Set the following shell variables:

```shell
export APIKEY=yourApikey
export APISECRET=yourApiSecret
export DATE=$(date -u +"%Y-%m-%d %H:%M:%S")
export SIGNATURE=$(echo -n $APISECRET$DATE | openssl sha1 | sed 's/.*= //')
```

Verify the validity of your signature by executing the following request:

```shell
$ curl "https://api.textmaster.com/test" \
  -H "Apikey: $APIKEY" \
  -H "Date: $DATE" \
  -H "Signature: $SIGNATURE"
  
> {"message":"You sent the following headers: { HTTP_APIKEY: yourApikey, HTTP_DATE: 2022-01-20 15:25:06, HTTP_SIGNATURE: dc5b5... }. Your api key is valid. Your date is well formatted. Your signature is valid."}%
```

#### Get your own user profile

When properly authenticated, you can take advantage of the permissions associated with your account on TextMaster. For example, try getting your own user profile:

```shell
curl "https://api.textmaster.com/v1/clients/users/me" \
  -H "Apikey: $APIKEY" \
  -H "Date: $DATE" \
  -H "Signature: $SIGNATURE"
```

#### Using OAuth tokens for apps

Apps that need to read or write private information using the API on behalf of another user should use [OAuth](/apps/about-oauth-apps).

OAuth uses *tokens*. Tokens provide two big features:

* **Revokable access:** users can revoke authorization to third party apps at any time
* **Limited access:** users can review the specific access that a token will provide before authorizing a third party app

Tokens should be created via a [web flow](/apps/building-oauth-apps/authorizing-oauth-apps#web-application-flow). An application sends users to TextMaster to log in. TextMaster then presents a dialog indicating the name of the app, as well as the level of access the app has once it's authorized by the user. After a user authorizes access, TextMaster redirects the user back to the application:

![](https://2001322700-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FiLJk7NFk70NqFY1GU7Oi%2Fuploads%2FPOQ7OziK6fD6Od7Wh4Zx%2Fauthorizing-oauth-app.png?alt=media\&token=7680419d-4344-41eb-9ca5-12e269dfbb74)

{% hint style="danger" %}
**Treat OAuth tokens like passwords!** Don't share them with other users or store them in insecure places. The tokens in these examples are fake and the names have been changed to protect the innocent.
{% endhint %}

Now that we've got the hang of making authenticated calls, let's move along to the Projects API.

## Projects

Almost any meaningful use of the TextMaster API will involve some level of Project information. We can [GET project details](/reference/projects#get-a-project) in the same way we fetched user details earlier:

```shell
curl "https://api.textmaster.com/v1/clients/projects/61698af48b81926d91c0f3d1" \
  -H "Apikey: $APIKEY" \
  -H "Date: $DATE" \
  -H "Signature: $SIGNATURE"
```

Or, we can list our projects:

```shell
curl "https://api.textmaster.com/v1/clients/projects" \
  -H "Apikey: $APIKEY" \
  -H "Date: $DATE" \
  -H "Signature: $SIGNATURE"
```

As the [docs](/reference/projects#filter-projects) indicate, you can query a filter endpoint that can be used to filter projects returned based on various attributes:

```shell
curl -Gg "https://api.textmaster.com/v1/clients/projects/filter" \
  --data-urlencode 'where={"total_word_count":{"$gt":1},"language_to_code":"en","language_from_code":"fr","level_name":"premium"}' \
  --data-urlencode 'order=level_name' \
  -H "Apikey: $APIKEY" \
  -H "Date: $DATE" \
  -H "Signature: $SIGNATURE"
```

For more informations on the different filters available, see:

{% content-ref url="/pages/DpuEFf3XhvSCTs2JRHt8" %}
[Filters](/overview/filters)
{% endcontent-ref %}

Woot! Now you know the basics of the TextMaster API!

* Signature & OAuth authentication
* Fetching and filtering projects

Dive deeper and get more details about available resources:

{% content-ref url="/pages/iQQLDpOFxLu35fhVd8pq" %}
[Resources in the REST API](/overview/resources-in-the-rest-api)
{% endcontent-ref %}

Or, start learning about OAuth Apps:

{% content-ref url="/pages/qYrz6Sqapsud2BoZSSbt" %}
[About OAuth Apps](/apps/about-oauth-apps)
{% endcontent-ref %}

Or, start exploring our API reference to get an idea of everything that's possible with the API:

{% content-ref url="/pages/2Z4rP31NK4SYpfSsPvfS" %}
[Reference](/reference/abilities)
{% endcontent-ref %}

Finally, don't forget to read about Webhooks to build or set up powerful integrations:

{% content-ref url="/pages/D7qSqAH0UxaINYqHIGBE" %}
[Webhooks & Events](/webhooks-and-events/webhooks)
{% endcontent-ref %}


# Postman

Use Postman to simplify the process of testing and interacting with TextMaster's API

To streamline your experience, we offer a readily available [Postman](https://www.postman.com/) collection that can be effortlessly downloaded and installed, enabling you to seamlessly interact with our API.

Ensure that you have the [Postman](https://www.postman.com/) application installed and running on your system, then proceed to download our Postman collection below and follow the provided steps to import it into Postman.

Open the Postman application and click on the **import** button at the top under the collections pad

<figure><img src="https://2001322700-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FiLJk7NFk70NqFY1GU7Oi%2Fuploads%2FoHpohuqpeUucWB7JYT4C%2F01-postman-import-collection.png?alt=media&amp;token=2af7ad28-69bc-4eb5-a013-6ba5df3f2ba8" alt=""><figcaption></figcaption></figure>

Drag & drop the following Postman collection and click on **import**

{% file src="/files/hHc66MqNdfdx0xT0RUTG" %}

<figure><img src="https://2001322700-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FiLJk7NFk70NqFY1GU7Oi%2Fuploads%2FQ06vcekSK7JvA1cZRnxZ%2F02-postman-import-collection.png?alt=media&amp;token=47ecec07-a7ae-4eee-a58a-c521f8379b7c" alt=""><figcaption></figcaption></figure>

Once the collection is imported, click again on the **import** button at the top

<figure><img src="https://2001322700-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FiLJk7NFk70NqFY1GU7Oi%2Fuploads%2Fl0RLNweBqNn3RHcZY5AA%2FXnapper-2023-06-12-16.21.00.png?alt=media&amp;token=c168bdb5-3a1c-4541-b9be-409b0d4c0a58" alt=""><figcaption></figcaption></figure>

Drag & drop the following Postman environment and click on **import**

{% file src="/files/I2mA1GnXwE61mk4tQzHm" %}

<figure><img src="https://2001322700-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FiLJk7NFk70NqFY1GU7Oi%2Fuploads%2FsMAA7pzWg0uCxMkTxZKQ%2FXnapper-2023-06-12-16.22.30.png?alt=media&amp;token=f6e84c6f-9844-40df-9225-8efc4f2bc6b2" alt=""><figcaption></figcaption></figure>

Time to head back to your TextMaster account and go into the **API & Loop** page

<figure><img src="https://2001322700-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FiLJk7NFk70NqFY1GU7Oi%2Fuploads%2FfsblUxJwBaq1zN9ud23B%2FXnapper-2023-06-12-15.22.21.png?alt=media&amp;token=4130f60e-50c4-422a-a835-806a3828b103" alt=""><figcaption></figcaption></figure>

Head to **View OAuth Applications**

<figure><img src="https://2001322700-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FiLJk7NFk70NqFY1GU7Oi%2Fuploads%2FZMzTBqVMPrSwr2zlhiUY%2FXnapper-2023-06-12-15.23.25.png?alt=media&amp;token=983e07db-98a4-4b03-a385-5311de41c886" alt=""><figcaption></figcaption></figure>

If you don't have any, **create a new OAuth application**

<figure><img src="https://2001322700-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FiLJk7NFk70NqFY1GU7Oi%2Fuploads%2FBsxyzHpFiZVO0wNBX8Jo%2FXnapper-2023-06-12-15.23.46.png?alt=media&amp;token=dd9b8f39-ef1c-49b3-a452-036e961f7a18" alt=""><figcaption></figcaption></figure>

Fill in your new application's details and click **save**

<figure><img src="https://2001322700-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FiLJk7NFk70NqFY1GU7Oi%2Fuploads%2Fmt1URmvHJnSltGPFhzDc%2FXnapper-2023-06-12-15.24.47.png?alt=media&amp;token=635b1218-71b2-49d3-b046-2f80d2575431" alt=""><figcaption></figcaption></figure>

{% hint style="info" %}
**Tips:** Use appropriate callback URL and permissions for production applications.
{% endhint %}

**Copy to your clipboard** the client ID and secret to be pasted in Postman's application

<figure><img src="https://2001322700-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FiLJk7NFk70NqFY1GU7Oi%2Fuploads%2F0iIQ6c42aodeSmhC0wpb%2FXnapper-2023-06-12-15.26.07.png?alt=media&amp;token=a36e3bb5-39d6-4aa7-9a71-87f28405065d" alt=""><figcaption></figcaption></figure>

**Paste** the client ID and secret into the Postman environment your imported earlier

<figure><img src="https://2001322700-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FiLJk7NFk70NqFY1GU7Oi%2Fuploads%2FGTX1gbWywdfb9tlH9RE0%2FXnapper-2023-06-12-15.45.25.png?alt=media&amp;token=7fe49de9-6dfa-4d17-befd-7fe8bbb9c1aa" alt=""><figcaption></figcaption></figure>

**Set** this environment as **active**

<figure><img src="https://2001322700-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FiLJk7NFk70NqFY1GU7Oi%2Fuploads%2F4phdQoRhlmbn0lhi99zc%2FXnapper-2023-06-12-16.29.52.png?alt=media&amp;token=4980a358-4177-44e4-bd8c-7e5ee810d13d" alt=""><figcaption></figcaption></figure>

Going back into the **collections pad** and into the **TextMaster API v1 collection**, request a **new access token** at the bottom of the page under the **Authorization** tab

<figure><img src="https://2001322700-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FiLJk7NFk70NqFY1GU7Oi%2Fuploads%2FloM1HD8qnirQsh4prlDN%2FXnapper-2023-06-12-15.41.14.png?alt=media&amp;token=59021cc4-d80c-42bd-8fb7-7407f62f99bf" alt=""><figcaption></figcaption></figure>

Postman will open a new window asking you to **authenticate** using your TextMaster credentials

<figure><img src="https://2001322700-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FiLJk7NFk70NqFY1GU7Oi%2Fuploads%2FoeWdw2j37d0O7vdjai1U%2FXnapper-2023-06-12-15.43.43.png?alt=media&amp;token=5997c64a-aea4-4084-9460-90bb3d8f7c7e" alt=""><figcaption></figcaption></figure>

**Authorize** your OAuth Application to perform operations on your behalf through the Postman application

<figure><img src="https://2001322700-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FiLJk7NFk70NqFY1GU7Oi%2Fuploads%2FUxCaOeRFGXL9W50DTRFl%2FXnapper-2023-06-12-15.47.02.png?alt=media&amp;token=57a684ad-693e-46fc-a11a-28a8e01ecc66" alt=""><figcaption></figcaption></figure>

Once the OAuth Application has been approved, Postman should list the available access tokens, make sure to use the latest one if you have multiple instances of them. **Click on the use token button**

<figure><img src="https://2001322700-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FiLJk7NFk70NqFY1GU7Oi%2Fuploads%2FJqBOOB8YWJCSDStTawmO%2FXnapper-2023-06-12-15.48.44.png?alt=media&amp;token=58f343d5-ad0b-4381-b30b-9eee166ea244" alt=""><figcaption></figcaption></figure>

Finally, verify everything works as expected by executing the **Retrieve my information** endpoint

<figure><img src="https://2001322700-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FiLJk7NFk70NqFY1GU7Oi%2Fuploads%2Fv0aaqd2WnsoYrQhvpwg0%2FXnapper-2023-06-12-15.49.15.png?alt=media&amp;token=e850a298-2b15-4bb2-843f-6d98dce5d55f" alt=""><figcaption></figcaption></figure>

Congratulations! You now have a fully working Postman collection you can use to query our API.


# OpenAPI

The TextMaster REST API is fully described in an OpenAPI 3.0 compliant document.

## About

[OpenAPI](https://swagger.io/docs/specification/about/) is a standard specification for describing REST APIs. OpenAPI descriptions allow both humans and machines to discover the capabilities of an API without needing to first read documentation or understand the implementation. TextMaster has made its REST API publicly available as an OpenAPI 3.0 compliant document.

## Getting the OpenAPI description

You can find the description at <https://app.textmaster.com/api-docs/v1/clients/specs.yaml>

## Using the OpenAPI description

There are many uses for an OpenAPI description. For example, you could:

* Generate your own API client.
* Validate and test a TextMaster REST API integration.
* Explore and interact with the TextMaster REST API using third-party tools, such as Insomnia or Postman.

For example, TextMaster uses the OpenAPI description to generate the [REST API reference](/reference/abilities) documentation.

{% hint style="info" %}
**Tips:** You can also explore our API through [**our interactive interface**](https://app.textmaster.com/api-docs/index.html) allowing you to send and inspect request without having to write any code!
{% endhint %}


# Resources in the REST API

Learn how to naviguate the resources provided by the TextMaster API.

This describe the resources that make up the official TextMaster REST API. If you have any problems or requests, please contact [TextMaster support](mailto:support@textmaster.com).

### Version

The current version of our API is **v1**. You must explicitly request this version by appending the version number at the end of the URL.

```
https://api.textmaster.com/v1/
```

### Schema

All API access is done over HTTPS, and accessed from `https://api.textmaster.com/`. All data is sent and received as JSON.

```shell
$ curl -i https://api.textmaster.com/ping

> HTTP/1.1 200 OK
> X-Frame-Options: SAMEORIGIN
> X-XSS-Protection: 1; mode=block
> X-Content-Type-Options: nosniff
> Content-Type: application/json; charset=utf-8
> Cache-Control: no-store, must-revalidate, private, max-age=0
> X-Request-Id: f73733ca-a8be-47ba-981d-fc22ef01865f
> X-Runtime: 0.238097
> Vary: Origin
> X-MiniProfiler-Original-Cache-Control: max-age=0, private, must-revalidate
> X-MiniProfiler-Ids: uhw0tm64jxvyu121sxkc
> Set-Cookie: __profilin=p%3Dt; path=/; HttpOnly
> Transfer-Encoding: chunked

{"message":"Textmaster API at your service"}
```

All timestamps return an [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format:

```
YYYY-MM-DDTHH:MM:SSZ
```

### Environment

A sandbox environment is made available for tests and can be accessed from `https://api.textmasterstaging.com/`. It behaves the same way as the production environment.

### Parameters

Many API endpoints take optional parameters. For `GET` requests, any parameters not specified as a segment in the path can be passed as an HTTP query string parameter.

```shell
curl "https://api.textmaster.com/v1/public/expertises/5f7dcc7d8b819239aff5af7c/sub_expertises?locale=en-EU"
```

In this example, the `5f7dcc7d8b819239aff5af7c` value is provided for the `:expertise_id` parameter in the path while `:locale` is passed in the query string.

For `POST`, `PATCH`, `PUT` and `DELETE` requests, parameters not included in the URL should be encoded as JSON with a `Content-Type` header set to `application/json`.

```shell
curl -X PUT \
  -H "Content-Type: application/json" \
  -d '{"my_author":{"description":"new description"}}' \
  https://api.textmaster.com/v1/clients/my_authors/5f7dcc3c8b819239aff5a391
```

### Client Errors

There are four possible types of client errors on API calls that receive request bodies:

#### Bad Request

Sending invalid JSON will result in a `400 Bad Request` response.

```
HTTP/1.1 400 Bad Request

{"errors":{"base":["Invalid Request."]}}
```

#### Method Not Allowed

Sending an unsupported HTTP verb will result in a `405 Method Not Allowed` response.

```
HTTP/1.1 405 Method Not Allowed

{"errors":{"base":["Method Not Allowed."]}}
```

#### Not Acceptable

Sending or requesting an unsupported format will result in a `406 Not Acceptable` response.

```
HTTP/1.1 406 Not Acceptable

{"errors":{"base":["Unacceptable Request."]}}
```

#### Unprocessable Entity

Sending invalid data will result in a `422 Unprocessable Entity` response.

```
HTTP/1.1 422 Unprocessable Entity

{"errors":{"base":["Error: Object could not be changed."]}}
```

### HTTP redirects

The API uses HTTP redirection where appropriate. Clients should assume that any request may result in a redirection. Receiving an HTTP redirection is *not* an error and clients should follow the redirect. Redirect responses will have a `Location` header field which contains the URI of the resource to which the client should repeat the requests.

| Status Code | Description                                                                                                                                                                                                          |
| ----------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 301         | Permanent redirection. The URI you used to make the request has been superseded by the one specified in the `Location` header field. This an all future requests to this resource should be directed to the new URI. |
| 302, 307    | Temporary redirection. The request should be repeated verbatim to the URI specified in the `Location` header field but clients should continue to use the original URI for future requests.                          |

Other HTTP status codes may be used in accordance to the [HTTP 1.1 specification](https://datatracker.ietf.org/doc/html/rfc2616).

### HTTP verbs

Where possible, the API strives to use appropriate HTTP verbs for each action.

| Verb   | Description                                  |
| ------ | -------------------------------------------- |
| GET    | Used for retrieving resources.               |
| POST   | Used for creating resources.                 |
| PUT    | Used for replacing resources or collections. |
| DELETE | Used for deleting resources.                 |

### Pagination

Requests that return multiple items will be paginated to 100 items by default. You can specify further pages with the `page` parameter. For some resources, you can also set a custom page size up to 100 with the `per_page` parameter. Note that for technical reasons, not all endpoints will honour this parameter.

```shell
curl 'https://api.textmaster.com/v1/clients/projects?page=2&per_page=100'
```

Note that page numbering is 1-based and that omitting the `page` parameter will return the first page.


# Authentication

Learn how to authenticate through TextMaster API.

While the API provides multiple methods for authentication, we strongly recommend using [OAuth](/apps/about-oauth-apps) for production applications. The other method provided is intended to be used for scripts or testing (i.e., cases where full OAuth would be overkill). Third party applications that rely on TextMaster for authentication should not ask for or collect TextMaster credentials. Instead, they should use the OAuth Authorization flow.

## OAuth2

OAuth2 is a protocol that lets external applications request authorization to private details in a user's TextMaster account without accessing their password.

```shell
$ curl https://api.textmaster.com/v1/clients/users/me \
  -H "Authorization: Bearer ACCESS-TOKEN"
```

{% hint style="info" %}
**Tips:** TextMaster recommends sending OAuth tokens using the Authorization header.
{% endhint %}

For more on OAuth Apps, see:

{% content-ref url="/pages/qYrz6Sqapsud2BoZSSbt" %}
[About OAuth Apps](/apps/about-oauth-apps)
{% endcontent-ref %}

## Signature

{% hint style="warning" %}
**Warning:** TextMaster discourages using the signature strategy to authenticate production applications to the API. Clients should use [OAuth2 Apps](/apps/about-oauth-apps) instead.
{% endhint %}

Signature is an authentication strategy that requires the client to compute a signature hash based on the user's pair of API keys. The signature is only valid for 5 minutes after its creation.

For example, the following Shell script will compute that signature hash:

```shell
#!/bin/bash

APIKEY=somekey
APISECRET=somesecret
DATE=$(date -u +"%Y-%m-%d %H:%M:%S")
SIGNATURE=$(echo -n $APISECRET$DATE | openssl sha1 | sed 's/.*= //')

curl "https://api.textmaster.com/test" \
  -H "Apikey: $APIKEY" \
  -H "Date: $DATE" \
  -H "Signature: $SIGNATURE"
```


# Troubleshooting

Learn how to resolve the most common problems people encounter in the REST API.

If you're encountering some oddities in the API, here's a list of resolutions to some of the problems you may be experiencing. If you have any problems or requests, please contact [TextMaster support](mailto:support@textmaster.com).

## Not all results returned

Most API calls accessing a list of resources (e.g., abilities, projects, documents, etc.) support pagination. If you're making requests and receiving an incomplete set of results, you're probably only seeing the first page. You'll need to request the remaining pages in order to get more results.

## OAuth Authentication errors

When exchanging a code for an access token, additional errors can occur. The format of these error responses is determined by the `Accept` header you pass.

The examples below only show JSON responses.

### Incorrect client credentials

If the `client_id` and or `client_secret` you pass are incorrect you will receive this error response:

```json
{
  "error": "invalid_client",
  "error_description": "Client authentication failed due to unknown client, no client authentication included, or unsupported authentication method."
}
```

To solve this error, make sure you have the correct credentials for your OAuth App. Double check the `client_id` and `client_secret` to make sure they are correct and being passed correctly to TextMaster.

### Redirect URI mismatch

If you provide a `redirect_uri` that doesn't match what you've registered with your OAuth App, you'll receive this error message:

```json
{
  "error": "invalid_grant",
  "error_description": "The provided authorization grant is invalid, expired, revoked, does not match the redirection URI used in the authorization request, or was issued to another client."
}
```

To correct this error, provide the same callback URL as registered with your application.

### Invalid verification code

If the verification code you pass is incorrect, expired, or doesn't match what you received in the first request for authorization you will receive this error:

```json
{
  "error": "invalid_grant",
  "error_description": "The provided authorization grant is invalid, expired, revoked, does not match the redirection URI used in the authorization request, or was issued to another client."
}
```

To solve this error, start the [OAuth authorization](/apps/building-oauth-apps/authorizing-oauth-apps#request-a-users-textmaster-identity-1) process again and get a new code.

### Invalid refresh token

If the refresh token you pass is incorrect, expired, or doesn't match what you received in access token request you will receive this error:

```json
{
  "error": "invalid_grant",
  "error_description": "The provided authorization grant is invalid, expired, revoked, does not match the redirection URI used in the authorization request, or was issued to another client."
}
```

To solve this error, start the [OAuth access token](/apps/building-oauth-apps/authorizing-oauth-apps#exchange-an-oauth-code-for-a-users-access-token) process again and get a new refresh token.

## Timeouts

If TextMaster takes more than 30 seconds to process an API request, TextMaster will terminate the request and you will receive a timeout response. Make sure to use async endpoints when available to overcome this issue.&#x20;


# Filters

Learn how to use filters to query specific resources.

Some API resources expose a `/filter` endpoint which you can use to filter and order resources based on given query selectors. It allows you to narrow down specific items using a standard API.

It consists of the two URL encoded fields:

|         |                                |
| ------- | ------------------------------ |
| `where` | JSON query selectors           |
| `order` | Comma seperated list of fields |

Filter endpoints accept a `where` parameter which includes JSON holding the query selectors. For example:

```json
{"status": "in_progress"}
```

You can also build more complex queries with query operators:

```json
{"status": {"$in": ["in_progress", "completed"]}}
```

For example, the following query scopes projects with `in_progress` `status` with most recently created projects being first.

```shell
curl -G \
  --data-urlencode 'where={"status": "in_progress"}' \
  --data-urlencode 'order=-created_at' \
  https://api.textmaster.com/v1/clients/projects/filter
```

{% hint style="warning" %}
**Warning:** Unsupported query selectors will result in a `422` HTTP response.
{% endhint %}

## Query Operators

### $gt

Selects resources where the value of the field is greater than the given value.

```json
{"word_count": {"$gt": 100}}
{"created_at": {"$gt": "1970-01-01T00:00:00Z"}}
```

### $gte

Selects resources where the value of the field is greater than or equal to the given value.

```json
{"word_count": {"$gte": 100}}
{"created_at": {"$gte": "1970-01-01T00:00:00Z"}}
```

### $lt

Selects resources where the value of the field is less than the given value.

```json
{"word_count": {"$lt": 100}}
{"created_at": {"$lt": "1970-01-01T00:00:00Z"}}
```

### $lte

Selects resources where the value of the field is less than or equal to the given value.

```json
{"word_count": {"$lte": 100}}
{"created_at": {"$lte": "1970-01-01T00:00:00Z"}}
```

### $in

Selects resources where the value of the field is included in the given list of values.

```json
{"status": {"$in": ["in_progress", "completed"]}}
```

### $nin

Selects resources where the value of the field is *not* included in the given list of values.

```json
{"status": {"$nin": ["in_progress", "completed"]}}
```

### $ne

Selects resources where the value of the field is not equal to the given value.

```json
{"status": {"$ne": "in_progress"}}
```

### $or

Performs a logical OR operation on a list of two or more expressions and selects resources that satisfy at least one of the expressions.

```json
{
  "$or": [
    {"status": "in_progress"},
    {"word_count": {"$gt": 100}}
  ]
}
```

### $regex

Selects resources where the value of the field matches the given regular expression. It must be a string representation of a [PCRE](https://pcre.org/pcre.txt) compatible regular expression.

Supported regular expression flags are:

* `i` toggles case insensitivity
* `m` toggles multi-line support
* `x` toggles an "extended" capability. When set, `$regex` ignores all white space characters unless escaped or included in a character class.

Selects all resources where their `name` field starts with "Some", ignoring the case:

```json
{"name": {"$regex": "/^Some/i"}}
```

## Order

`order` is a string parameter containing comma separated list of fields to sort by. If a field is prefixed with a `-` the sort order will be descending. For example, the following order specification will sort resources by `status` ascending and `created_at` descending order:

```
status,-created_at
```


# Workflow

Learn what are projects and document and how they flows into TextMaster architecture.

## Projects

A project is the base of the process. It is the main element determining the general and common characteristic to all the types of projects:

* Translation
* Copywriting
* Proofreading

A project holds metadata such as language pairs, category, general briefing, author assignment methods, templates, options, etc. It's a placeholder made of one or multiple documents.

Projects follow a logic succession of statuses. The accessible steps are represented in the diagram. The definition of each status is given in the table. Some transitions are triggered by the client, other result from document status, you'll find more details about "how" a project got into this state in the table below.

![The project workflow and its statuses](https://2001322700-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FiLJk7NFk70NqFY1GU7Oi%2Fuploads%2FA2MiMAhGidmQ0y0nmQBf%2Fproject-workflow.png?alt=media\&token=7402838f-c645-44b0-96ab-5ee7d7ef0040)

| Status        | How                                                            | Definition                                                                                                                           |
| ------------- | -------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------ |
| `in_creation` | Initial status.                                                | Project is not accessible to authors, the client can edit the project and add or remove documents from it.                           |
| `in_progress` | The client launched or resumed after being paused the project. | Project is available to be claimed by authors, the client can't edit the project anymore.                                            |
| `in_review`   | Depends on document(s) status(es).                             | All project's documents were submitted and are in internal quality control or `in_review`.                                           |
| `completed`   | Depends on document(s) status(es).                             | All project's documents are `completed`.                                                                                             |
| `paused`      | The client paused the project.                                 | Project is not accessible to authors. Only documents already claimed remain accessible.                                              |
| `canceled`    | The client canceled the project.                               | Project is not accessible to authors. Only documents already claimed remain accessible. Client is refunded for un-claimed documents. |

## Documents

A document is the element corresponding to the expected content to be worked on. It's attached to a project and its characteristics are specific. If many documents may share the same characteristics within the same project, they may also all be different.

Documents follow a logic succession of statuses. The accessible steps are represented in the diagram. The definition of each status is given in the table. Some transitions are triggered by the client or the author, you'll find more details about "how" a document got into this state in the table below.

![The document workflow and its statuses](https://2001322700-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FiLJk7NFk70NqFY1GU7Oi%2Fuploads%2FtVuWwZM0dyu3mUdlMk7k%2Fdocument-workflow.png?alt=media\&token=8e0ccb26-3773-423a-a31c-628a7f62156e)

| Status               | How                                                                                                                           | Definition                                                                                                     |
| -------------------- | ----------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------- |
| `in_creation`        | Initial status.                                                                                                               | Document is not accessible to authors and the client can edit the document.                                    |
| `waiting_assignment` | After the project has been launched or resumed after being paused by the client.                                              | Document can be claimed by authors. The client can't edit the document anymore.                                |
| `in_progress`        | The author has claimed the document.                                                                                          | An author has claimed the document and is working on it.                                                       |
| `in_review`          | The author has submitted the document's work and document has successfully passed through the quality controls (if required). | Document is now under the review of the client.                                                                |
| `completed`          | The client has completed the document which terminates the workflow.                                                          | Document is now completed and the author has been paid for the work done.                                      |
| `incomplete`         | The author asked a question, the client requested a revision or automated quality control checks failed.                      | Document's work has been started but is not completed yet.                                                     |
| `paused`             | The client paused the document.                                                                                               | Document is not accessible to authors.                                                                         |
| `canceled`           | The client canceled the document.                                                                                             | Document is not accessible to authors and the client is refunded.                                              |
| `copyscape`          | The author has submitted the document's work.                                                                                 | This step is specific to copywriting activity. Plagiarism detection analysis is being run on the document.     |
| `counting_words`     | The author has submitted the document's work.                                                                                 | This step is specific to copywriting activity. We are counting words written and running a few quality checks. |
| `quality_control`    | Depends on the project status.                                                                                                | Quality control checks are being run on the document.                                                          |


# File uploads

Learn how to upload files using the TextMaster REST API

You are free to use your own storage service and provide us with the publicly accessible URLs which will be used to copy your files when creating documents.

If you don't use a storage service or would prefer to upload your files on TextMaster, you can use our [Uploads](/reference/uploads) endpoint which consist of the following steps:

1. Retrieving the file upload properties from the API.
2. Make a request using the file upload properties and passing signed metadata as HTTP headers.

{% hint style="info" %}
**Tips:** TextMaster uses a temporary storage to store your files until they are linked to a document. They will be automatically deleted after a period of 60 days from this temporary storage location.
{% endhint %}

For more information about how to use this endpoint, see:

{% content-ref url="/pages/KvEl5i49nrCsyBsXp4ZU" %}
[Uploads](/reference/uploads)
{% endcontent-ref %}


# Loop

Learn how to use Loop to create projects by sending a simple email.

{% hint style="danger" %}
**Deprecated:** Loop is deprecated and will be removed in the future. Use the REST API instead. The following documentation is for clients already using this feature.
{% endhint %}

Loop is a solution which allows clients to set up a project template and create projects from it by sending an email to the special project template address.

## Overview

The process is based on a project template, registered by the user. It identifies accounts that could send an email, a setup required information for a project creation, like:

* The source language of the email
* The target language
* The category
* The minimum language level

In order to help users to quickly use this system, pre-setup settings are available, as well as some pre-formatted template. Each pre-formatted template has a unique ID that the user includes in the email address, in order to know what to include inside the created project. Basically, after creating a project template, an email address is generated for the client to send emails to.

## Create a Loop project template

In the top-bar navigation, click on **API & Loop**

![](https://2001322700-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FiLJk7NFk70NqFY1GU7Oi%2Fuploads%2FVTbiBp7mMUZYzEbpMgx8%2Fcreating-oauth-app-step-1.png?alt=media\&token=1f9cc0d6-5bd5-4390-8ec1-34ae2ff3c77e)

In the right panel, click **Configure Loop**

![](https://2001322700-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FiLJk7NFk70NqFY1GU7Oi%2Fuploads%2FiAA7gx7PxYE4hAiIKnLy%2Floop-step-2.png?alt=media\&token=d23a1fca-656c-4805-8d3f-4da7640b6599)

Click the **New Loop** button

![](https://2001322700-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FiLJk7NFk70NqFY1GU7Oi%2Fuploads%2FdXusOA94GBT3XLsTBEWY%2Floop-step-3.png?alt=media\&token=ef08d6be-0c05-4970-ad75-a3e2111b3bb9)

Fill-in your project template information and click **Save Loop** at the bottom of the page.

You will be redirected to your Loop project templates. Notice the email address generated for you. This is the address to send the email to in order to create a new project from this template.

![](https://2001322700-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FiLJk7NFk70NqFY1GU7Oi%2Fuploads%2FLiyx5B7KzL8XW0vHRFTQ%2Floop-step-4.png?alt=media\&token=d88d735b-320b-49dd-99f2-be2f1836b0a3)

## Email the Loop address

Use the email address generated when creating the Loop project template to send the email to. By default, the email body will be used as the content to work on. You can also send a file as an email attachment instead. Loop only supports one file as an attachment.

{% hint style="warning" %}
**Warning:** The sender's email address has to match the email address used to register on TextMaster. Projects will not be created otherwise, and a failure notification will be sent back.
{% endhint %}

Assuming the client account has enough funds, a project is created a few minutes after sending the email. A notification will be sent to the client otherwise.

{% hint style="success" %}
**Tips:** Make sure to send the email as plain text to avoid any HTML boilerplate that could be added and counted as words by TextMaster.
{% endhint %}


# Integrator best practices

Learn how to build app that reliably interacts with the TextMaster API and provides the best experience for you users.

This guide will help you build an app that provides the best experience for your users and ensure that it's reliably interacting with the API.

## Add documents to project in batch

A project holds metadata such as language pairs, category, general briefing, author assignment methods, templates, options, etc. It's a placeholder made of one or multiple documents. A document is the element corresponding to the expected content to be worked on. It's attached to a project and its characteristics are specific.

For translation and proofreading, the size of your documents will depends on the number of words of your content. When you are sending your content directly through our API as plain text, you should [add documents to project in batch](/reference/documents#create-batch-of-documents).

TextMaster's API will abort any connection which takes longer than 30 seconds to execute. Depending on the size of your content and the endpoint you use to create documents you might hit this limit. Using the batch API will ensure that the HTTP payload size stays reasonably small and requests succeed.

If you need to add a large number of documents to a project, you can split even further by making multiple batch requests. Using a "divide & conquer" strategy here will make sure you never hit the API limits.

For example, let's say you want to create a project with `100` documents. Each document size depends on its individual content (number of words to translate) but average out around `100_000` words. It would not be reasonable to expect the API to accept a single batch request made of `100` documents with roughly `100_000 * 100` words. You would simply hit the 30 seconds timeout limit and the connection would be aborted.

You should instead split into multiple batch requests of for example `10` documents each or even lower. Use common sense here to make reasonably sized requests against the API.

{% hint style="info" %}
**Tips:** You cannot add more than `99_999` documents to a single project.
{% endhint %}

## Favor sending your content as files

TextMaster currently accept your content as either plain text or a URL that points to a file either hosted on a remote location by yourself or through our[ upload API](/overview/file-uploads). Whenever possible, you should prefer sending your content as a file URL to prevent potential performance issues with large content.

When sending your content by using your hosted URL, TextMaster will create a copy of your file to be hosted on our platform.

## Favor webhooks to retrieve your content

You can either [request your content](/reference/documents#get-a-document) or being it pushed to you using [webhooks](/webhooks-and-events/webhooks). Webhooks should always be preferred as you reduce the risk of hitting API limits when dealing with large documents.

It's also usually easier to implement a server for receiving events as they happen on TextMaster rather than randomly polling the API for status updates.

{% hint style="success" %}
**Hint:** Always prefer using webhooks over HTTP polling for reliability.
{% endhint %}

## Secure payloads delivered from TextMaster

It's very important that you secure the [payloads sent from TextMaster.](/webhooks-and-events/events) Although no personal information (like passwords) is ever transmitted in a payload, leaking any information is not good. Some information that might be sensitive include client email address or project content.

There are several steps you can take to secure receipt of payloads delivered by TextMaster:

* Ensure that your receiving server is on an HTTPS connection. By default, TextMaster will verify SSL certificates when delivering payloads. Any SSL errors will be logged in the webhook's response.
* You can add the IP address we use when delivering hooks to your server's allow list. Please note that we do make changes to our IP addresses from time to time. We do not recommend allowing by IP addresses, however if you use these we strongly encourage regular monitoring of our documentation. Our current public IP addresses are the following:

{% tabs %}
{% tab title="Production" %}

```
104.155.57.91
104.155.91.236
35.205.172.93
34.140.71.130
```

{% endtab %}

{% tab title="Sandbox" %}

```
34.76.154.26
34.76.94.225
34.76.144.86
35.241.160.58
```

{% endtab %}
{% endtabs %}

{% hint style="warning" %}
**Warning:** Our public IP addresses are subject to changes, we do not encourage using IP addresses to secure payload delivery.
{% endhint %}

* Provide [a secret token](/webhooks-and-events/webhooks/securing-webhooks) to ensure payloads are definitely coming from TextMaster. By enforcing a secret token, you're ensuring that any data received by your server is absolutely coming from TextMaster. Ideally, you should provide a different secret token *per user* of your service. That way, if one token is compromised, no other user would be affected.

## Favor asynchronous work over synchronous

TextMaster expects that integrations respond within 30 seconds of receiving the webhook payload. If your service takes longer than that to complete, then TextMaster terminates the connection and the payload is lost.

Since it's impossible to predict how fast your service will complete, you should do all of "the real work" in a background job. [Sidekiq](https://sidekiq.org/) (for Ruby), [RQ](http://python-rq.org/) (for Python), or [RabbitMQ](http://www.rabbitmq.com/) (for Java) are examples of libraries that can handle queuing and processing of background jobs.

Note that even with a background job running, TextMaster still expects your server to respond within 30 seconds. Your server needs to acknowledge that it received the payload by sending some sort of response. It's critical that your service performs any validations on a payload as soon as possible, so that you can accurately report whether your server will continue with the request or not.

{% hint style="success" %}
**Hint:** Prefer handling webhooks asynchronously to avoid timeout issues.
{% endhint %}

## Use appropriate HTTP status codes

You can list recent deliveries to quickly look whether a delivery was successful or failed.

![](https://2001322700-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FiLJk7NFk70NqFY1GU7Oi%2Fuploads%2F2wmaewcezKG2qZKM4RuT%2Flist-deliveries.png?alt=media\&token=cf29711c-461a-4e7e-a0b6-241ad3a01efd)

You should make use of proper HTTP status codes in order to inform users. You can use codes like `201` or `202` to acknowledge receipt of payload that won't be processed (for example, a payload delivered by a project with a stale status). Reserve the `500` error code for catastrophic failures.

{% hint style="success" %}
**Hint:** Use appropriate status code to communicate what happened.
{% endhint %}

## Provide as much information as possible to the user

Users can dig into the server responses you send back to TextMaster. Ensure that your messages are clear and informative.

![](https://2001322700-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FiLJk7NFk70NqFY1GU7Oi%2Fuploads%2FhJVyCQOXeRxkaeLiL5mf%2Fuseful-information-from-webhook.png?alt=media\&token=96114c2f-4801-4779-907c-96ea901f0d36)

{% hint style="success" %}
**Hint:** Provide clear information in the webhook response body to communicate what happened.
{% endhint %}

## Follow any redirects that the API sends you

TextMaster is explicit in telling you when a resource has moved by providing a redirect status code. You should follow these redirections. Every redirect response sets the `Location` header with the new URI to go to. If you receive a redirect, it's best to update your code to follow the new URI, in case you're requesting a deprecated path that we might remove.

## Don't manually parse URLs

Often, API responses contain data in the form of URLs. For example, when requesting a document, we'll send a key called `author_work` with a URL you can use to retrieve your content.

For the stability of your app, you shouldn't try to parse this data or try to guess and construct the format of future URLs. Your app is liable to break if we decide to change the URL.

## Check the event type and action before processing the event

There are multiple [webhook event types](/webhooks-and-events/events), and each event can have multiple actions. As TextMaster's feature set grows, we will occasionally add new event types or add new actions to existing event types. Ensure that your application explicitly checks the type and action of an event before doing any webhook processing. The `X-TextMaster-Event` request header can be used to know which event has been received so that processing can be handled appropriately.

## Dealing with API errors

Although your code would never introduce a bug, you may find that you've encountered successive errors when trying to access the API.

Rather than ignore repeated `4xx` and `5xx` status codes, you should ensure that you're correctly interacting with the API. For example, if an endpoint requests a string and you're passing it a numeric value, you're going to receive a 4`xx` validation error, and your call won't succeed. Similarly, attempting to access an unauthorized or nonexistent endpoint will result in a `4xx` error.

Intentionally ignoring repeated validation errors may result in the suspension of your app for abuse.


# About OAuth Apps

Learn how to build integrations with the TextMaster API.

Apps on TextMaster allow you to automate and improve your workflow. You can build integrations with the TextMaster API to let your clients order translation, copywriting or proofreading projects of their content.

## Overview

OAuth2 is a protocol that lets external applications request authorization to private details in a user's TextMaster account without accessing their password. This is preferred over Signature Authentication because tokens can be limited to specific types of data and can be revoked by users at any time.

An OAuth App uses TextMaster as an identity provider to authenticate as the user who grants access to the app. When users grant an OAuth App access, they grant permissions to *all* projects they have access to in their account.

Building an OAuth App is a good option if you are creating more complex processes than a simple script can handle. Note that OAuth Apps are applications that need to be hosted somewhere.

Keep these ideas in mind when creating OAuth Apps:

* An OAuth App should always act as the authenticated user across all of TextMaster
* An OAuth App can be used as an identity provider by enabling a "Login with TextMaster" for the authenticated user.
* An OAuth App should only request the permissions it requires to operate normally.

{% hint style="info" %}
**Tips:** OAuth Apps should request only the permissions it is required to operate normally.
{% endhint %}

For more on OAuth Apps, see:

{% content-ref url="/pages/KdE5bevnIWDoVgFcaDXZ" %}
[Building OAuth Apps](/apps/building-oauth-apps)
{% endcontent-ref %}

{% content-ref url="/pages/HbmeYUagKBbuFZOxdlJs" %}
[Managing OAuth Apps](/apps/managing-oauth-apps)
{% endcontent-ref %}


# Building OAuth Apps


# Creating an OAuth App

Learn how to create an OAuth App from TextMaster.

You can create and register an OAuth App under your personal account or under any organization you have administrative access to. While creating your OAuth app, remember to protect your privacy by only using information you consider public.

In the top-bar navigation, click on **API & Loop**

![](https://2001322700-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FiLJk7NFk70NqFY1GU7Oi%2Fuploads%2FVTbiBp7mMUZYzEbpMgx8%2Fcreating-oauth-app-step-1.png?alt=media\&token=1f9cc0d6-5bd5-4390-8ec1-34ae2ff3c77e)

In the left panel, click **View OAuth applications**

![](https://2001322700-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FiLJk7NFk70NqFY1GU7Oi%2Fuploads%2F77HIJmzcIIAYe3cz505t%2Fcreating-oauth-app-step-2.png?alt=media\&token=a5784a2c-045b-4b31-935d-7ef12d4a81d6)

Click **New Application**

![](https://2001322700-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FiLJk7NFk70NqFY1GU7Oi%2Fuploads%2FtIbhCCLwRLdr9EjN9Jai%2Fcreating-oauth-app-step-3.png?alt=media\&token=bf88d008-774a-4ebf-8020-31e807200153)

1. In "Name", type the name of your app.
2. In "Authorization callback URL", type the callback URL of your app.
3. In "Scopes", type the scopes of your app, separated with spaces. See [the list of available scopes](/apps/building-oauth-apps/scopes-for-oauth-apps) for more details.
4. Click on **Submit**

{% hint style="warning" %}
**Warning:** Only use information in your OAuth app that you consider public. Avoid using sensitive data, such as internal URLs, when creating an OAuth App.
{% endhint %}

![](https://2001322700-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FiLJk7NFk70NqFY1GU7Oi%2Fuploads%2Fr6mMUqjQDGludn7hLVyx%2Fcreating-oauth-app-step-4.png?alt=media\&token=dbd47857-63d0-4994-a4e5-cbadfc451967)


# Authorizing OAuth Apps

Learn how to enable other users to authorize your OAuth App.

TextMaster's OAuth implementation supports the standard [Authorization Code Grant](https://tools.ietf.org/html/rfc6749#section-4.1).

See the [skip authorization](#skip-authorization-for-testing-purposes) section if you want to skip authorizing your app in the standard way, such as when testing your app, you can use our special callback url.

To authorize your OAuth app, consider which authorization flow best fits your app:

* **Web Application Flow**: Used to authorize users for standard OAuth apps that run in the browser. (The [implicit grant type](https://tools.ietf.org/html/rfc6749#section-4.2) is not supported)

## Web Application Flow

The web application flow to authorize users for your app is:

1. Users are redirected to request their TextMaster identity
2. Users are redirected back to your site by TextMaster
3. Your app accesses the API with the user's access token

### Request a user's TextMaster identity

Use the following query to request user's TextMaster identity. User will have to be signed in to authorize your app.

## Request a user's TextMaster identity

<mark style="color:blue;">`GET`</mark> `https://app.textmaster.com/oauth/authorize`

#### Query Parameters

| Name                                             | Type   | Description                                                              |
| ------------------------------------------------ | ------ | ------------------------------------------------------------------------ |
| client\_id<mark style="color:red;">\*</mark>     | String | The client ID you received from TextMaster when you registered your app. |
| redirect\_uri<mark style="color:red;">\*</mark>  | String | The callback URL that is configured in your registered app.              |
| scope<mark style="color:red;">\*</mark>          | String | A space-delimited list of scopes.                                        |
| response\_type<mark style="color:red;">\*</mark> | String | Value must be `code` (required by the OAuth specification).              |

{% tabs %}
{% tab title="200: OK " %}

```javascript
{
    // Response
}
```

{% endtab %}
{% endtabs %}

```shell
curl -G https://app.textmaster.com/oauth/authorize \
  -d 'client_id=bd5f986c3e0ca8e3c8f5e9be837631ec1f5003' \
  -d 'redirect_uri=https://example.com' \
  -d 'response_type=code' \
  -d 'scope=user:read user:email'
```

![](https://2001322700-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FiLJk7NFk70NqFY1GU7Oi%2Fuploads%2FPOQ7OziK6fD6Od7Wh4Zx%2Fauthorizing-oauth-app.png?alt=media\&token=7680419d-4344-41eb-9ca5-12e269dfbb74)

### Users are redirected back to your site by TextMaster

If the user accepts your request, TextMaster redirects back to your site with a temporary `code` in a code parameter. The temporary code will expire after 10 minutes.

Exchange this `code` for an access token:

## Exchange an OAuth code for a user's access token

<mark style="color:green;">`POST`</mark> `https://app.textmaster.com/oauth/token`

#### Query Parameters

| Name                                             | Type   | Description                                                                  |
| ------------------------------------------------ | ------ | ---------------------------------------------------------------------------- |
| client\_id<mark style="color:red;">\*</mark>     | String | The client ID you received from TextMaster when you registered your app.     |
| client\_secret<mark style="color:red;">\*</mark> | String | The client secret you received from TextMaster when you registered your app. |
| grant\_type<mark style="color:red;">\*</mark>    | String | Value must be `authorization_code` (required by the OAuth specification).    |
| redirect\_uri                                    | String | The same callback URL as sent in step 1.                                     |
| code<mark style="color:red;">\*</mark>           | String | The `code` you received as a response to step 1.                             |

{% tabs %}
{% tab title="200: OK " %}

```javascript
{
  "access_token":"8129442026644ebe93039fecafd79cf776b65",
  "token_type":"Bearer",
  "expires_in":28800,
  "refresh_token":"ce93ba212d2ef6d7a350ba52069839b13882332",
  "scope":"public",
  "created_at":1605191853
}
```

{% endtab %}
{% endtabs %}

```shell
curl https://app.textmaster.com/oauth/token \
  -F code="80972c05d9012231c493458ed9b98d8d770242d1ceb81895d094b519315b9a51" \
  -F grant_type="authorization_code" \
  -F redirect_uri="https://example.com" \
  -F client_id="bd5f986c3e0ca8e3c8f5e9be837631ec1f5003" \
  -F client_secret="4d556ed945c0735d26663694b24bf0589b"
```

The response includes two tokens:

* An `access_token` which is used to access the API on behalf of a user
* A `refresh_token` which is used to get a new access token when it has expired

{% hint style="info" %}
**Tips:** Access token expires after 8 hours. For more information about refresh tokens, see [Refreshing access tokens](#undefined).
{% endhint %}

### Use the access token to access the API

The access token allows you to make requests to the API on a behalf of a user.

```
Authorization: Bearer ACCESS-TOKEN
GET https://api.textmaster.com/v1/clients/users/me
```

For example, by setting the `Authorization` header like this:

## Get user informations referenced by given access token

<mark style="color:blue;">`GET`</mark> `https://api.textmaster.com/v1/clients/users/me`

#### Headers

| Name                                     | Type   | Description         |
| ---------------------------------------- | ------ | ------------------- |
| Accept<mark style="color:red;">\*</mark> | String | application/json    |
| Authorization                            | String | Bearer ACCESS-TOKEN |

### Skip authorization for testing purposes

If you want to skip authorizing your app in the standard way, for example when testing your app, you can register it with the following value as callback URL: `urn:ietf:wg:oauth:2.0:oob`.

{% hint style="info" %}
**Tips:** Use `urn:ietf:wg:oauth:2.0:oob` special callback URL for testing purposes.
{% endhint %}

At the end of step 1, users will not be redirected to your app's callback URL and the authorization code will be displayed to you instead.

### Refreshing access tokens

To enforce regular token rotation and reduce the impact of a compromised token, access tokens automatically expire after 8 hours. You can use refresh tokens to request new access token.

When you receive an access token, the response will also contain a refresh token, which can be exchanged for a new access token and refresh token.

To renew an expiring access token, you can exchange the `refresh_token` for a new `access_token` and `refresh_token`.

{% hint style="info" %}
**Tips:** Use the `refresh_token` to get a new `access_token` when it has expired. `refresh_token` do not expire.
{% endhint %}

## Exchange an OAuth code for a user's access token

<mark style="color:green;">`POST`</mark> `https://app.textmaster.com/oauth/token`

#### Query Parameters

| Name                                             | Type   | Description                                                                  |
| ------------------------------------------------ | ------ | ---------------------------------------------------------------------------- |
| client\_id<mark style="color:red;">\*</mark>     | String | The client ID you received from TextMaster when you registered your app.     |
| client\_secret<mark style="color:red;">\*</mark> | String | The client secret you received from TextMaster when you registered your app. |
| grant\_type<mark style="color:red;">\*</mark>    | String | Value must be `refresh_token` (required by the OAuth specification).         |
| refresh\_token<mark style="color:red;">\*</mark> | String | The token received with the `access_token`.                                  |

{% tabs %}
{% tab title="200: OK " %}

```javascript
{
  "access_token":"1d9ac1a4eb8ebcdb90ceb0a681c83f12cc65",
  "token_type":"Bearer",
  "expires_in":28800,
  "refresh_token":"9081dbe2b7dfc3ffb4e0861e4f4c471d7",
  "scope":"public",
  "created_at":1605192507
}
```

{% endtab %}
{% endtabs %}


# Scopes for OAuth Apps

Scopes let you specify exactly what type of access you need. Scopes limit access for OAuth tokens. They do not grant any additional permission beyond that which the user already has.

When setting up an OAuth App on TextMaster, requested scopes are displayed to the user on the authorization form.

## Available Scopes

| Name                      | Description                                                                                                                                                                    |
| ------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `public`                  | Grants read-only access to public information (such as, but not limited to, available languages, options, pricing, expertises). This is the default scope if none is provided. |
| `user:manage`             | Grants full access to user's profile info only (includes `user:email`).                                                                                                        |
| `user:read`               | Grants read-only access to user's profile info.                                                                                                                                |
| `user:write`              | Grants read/write access to user's profile info.                                                                                                                               |
| `user:email`              | Grants read-only access to user's private email address.                                                                                                                       |
| `glossary:manage`         | Grants full access to glossaries (includes the ones shared from organization).                                                                                                 |
| `glossary:read`           | Grants read-only access to glossaries (includes the ones shared from organization).                                                                                            |
| `glossary:write`          | Grants read/write access to glossaries (includes the ones shared from organization).                                                                                           |
| `project:manage`          | Grants full access to projects, documents and templates (includes `project:launch` and `project:quote`).                                                                       |
| `project:read`            | Grants read-only access to projects, documents and templates.                                                                                                                  |
| `project:write`           | Grants read/write access to projects, documents and templates.                                                                                                                 |
| `project:launch`          | Grants access to launch projects and debit the client's account.                                                                                                               |
| `project:quote`           | Grants access to request project quotations.                                                                                                                                   |
| `discussion:manage`       | Grants full access to team discussions.                                                                                                                                        |
| `discussion:read`         | Grants read-only access to team discussions.                                                                                                                                   |
| `discussion:write`        | Grants read/write access to team discussions.                                                                                                                                  |
| `transaction:read`        | Grants read-only access to financial transactions.                                                                                                                             |
| `preferred_author:manage` | Grants full access to client's preferred authors.                                                                                                                              |
| `preferred_author:read`   | Grants read-only access to client's preferred authors.                                                                                                                         |
| `preferred_author:write`  | Grants read/write access to client's preferred authors.                                                                                                                        |

`resource:manage` scopes grants full access to the resource. You should use this scope as a shortcut for requesting all permissions on a given resource instead of listing them individually. However, we advise that OAuth apps only request the permissions they absolutely need to operate under normal conditions.

{% hint style="info" %}
**Tips:** Your OAuth App can request the scopes in the initial redirection. You can specify multiple scopes by separating them with a space using `%20`:

```
https://app.textmaster.com/oauth/authorize?
  client_id=...&
  scope=user:read%20user:email
```

{% endhint %}

## Requested scopes and granted scopes

The `scope` attribute lists scopes attached to the token that were granted by the user. Normally, these scopes will be identical to what you requested. However, users can edit their scopes, effectively granting your application less access than you originally requested. Also, users can edit token scopes after the OAuth flow is completed. You should be aware of this possibility and adjust your application's behavior accordingly.

It is important to handle error cases when a user chooses to grant you less access than you originally requested. For example, applications can warn or otherwise communicate with their users that they will see reduced functionality or be unable to perform some actions.

Also, applications can always send users back through the flow again to request additional permissions, but don’t forget that users can always deny those.


# Managing OAuth Apps

Learn about how OAuth scopes work and let you choose what type of access you need.


# Modifying an OAuth App

Learn how to make changes to your OAuth App.

In the top-bar navigation, click on **API & Loop**

![](https://2001322700-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FiLJk7NFk70NqFY1GU7Oi%2Fuploads%2FVTbiBp7mMUZYzEbpMgx8%2Fcreating-oauth-app-step-1.png?alt=media\&token=1f9cc0d6-5bd5-4390-8ec1-34ae2ff3c77e)

In the left panel, click **View OAuth applications**

![](https://2001322700-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FiLJk7NFk70NqFY1GU7Oi%2Fuploads%2F77HIJmzcIIAYe3cz505t%2Fcreating-oauth-app-step-2.png?alt=media\&token=a5784a2c-045b-4b31-935d-7ef12d4a81d6)

Click the **edit icon** on the right of the OAuth App you want to modify.

![](https://2001322700-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FiLJk7NFk70NqFY1GU7Oi%2Fuploads%2FaAwCefkLmEj1uutLyzjg%2Fmodifying-oauth-app-step-3.png?alt=media\&token=9dc35391-d18b-494a-8306-12e747e61e25)

1. Modify the OAuth App information that you'd like to change.
2. Click on **Submit**

{% hint style="warning" %}
**Warning:** Only use information in your OAuth app that you consider public. Avoid using sensitive data, such as internal URLs, when creating an OAuth App.
{% endhint %}

![](https://2001322700-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FiLJk7NFk70NqFY1GU7Oi%2Fuploads%2FI3DTVwyw0QirAANFeNUA%2Fmodifying-oauth-app-step-4.png?alt=media\&token=af92c990-6ca3-49a1-98a7-13297265943c)


# Deleting an OAuth App

Learn how to delete OAuth Apps when you no longer use them.

In the top-bar navigation, click on **API & Loop**

![](https://2001322700-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FiLJk7NFk70NqFY1GU7Oi%2Fuploads%2FVTbiBp7mMUZYzEbpMgx8%2Fcreating-oauth-app-step-1.png?alt=media\&token=1f9cc0d6-5bd5-4390-8ec1-34ae2ff3c77e)

In the left panel, click **View OAuth applications**

![](https://2001322700-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FiLJk7NFk70NqFY1GU7Oi%2Fuploads%2F77HIJmzcIIAYe3cz505t%2Fcreating-oauth-app-step-2.png?alt=media\&token=a5784a2c-045b-4b31-935d-7ef12d4a81d6)

Click the **delete icon** on the right of the OAuth App you want to modify.

![](https://2001322700-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FiLJk7NFk70NqFY1GU7Oi%2Fuploads%2FaAwCefkLmEj1uutLyzjg%2Fmodifying-oauth-app-step-3.png?alt=media\&token=9dc35391-d18b-494a-8306-12e747e61e25)


# Webhooks

Learn the basics of how webhooks work to help you build and set up integrations.

Webhooks allow you to build or set up integrations, such as OAuth Apps, which subscribe to certain events on TextMaster. When one of those events is triggered, we'll send an HTTP POST payload to the webhook's configured URL. Webhooks can be used to update an external CMS or anything else. You're only limited by your imagination.

Webhooks can be installed on a specific user, or specific resources. Once installed, the webhook will be sent each time one or more subscribed events occurs.

## Events

When configuring a webhook, you can use the API to choose which events will send you payloads. Only subscribing to the specific events you plan on handling limits the number of HTTP requests to your server.

Each event corresponds to a certain set of actions that can happen to your account and/or projects. For example, if you subscribe to the document's `in_progress` event you'll receive detailed payloads every time a document moves to in progress.


# Creating webhooks

Learn to build a webhook, choosing the events your webhook will listen for on TextMaster and how to set up a server to receive and manage the webhook payload.

Now that you understand [the basics of webhooks](/webhooks-and-events/webhooks), let's go through the process of building out our own webhook-powered integration. In this tutorial, we'll create a webhook defined at the user level that will be responsible for receiving a notification every time word count has been computed on a new document.

Creating a webhook is a two-steps process. You'll first need to set up how you want your webhook to behave through TextMaster: which events it should listen to. After that, you'll set up your server to receive and manage the payload.

The REST API allows you to manage webhooks. You can use it to list configured webhooks and change their configuration. For example, you can modify their payload URL and/or associated events.

## Exposing localhost to the internet

For the purposes of this tutorial, we're going to use a local server to receive messages from TextMaster. First of all, we need to expose our local development environment to the internet. We'll use ngrok to do this. ngrok is available, free of charge, for all major operating systems. For more information, see [the ngrok download page](https://ngrok.com/download).

After installing ngrok, you can expose your localhost by running `./ngrok http 4567` on the command line. 4567 is the port number on which our server will listen for messages. You should see a line that looks something like this:

```
$ Forwarding    http://7e9ea9dc.ngrok.io -> 127.0.0.1:4567
```

Make a note of the `*.ngrok.io` URL. We'll use it to set up our webhook later.

## Setting up a webhook

You can set up webhooks either globally, on your user account or on a specific resource. In this tutorial, we'll set up a global webhook.

You can use the following cURL query to create/update webhooks set up on your user account.

```shell
curl "https://api.textmaster.com/v1/clients/users/USER_ID" \
     -X PUT \
     -H "Authorization: Bearer ACCESS_TOKEN" \
     -H "Content-Type: application/json" \
     -d '
     {
       "user": {
         "callback": {
           "word_count_finished": {
             "url": "http://7e9ea9dc.ngrok.io/payload"
           }
         }
       }
     }
     '
```

You'll need to replace `USER_ID` with your own user id and `ACCESS_TOKEN` with a valid OAuth2 access token.

{% content-ref url="/pages/qYrz6Sqapsud2BoZSSbt" %}
[About OAuth Apps](/apps/about-oauth-apps)
{% endcontent-ref %}

Notice the payload URL is the URL of the server that will receive the webhook POST requests. Since we're developing locally for our tutorial, we've set it to the `*.ngrok.io` URL, followed by `/payload`.


# Configuring your server for webhooks

Learn how to set up a server to manage incoming webhook payloads.

Now that our webhook is ready to deliver messages, we'll set up a basic [Sinatra](http://sinatrarb.com/) server to handle incoming payloads.

## Writing the server

We want our server to listen to POST requests, at `/payload`, because that's where we told TextMaster our webhook URL was. Because we're using ngrok to expose our local environment, we don't need to set up a real server somewhere online, and can happily test out our code locally.

Let's set up a Sinatra application to do something with the webhook's payload. Our initial setup might look something like this:

{% code title="server.rb" %}

```ruby
require 'sinatra'
require 'json'

post '/payload' do
  push = JSON.parse(request.body.read)
  puts "I got some JSON: #{push.inspect}"
end
```

{% endcode %}

{% hint style="info" %}
**Tips:** If you're unfamiliar with how Sinatra works, we recommend reading [the Sinatra guide.](http://sinatrarb.com/)
{% endhint %}

Start this server up with:

```shell
$ ruby server.rb
```

Since we set up our webhook to listen to word count completion on documents, go ahead and create a new project with at least one document and let the word count analysis complete. Switch back to your terminal, you should see something like this in your server's output:

```shell
$ ruby server.rb
== Sinatra (v2.0.8.1) has taken the stage on 4567 for development with backup from Puma
Puma starting in single mode...
* Puma version: 5.5.2 (ruby 2.6.6-p146) ("Zawgyi")
*  Min threads: 0
*  Max threads: 5
*  Environment: development
*          PID: 819
* Listening on http://127.0.0.1:4567
* Listening on http://[::1]:4567
Use Ctrl-C to stop
> I got some JSON: {"word_count"=>100, "title"=>"...
```

Congratulations! You've successfully configured your server to listen to webhooks. Your server can now process this information any way you see fit. For example, if you were setting up a "real" web application, you might want to log some of the JSON output to a database and trigger business workflows.

{% hint style="info" %}
**Tips:** When setting up production servers, we strongly advise on handling webhook payloads asynchronously. Payloads may include heavy pieces of text which might take time to process on your server. HTTP connections are dropped after 30 seconds.
{% endhint %}


# Securing webhooks

Ensure your server is only receiving the expected TextMaster requests for security reasons.

Once your server is configured to receive payloads, it'll listen for any payload sent to the endpoint you configured. For security reasons, you probably want to limit requests to those coming from TextMaster. There are a few ways to go about this. For example, you could opt to allow requests from TextMaster's IP address but a far easier method is to set up a secret token and validate the information.

## Setting your secret token

You'll need to set up your secret token in two places: on TextMaster when setting up the webhook URL and your server.

To set your token on TextMaster, simply include the token in the callback URL either globally on the user account or on specific resources. Use a random string with high entropy to generate your token. You can use the following ruby command for example:

```shell
ruby -rsecurerandom -e 'puts SecureRandom.hex(20)'
```

You can then include your secret token as an URL parameter of your choice. For example:

```shell
curl "https://api.textmaster.com/v1/clients/users/USER_ID" \
     -X PUT \
     -H "Authorization: Bearer ACCESS_TOKEN" \
     -H "Content-Type: application/json" \
     -d '
     {
       "user": {
         "callback": {
           "word_count_finished": {
             "url": "https://example.com/payload?token=6f90f415ca54b100c3e9d24fdf2988cbb0815f5d"
           }
         }
       }
     }
     '
```

{% hint style="info" %}
**Tips:** In the future, TextMaster will use your secret token to create a hash signature of each payload. This will allow to validate the payload sent from TextMaster and make sure it has not be tempered.
{% endhint %}


# Troubleshooting webhooks

Learn how to review your webhook deliveries on TextMaster, including the HTTP request and response.

TextMaster provides some tooling for testing and troubleshooting your webhooks.

## Listing deliveries

You can list recent deliveries to quickly look whether a delivery was successful or failed. You can also identify when each delivery was attempted. TextMaster keeps a log of each webhook delivery for some period of time.

By expanding an individual delivery, you'll be able to witness *precisely* what information TextMaster is attempting to send to your server. This includes both the HTTP Request and Response.

![List recent deliveries](https://2001322700-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FiLJk7NFk70NqFY1GU7Oi%2Fuploads%2F2wmaewcezKG2qZKM4RuT%2Flist-deliveries.png?alt=media\&token=cf29711c-461a-4e7e-a0b6-241ad3a01efd)

### Request

The webhook delivery view provides information on which HTTP Headers were sent by TextMaster. It also includes details about the JSON payload.

![](https://2001322700-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FiLJk7NFk70NqFY1GU7Oi%2Fuploads%2FC47Cuhyrznf5JpScZlwa%2Fshow-request-delivery.png?alt=media\&token=ae9a02e6-3f56-4a13-9d6a-33924b3fe9c8)

### Response

The response tab lists how your server replied once it received the payload from TextMaster. This includes the status code, the headers, and any additional data within the response body.

![](https://2001322700-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FiLJk7NFk70NqFY1GU7Oi%2Fuploads%2FTeAAe0jE3ns1xuwtaKG1%2Fshow-response-delivery.png?alt=media\&token=4d54a8e5-825c-437b-bbbf-86701a8d7df9)

{% hint style="info" %}
**Tips:** We will sometime populate the response body with error messages on un-expected exceptions received from your server.
{% endhint %}

## Retries

TextMaster will automatically retry failed deliveries for up to 20 times with exponential backoff time in between each retry attempts. Each new attempt will appear as a new delivery in the recent delivery list.

## Idempotency

Your server implementation should be idempotent, meaning it should not error out if receiving the same webhook multiple times. We guarantee delivery of webhook at least once but webhooks can be delivered more than once to your server.

We also cannot guarantee the order in which webhooks are delivered. Your server should handle receiving events out of order.

## Timeout

The HTTP connection between TextMaster and your server will stay opened for at most 30 seconds. After that, the connection is automatically closed and the delivery is considered a failure.

Since webhooks can include heavy payloads, we recommend setting up an asynchronous server implementation which delegates the payload handling to a background job as soon as possible. This will ensure your server replies quickly to the delivery attempt and avoid timeout errors.


# Events

Learn what are events and how you can subscribe to them to build and set up integrations.

## Projects

The following events are available to subscribe to on projects.

| Event                       | Description                                                                                                                                                                                                                                                                         |
| --------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `project_in_progress`       | Triggered when a project has launched and is made available to be claimed by an author. This event turns out to be useful when project are launched asynchronously. Launch process can safely be retried if this event is not received in a reasonable time (more than 30 minutes). |
| `project_finalized`         | Triggered when a project is finalized, meaning that all documents have been attached to it and translation memory analysis and/or PEMT have been ran successfully.                                                                                                                  |
| `project_not_launched`      | Triggered when project with `auto_launch` option cannot be launched, often due to the client account not having enough credits on its wallet.                                                                                                                                       |
| `project_canceled`          | Triggered when a project is canceled.                                                                                                                                                                                                                                               |
| `project_tm_completed`      | Triggered when a project's translation memory analysis has successfully completed and project's cost has been updated accordingly.                                                                                                                                                  |
| `project_tm_diff_completed` | Triggered when a project's translation "Force Exact Matches" analysis has successfully completed and project's cost has been updated accordingly.                                                                                                                                   |
| `project_in_review`         | Triggered when the work for all project's documents has been submitted, and the documents are ready for internal quality control or for review by the client.                                                                                                                       |

## Documents

The following status change events are available on documents:

* `waiting_assignment`
* `in_progress`
* `in_review`
* `incomplete`
* `completed`
* `paused`
* `canceled`
* `quality_control`
* `copyscape`
* `counting_words`

See the following page for more informations about status change events:

{% content-ref url="/pages/n3yb0iOme9I20Uq0w94A" %}
[Workflow](/overview/workflow)
{% endcontent-ref %}

In addition, the following events are also available on documents.

| Event                     | Description                                                                           |
| ------------------------- | ------------------------------------------------------------------------------------- |
| `word_count_finished`     | Triggered when a the task of counting words on a document has successfully completed. |
| `support_message_created` | Triggered when a new message has been created on a document's support thread.         |


# Akeneo

Learn how our Akeneo integration works

[TextMaster](https://app.textmaster.com/), is designed to seamlessly integrate with [Akeneo's Product Information Management](https://www.akeneo.com/) (PIM) system, acting as an efficient bridge between the two platforms. Utilizing a specially designed middleware connector, TextMaster harnesses the power of its APIs to streamline the translation process of your product information.

With TextMaster, you can effortlessly send product translation requests directly from your Akeneo instance. The connector ensures the requests are properly transmitted, retrieves the source content, and initiates a new translation project within TextMaster.

Once the translation is completed, the connector diligently monitors for finished projects. It promptly identifies these newly translated materials and ensures they are automatically imported back into your Akeneo instance. This ensures a consistent and efficient content flow, eliminating the need for manual data transfers.

In essence, TextMaster not only enhances your product translation workflow but also enriches your product and product model data directly within the Akeneo platform. This integration bridges the gap between content management and translation, creating a cohesive and streamlined product information management process.


# Getting Started

Learn how as an administrator of your Akeneo instance you can integrate with TextMaster

{% hint style="info" %}
**Tips:** The following guide assumes you have a TextMaster account with enough credits to launch projects. If it's not the case, you can create a new account [here](https://app.textmaster.com/sign_up).
{% endhint %}

Open the "Connection settings" from the "Connect" menu and click the create button.

<figure><img src="https://2001322700-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FiLJk7NFk70NqFY1GU7Oi%2Fuploads%2Ff1V2JBkmesBSoddDybY1%2FXnapper-2023-06-22-11.02.48.png?alt=media&amp;token=53a06eaa-f1ad-4dff-b5c1-43cb5e789d27" alt=""><figcaption></figcaption></figure>

Specify a label for your new connection and click "save".

<figure><img src="https://2001322700-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FiLJk7NFk70NqFY1GU7Oi%2Fuploads%2F54umXdSIHnNL3u3Tb5O4%2FXnapper-2023-06-22-11.05.01.png?alt=media&amp;token=2e2a521a-0574-4d8a-84dc-875cf96cb89a" alt=""><figcaption></figcaption></figure>

Once the new connection is created, locate and share the credentials with your TextMaster's project manager, who will configure our TextMaster/Akeneo bridge for you to complete the installation.

{% hint style="info" %}
**Tips:** Share all four attributes, the Client ID, Secret, Username and Password with your project manager.
{% endhint %}

<figure><img src="https://2001322700-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FiLJk7NFk70NqFY1GU7Oi%2Fuploads%2FHWdMxMYJQgFI2kpUT1v1%2FXnapper-2023-06-22-11.08.12.png?alt=media&amp;token=669a92d3-4e8b-4ad1-a013-e5836252d4ee" alt=""><figcaption></figcaption></figure>

Open the Event subscription page and activate it. Request the URL to your TextMaster's project manager who should have provided it to you when you shared the credentials in the last step.

{% hint style="info" %}
**Tips:** Don't forget to click the "save" button at the top-right corner!
{% endhint %}

<figure><img src="https://2001322700-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FiLJk7NFk70NqFY1GU7Oi%2Fuploads%2F0Pp1q0lNGKRQ9lgBHt5e%2FXnapper-2023-06-22-11.16.03.png?alt=media&amp;token=e89a6b8c-e687-4959-b3bf-71782d02d4fd" alt=""><figcaption></figcaption></figure>

Once the Event subscription is saved, locate and share the secret with your TextMaster's project manager, who will configure our TextMaster/Akeneo bridge for you to complete the installation.

<figure><img src="https://2001322700-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FiLJk7NFk70NqFY1GU7Oi%2Fuploads%2FUO3vJ02bSPpqLzaBAV96%2FXnapper-2023-06-22-11.19.22.png?alt=media&amp;token=fc91f588-1a09-4a86-9eac-21a6800dc12f" alt=""><figcaption></figcaption></figure>

{% hint style="info" %}
**Tips:** If you click on the "test" button before your project manager have finish setting everything up, it is expected to get a `403` status code. However, you should get a `204` status code when everything is configured properly.
{% endhint %}

That's it! Your project manager will come back to you with a link to authorize our Akeneo connector to perform operations on your TextMaster's account on your behalf.

<figure><img src="https://2001322700-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FiLJk7NFk70NqFY1GU7Oi%2Fuploads%2FKKosQIef9NVuYLJccXIC%2FXnapper-2023-06-22-11.27.50.png?alt=media&amp;token=6c9c3266-852d-49f2-bd03-5ec26d84dfd8" alt=""><figcaption></figcaption></figure>


# Configuration

Learn how to configure your Akeneo instance to send products for translation on TextMaster

Now that you've successfully established a connection between Akeneo, your TextMaster account, and the middleware connector, the next crucial step is configuring your Akeneo setup. This involves importing a new group of attributes that are essential for the translation process.

This attribute group plays a vital role in tailoring the translation to your specific needs. It allows you to control and define the parameters for the translation requests you send from your Akeneo instance to TextMaster. Configuring these settings ensures the translation process aligns perfectly with your product information management strategy.

Follow the step-by-step instructions in the next section to import and configure these attributes effectively within your Akeneo platform.

{% hint style="info" %}
**Warning:** In order to complete the following steps, make sure your TextMaster's project manager shared with you 3 CSV files we will be importing. If it's not the case, feel free to contact us.
{% endhint %}

Open the "Imports" page, search for `CSV attribute_group` and click on the result.

<figure><img src="https://2001322700-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FiLJk7NFk70NqFY1GU7Oi%2Fuploads%2FJW7oIG2WOIJPo64NEuwl%2FXnapper-2023-06-22-11.32.51.png?alt=media&amp;token=8159fb4b-31fb-442c-9fc3-0bb23e29a232" alt=""><figcaption></figcaption></figure>

Click on the "Upload a file" button and drag & drop the first CSV file called `group`.

<figure><img src="https://2001322700-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FiLJk7NFk70NqFY1GU7Oi%2Fuploads%2FCSUrdfzu9vZ6kIQy2m8z%2FXnapper-2023-06-22-11.36.50.png?alt=media&amp;token=fa713159-f0e0-470c-a479-0c27505abc20" alt=""><figcaption></figcaption></figure>

Follow the same steps for the other imports:

* `Demo CSV attribute import` use the file called `attribute`
* `Demo CSV option import` use the file called `options`

Wait for the imports to be completed before proceeding to the next steps.

<figure><img src="https://2001322700-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FiLJk7NFk70NqFY1GU7Oi%2Fuploads%2FInhnME5HJ1ZlEPIMWOTC%2FXnapper-2023-06-22-11.44.51.png?alt=media&amp;token=cb8ed594-045e-4500-a71a-97ad79498afb" alt=""><figcaption></figcaption></figure>

#### Add Options to Translation Attributes

1. Go to "Settings" → "Attributes" panel
2. Search for "Translation"
3. Click on the "Translation attributes" item
4. Under the "Options" tab, press "Add option"

{% hint style="info" %}
**Tips:** This is where you can add the Akeneo attributes that you wish to translate. They will be displayed in the drop-down list of the "Translation Attributes" field. All fields requiring translation for products or product models must be set as localizable.
{% endhint %}

1. Input the "Option code" and press "Done"
2. Update the label for option, then press "Done"

{% hint style="info" %}
**Tips:** You can find the option code in the Attributes setting page (Settings → Attributes → Search the attributes → Click to attribute).
{% endhint %}

<div><figure><img src="https://2001322700-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FiLJk7NFk70NqFY1GU7Oi%2Fuploads%2F7SeVTDxiNZfU70VBy1gd%2FXnapper-2023-06-22-15.29.48.png?alt=media&amp;token=215ac6c3-226d-4fc3-bd23-66f4135a5c9a" alt=""><figcaption></figcaption></figure> <figure><img src="https://2001322700-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FiLJk7NFk70NqFY1GU7Oi%2Fuploads%2FCs0WwUNXLO9F4K5Er196%2FXnapper-2023-06-22-15.29.58.png?alt=media&amp;token=9eb22bfc-b804-4fb6-99fa-29c9a40cc6f9" alt=""><figcaption></figcaption></figure> <figure><img src="https://2001322700-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FiLJk7NFk70NqFY1GU7Oi%2Fuploads%2FZ6BeoUiQvhIJCzLQTXKT%2FXnapper-2023-06-22-15.30.15.png?alt=media&amp;token=431943e8-5b27-42b2-9bd0-8d87dead3e46" alt=""><figcaption></figcaption></figure> <figure><img src="https://2001322700-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FiLJk7NFk70NqFY1GU7Oi%2Fuploads%2Fuc27VdRilC3yodbHPpSk%2FXnapper-2023-06-22-15.30.36.png?alt=media&amp;token=3920084a-4103-4dbe-b4b7-09bb8e320845" alt=""><figcaption></figcaption></figure></div>

#### Add Translation Group to All Families

1. Go to "Settings" → "Families" panel
2. Select all the product families and choose "Bulk Actions"
3. Choose "Set Attributes Requirements" and click "Next"
4. Click the "Add by Groups" button, find the "Translation" group, and press "Add"

{% hint style="info" %}
**Tips:** You just need to add the group of attributes. If you check the boxes, it simply indicates that you want these attributes to be required for product content completion, which may not be relevant in this context.
{% endhint %}

Click "Next" and "Confirm".

{% hint style="info" %}
**Tips:** When you add a new Family, you must add the Translation Group to this new family. Also, when you add a new locale, you should update the label for the Attribute and Attribute option.
{% endhint %}

<div><figure><img src="https://2001322700-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FiLJk7NFk70NqFY1GU7Oi%2Fuploads%2FH0JX86w5XczQ3iqPKbFg%2FXnapper-2023-06-22-15.30.51.png?alt=media&amp;token=0bf51be7-89ab-4f42-ada1-e04d234b0395" alt=""><figcaption></figcaption></figure> <figure><img src="https://2001322700-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FiLJk7NFk70NqFY1GU7Oi%2Fuploads%2F6gHQJ1hB0eV9XC9X4xmW%2FXnapper-2023-06-22-15.35.29.png?alt=media&amp;token=b804af78-d368-439b-bdcb-cfe585226567" alt=""><figcaption></figcaption></figure> <figure><img src="https://2001322700-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FiLJk7NFk70NqFY1GU7Oi%2Fuploads%2FSJRtv97Ss2KAGHrXb9nB%2FXnapper-2023-06-22-15.35.43.png?alt=media&amp;token=e7201120-4c85-4fc3-b167-3895789f6d5b" alt=""><figcaption></figcaption></figure> <figure><img src="https://2001322700-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FiLJk7NFk70NqFY1GU7Oi%2Fuploads%2FxXvZQSagmfTWk6Uo0MPt%2FXnapper-2023-06-22-15.35.52.png?alt=media&amp;token=f1c52621-587a-4c51-bc96-7a43d8cc6d37" alt=""><figcaption></figcaption></figure> <figure><img src="https://2001322700-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FiLJk7NFk70NqFY1GU7Oi%2Fuploads%2FY4xo1Q7OlUoPazUASIAK%2FXnapper-2023-06-22-15.36.07.png?alt=media&amp;token=c6a97bdc-1c17-4daf-bd56-12fff818d1bf" alt=""><figcaption></figcaption></figure></div>

### Permissions

In your Akeneo connection settings, you will be requested to enter a 'Role' and if possible a 'Group'. You need to ensure that the users who will need to use the TextMaster connector have this Role defined on their Akeneo account.

<figure><img src="https://2001322700-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FiLJk7NFk70NqFY1GU7Oi%2Fuploads%2FQgnnp1BpudHUpYz1xOH7%2Fconnection_permissions.png?alt=media&amp;token=c803e3ee-84b3-4152-814c-39cb94787b89" alt=""><figcaption></figcaption></figure>

This Role needs to have all the necessary authorizations. To do this, go to "System > Roles > select Role", then go to the "Web API permissions" tab and check all the boxes.

<figure><img src="https://2001322700-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FiLJk7NFk70NqFY1GU7Oi%2Fuploads%2FNNCM88moAhd2otkRiBoH%2Fweb_api_permissions.png?alt=media&amp;token=754f4e43-b89b-4c4d-872e-e93fb1c42e53" alt=""><figcaption></figcaption></figure>

In addition, depending on your version of Akeneo or your configuration, you may need to define permissions for your "Categories" and "Attribute groups".

If this is the case, go to the "Settings" section of Akeneo, then to the "Categories" page, select the "Master" category and, in the "Permissions" tab, add the User groups/Roles that have been set in the Connection.

The same applies to Attribute groups, just select the group containing your attributes to be translated and go to the "Permissions" tab.

<div><figure><img src="https://2001322700-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FiLJk7NFk70NqFY1GU7Oi%2Fuploads%2Frbh58wcW5y9rA5Mf6P7o%2Fcategory_root_permissions.png?alt=media&amp;token=41355fbe-4b44-40db-8875-a9bf038f5f17" alt=""><figcaption></figcaption></figure> <figure><img src="https://2001322700-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FiLJk7NFk70NqFY1GU7Oi%2Fuploads%2FMgiT8gPGTfs0kg1g16ls%2Fattribute_group_permissions.png?alt=media&amp;token=fd9bdaa7-70d6-4b3b-835d-ec7ce88841a5" alt=""><figcaption></figcaption></figure></div>


# Usage

Learn how to send products for translation to TextMaster from your Akeneo instance.

### Send products for translation

1. Navigate to the Product Grid
2. Select the desired products
3. Click on the "Bulk Actions" button

<figure><img src="https://2001322700-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FiLJk7NFk70NqFY1GU7Oi%2Fuploads%2FVO9gf4Knm0tWczxLa1hj%2FXnapper-2023-06-22-15.16.47.png?alt=media&amp;token=54a89f33-7ea9-432e-b8e9-5fc3d1571ae0" alt=""><figcaption></figcaption></figure>

On the "Product Bulk Action" page, choose "Edit Attributes Values".

<figure><img src="https://2001322700-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FiLJk7NFk70NqFY1GU7Oi%2Fuploads%2FJO0O9MNSToomj545sjDC%2FXnapper-2023-06-22-15.16.58.png?alt=media&amp;token=bf5272c3-0bb2-4e50-adbc-8a46a50b2603" alt=""><figcaption></figcaption></figure>

1. Click on "Select Attributes"
2. Find the "Translation" group attributes
3. Select only:
   * "Translation Project"
   * "Translation Template"
   * "Translation Attributes"
4. Click on "Add"

<figure><img src="https://2001322700-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FiLJk7NFk70NqFY1GU7Oi%2Fuploads%2F549ne97tOeSpIVmKNTFX%2FXnapper-2023-06-22-15.17.05.png?alt=media&amp;token=58454ff7-ea34-4e58-bdc3-a303e76de4d6" alt=""><figcaption></figcaption></figure>

1. Input the information:
   * "Translation Project": Enter a unique project name
   * "Translation Template": Select one or multiple API templates
   * "Translation Attributes": Select the Akeneo attributes to be translated
2. Click "Next" and confirm

<figure><img src="https://2001322700-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FiLJk7NFk70NqFY1GU7Oi%2Fuploads%2FHC6xH1kmpNmmfWxakG6L%2FXnapper-2023-06-22-15.17.18.png?alt=media&amp;token=72bc9303-fe2d-402d-b81e-fabe6ed8dc82" alt=""><figcaption></figcaption></figure>

The attributes have now been edited, and the project will be sent to TextMaster. Akeneo will send an event with your product(s) information to the middleware to be processed. You can verify that everything is in order by first checking that the event was sent then visit your TextMaster account or the middleware dashboard.

<figure><img src="https://2001322700-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FiLJk7NFk70NqFY1GU7Oi%2Fuploads%2F2zLHgCivEGG7B9euHJNN%2FXnapper-2023-06-29-09.53.27.png?alt=media&amp;token=3e6d700e-e72c-4079-a6e8-e6c38c6c32e3" alt=""><figcaption></figcaption></figure>

{% hint style="info" %}
**Tips:** If you don't see any events in the Event logs, please head to the [Troubleshooting](/integrations/akeneo/troubleshooting) section to help you fix the problem.
{% endhint %}

### **Points to consider**

* The "Project Name" must be unique for the same locales
* Multiple project templates can be selected
* Project templates are used to define source and target languages
* You can send both products and product models
* Ensure that the products you send have translatable content
* If a selected product is already being translated in the same selected locale, it won't be included in the project
* None of the three fields must be empty
* The attributes "Translation Status" and "TextMaster Updated At" are localized, meaning their value will be updated on the destination locale
* These two attributes inform the status of the project on TextMaster
* You can create a view on the product grid page to monitor the translation attribute group

{% hint style="info" %}
**Tips:** It’s not recommended to send products to TextMaster one by one. This creates multiple projects with only one document, which can overload the platform and is not ideal for authors.
{% endhint %}

By following these steps, you're on your way to creating multilingual product descriptions that cater to your diverse audience. Happy translating!


# Monitoring

Learn how to monitor your translation projects sent to TextMaster.

Monitoring the progress of your translation projects and their respective products sent to TextMaster is conveniently manageable directly from either an Akeneo view or the middleware's dashboard.

## **Create a view**

To monitor translation status per product and to see if they've already been sent, you can create a view by following these steps:

1. Press "Create View" on the product grid page
2. Name this new view
3. Modify the columns:
   * Add the Translation attributes
   * Find them
   * Drag them to the right columns
   * Then press "Apply"
4. Click on the three dots button and save the view

<div><figure><img src="https://2001322700-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FiLJk7NFk70NqFY1GU7Oi%2Fuploads%2FnhTohoHM551jeUybikyh%2FXnapper-2023-06-22-15.19.26.png?alt=media&amp;token=a9f4f317-80a7-4874-8246-894c5c385204" alt=""><figcaption></figcaption></figure> <figure><img src="https://2001322700-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FiLJk7NFk70NqFY1GU7Oi%2Fuploads%2FetWATnfP2NZ3ZzXz7ICu%2FXnapper-2023-06-22-15.19.45.png?alt=media&amp;token=592710b5-99b6-4564-b19c-1da8876b2df2" alt=""><figcaption></figcaption></figure> <figure><img src="https://2001322700-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FiLJk7NFk70NqFY1GU7Oi%2Fuploads%2FDXA33suEMyhcwiW1thv3%2FXnapper-2023-06-22-15.21.59.png?alt=media&amp;token=5d1256b6-4e50-4c82-9ff5-095ba3423c82" alt=""><figcaption></figcaption></figure></div>

## Middleware's dashboard

To monitor the overall translation status and synchronization between TextMaster and your Akeneo instance, you can use your personal dashboard your project manager provided you during the configuration phase.

<figure><img src="https://2001322700-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FiLJk7NFk70NqFY1GU7Oi%2Fuploads%2FQ86c9kmjuMUGef24qmb6%2FXnapper-2023-06-22-15.17.35.png?alt=media&amp;token=50325b4a-49ca-4db9-9b4f-8dc1a2df27d7" alt=""><figcaption></figcaption></figure>


# Troubleshooting

Learn how to resolve the most common problems people encounter with the Akeneo integration.

If you're encountering some oddities during the integration, here's a list of resolutions to some of the problems you may be experiencing. If you have any problems or requests, please contact [TextMaster support](mailto:support@textmaster.com).

## Debug events

{% hint style="warning" %}
**Warning:** The following features are only available since the 6.0 version and for SaaS customers.
{% endhint %}

Events are sent from your Akeneo instance to the middleware every time you create or update a product. The event's payload includes your product(s) metadata. In the context of the TextMaster integration, we use these events to detect when you selected product(s) you wish you send to translation.

The logs of these events can be found under the "Connect" → "Event logs" page.

<figure><img src="https://2001322700-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FiLJk7NFk70NqFY1GU7Oi%2Fuploads%2F2zLHgCivEGG7B9euHJNN%2FXnapper-2023-06-29-09.53.27.png?alt=media&amp;token=3e6d700e-e72c-4079-a6e8-e6c38c6c32e3" alt=""><figcaption></figcaption></figure>

### No events in the Event logs

If there are no events sent after you've sent product(s) for translation, your Akeneo instance is most likely not configured properly. Make sure you have configured the consumer worker correctly for the Events API to work. More information about this is available here:

{% embed url="<https://docs.akeneo.com/master/install_pim/manual/events_api.html>" %}

### Log types

{% hint style="info" %}
**Tips:** Please note that Akeneo store errors and warnings for the past 72 hours, and only the latest 100 notices and info logs.
{% endhint %}

You may see the following messages in your event logs:

* `ERROR The endpoint returned an error.` In that case, the middleware received the event request, but something went wrong, and it answered with an error
* `ERROR The endpoint failed to answer under 500 ms.` This error means that the middleware did not respond quickly enough
* `WARNING The maximum number of requests per hour has been reached.` If you have this warning log, you might be interested in increasing your limit and scalability restrictions
* `NOTICE The event was not sent because the product does not exist or the connection does not have the required permissions.` When the PIM doesn't send an event because of a lack of permission, it can be normal. For instance, if you previously set up the connection permission to not receive events on products that are irrelevant for this particular app. If you think you should have received this event, please take a look at the [permission configuration section](https://help.akeneo.com/pim/serenity/articles/manage-event-subscription.html#manage-your-permissions)

{% embed url="<https://api.akeneo.com/events-documentation/more-about-events.html>" %}

## Invalid 'pim\_source' URL Error

Your content may not be sent correctly to TextMaster if you encounter this error (which will be reported to you by our teams in charge of your connection).

This error occurs when the `AKENEO_PIM_URL` environment variable does not match the expected Akeneo PIM URL. To resolve it, ensure that the `AKENEO_PIM_URL` variable is set to the correct URL as specified for the connector configuration.

To resolve this problem you need to:

* Locate the `AKENEO_PIM_URL` environment variable in your configuration files.
* Ensure the value of this variable aligns with the PIM URL configured for your connector, rather than a local development address (`http://localhost:8080`).

If the environment variable does not update, modify the argument directly in the Akeneo code configuration as a workaround. For example, update the `pim_source` argument in the relevant handler file to the correct PIM URL.


# Salesforce Commerce Cloud

Learn how our SFCC integration works

Our [TextMaster](https://app.textmaster.com/) extension, integrated with [Salesforce Commerce Cloud](https://www.salesforce.com/products/commerce-cloud/overview/) (SFCC), empowers you to effortlessly translate your content across a myriad of languages. This sophisticated tool makes the process simple, enabling mass edits to ensure your content resonates with a global audience.

The extension is comprehensive, allowing for the translation of a wide range of content types within the SFCC platform:

1. **Products:** Translate product information and descriptions to help your customers understand the features and benefits of your offerings in their native language
2. **Categories:** Localize your product categories, making your site navigation more user-friendly for your international audience
3. **Content Assets:** Translate all types of digital content assets, such as blogs, articles, and guides, ensuring that they resonate with your global audience
4. **Library Folders:** Easily manage and translate content in your library folders, ensuring a coherent content strategy across all markets
5. **Page Designers:** Localize your page designs and layouts to ensure your pages communicate effectively to users in different regions
6. **Page Designer Components:** Translate specific elements within your page designs to ensure every detail of your site speaks to your global audience

By enabling mass translation processes, the TextMaster extension with Salesforce Commerce Cloud ensures that your e-commerce platform is not just multilingual, but also culturally sensitive and relevant, driving customer engagement and boosting international sales.

## How it works

The TextMaster menu is conveniently located under the 'Merchant Tools' section on each site where the module is installed. This menu is your gateway to managing all your translation-related activities within the Salesforce Commerce Cloud (SFCC) environment.

<figure><img src="https://2001322700-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FiLJk7NFk70NqFY1GU7Oi%2Fuploads%2F1pUmdrv5EcHU1MwnyHcU%2FXnapper-2023-06-22-14.20.13.png?alt=media&amp;token=e837690f-06ee-491f-8c97-d0570d5f21be" alt=""><figcaption></figcaption></figure>

The menu comprises six main tabs, each serving a unique purpose:

1. **Send Content for Translation to TextMaster:** This tab serves as your launchpad for translation projects. It enables you to search for and select the content that you wish to translate, and then send it directly to TextMaster
2. **Translation Dashboard:** The dashboard provides you with a bird's eye view of all your translation activities. You can track the status of the items you've sent for translation and manage your projects in real-time
3. **Attribute Setup:** Here, you can register the default attributes that will be sent for translation each time you initiate a new translation request. Don't worry; you'll still have the flexibility to select additional attributes during the project creation phase
4. **Language Mapping:** Before you can send content for translation from or to a particular SFCC locale, you need to add the locale to this list and link it with a TextMaster language. This ensures accurate and effective translations across different regional settings
5. **API Setup:** This page is where you preset the attributes that should apply to translation projects by default. It's a timesaver when creating your translation projects, as it eliminates the need for repeated attribute selection
6. **API Authentication:** After saving your configurations on the API Setup page, you will need to complete the OAuth connection process here. It establishes a secure connection between this interface and your TextMaster account

<figure><img src="https://2001322700-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FiLJk7NFk70NqFY1GU7Oi%2Fuploads%2Fxb4ZdVstdLorJBoso3XY%2FXnapper-2023-06-22-14.21.22.png?alt=media&amp;token=f468da18-8212-4cce-8b2a-cb7a21b5636a" alt=""><figcaption></figcaption></figure>

The TextMaster menu in your SFCC is designed to provide you with the most efficient and seamless translation management experience.


# Getting Started

Learn how as an administrator of your SFCC instance you can integrate with TextMaster

{% hint style="info" %}
**Tips:** The following guide assumes you have a TextMaster account with enough credits to launch projects. If it's not the case, you can create a new account [here](https://app.textmaster.com/sign_up).
{% endhint %}

## Project Templates

The project template serves as a blueprint for your translation projects, enabling you to predefine and save your preferred translation settings such as translation memory, glossaries, preferred authors, and expertise. Each of these options is critical in tailoring the translation process to meet your specific needs.

One key aspect of these templates is that they are language-pair specific. This means you need to create a separate template for each language pair you're working with. Although this might require some initial effort, it significantly streamlines your subsequent translation requests.

Once your templates are set up, sending an item for translation becomes a breeze. You won't need to manually select your options every time. Instead, the predefined settings in your template will automatically apply, saving you time and ensuring consistency across all your translations.

In essence, the project template is not just a tool for convenience; it's an essential component of an efficient and effective translation process within TextMaster.

You can manage your project templates under the API & Loop menu on your TextMaster account.

<figure><img src="https://2001322700-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FiLJk7NFk70NqFY1GU7Oi%2Fuploads%2Fo3z3RrGy74en3HpP12XP%2FXnapper-2023-06-22-14.12.57.png?alt=media&amp;token=775622ca-d104-48f2-95f5-a1292894c3ac" alt=""><figcaption></figcaption></figure>

Click the New API Template button.

<figure><img src="https://2001322700-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FiLJk7NFk70NqFY1GU7Oi%2Fuploads%2FiV1pdWMrjCO7BmaYfgzK%2FXnapper-2023-06-22-14.10.17.png?alt=media&amp;token=749085dd-bde4-487b-a63b-76fd7ad94a25" alt=""><figcaption></figcaption></figure>

## Installation

The plugin can be downloaded from the following link. Comprehensive installation documentation is included within the plugin package to guide you through the setup process.

{% hint style="info" %}
**Tips:** Our cartridge is designed with forward-thinking technology, incorporating JS controllers and the new job framework. Based on SiteGenesis, it is also fully compatible with the Salesforce Commerce Cloud's Storefront Reference Architecture (SFRA).
{% endhint %}

{% embed url="<https://github.com/textmaster/demandware-cartridge>" %}

#### Important notes

* **Primary Site Activation:** We strongly recommend activating the TextMaster plugin on your primary site - the one where the main source languages are enabled. This ensures optimal functionality and translation management
* **Account Usage:** To maintain efficiency and streamlined operations, we highly recommend against using multiple TextMaster accounts with SFCC. Centralizing all translation requests in one TextMaster account is preferred. You can still separate invoicing by target language if necessary
* **Testing:** Before transitioning the connector to a production environment, we strongly recommend conducting end-to-end testing. This includes:
  * Technical validation of the installation with our team
  * Functional validation of the connector with our team

For any questions or assistance, you can reach out to your dedicated TextMaster project manager directly or use [this form](https://www.textmaster.com/contact/) and select the "API/Integration" topic. We are here to ensure a smooth integration process for your SFCC setup.


# Configuration

Learn how to configure your SFCC instance to send products for translation on TextMaster

1. Navigate to "Merchant Tools" → "TextMaster" → "API Setup" in your SFCC environment
2. Under the "API Environment" section, select "Live". This will ensure that your live content is being translated
3. Enter the ID of your master catalog. This ensures that the right product information is being selected for translation
4. If your environment is password-protected, enter your password in the "Storefront Protection Password" field. This helps maintain the security of your translation process
5. Provide the API Base URLs for both the Demo and Live environments. These URLs enable the communication between SFCC and TextMaster
6. Once you've filled in all the necessary fields, click the "Save" button

<figure><img src="https://2001322700-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FiLJk7NFk70NqFY1GU7Oi%2Fuploads%2FdZtrG87DRaai0oIRnnjg%2FXnapper-2023-06-22-14.26.55.png?alt=media&amp;token=53ae9d47-4b29-4f2e-b3d0-9a43a748b248" alt=""><figcaption></figcaption></figure>

In order to facilitate a secure connection between your TextMaster account and the Salesforce Commerce Cloud (SFCC) plugin, you need to create an OAuth application in your TextMaster account. Once created, this OAuth application needs to be linked in the "API Authentication" page of the SFCC plugin.

Log in to your TextMaster account, navigate to the API & Loop menu.

<figure><img src="https://2001322700-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FiLJk7NFk70NqFY1GU7Oi%2Fuploads%2FSVaZBX1evFEAx8kuJgcG%2FXnapper-2023-06-22-14.32.17.png?alt=media&amp;token=d1050ad6-c7ae-48a2-8eb1-e35fce731496" alt=""><figcaption></figcaption></figure>

Click on the "New application" button and follow these instructions:

1. In the "Name" field, type a unique name for your application to easily identify it
2. For the "Authorization callback URL", enter your callback URL in the following format: `https://<hostname of your SFCC business manager environment>/on/demandware.store/Sites-Site/default/TMTranslation-Authentication`

   Alternatively, you can copy this URL directly from the "API Authentication" page of the TextMaster plugin modules in the Business Manager of your SFCC instance
3. In the "Scopes" section, select the checkboxes for `project:manage` and `user:manage`. This will allow your application to manage projects and users on your behalf
4. Click "Save" to finalize the creation of your new application

<figure><img src="https://2001322700-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FiLJk7NFk70NqFY1GU7Oi%2Fuploads%2FciByYVB6JVAQ1ymmzLAi%2FXnapper-2023-06-22-14.36.18.png?alt=media&amp;token=af921949-3bf7-4158-bcfb-b831b049ae98" alt=""><figcaption></figcaption></figure>

After creating your OAuth application within TextMaster, you'll need to link it with your Salesforce Commerce Cloud (SFCC) instance. To do this:

1. Take note of the Application ID and Secret that were generated when you created your application in TextMaster
2. Return to the Business Manager of your SFCC instance and navigate to the "API Authentication" page within the TextMaster plugin modules
3. In the form provided, enter the Application ID and Secret that you noted down earlier
4. Click on "Authorize in TextMaster". This will redirect you to your TextMaster account
5. Follow the instructions provided by TextMaster to authorize the app. This process will involve granting the necessary permissions to the app to ensure it can function as expected
6. Once you've authorized the app, you will be redirected back to your SFCC page. Here, click on the "Generate Token" button. This action will generate a token internally which will be used by the plugin for all subsequent API communications

By following these steps, you'll successfully connect your TextMaster OAuth application with your SFCC instance. This connection is essential for enabling seamless and secure communication between TextMaster and SFCC. If you encounter any issues or need further clarification, please don't hesitate to reach out to our support team.

## Language Mapping

Language Mapping is a vital step in ensuring accurate and context-specific translations between Salesforce Commerce Cloud (SFCC) and TextMaster. Here's how to establish this link:

1. In your SFCC environment, navigate to "Merchant Tools" → "TextMaster" → "Language Mapping"
2. Click on the "Add a Language Mapping" button. This will open up a new mapping form
3. From the selection dropdown, choose the appropriate SFCC Language that you want to map
4. Similarly, select the corresponding TextMaster Language from the next dropdown. This language will be used by TextMaster for the translations
5. After selecting the languages, click on the "Save" button to finalize the mapping

<figure><img src="https://2001322700-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FiLJk7NFk70NqFY1GU7Oi%2Fuploads%2FewOsucqu5DKsCARVOcIc%2FXnapper-2023-06-22-14.39.35.png?alt=media&amp;token=e7e0a2ef-6d8f-4015-a8cd-d4b738e5a784" alt=""><figcaption></figcaption></figure>

By following these steps, you've successfully created a language mapping between SFCC and TextMaster. This mapping will ensure that your translations are properly localized for your specified regions. If you need to map more languages, just repeat these steps for each language pair.

## Default Attributes

Establishing default attributes streamlines the translation process by automatically including specific attributes every time you initiate a translation request. Here's how to set up default attributes:

1. Navigate to "Merchant Tools" → "TextMaster" → "Attribute Setup" in your Salesforce Commerce Cloud (SFCC) environment
2. From the options available, select the "Item Type" that your default attributes will apply to. This could be Products, Categories, Content Assets, etc., depending on what your SFCC setup includes
3. Next, review the list of available attributes for the selected item type. Check the boxes next to the attributes that you want to include by default in every translation request sent to TextMaster
4. Once you've selected all the relevant attributes, click on the "Save" button to finalize your selections

<figure><img src="https://2001322700-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FiLJk7NFk70NqFY1GU7Oi%2Fuploads%2FkTHWYWV3cRnFIsd5BuL5%2FXnapper-2023-06-22-14.41.23.png?alt=media&amp;token=d634a820-527d-4f71-ba48-4bb03f73ccbf" alt=""><figcaption></figcaption></figure>

Remember, the attributes you set as defaults will automatically be included in every translation request for the corresponding item type. You can always come back to this page to adjust your default attributes as needed.


# Usage

Learn how to send products for translation to TextMaster from your SFCC instance.

### Send products for translation

1. Navigate to "Merchant Tools" → "TextMaster"
2. Select "Send Content for Translation"
3. Choose the type of content you want to translate: product, category, content asset, library folder, Page Designer, or Page Component. Remember, you can select only one item type for each translation project

<figure><img src="https://2001322700-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FiLJk7NFk70NqFY1GU7Oi%2Fuploads%2FS4r5ffEtd2sOmnSOAGl0%2FXnapper-2023-06-22-14.44.40.png?alt=media&amp;token=7ee0c5e7-dd96-4ef6-824c-9fa3a6b7d844" alt=""><figcaption></figcaption></figure>

Launch your content search. You can refine your search using filters such as ID, category, available language(s), and date. For more control over the selection, you can use the "Exclude items exported on or after date" option to eliminate items already sent to TextMaster after a specific date.

<figure><img src="https://2001322700-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FiLJk7NFk70NqFY1GU7Oi%2Fuploads%2F3J1M9ne9h5jafLJSTWQk%2FXnapper-2023-06-22-14.46.12.png?alt=media&amp;token=16b9b429-cdb4-4963-85d5-a30d52e00849" alt=""><figcaption></figcaption></figure>

Select the item(s) you want to translate.

{% hint style="info" %}
**Tips:** Note that you can export up to 4000 items, but it's recommended to split large exports into several batches.
{% endhint %}

<figure><img src="https://2001322700-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FiLJk7NFk70NqFY1GU7Oi%2Fuploads%2FYYLxTn4AOhtvCBvczStF%2FXnapper-2023-06-22-14.47.32.png?alt=media&amp;token=39c1addb-a971-4a72-a801-0adb10077438" alt=""><figcaption></figcaption></figure>

Choose the attributes you want to translate. This can include or exclude the default attributes you've previously set.

<figure><img src="https://2001322700-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FiLJk7NFk70NqFY1GU7Oi%2Fuploads%2FpuPxDelfTrGnafncS61G%2FXnapper-2023-06-22-14.48.24.png?alt=media&amp;token=bb2f7294-df70-471b-8db6-6eee34b66008" alt=""><figcaption></figcaption></figure>

For Page Designer content, you have two export methods:

* Select different Pages Designers to send global page attributes (like title, SEO elements, etc.)
* To send specific content for a Page Designer, select the "Page Component" item type. You must select at least one attribute per Component

<div><figure><img src="https://2001322700-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FiLJk7NFk70NqFY1GU7Oi%2Fuploads%2FpGpxQPC7XJeIFdRo9OhN%2FXnapper-2023-06-22-14.49.08.png?alt=media&amp;token=74d15076-feaf-4fae-ae24-1bf3dc181fe1" alt=""><figcaption></figcaption></figure> <figure><img src="https://2001322700-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FiLJk7NFk70NqFY1GU7Oi%2Fuploads%2FKN8FJhTIvmTfB3v1hdjD%2FXnapper-2023-06-22-14.50.19.png?alt=media&amp;token=103abbda-8fcb-4082-bb6f-8f7b9d818527" alt=""><figcaption></figcaption></figure></div>

Choose the different Components you want to send from the dashboard.

<figure><img src="https://2001322700-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FiLJk7NFk70NqFY1GU7Oi%2Fuploads%2Fm8NsqF0UMWhDVixk58Nw%2FXnapper-2023-06-22-14.51.08.png?alt=media&amp;token=661cec2d-b928-4a2f-a3ea-983c0fcaaa34" alt=""><figcaption></figcaption></figure>

For each selected Component, you then need to select the attributes of the Component you want to send for translation.

{% hint style="info" %}
**Tips:** You must select at least 1 attribute per Component, otherwise the project cannot be validated and a warning message will indicate which Component is "empty".
{% endhint %}

<figure><img src="https://2001322700-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FiLJk7NFk70NqFY1GU7Oi%2Fuploads%2FEP3pBXpkgQjOfFD4nUon%2FXnapper-2023-06-22-14.51.47.png?alt=media&amp;token=ed1a64a2-7985-45ff-b4ee-0d6e75a7bd88" alt=""><figcaption></figcaption></figure>

Define your project parameters. You need to select a source language and one or more target languages. You can also select an automatic or manual naming method for your project.

<figure><img src="https://2001322700-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FiLJk7NFk70NqFY1GU7Oi%2Fuploads%2FdcNwR1EdxNFw9Qn9BVai%2FXnapper-2023-06-22-14.53.08.png?alt=media&amp;token=bf6c497d-5b02-4f7c-8a36-36f8fffa48a9" alt=""><figcaption></figcaption></figure>

Click "Send to TextMaster" once you've selected your content and defined the parameters.

Select your translation templates. For each language pair selected, you'll need to choose a [project template](/reference/project-templates), which contains all your pre-set options.

Finally, click "Place order".

<figure><img src="https://2001322700-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FiLJk7NFk70NqFY1GU7Oi%2Fuploads%2FMjWVUEZyRY4YlYctgLA2%2FXnapper-2023-06-22-14.54.52.png?alt=media&amp;token=2f6b1692-f82c-42ee-b33a-6c137889ac35" alt=""><figcaption></figcaption></figure>

By following these steps, you're on your way to creating multilingual product descriptions that cater to your diverse audience. Happy translating!


# Monitoring

Learn how to monitor your translation projects sent to TextMaster.

Monitoring the progress of your translation projects and their respective items sent to TextMaster is conveniently manageable directly from the Salesforce Commerce Cloud (SFCC) plugin. Here's how:

## Translation Dashboard

Access the project monitoring dashboard either through "Merchant Tools" → "TextMaster" → "Translation Dashboard" in the SFCC plugin, or from the shortcut button that appears at the end of the project creation process.

### Primary Dashboard

<figure><img src="https://2001322700-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FiLJk7NFk70NqFY1GU7Oi%2Fuploads%2F7wkS1dqDFBmC4yCK1HSy%2FXnapper-2023-06-22-14.58.29.png?alt=media&amp;token=6da14643-4c3d-492e-b86b-694d5da9b31a" alt=""><figcaption></figcaption></figure>

This dashboard lists all the projects created within the plugin for TextMaster. Each project entry includes information such as the Project Name, Item Type, Target Locale, Price, Last Update, and Status on TextMaster.

When you initiate a project, its status will appear as "In Creation" on this dashboard. To start the translation process, click the "Translate" action button. This action indicates your acceptance of the project's content and cost.

{% hint style="info" %}
**Tips:** If the TextMaster template you've chosen includes the "Autolaunch" option, the project will automatically launch regardless of the price, transitioning from "In Creation" to "In Progress" status without any action on your part.
{% endhint %}

Clicking on a project name will take you to a secondary dashboard.

### Secondary Dashboard

<figure><img src="https://2001322700-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FiLJk7NFk70NqFY1GU7Oi%2Fuploads%2FOdA9OMZz2C3Se2SIQOrp%2FXnapper-2023-06-22-15.00.19.png?alt=media&amp;token=ee194ece-1381-405b-a7f9-24c28780035f" alt=""><figcaption></figcaption></figure>

This dashboard displays all the documents associated with the chosen project. It provides a detailed status update for each item included in the project on TextMaster.

If you wish to view the project in your TextMaster account, you can access it directly from the dashboard.

When a document's status is "In Review", this means the translation is complete. Several actions are then available:

* **Preview Content**: Allows you to preview the translation of your document in the context of your storefront
* **Review Translation**: This button redirects you to the specific document page on TextMaster for any necessary revisions
* **Validate**: Once a document is "In Review", you can validate the translation and change the document status to "Completed"

<figure><img src="https://2001322700-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FiLJk7NFk70NqFY1GU7Oi%2Fuploads%2F19M4ADxVfWZv5vDJdyt1%2FXnapper-2023-06-22-15.01.17.png?alt=media&amp;token=4bbafacd-672b-45cf-854e-500d70ed1387" alt=""><figcaption></figcaption></figure>

You don't need to manually import the translations back into SFCC. The plugin automatically imports content as soon as it reaches the "In Review" status. A second import is triggered when the document goes to "Completed", ensuring that the most recent content updates are reflected in your SFCC environment.


# Troubleshooting

If you're encountering some oddities during the integration, here's a list of resolutions to some of the problems you may be experiencing. If you have any problems or requests, please contact [TextMaster support](mailto:support@textmaster.com).

## Common issues

### Translated Content Not Appearing on SFCC

If your translation projects are complete on TextMaster but you can’t find your translated content on SFCC, several issues could be the cause:

* **OCAPI Permission Issues on SFCC**

Make sure that OCAPI permissions are correctly set up on SFCC. Verify that the OCAPI credentials are correct. Check the configuration of the site path and storefront password.

* **Server Blocking External Callbacks**

If your server blocks external callbacks, ensure it allows connections for the necessary operations.\
Check for any callback errors by navigating to **TextMaster > API & Loop > View My Callbacks** and verifying if there are any errors.

### Page Designer Content Not Available

If your Page Designer content is not displayed in the SFCC plugin, this means that you must first run a Job in SFCC to enable the plugin to find this content.

{% hint style="info" %}
**Context**: Page Designer content is not immediately available upon plugin installation because there are no straightforward API methods to retrieve lists of Page Designers and Page Components. The plugin’s user interface may have difficulty accessing data for these objects.&#x20;

To address this:

* A custom cache-based mechanism is used to export Page Designer and Page Component content.&#x20;
* After integrating the cartridge, convert all page data to a custom cache so that the plugin can access it.
  {% endhint %}

#### How to fix

To update custom cache for Page Designer content, execute the job `TextMasterConvertPagesToCache<siteID>`

* This job is not required for regular updates but must be executed whenever a manual change is made to page data. Before an export session, execute this job to ensure that recent changes are reflected in the custom cache and export page.
* A single execution of this job is sufficient after any manual data update.

#### Verify your changes

Go to **Administration panel > Jobs** and select `TextMasterConvertPagestoCache`

* In the **Job Steps** panel:
  * Confirm that the **Library ID** is correct.
  * Ensure the **Scope** is set to the appropriate site, the one including the content.

In **Merchant Tools > Site Preferences > TextMaster**:

* Verify that the **OCAPI Client ID and Password** are filled in correctly and are valid.
* Confirm that the **Storefront Password** is correctly filled in and valid.

{% hint style="info" %}
**Tips:** Once these verifications have been made, be sure to restart the job and ensure that the content is now available on the plugin.
{% endhint %}


# Abilities

## Listing abilities

Public endpoint to list all available abilities and their pricing supported by TextMaster.

{% openapi src="<https://app.textmaster.com/api-docs/v1/clients/specs.yaml>" path="/v1/clients/abilities" method="get" %}
<https://app.textmaster.com/api-docs/v1/clients/specs.yaml>
{% endopenapi %}

#### Code samples

```shell
curl -G "https://api.textmaster.com/v1/clients/abilities" \
  --data-urlencode 'activity=copywriting' \
  --data-urlencode 'locale=en-US' \
  -H "Authorization: Bearer 427ba17dc03db4792cd8d3c731ed53addd261b1baa7eef1ceda2cf2ca20f2b79"
```


# Authors

## List authors able to work on project

Returns a list of authors who have the skills to work on a project given its attributes without actually creating it.

{% hint style="info" %}
**OAuth:** This endpoint requires the default `public` scope.
{% endhint %}

{% openapi src="<https://app.textmaster.com/api-docs/v1/clients/specs.yaml>" path="/v1/clients/authors" method="get" %}
<https://app.textmaster.com/api-docs/v1/clients/specs.yaml>
{% endopenapi %}

#### Code samples

```shell
curl -G "https://api.textmaster.com/v1/clients/authors" \
  --data-urlencode "activity_name=translation" \
  --data-urlencode "options[language_level]=premium" \
  --data-urlencode "options[quality]=yes" \
  --data-urlencode "language_from=fr" \
  --data-urlencode "language_to=en" \
  -H "Authorization: Bearer ad1ad5c7f13b73e215dff82b9bafd55b91c66a01dccf8b16a90c75af633087ca"
```


# Documents

## List documents of a project

{% hint style="info" %}
**OAuth:** This endpoint requires one of the following scopes: `project:manage`, `project:write` or `project:read`.
{% endhint %}

{% openapi src="<https://app.textmaster.com/api-docs/v1/clients/specs.yaml>" path="/v1/clients/projects/{project\_id}/documents" method="get" %}
<https://app.textmaster.com/api-docs/v1/clients/specs.yaml>
{% endopenapi %}

#### Code samples

```shell
curl -G "https://api.textmaster.com/v1/clients/projects/61698a9e8b81926d91c0e3a3/documents" \
  -H "Authorization: Bearer d8d6b2738bb6e88bb32351fad917d9b6e726a0413251e6a7e3d1da164cadeb65"
```

## Filter documents of a project

{% hint style="info" %}
**OAuth:** This endpoint requires one of the following scopes: `project:manage`, `project:write` or `project:read`.
{% endhint %}

{% openapi src="<https://app.textmaster.com/api-docs/v1/clients/specs.yaml>" path="/v1/clients/projects/{project\_id}/documents/filter" method="get" %}
<https://app.textmaster.com/api-docs/v1/clients/specs.yaml>
{% endopenapi %}

#### Parameters

You can use the [Filter API](/overview/filters) to filter documents on a collection of criteria.

| Name                   | Type    | Description                                                                                                                                                                          |
| ---------------------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `id`                   | string  | The unique identifier of the document.                                                                                                                                               |
| `ref`                  | string  | The reference identifying the document.                                                                                                                                              |
| `title`                | string  | The title of the document.                                                                                                                                                           |
| `activity_name`        | string  | The activity of the document.                                                                                                                                                        |
| `status`               | string  | The status of the document. See the [Workflow](/overview/workflow#documents) section.                                                                                                |
| `created_at`           | string  | Describes the time the document was created. This is a timestamp in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format: `YYYY-MM-DDTHH:MM:SSZ`.                               |
| `updated_at`           | string  | Describes the time the document was last updated. This is a timestamp in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format: `YYYY-MM-DDTHH:MM:SSZ`.                          |
| `completed_at`         | string  | Describes the time the document was completed. This is a timestamp in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format: `YYYY-MM-DDTHH:MM:SSZ`.                             |
| `started_at`           | string  | Describes the time the assigned author started working on the document. This is a timestamp in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format: `YYYY-MM-DDTHH:MM:SSZ`.    |
| `submitted_at`         | string  | Describes the time the assigned author submitted its work on the document. This is a timestamp in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format: `YYYY-MM-DDTHH:MM:SSZ`. |
| `language_from_code`   | string  | The source language code of the document. One of the language code returned by the [Language](/reference/languages) endpoint.                                                        |
| `language_to_code`     | string  | The target language code of the document. One of the language code returned by the [Language](/reference/languages) endpoint.                                                        |
| `word_count`           | integer | The number of words in the document.                                                                                                                                                 |
| `category`             | string  | The category identifier for the document. One of the category returned by the [Category](/reference/categories) endpoint.                                                            |
| `deliver_work_as_file` | boolean | Whether the author has to submit its work as a file or not. Default to `false`.                                                                                                      |

#### Code samples

```shell
curl -G "https://api.textmaster.com/v1/clients/projects/61698a9c8b81926d91c0e32b/documents/filter" \
  --data-urlencode 'where={"status":"waiting_assignment","language_from_code":"fr","language_to_code":"en"}' \
  --data-urlencode 'order=level_name' \
  -H "Authorization: Bearer b3e8b9c6653dc5b1ec5e5c3607b6720a8cee46fd483972595dcb17fc1eae183a"
```

## Get a document

{% hint style="info" %}
**OAuth:** This endpoint requires one of the following scopes: `project:manage`, `project:write` or `project:read`.
{% endhint %}

{% openapi src="<https://app.textmaster.com/api-docs/v1/clients/specs.yaml>" path="/v1/clients/projects/{project\_id}/documents/{document\_id}" method="get" %}
<https://app.textmaster.com/api-docs/v1/clients/specs.yaml>
{% endopenapi %}

#### Code samples

```shell
curl -G "https://api.textmaster.com/v1/clients/projects/61698aa18b81926d91c0e4e3/documents/61698aa18b81926d91c0e4e4" \
  -H "Authorization: Bearer 0fbf01dc293e00fb51d17069e68e8525f50d7d73000adfb1a1921dd2155833af"
```

## Create a document

TextMaster supports automated word-counting for translation and proofreading documents. An automated count can be scheduled by setting the `perform_word_count` to `true`. In this case `word_count` can be omitted and will be ignored if provided.

In case of an error, the API response will contain a `word_count_error: true` node. The two most common failure reasons are:

* Unsupported file format for file attachments, in this case a new file must be uploaded
* For key/value and plain text documents, the only possible reason is a temporary service issue

For translation documents, the automated word-counting can be configured to count *translatable* content in HTML/XML documents, using the `markup_in_content` option.

{% hint style="warning" %}
**Warning:** If you're using this parameter, you must wait for `word_count_finished` callback for all documents before attempting to launch the project.
{% endhint %}

#### Original Content

{% hint style="warning" %}
**Warning:** Due to HTTP protocol limitation, prefere using `remote_file_url` field  than `original_content` field to avoid server timeout. HTTP protocol purpose is not to transfer large amount of data, like file or various document. See Providing content as a file Section for more information
{% endhint %}

For translation or proofreading documents, the content can be provided directly or by using the [Upload API](/overview/file-uploads). For standard documents, the original content is a sentence in a readable format (txt, html, …). For key/value documents, the original content has to be an object with a unique key associated to a value.

```json
{
  "document": {
    ...,
    "original_content": {
      "some_unique_key": {
        "original_phrase": "Some text to translate.",
        "details": "Some context that will be displayed to the translator."
      },
      "some_other_unique_key": {
        "original_phrase": "Some other text to translate."
      }
    }
  }
}
```

#### Markup in content

The `markup_in_content` option indicates whether the original content contains markup (HTML, XML, …) or not. Always set it to `true` for HTML/XML files. Failing to doing so could result in tag names being counted as words and translated, and you being charged for that work. For files with a `.html` or `.xml` extension, this parameter is assumed to be `true`. It defaults to `false` otherwise.

{% hint style="info" %}
**Tips:** Always set `markup_in_content` to `true` for content which contains HTML or XML.
{% endhint %}

#### Providing content as a file

You can choose to provide the original content as a file instead of providing as raw data. To do so, provide the URL of the file as `remote_file_url`. To learn more about uploading files, see:

{% content-ref url="/pages/vlgs9YcbWnUkji5ZslAy" %}
[File uploads](/overview/file-uploads)
{% endcontent-ref %}

#### Callbacks

You can specify callbacks using the `callback` property. It's an object listing URLs to call for each document status. See the list of supported [events](/webhooks-and-events/events#documents).

```json
{
  "document": {
    ...,
    "callback": {
      "waiting_assignment": {
        "url": "http://my.host/waiting_assignment_callback",
        "format": "json"
      },
      "completed": {
        "url": "http://my.host/completed_callback",
        "format": "json"
      }
    }
  }
}
```

{% hint style="info" %}
**OAuth:** This endpoint requires one of the following scopes: `project:manage` or `project:write`.
{% endhint %}

{% openapi src="<https://app.textmaster.com/api-docs/v1/clients/specs.yaml>" path="/v1/clients/projects/{project\_id}/documents" method="post" %}
<https://app.textmaster.com/api-docs/v1/clients/specs.yaml>
{% endopenapi %}

#### Code samples

```bash
curl "https://api.textmaster.com/v1/clients/projects/61698a9f8b81926d91c0e41b/documents" \
  -X POST \
  -d '{
    "document": {
      "title": "My document",
      "instructions": "Some instructions.",
      "activity_name": "proofreading",
      "word_count": 200,
      "word_count_rule": 0,
      "keywords_repeat_count": 1,
      "keyword_list": "foo,bar,baz",
      "language_from": "fr-fr",
      "language_to": "fr-fr",
      "original_content": "Some text to proofread.",
      "callback": {
        "support_message_created": {
          "url": "https://callback.example.com/support"
        }
      },
      "custom_data": {
        "tags": ["red","soft"],
        "external_client_id": 1234
      }
    }
  }' \
  -H "Authorization: Bearer edd5e969f9ef1055c5804ee62233d271df8eb4ed30687c2b77d21f93b6301afa" \
  -H "Content-Type: application/json"
```

## Create batch of documents

Creates several documents at once. Accepts the same document parameters as singular version, but there can be several of them and they must be placed into a `documents` array.

{% hint style="info" %}
**OAuth:** This endpoint requires one of the following scopes: `project:manage` or `project:write`.
{% endhint %}

{% openapi src="<https://app.textmaster.com/api-docs/v1/clients/specs.yaml>" path="/v1/clients/projects/{project\_id}/batch/documents" method="post" %}
<https://app.textmaster.com/api-docs/v1/clients/specs.yaml>
{% endopenapi %}

#### Code samples

```shell
curl "https://api.textmaster.com/v1/clients/projects/61698aa08b81926d91c0e47b/batch/documents" \
  -X POST \
  -d '{
    "documents": [
      {
        "title": "My document 1",
        "instructions": "Some instructions.",
        "activity_name": "proofreading",
        "word_count":200,
        "word_count_rule":0,
        "keywords_repeat_count":1,
        "keyword_list": "foo,bar,baz",
        "language_from": "fr-fr",
        "language_to": "fr-fr",
        "original_content": "Some text to proofread."
      },
      {
        "title": "My document 2",
        "instructions": "Some instructions.",
        "activity_name": "translation",
        "keyword_list": "foo,bar,baz",
        "language_from": "fr-fr",
        "language_to": "en-us",
        "original_content": "Some text to translate."
      },
    ]
  }' \
  -H "Authorization: Bearer b59de755d0f4ab0026cd0f8dcef611fef717858015bd7559992785ff72ad7fa4" \
  -H "Content-Type: application/json"
```

## Get a document review URL

Generates the document's review URL which points to the work that requires author review. You can for example, copy this URL into the message sent to the assigned author when completed the document.

{% hint style="info" %}
**OAuth:** This endpoint requires one of the following scopes: `project:manage`, `project:write` or `project:read`.
{% endhint %}

{% openapi src="<https://app.textmaster.com/api-docs/v1/clients/specs.yaml>" path="/v1/clients/projects/{project\_id}/documents/{document\_id}/review\_url" method="post" %}
<https://app.textmaster.com/api-docs/v1/clients/specs.yaml>
{% endopenapi %}

#### Code samples

```shell
curl "https://api.textmaster.com/v1/clients/projects/61698aa88b81926d91c0e71b/documents/61698aa88b81926d91c0e71c/review_url" \
  -X POST \
  -d '{ "keys": ["some-key","another-key"] }' \
  -H "Authorization: Bearer 977991a2b31ca7f22adf293e5780c683cc18bc72e4cec8a11014e5f6531349de" \
  -H "Content-Type: application/json"
```

## Update a document

The update document endpoint takes the same parameters as the create endpoint. Note that you can't update a document once its project has been launched.

{% hint style="info" %}
**OAuth:** This endpoint requires one of the following scopes: `project:manage` or `project:write`.
{% endhint %}

{% openapi src="<https://app.textmaster.com/api-docs/v1/clients/specs.yaml>" path="/v1/clients/projects/{project\_id}/documents/{document\_id}" method="put" %}
<https://app.textmaster.com/api-docs/v1/clients/specs.yaml>
{% endopenapi %}

#### Code samples

```shell
curl "https://api.textmaster.com/v1/clients/projects/61698aa38b81926d91c0e55b/documents/61698aa48b81926d91c0e5c5" \
  -X PUT \
  -d '{
    "document": {
      "original_content": "Some text to translate.",
      "instructions": "Some instructions."
    }
  }' \
  -H "Authorization: Bearer 3b0eda84b87968d41167bf67b6afbddefd694dcf064f3157dbb70678d7b16b44" \
  -H "Content-Type: application/json"
```

## Complete a document

Approve the work done by the assigned author and mark the document as `completed`. Note that when all documents in a project are completed, the project is considered completed too.

{% hint style="info" %}
**OAuth:** This endpoint requires one of the following scopes: `project:manage` or `project:write`.
{% endhint %}

{% openapi src="<https://app.textmaster.com/api-docs/v1/clients/specs.yaml>" path="/v1/clients/projects/{project\_id}/documents/{document\_id}/complete" method="put" %}
<https://app.textmaster.com/api-docs/v1/clients/specs.yaml>
{% endopenapi %}

#### Code samples

```shell
curl "https://api.textmaster.com/v1/clients/projects/61698aa58b81926d91c0e684/documents/61698aa58b81926d91c0e685/complete" \
  -X PUT \
  -d '{
    "satisfaction": "positive",
    "message": "Well done!"
  }' \
  -H "Authorization: Bearer 7dddb9f65cfcb17d0de74fe184d62f647842eef357739f00b26029bec27c32f1" \
  -H "Content-Type: application/json"
```

## Complete batch of documents

{% hint style="info" %}
**OAuth:** This endpoint requires one of the following scopes: `project:manage` or `project:write`.
{% endhint %}

{% openapi src="<https://app.textmaster.com/api-docs/v1/clients/specs.yaml>" path="/v1/clients/projects/{project\_id}/batch/documents/complete" method="post" %}
<https://app.textmaster.com/api-docs/v1/clients/specs.yaml>
{% endopenapi %}

#### Code samples

```shell
curl "https://api.textmaster.com/v1/clients/projects/61698aab8b81926d91c0e7a8/batch/documents/complete" \
  -X POST \
  -d '{
    "documents": ["61698aab8b81926d91c0e7a9"],
    "satisfaction": "positive",
    "message": "Well done!"
  }' \
  -H "Authorization: Bearer cb9f19a679fcf674259da57d457820ac965e7b7c6324d59eacc0b0bde6ca123c" \
  -H "Content-Type: application/json"
```

## Delete a document

Note that you can't delete a document once its project has been launched.

{% hint style="info" %}
**OAuth:** This endpoint requires one of the following scopes: `project:manage` or `project:write`.
{% endhint %}

{% openapi src="<https://app.textmaster.com/api-docs/v1/clients/specs.yaml>" path="/v1/clients/projects/{project\_id}/documents/{document\_id}" method="delete" %}
<https://app.textmaster.com/api-docs/v1/clients/specs.yaml>
{% endopenapi %}

#### Code samples

```shell
curl "https://api.textmaster.com/v1/clients/projects/61698aa48b81926d91c0e5fd/documents/61698aa58b81926d91c0e64b" \
  -X DELETE \
  -H "Authorization: Bearer 5d57d0dce1eeef52be88cf86f98066856eeddd96f12aa7685877adb6769bc894"
```


# Categories

## List categories

Public endpoint to list all available categories supported by TextMaster.

{% openapi src="<https://app.textmaster.com/api-docs/v1/clients/specs.yaml>" path="/v1/public/categories" method="get" %}
<https://app.textmaster.com/api-docs/v1/clients/specs.yaml>
{% endopenapi %}

#### Code samples

```shell
curl "https://api.textmaster.com/v1/public/categories"
```


# Countries

## List countries

Public endpoint to list all available countries supported by TextMaster.

{% openapi src="<https://app.textmaster.com/api-docs/v1/clients/specs.yaml>" path="/v1/public/countries" method="get" %}
<https://app.textmaster.com/api-docs/v1/clients/specs.yaml>
{% endopenapi %}

#### Code samples

```shell
curl -G "https://api.textmaster.com/v1/public/countries" \
  --data-urlencode 'locale=en-US'
```

For a list of supported locales, see:

{% content-ref url="/pages/1g4Q4SI5DnPmLwk51PSX" %}
[Locales](/reference/locales)
{% endcontent-ref %}


# Expertises

## List expertises

Public endpoint to list all available expertises supported by TextMaster.

Expertises allow clients to create projects that will only be available to authors with expert skills in a given area. Author expertises are validated by TextMaster's community managers in order to allow verified experts to work on sensible projects.

Expertises are scoped by activity, meaning that the copywriting activity has a different expertise set than the translation and proofreading activities.

{% openapi src="<https://app.textmaster.com/api-docs/v1/clients/specs.yaml>" path="/v1/public/expertises" method="get" %}
<https://app.textmaster.com/api-docs/v1/clients/specs.yaml>
{% endopenapi %}

#### Code samples

```shell
curl -G "https://api.textmaster.com/v1/public/expertises" \
  --data-urlencode "filter=professional"
```

## List sub-expertises

Public endpoint to list all available sub-expertises supported by TextMaster.

An expertise can have one or more sub-expertise(s), they can be seen as a refinements of the subject described by the expertise (ex: `Finance → Banking` or `Finance → Equity Markets`).

{% openapi src="<https://app.textmaster.com/api-docs/v1/clients/specs.yaml>" path="/v1/public/expertises/{expertise\_id}/sub\_expertises" method="get" %}
<https://app.textmaster.com/api-docs/v1/clients/specs.yaml>
{% endopenapi %}

#### Code samples

```shell
curl "https://api.textmaster.com/v1/public/expertises/61698b008b81926d91c0f779/sub_expertises"
```

## Get a sub-expertise

{% openapi src="<https://app.textmaster.com/api-docs/v1/clients/specs.yaml>" path="/v1/public/expertises/{expertise\_id}/sub\_expertises/{sub\_expertise\_id}" method="get" %}
<https://app.textmaster.com/api-docs/v1/clients/specs.yaml>
{% endopenapi %}

#### Code samples

```shell
curl "https://api.textmaster.com/v1/public/expertises/61698b008b81926d91c0f770/sub_expertises/61698b008b81926d91c0f773"
```


# Glossaries

## List glossaries

{% hint style="info" %}
**OAuth:** This endpoint requires one of the following scopes: `glossary:manage`, `glossary:read` or `glossary:write`.
{% endhint %}

{% openapi src="<https://app.textmaster.com/api-docs/v1/clients/specs.yaml>" path="/v1/clients/glossaries" method="get" %}
<https://app.textmaster.com/api-docs/v1/clients/specs.yaml>
{% endopenapi %}

#### Code samples

```shell
curl -G "https://api.textmaster.com/v1/clients/glossaries" \
  --data-urlencode "filter=all" \
  -H "Authorization: Bearer 6aa131e7cd6a037c8e955fa5e3231bd8e201bb4218644aa8a28125769e1b91a1"
```


# Languages

## List languages

Public endpoint to list all available languages supported by TextMaster.

{% openapi src="<https://app.textmaster.com/api-docs/v1/clients/specs.yaml>" path="/v1/public/languages" method="get" %}
<https://app.textmaster.com/api-docs/v1/clients/specs.yaml>
{% endopenapi %}

#### Code samples

```shell
curl "https://api.textmaster.com/v1/public/languages"
```


# Levels

## List levels

List the available level of authors proficiencies.

<table><thead><tr><th width="215.58101723822637">Name</th><th>Description</th></tr></thead><tbody><tr><td><code>premium</code></td><td>The <code>premium</code> level is handled by experienced native speakers and is best for general business writing and translation. This level makes it possible to order additional options, including Extra Proofreading and Graphic Files.</td></tr><tr><td><code>enterprise</code></td><td>The <code>enterprise</code> level is handled by professionals in their field and is best for complex projects requiring an advanced writing style, research and adaptation. This level makes it possible to order additional options, including Extra Proofreading and Graphic Files.</td></tr></tbody></table>

{% hint style="info" %}
**Tips:** Please note that the `premium` level is called `standard` on the application.
{% endhint %}

{% hint style="success" %}
**Tips:** TextMaster recommend using the `enterprise` level to guarantee the best translation quality possible.
{% endhint %}

Note that not all levels are available for all kinds of project. For example, `enterprise` level is not available for proofreading projects at all.

For some complex languages (translation from Norwegian to Swahili, for example), on the contrary, the only available level will be `enterprise`.

Attempting to create a project with unavailable level will result in an error. You should check the response from the [Create a project](/reference/projects#create-a-project) endpoint for such errors.


# Locales

## List locales

Public endpoint to list all available locales supported by TextMaster.

{% openapi src="<https://app.textmaster.com/api-docs/v1/clients/specs.yaml>" path="/v1/public/locales" method="get" %}
<https://app.textmaster.com/api-docs/v1/clients/specs.yaml>
{% endopenapi %}

#### Code samples

```shell
curl "https://api.textmaster.com/v1/public/locales"
```


# Preferred Authors

## List preferred authors

By default, you can only list preferred authors which have been whitelisted or blacklisted. Preferred authors who completed a document of yours but you didn't rate can be found within the `uncategorized` list.

{% hint style="info" %}
**OAuth:** This endpoint requires one of the following scopes: `preferred_author:manage`, `preferred_author:read` or `preferred_author:write`.
{% endhint %}

{% openapi src="<https://app.textmaster.com/api-docs/v1/clients/specs.yaml>" path="/v1/clients/my\_authors" method="get" %}
<https://app.textmaster.com/api-docs/v1/clients/specs.yaml>
{% endopenapi %}

#### Code samples

```shell
curl -G "https://api.textmaster.com/v1/clients/my_authors" \
  -H "Authorization: Bearer 790d0c6d24747b818f1c83184a2720e603db74b0b7de246365636765569c5d4e"
```

### Add a preferred author

{% hint style="info" %}
**OAuth:** This endpoint requires one of the following scopes: `preferred_author:manage` or `referred_author:write`.
{% endhint %}

{% openapi src="<https://app.textmaster.com/api-docs/v1/clients/specs.yaml>" path="/v1/clients/my\_authors" method="post" %}
<https://app.textmaster.com/api-docs/v1/clients/specs.yaml>
{% endopenapi %}

#### Code samples

```shell
curl "https://api.textmaster.com/v1/clients/my_authors" \
  -X POST \
  -d '{
    "my_author": {
      "description": "Some description",
      "status": "my_textmaster",
      "author_id": "61698aaf8b81926d91c0e889"
    }
  }' \
  -H "Authorization: Bearer 3a7dce3b773247c159b8f18be707c2c5838d2e7138d36813e74e28e43caa0aaa" \
  -H "Content-Type: application/json"
```

## Get a preferred author

{% hint style="info" %}
**OAuth:** This endpoint requires one of the following scopes: `preferred_author:manage`, `preferred_author:read` or `referred_author:write`.
{% endhint %}

{% openapi src="<https://app.textmaster.com/api-docs/v1/clients/specs.yaml>" path="/v1/clients/my\_authors/{author\_id}" method="get" %}
<https://app.textmaster.com/api-docs/v1/clients/specs.yaml>
{% endopenapi %}

#### Code samples

```shell
curl -G "https://api.textmaster.com/v1/clients/my_authors/61698ab08b81926d91c0e8a3" \
  -H "Authorization: Bearer f2a5e17102ca9e6c8494b323847b784615ab1da0f1525258d85af89385fef5f5"
```

## Update a preferred author

{% hint style="info" %}
**OAuth:** This endpoint requires one of the following scopes: `preferred_author:manage` or `referred_author:write`.
{% endhint %}

{% openapi src="<https://app.textmaster.com/api-docs/v1/clients/specs.yaml>" path="/v1/clients/my\_authors/{author\_id}" method="put" %}
<https://app.textmaster.com/api-docs/v1/clients/specs.yaml>
{% endopenapi %}

#### Code samples

```shell
curl "https://api.textmaster.com/v1/clients/my_authors/61698ab08b81926d91c0e8bd" \
  -X PUT \
  -d '{ "my_author": { "description": "Some new description" } }' \
  -H "Authorization: Bearer b9ad70843ca9d09f59bc27f87c1aab02de0dfd03109ca1a7a3f70f899f6427c6" \
  -H "Content-Type: application/json"
```

## List preferred authors able to work on project

{% hint style="info" %}
**OAuth:** This endpoint requires one of the following scopes: `preferred_author:manage`, `preferred_author:read` or `referred_author:write`.
{% endhint %}

{% openapi src="<https://app.textmaster.com/api-docs/v1/clients/specs.yaml>" path="/v1/clients/projects/{project\_id}/my\_authors" method="get" %}
<https://app.textmaster.com/api-docs/v1/clients/specs.yaml>
{% endopenapi %}

#### Code samples

```shell
curl -G "https://api.textmaster.com/v1/clients/projects/61698ab18b81926d91c0e8e7/my_authors" \
  -H "Authorization: Bearer 2a1e347c6aaf1515cbb7e146601e93832359d4edc437e0d494ed195facccc05d"
```


# Projects

## Get a project quote

Get a quote of a project given the provided parameters without actually creating it.

{% hint style="info" %}
**OAuth:** This endpoint requires one of the following scopes: `project:manage`, `project:write`, `project:read` or `project:quote`.
{% endhint %}

{% openapi src="<https://app.textmaster.com/api-docs/v1/clients/specs.yaml>" path="/v1/clients/projects/quotation" method="get" %}
<https://app.textmaster.com/api-docs/v1/clients/specs.yaml>
{% endopenapi %}

#### Code samples

```shell
curl -G "https://api.textmaster.com/v1/clients/projects/quotation" \
  --date-urlencode "project[activity_name]=translation" \
  --date-urlencode "project[options][language_level]=enterprise" \
  --date-urlencode "project[options][quality]=yes" \
  --date-urlencode "project[language_from]=fr" \
  --date-urlencode "project[language_to]=en" \
  --data-urlencode "project[total_word_count]=100" \
  -H "Authorization: Bearer 59e939b01b92c929166f18b9888990f0e4ef5b691ddbbabed2d75aa21317d452"
```

## List projects

{% hint style="info" %}
**OAuth:** This endpoint requires one of the following scopes: `project:manage`, `project:write` or `project:read`.
{% endhint %}

{% openapi src="<https://app.textmaster.com/api-docs/v1/clients/specs.yaml>" path="/v1/clients/projects" method="get" %}
<https://app.textmaster.com/api-docs/v1/clients/specs.yaml>
{% endopenapi %}

#### Code samples

```shell
curl -G "https://api.textmaster.com/v1/clients/projects" \
  -H "Authorization: Bearer 54561947218dc1abd692a19cb02c7256cac140dc33e10406d3e093b2accd68f1"
```

## Filter projects

{% hint style="info" %}
**OAuth:** This endpoint requires one of the following scopes: `project:manage`, `project:write` or `project:read`.
{% endhint %}

{% openapi src="<https://app.textmaster.com/api-docs/v1/clients/specs.yaml>" path="/v1/clients/projects/filter" method="get" %}
<https://app.textmaster.com/api-docs/v1/clients/specs.yaml>
{% endopenapi %}

#### Parameters

You can use the [Filter API](/overview/filters) to filter documents on a collection of criteria.

<table><thead><tr><th width="319">Name</th><th width="150">Type</th><th width="449.2">Description</th></tr></thead><tbody><tr><td><code>id</code></td><td>string</td><td>The unique identifier of the project.</td></tr><tr><td><code>ref</code></td><td>string</td><td>The reference identifying the project.</td></tr><tr><td><code>name</code></td><td>string</td><td>The name of the project.</td></tr><tr><td><code>activity_name</code></td><td>string</td><td>The activity of the project.</td></tr><tr><td><code>archived</code></td><td>boolean</td><td>Whether the project is archived or not.</td></tr><tr><td><code>status</code></td><td>string</td><td>The status of the project. See the <a href="/overview/workflow#projects">Workflow</a> section.</td></tr><tr><td><code>created_at</code></td><td>string</td><td>Describes the time the project was created. This is a timestamp in <a href="https://en.wikipedia.org/wiki/ISO_8601">ISO 8601</a> format: <code>YYYY-MM-DDTHH:MM:SSZ</code>.</td></tr><tr><td><code>updated_at</code></td><td>string</td><td>Describes the time the project was last updated. This is a timestamp in <a href="https://en.wikipedia.org/wiki/ISO_8601">ISO 8601</a> format: <code>YYYY-MM-DDTHH:MM:SSZ</code>.</td></tr><tr><td><code>launched_at</code></td><td>string</td><td>Describes the time the project was launched. This is a timestamp in <a href="https://en.wikipedia.org/wiki/ISO_8601">ISO 8601</a> format: <code>YYYY-MM-DDTHH:MM:SSZ</code>.</td></tr><tr><td><code>completed_at</code></td><td>string</td><td>Describes the time the project was completed. This is a timestamp in <a href="https://en.wikipedia.org/wiki/ISO_8601">ISO 8601</a> format: <code>YYYY-MM-DDTHH:MM:SSZ</code>.</td></tr><tr><td><code>cached_documents_count</code></td><td>integer</td><td>The number of documents in a project.</td></tr><tr><td><code>language_from_code</code></td><td>string</td><td>The source language code of the project. One of the language code returned by the <a href="/reference/languages">Language</a> endpoint.</td></tr><tr><td><code>language_to_code</code></td><td>string</td><td>The target language code of the project. One of the language code returned by the <a href="/reference/languages">Language</a> endpoint.</td></tr><tr><td><code>level_name</code></td><td>string</td><td>The level of the project. One of the level names returned by the <a href="/reference/levels">Level</a> endpoint.</td></tr><tr><td><code>pricing.total_cost_at_launch_time</code></td><td>integer</td><td>The cost of the project at launch time in credits.</td></tr><tr><td><code>total_word_count</code></td><td>integer</td><td>The number of words in the project.</td></tr><tr><td><code>progress</code></td><td>integer</td><td>Describe the progress in percent towards the project's completion.</td></tr><tr><td><code>category</code></td><td>string</td><td>The category identifier for the project. One of the category returned by the <a href="/reference/categories">Category</a> endpoint.</td></tr><tr><td><code>platform_id</code></td><td>string</td><td>The unique UUID of the integration platform. Used to tell apart projects created via different integrations (Salesforce, Wordpress, etc.).</td></tr></tbody></table>

#### Code samples

```shell
curl -G "https://api.textmaster.com/v1/clients/projects/filter" \
  --data-urlencode 'where={"total_word_count":{"$gt":100},"language_from_code":"fr","language_to_code":"en"}' \
  --data-urlencode 'order=level_name' \
  -H "Authorization: Bearer 423773845b670244f0d3025c0be48de100f29482665e98c89e8dd31051938f12"
```

## Get a project

{% hint style="info" %}
**OAuth:** This endpoint requires one of the following scopes: `project:manage`, `project:write` or `project:read`.
{% endhint %}

{% openapi src="<https://app.textmaster.com/api-docs/v1/clients/specs.yaml>" path="/v1/clients/projects/{project\_id}" method="get" %}
<https://app.textmaster.com/api-docs/v1/clients/specs.yaml>
{% endopenapi %}

#### Code samples

```shell
curl -G "https://api.textmaster.com/v1/clients/projects/61698af48b81926d91c0f3d1" \
  -H "Authorization: Bearer f93607587ed3813d5339f77aa69fe0aa217366030149b9d60e50f21ed2182313"
```

## Create a project

Creates a project and assign its document later or create a project with documents embedded along with the project's attributes.

When setting the `auto_launch` option on a project, assuming your account's balance has enough funds, TextMaster will automatically attempt to launch the project as soon as all asynchronous operations have been completed, such as [Translation Memory](https://eu.textmaster.com/memento-translation-memory/) analysis for example.

{% hint style="warning" %}
**Warning:** Using the `auto_launch` option assumes your account has enough funds to launch the project. The project will remain`in_creation` otherwise and will need to be launched manually with the [Launch a project](#launch-a-project) endpoint.
{% endhint %}

#### Using a project template

Create a project from a project template by providing its unique ID. The newly created project will inherit its attributes from the template. Projects created from a template still require documents to be assigned to them through the [Create document](/reference/documents#create-a-document) endpoint.

Some operations on projects created from templates with `translation_memory` ,`translation_diff` or `post_editing_machine_translation` options enabled will not be performed until explicitly requested through the [Finalize project](#undefined) endpoint.

#### Available options

A project can be created with the following options:

<table><thead><tr><th>Name</th><th width="150">Type</th><th width="324">Description</th></tr></thead><tbody><tr><td><code>language_level</code></td><td>string</td><td><strong>Required.</strong> <code>premium</code> or <code>enterprise</code>.</td></tr><tr><td><code>quality</code></td><td>boolean</td><td>Whether the project should be sent to quality control or not.</td></tr><tr><td><code>expertise</code></td><td>string</td><td>The expertise or sub-expertise ID. One of the expertise ID returned by the <a href="/reference/expertises">Expertise</a> endpoint.</td></tr><tr><td><code>specific_attachment</code></td><td>boolean</td><td>Whether the project has documents with exotic file extensions such as <code>.idml</code> or <code>.psd</code> for example.</td></tr><tr><td><code>priority</code></td><td>boolean</td><td>Whether the project is urgent or not. A financial incentive will be shown to authors to ensure the project gets picked up faster.</td></tr><tr><td><code>uniq_author</code></td><td>boolean</td><td>Whether the project should be assigned to the same author for all its documents or not. Slower but with editorial continuity.</td></tr><tr><td><code>translation_memory</code></td><td>boolean</td><td>Whether translation memory analysis should be applied or not.</td></tr><tr><td><code>translation_diff</code></td><td>boolean</td><td>Whether translation diff pre-fill should be applied or not.</td></tr><tr><td><code>post_editing_machine_translation</code></td><td>boolean</td><td>Whether machine translation should be applied or not.</td></tr></tbody></table>

{% hint style="warning" %}
**Warning:** Some options such as `translation_memory`, `translation_diff` and `post_editing_machine_translation` require a project to have documents assigned to work properly. Enabling these options on a project without documents will result in an error.
{% endhint %}

{% hint style="info" %}
**OAuth:** This endpoint requires one of the following scopes: `project:manage`, `project:read` or `project:write`.
{% endhint %}

{% openapi src="<https://app.textmaster.com/api-docs/v1/clients/specs.yaml>" path="/v1/clients/projects" method="post" %}
<https://app.textmaster.com/api-docs/v1/clients/specs.yaml>
{% endopenapi %}

#### Code samples

```shell
curl "https://api.textmaster.com/v1/clients/projects" \
  -X POST \
  -d '{
    "project": {
      "name": "My project",
      "activity_name": "translation",
      "language_from": "fr-fr",
      "language_to": "en-us",
      "category": "C014",
      "vocabulary_type": "not_specified",
      "target_reader_groups": "not_specified",
      "grammatical_person": "not_specified",
      "project_briefing": "Some instructions.",
      "glossaries": [
        "e9a081d1-c920-4bb4-b63e-6faea5ae9a8d"
      ],
      "textmasters": [
        "641c18428b81928a34b96070"
      ],
      "options": {
        "translation_memory": true,
        "post_editing_machine_translation": true,
        "language_level": "enterprise",
        "expertise": "61698af08b81926d91c0f320"
      },
      "custom_data": {
        "tags":["red", "soft"],
        "external_client_id": 1234
      },
      "deadline": "2020-12-30 12:34:56 UTC"
    }
  }' \
  -H "Authorization: Bearer f8f704163650f148b6a4b4e7eb8826d4e6c8cdb2bd16dfa84704ca651b251d34" \
  -H "Content-Type: application/json"
```

## Duplicate a project

Creates a new project from the given one. It copies the following attributes from the source project:

* `category`
* `grammatical_person`
* `keywording_requirements`
* `language_from`
* `language_level`
* `language_to`
* `name`
* `project_briefing`
* `same_author_must_do_entire_project`
* `target_reader_groups`
* `textmasters`
* `vocabulary_type`
* `work_template`

{% hint style="info" %}
**OAuth:** This endpoint requires one of the following scopes: `project:manage`, `project:read` or `project:write`.
{% endhint %}

{% openapi src="<https://app.textmaster.com/api-docs/v1/clients/specs.yaml>" path="/v1/clients/projects/{project\_id}/duplicate" method="post" %}
<https://app.textmaster.com/api-docs/v1/clients/specs.yaml>
{% endopenapi %}

#### Code samples

```shell
curl "https://api.textmaster.com/v1/clients/projects/61698aca8b81926d91c0ed01/duplicate" \
  -X POST \
  -H "Authorization: Bearer cd11cd0b8ba828e611ebdcf57a173f5eb8e9b5776711d3c4d694f93c51ad3368" \
  -H "Content-Type: application/json"
```

## Launch a project

This endpoint is asynchronous and instruct the API to queue the project to be launched as soon as possible. Trying to launch a project while an asynchronous operation is still running on it or its documents will result in an error.

{% hint style="info" %}
**OAuth:** This endpoint requires one of the following scopes: `project:manage or` `project:launch`.
{% endhint %}

{% openapi src="<https://app.textmaster.com/api-docs/v1/clients/specs.yaml>" path="/v1/clients/projects/{project\_id}/async\_launch" method="post" %}
<https://app.textmaster.com/api-docs/v1/clients/specs.yaml>
{% endopenapi %}

#### Code samples

```shell
curl "https://api.textmaster.com/v1/clients/projects/61698ad18b81926d91c0ee39/async_launch" \
  -X POST \
  -H "Authorization: Bearer e5bb8d4315309202ea38b43cab7d09a95797aa2bc12d29e9c48a7347cd256094" \
  -H "Content-Type: application/json"
```

## Launch a project synchronously

{% hint style="danger" %}
**Deprecated:** This endpoint is deprecated and will be removed in future version of the API. Use the [Launch a project](#launch-a-project) endpoint instead.
{% endhint %}

Launches given project assuming your account has enough funds and all asynchronous operations have been run. Launching a project synchronously might result in HTTP timeouts depending on the its size and complexity.

{% hint style="info" %}
**OAuth:** This endpoint requires one of the following scopes: `project:manage or` `project:launch`.
{% endhint %}

{% openapi src="<https://app.textmaster.com/api-docs/v1/clients/specs.yaml>" path="/v1/clients/projects/{project\_id}/launch" method="put" %}
<https://app.textmaster.com/api-docs/v1/clients/specs.yaml>
{% endopenapi %}

#### Code samples

```shell
curl "https://api.textmaster.com/v1/clients/projects/61698acd8b81926d91c0eda9/launch" \
  -X PUT \
  -H "Authorization: Bearer 3d4f8762bd41328ded69d9eeddbf9fa21ffca3ee6bf207fbcece15746a504a12"
```

## Finalize a project

Instruct the API that all documents have been created for this project and asynchronous operations should be started as soon as possible.

{% hint style="info" %}
**Tips:** The finalize endpoint only applies to projects created from a project template.
{% endhint %}

Since asynchronous operations such as `translation`\_`memory` , `translation_diff` or `post_editing_machine_translation` should only be run on projects when all their documents have been created, this endpoint gives you the opportunity to tell TextMaster when you have created all documents for a project.

Under the hood, the API will schedule any asynchronous operations configured on the project such as for example, counting the number of words on each document, running the [Translation Memory](https://eu.textmaster.com/memento-translation-memory/) analysis and pre-fill translations.

When combined with the `auto_launch` option from the [create project](/reference/projects#create-a-project) endpoint, assuming your account has enough funds, the API will attempt to automatically launch the project as soon as all asynchronous operations have been executed.

Note that you don't have to monitor and wait for asynchronous operations to be completed on each document before calling this endpoint. TextMaster takes care of queuing this request to be executed as soon as possible.

{% hint style="info" %}
**OAuth:** This endpoint requires one of the following scopes: `project:manage` or `project:launch`.
{% endhint %}

{% openapi src="<https://app.textmaster.com/api-docs/v1/clients/specs.yaml>" path="/v1/clients/projects/{project\_id}/finalize" method="put" %}
<https://app.textmaster.com/api-docs/v1/clients/specs.yaml>
{% endopenapi %}

#### Code samples

```shell
curl "https://api.textmaster.com/v1/clients/projects/61698aea8b81926d91c0f20a/finalize" \
  -X PUT \
  -H "Authorization: Bearer 12e8b324b29b41a23362ab5a44349e712d1f1beb80dec043b6168f6706ae4e64"
```

## Update a project

{% hint style="info" %}
**OAuth:** This endpoint requires one of the following scopes: `project:manage` or `project:write`.
{% endhint %}

{% openapi src="<https://app.textmaster.com/api-docs/v1/clients/specs.yaml>" path="/v1/clients/projects/{project\_id}" method="put" %}
<https://app.textmaster.com/api-docs/v1/clients/specs.yaml>
{% endopenapi %}

#### Code samples

```shell
curl "https://api.textmaster.com/v1/clients/projects/61698af88b81926d91c0f4bf" \
  -X PUT \
  -d '{ "project": { "name": "Some new name" } }' \
  -H "Authorization: Bearer bc02a6185a7c47bc20fff504b31a0c5b502b7ea3ab4f7cf6b85a8ac4c261cfc3" \
  -H "Content-Type: application/json"
```

## Pause a project

{% hint style="info" %}
**OAuth:** This endpoint requires one of the following scopes: `project:manage` or `project:write`.
{% endhint %}

{% openapi src="<https://app.textmaster.com/api-docs/v1/clients/specs.yaml>" path="/v1/clients/projects/{project\_id}/pause" method="put" %}
<https://app.textmaster.com/api-docs/v1/clients/specs.yaml>
{% endopenapi %}

#### Code samples

```shell
curl "https://api.textmaster.com/v1/clients/projects/61698ad88b81926d91c0ef5b/pause" \
  -X PUT \
  -H "Authorization: Bearer 4c66e6c844e7dacf5916e701030eeb06c6fd572c228b04a3572c763197e84b83"
```

## Resume a paused project

{% hint style="info" %}
**OAuth:** This endpoint requires one of the following scopes: `project:manage` or `project:write`.
{% endhint %}

{% openapi src="<https://app.textmaster.com/api-docs/v1/clients/specs.yaml>" path="/v1/clients/projects/{project\_id}/resume" method="put" %}
<https://app.textmaster.com/api-docs/v1/clients/specs.yaml>
{% endopenapi %}

#### Code samples

```shell
curl "https://api.textmaster.com/v1/clients/projects/61698adc8b81926d91c0efe2/resume" \
  -X PUT \
  -H "Authorization: Bearer c778bb88983e52afc9cf90f78efeee5fdcc49f96bda75ce6a785274fb7fc552f"
```

## Cancel a project

{% hint style="info" %}
**OAuth:** This endpoint requires one of the following scopes: `project:manage` or `project:write`.
{% endhint %}

{% openapi src="<https://app.textmaster.com/api-docs/v1/clients/specs.yaml>" path="/v1/clients/projects/{project\_id}/cancel" method="put" %}
<https://app.textmaster.com/api-docs/v1/clients/specs.yaml>
{% endopenapi %}

#### Code samples

```shell
curl "https://api.textmaster.com/v1/clients/projects/61698ad58b81926d91c0eece/cancel" \
  -X PUT \
  -H "Authorization: Bearer 3da5d6892b3c8f70c3f6db295451474c6c50cd3cf433600719994e2263aa2d1c"
```

## Archive a project

Only completed or cancelled projects can be archived.

{% hint style="info" %}
**OAuth:** This endpoint requires one of the following scopes: `project:manage` or `project:write`.
{% endhint %}

{% openapi src="<https://app.textmaster.com/api-docs/v1/clients/specs.yaml>" path="/v1/clients/projects/{project\_id}/archive" method="put" %}
<https://app.textmaster.com/api-docs/v1/clients/specs.yaml>
{% endopenapi %}

#### Code samples

```shell
curl "https://api.textmaster.com/v1/clients/projects/61698ae08b81926d91c0f06b/archive" \
  -X PUT \
  -H "Authorization: Bearer 125a475543052ee628fd9f0841f07953d4ae23575762e0ce7e21dfd543c66666"
```

## Unarchive a project

{% hint style="info" %}
**OAuth:** This endpoint requires one of the following scopes: `project:manage` or `project:write`.
{% endhint %}

{% openapi src="<https://app.textmaster.com/api-docs/v1/clients/specs.yaml>" path="/v1/clients/projects/{project\_id}/unarchive" method="put" %}
<https://app.textmaster.com/api-docs/v1/clients/specs.yaml>
{% endopenapi %}

#### Code samples

```shell
curl "https://api.textmaster.com/v1/clients/projects/61698ae68b81926d91c0f17d/unarchive" \
  -X PUT \
  -H "Authorization: Bearer 88d733ecc072991e0a9e9a761e8dfc77abbedef0aa7e38705be4ba1bd9fe8ac1"
```

## Enable translation memory options on project

Enables the `translation_memory` , `translation_diff`  and/or `post_editing_machine_translation` option(s) on given project. This endpoint should be used to request the [Translation Memory](https://eu.textmaster.com/memento-translation-memory/) analysis and/or pre-fill translations operation to be run on the project or [Machine Translation](https://eu.textmaster.com/post-editor-machine-translation/).

{% hint style="info" %}
**OAuth:** This endpoint requires one of the following scopes: `project:manage` or `project:write`.
{% endhint %}

{% openapi src="<https://app.textmaster.com/api-docs/v1/clients/specs.yaml>" path="/v1/clients/projects/{project\_id}/activate\_tm\_options" method="put" %}
<https://app.textmaster.com/api-docs/v1/clients/specs.yaml>
{% endopenapi %}

#### Code samples

```shell
curl "https://api.textmaster.com/v1/clients/projects/61698aec8b81926d91c0f287/activate_tm_options" \
  -X PUT \
  -d '{
    "project": {
      "options": {
        "translation_memory": true,
        "translation_diff": true,
        "post_editing_machine_translation": true
      }
    }
  }' \
  -H "Authorization: Bearer ebdeb743536b7300db174320b7b4924e388bb7a5bd252faa888016ca68d2366d" \
  -H "Content-Type: application/json"
```


# Project Templates

## List project templates

{% hint style="info" %}
**OAuth:** This endpoint requires one of the following scopes: `project:manage`, `project:read` or `project:write`.
{% endhint %}

{% openapi src="<https://app.textmaster.com/api-docs/v1/clients/specs.yaml>" path="/v1/clients/api\_templates" method="get" %}
<https://app.textmaster.com/api-docs/v1/clients/specs.yaml>
{% endopenapi %}

#### Code samples

```shell
curl -G "https://api.textmaster.com/v1/clients/api_templates" \
  -H "Authorization: Bearer d90944ea5d7cd52b350b879cfc390f62cb24661eff398141ce6ab040dc5aee4e"
```

## Filter project templates

{% hint style="info" %}
**OAuth:** This endpoint requires one of the following scopes: `project:manage`, `project:write` or `project:read`.
{% endhint %}

{% openapi src="<https://app.textmaster.com/api-docs/v1/clients/specs.yaml>" path="/v1/clients/api\_templates/filter" method="get" %}
<https://app.textmaster.com/api-docs/v1/clients/specs.yaml>
{% endopenapi %}

#### Parameters

You can use the [Filter API](/overview/filters) to filter documents on a collection of criteria.

<table><thead><tr><th width="319">Name</th><th width="150">Type</th><th width="449.2">Description</th></tr></thead><tbody><tr><td><code>id</code></td><td>string</td><td>The unique identifier of the project template.</td></tr><tr><td><code>name</code></td><td>string</td><td>The name of the project template.</td></tr><tr><td><code>activity_name</code></td><td>string</td><td>The activity of the project template.</td></tr><tr><td><code>created_at</code></td><td>string</td><td>Describes the time the project template was created. This is a timestamp in <a href="https://en.wikipedia.org/wiki/ISO_8601">ISO 8601</a> format: <code>YYYY-MM-DDTHH:MM:SSZ</code>.</td></tr><tr><td><code>updated_at</code></td><td>string</td><td>Describes the time the project template was last updated. This is a timestamp in <a href="https://en.wikipedia.org/wiki/ISO_8601">ISO 8601</a> format: <code>YYYY-MM-DDTHH:MM:SSZ</code>.</td></tr><tr><td><code>language_from_code</code></td><td>string</td><td>The source language code of the project template. One of the language code returned by the <a href="/reference/languages">Language</a> endpoint.</td></tr><tr><td><code>language_to_code</code></td><td>string</td><td>The target language code of the project template. One of the language code returned by the <a href="/reference/languages">Language</a> endpoint.</td></tr><tr><td><code>level_name</code></td><td>string</td><td>The level of the project template. One of the level names returned by the <a href="/reference/levels">Level</a> endpoint.</td></tr></tbody></table>

#### Code samples

```shell
curl -G "https://api.textmaster.com/v1/clients/api_templates/filter" \
  --data-urlencode 'where={"name":"Lorem","language_from_code":"fr","language_to_code":"en"}' \
  --data-urlencode 'order=level_name' \
  -H "Authorization: Bearer 423773845b670244f0d3025c0be48de100f29482665e98c89e8dd31051938f12"
```


# Negotiated Contracts

## List negotiated contracts

A negotiated contract is a special arrangement between client and TextMaster which affects price-per-word paid by client on their projects. Normally, it's a fixed amount price reduction.

{% hint style="info" %}
**OAuth:** This endpoint requires the default `public` scope.
{% endhint %}

{% openapi src="<https://app.textmaster.com/api-docs/v1/clients/specs.yaml>" path="/v1/clients/negotiated\_contracts" method="get" %}
<https://app.textmaster.com/api-docs/v1/clients/specs.yaml>
{% endopenapi %}

#### Code samples

```shell
curl -G "https://api.textmaster.com/v1/clients/negotiated_contracts" \
  -H "Authorization: Bearer 37c102e8d6017d4d63625fd968aac8c225e5553f30f533d88f309499692ae310"
```


# Support Messages

## List support messages for document

{% hint style="info" %}
**OAuth:** This endpoint requires one of the following scopes: `discussion:manage`, `discussion:read` or `discussion:write`.
{% endhint %}

{% openapi src="<https://app.textmaster.com/api-docs/v1/clients/specs.yaml>" path="/v1/clients/projects/{project\_id}/documents/{document\_id}/support\_messages" method="get" %}
<https://app.textmaster.com/api-docs/v1/clients/specs.yaml>
{% endopenapi %}

#### Code samples

```shell
curl -G "https://api.textmaster.com/v1/clients/projects/61698af88b81926d91c0f514/documents/61698af88b81926d91c0f515/support_messages" \
  -H "Authorization: Bearer c5b2a822f9f3e2914e7056b7089503cd1be33ab9bdb4313403341a315c7dbacf"
```

## Create a support message

Creates a support message for given a document `in_review`, or creates a reply to an existing message from the author when the document is `in_progress`.

{% hint style="info" %}
**OAuth:** This endpoint requires one of the following scopes: `discussion:manage` or `discussion:write`.
{% endhint %}

{% openapi src="<https://app.textmaster.com/api-docs/v1/clients/specs.yaml>" path="/v1/clients/projects/{project\_id}/documents/{document\_id}/support\_messages" method="post" %}
<https://app.textmaster.com/api-docs/v1/clients/specs.yaml>
{% endopenapi %}

#### Code samples

```shell
curl "https://api.textmaster.com/v1/clients/projects/61698afa8b81926d91c0f5a8/documents/61698afa8b81926d91c0f5a9/support_messages" \
  -X POST \
  -d '{
    "support_message": {
      "message": "Some message explaining why a revision is requested."
    },
    "revision_request": true
  }' \
  -H "Authorization: Bearer 44c90f771334393a72a9b3135711a603d4856e499d96ba5ffaac5e1ab55b21bd" \
  -H "Content-Type: application/json"
```


# Transactions

## List transactions

{% hint style="info" %}
**OAuth:** This endpoint requires one of the following scopes: `transaction:manage`, `transaction:write` or `transaction:read`.
{% endhint %}

{% openapi src="<https://app.textmaster.com/api-docs/v1/clients/specs.yaml>" path="/v1/clients/transactions" method="get" %}
<https://app.textmaster.com/api-docs/v1/clients/specs.yaml>
{% endopenapi %}

#### Parameters

| Name    | Type             | Description                                                                 |
| ------- | ---------------- | --------------------------------------------------------------------------- |
| `types` | array of strings | The transaction types to filter against. See list of supported types below. |

#### Transaction types

You will find below the list of supported transaction types to filter against:

```
Transaction::CashWithdrawal::BundleCancellation
Transaction::CashWithdrawal::BundleRefund
Transaction::CreditExchange::GivenByManager
Transaction::CreditExchange::GivenToSubordinate
Transaction::CreditPayment::AdminLoan
Transaction::CreditPayment::AdminRefund
Transaction::CreditPayment::CompletingAJob
Transaction::CreditPayment::ExpiringWallet
Transaction::CreditPayment::FinancialCompensation
Transaction::CreditPurchase::AdminLevy
Transaction::CreditPurchase::BuyingACreditBundle
Transaction::CreditPurchase::EarningFromReferredClient
Transaction::CreditPurchase::ExpiringCredits
Transaction::CreditPurchase::FinancialCompensation
Transaction::CreditPurchase::RedeemingAPromoCode
Transaction::CreditPurchase::Rollback
Transaction::NonCreditPurchase::InHouseServiceFee
Transaction::NonCreditPurchase::SubscriptionPrepayment
Transaction::NonCreditSpending::SubscriptionInstallment
Transaction::ProjectSpending::AdminProjectLevy
Transaction::ProjectSpending::AdminProjectRefund
Transaction::ProjectSpending::CancelingAProject
Transaction::ProjectSpending::LaunchingAProject
Transaction::ProjectSpending::RepoDocWithCanceledProject
```

#### Code samples

```shell
curl -G "https://api.textmaster.com/v1/clients/transactions" \
  --data-urlencode "types[]=Transaction%3A%3AProjectSpending%3A%3ACancelingAProject" \
  --data-urlencode "types[]=Transaction%3A%3AProjectSpending%3A%3ALaunchingAProject" \
  -H "Authorization: Bearer 0c950ba36bc15a8a10a6d179ca3db112e0cd5e29dca1aa03f4d2b53f01c06db8"
```

## List invoices

{% hint style="info" %}
**OAuth:** This endpoint requires one of the following scopes: `transaction:manage`, `transaction:write` or `transaction:read`.
{% endhint %}

#### Parameters

| Name    | Type             | Description                                                                         |
| ------- | ---------------- | ----------------------------------------------------------------------------------- |
| `types` | array of strings | The invoice transaction types to filter against. See list of supported types below. |

#### Transaction types

You will find below the list of supported transaction types that include an invoice, to filter against:

```
Transaction::CashWithdrawal::BundleCancellation
Transaction::CashWithdrawal::BundleRefund
Transaction::CreditPurchase::BuyingACreditBundle
Transaction::NonCreditPurchase::InHouseServiceFee
Transaction::NonCreditPurchase::SubscriptionPrepayment
```

{% openapi src="<https://app.textmaster.com/api-docs/v1/clients/specs.yaml>" path="/v1/clients/invoices" method="get" %}
<https://app.textmaster.com/api-docs/v1/clients/specs.yaml>
{% endopenapi %}

#### Code samples

```shell
curl -G "https://api.textmaster.com/v1/clients/invoices" \
  --data-urlencode "types[]=Transaction%3A%3ACashWithdrawal%3A%3ABundleCancellation" \
  --data-urlencode "types[]=Transaction%3A%3ACashWithdrawal%3A%3ABundleRefund" \
  --data-urlencode "types[]=Transaction%3A%3ACreditPurchase%3A%3ABuyingACreditBundle" \
  --data-urlencode "types[]=Transaction%3A%3ANonCreditPurchase%3A%3ABuyingAInHouseServiceFee" \
  --data-urlencode "types[]=Transaction%3A%3ANonCreditPurchase%3A%3ABuyingASubscriptionPrepayment" \
  -H "Authorization: Bearer d8484b69ffe1f749ce720cb370bc65d9fa3e8c3b6e0d19144db676678b0988db"
```

## List receipts

{% hint style="info" %}
**OAuth:** This endpoint requires one of the following scopes: `transaction:manage`, `transaction:write` or `transaction:read`.
{% endhint %}

{% openapi src="<https://app.textmaster.com/api-docs/v1/clients/specs.yaml>" path="/v1/clients/receipts" method="get" %}
<https://app.textmaster.com/api-docs/v1/clients/specs.yaml>
{% endopenapi %}

#### Code samples

```shell
curl -G "https://api.textmaster.com/v1/clients/receipts" \
  -H "Authorization: Bearer 1c1a2c26e3f9a890bd232a987f6b538ded6f48f6a02796e54a7ce405eeeb9b19"
```


# Uploads

## Get upload properties for a file

Get upload properties for a file a client wish to upload on TextMaster. Theses properties can then be used to make the HTTP request on the storage provider with the file sent as HTTP form data.

{% hint style="info" %}
**OAuth:** This endpoint requires the default `public` scope.
{% endhint %}

{% openapi src="<https://app.textmaster.com/api-docs/v1/clients/specs.yaml>" path="/v1/clients/upload\_properties" method="post" %}
<https://app.textmaster.com/api-docs/v1/clients/specs.yaml>
{% endopenapi %}

#### **Code samples**

{% tabs %}
{% tab title="HTTP" %}

```shell
curl "https://api.textmaster.com/v1/clients/upload_properties" \
  -X POST \
  --data-urlencode "file_name=my-file.pdf" \
  --data-urlencode "hashed_payload=f2ca1bb6c7e907d06dafe4687e579fce76b37e4e93b7605022da52e6ccc26fd2" \
  -H "Authorization: Bearer 427ba17dc03db4792cd8d3c731ed53addd261b1baa7eef1ceda2cf2ca20f2b79"

# Response:
#
# {
#   "url": "https://storage-proxy.textmaster.com/api-files/uploads/10922fb8-9265-4ef2-92e8-c4177c3b03da/ef9f1ca8/my-file.pdf",
#   "headers": {
#     "x-upload-path": "uploads/10922fb8-9265-4ef2-92e8-c4177c3b03da/ef9e1ca8/my-file.pdf",
#     "x-upload-sha256": "f2ca1bb6c7e907d06dafe4687e579fce76b37e4e39b7605022da52e6ccc26fd2",
#     "x-upload-date": "20500102T123456Z",
#     "Authorization": "78ca271e7f6464961ef3db6903da3d2c51cd4156975322ba678840e12a334de3"
#   }
# }

curl "https://storage-proxy.textmaster.com/api-files/uploads/10922fb8-9265-4ef2-92e8-c4177c3b03da/ef9f1ca8/my-file.pdf" \
  -X PUT \
  -H "x-upload-path: uploads/10922fb8-9265-4ef2-92e8-c4177c3b03da/ef9e1ca8/my-file.pdf" \
  -H "x-upload-sha256: f2ca1bb6c7e907d06dafe4687e579fce76b37e4e39b7605022da52e6ccc26fd2" \
  -H "x-upload-date: 20500102T123456Z" \
  -H "Authorization: 78ca271e7f6464961ef3db6903da3d2c51cd4156975322ba678840e12a334de3" \
  -H "Content-Type: application/pdf" \
  -d "@path/to-the-actual-file/my-file.pdf"
```

{% endtab %}

{% tab title="Ruby" %}
This sample Ruby code requires the `excon` gem to be installed.

```ruby
require 'digest/sha1'
require 'excon'
require 'json'
require 'time'

file_name      = 'my-file.pdf'
file_path      = 'path/to-the-actual-file/my-file.pdf'
file_content   = File.binread(file_path)
hashed_payload = Digest::SHA256.hexdigest(file_content)

apikey = 'YOUR TEXTMASTER API KEY'
apisecret = 'YOUR TEXTMASTER API SECRET'
current_time = Time.now.utc.httpdate
signature = Digest::SHA1.hexdigest(apisecret + current_time)

response = Excon.post(
  'https://api.textmaster.com/v1/clients/upload_properties.json',
  body: URI.encode_www_form(file_name: file_name, hashed_payload: hashed_payload),
  headers: {
    apikey: apikey,
    date: current_time,
    signature: signature,
  }
)

properties = JSON.parse(response.body)
# {
#   "url" => "https://storage-proxy.textmaster.com/api-files/uploads/10922fb8-9265-4ef2-92e8-c4177c3b03da/ef9f1ca8/my-file.pdf",
#   "headers" => {
#     "x-upload-path" => "uploads/10922fb8-9265-4ef2-92e8-c4177c3b03da/ef9e1ca8/my-file.pdf",
#     "x-upload-sha256" => "f2ca1bb6c7e907d06dafe4687e579fce76b37e4e39b7605022da52e6ccc26fd2",
#     "x-upload-date" => "20500102T123456Z",
#     "Authorization" => "78ca271e7f6464961ef3db6903da3d2c51cd4156975322ba678840e12a334de3"
#   }
# }

response = Excon.put(
  properties['url'],
  body: file_content,
  headers: properties['headers'].merge('Content-Type' => 'application/pdf')
)

response.status
#=> 200
```

{% endtab %}
{% endtabs %}


# Users

## Get my user information

{% hint style="info" %}
**OAuth:** This endpoint requires one of the following scopes: `user:manage`, `user:read` or `user:write`.
{% endhint %}

{% hint style="warning" %}
**Warning:** Only OAuth Apps with the `user:email` scope will be able to access the authenticated user's private email address.
{% endhint %}

{% openapi src="<https://app.textmaster.com/api-docs/v1/clients/specs.yaml>" path="/v1/clients/users/me" method="get" %}
<https://app.textmaster.com/api-docs/v1/clients/specs.yaml>
{% endopenapi %}

#### Code samples

```shell
curl -G "https://api.textmaster.com/v1/clients/users/me" \
  -H "Authorization: Bearer c6446be4ac320b1a5dd746a349ac0f750e4867ea7fa474092dfde404c1dfb64b"
```

## Update callbacks for my user

{% hint style="info" %}
**OAuth:** This endpoint requires one of the following scopes: `user:manage` or `user:write`.
{% endhint %}

{% openapi src="<https://app.textmaster.com/api-docs/v1/clients/specs.yaml>" path="/v1/clients/users/{user\_id}" method="put" %}
<https://app.textmaster.com/api-docs/v1/clients/specs.yaml>
{% endopenapi %}

#### Code samples

```shell
curl "https://api.textmaster.com/v1/clients/users/61698aff8b81926d91c0f72c" \
  -X PUT \
  -d '{
    "user": {
      "callback": {
        "waiting_assignment": {
          "url": "https://example.com/waiting_assignment_callback",
          "format": "json"
        },
        "completed": {
          "url": "https://example.com/completed_callback",
          "format": "json"
        }
      }
    }
  }' \
  -H "Authorization: Bearer ac343d393b0041cb8f73b92010a09a543f55605b7877bff63ae359dcd103ad4a" \
  -H "Content-Type: application/json"
```


# Work Templates

## List work templates

{% hint style="info" %}
**OAuth:** This endpoint requires the default `public` scope.
{% endhint %}

{% openapi src="<https://app.textmaster.com/api-docs/v1/clients/specs.yaml>" path="/v1/clients/work\_templates" method="get" %}
<https://app.textmaster.com/api-docs/v1/clients/specs.yaml>
{% endopenapi %}

#### Code samples

```shell
curl -G "https://api.textmaster.com/v1/clients/work_templates" \
  -H "Authorization: Bearer a9ff7f5dd9b264f5315dd9bbf75c161f5dde794157a8145cde2856e4b80d9548"
```

## Get a work template

{% hint style="info" %}
**OAuth:** This endpoint requires the default `public` scope.
{% endhint %}

{% openapi src="<https://app.textmaster.com/api-docs/v1/clients/specs.yaml>" path="/v1/clients/work\_templates/{work\_template\_name}" method="get" %}
<https://app.textmaster.com/api-docs/v1/clients/specs.yaml>
{% endopenapi %}

#### Code samples

```shell
curl -G "https://api.textmaster.com/v1/clients/work_templates/2_paragraphs" \
  -H "Authorization: Bearer 1435f72240cb208fac8cecd8d09a341b8f6260bd66d41c0cd890955e3ec9229c"
```


