# Copystake Integration

## **CopyStake overview**

CopyStake consists of two parts:

* **Lobby**. The page of active streamers (with video streams) and regular users actively playing allowed games.
* **Broadcast**. The page of the selected streamer, where the live stream is displayed (or, if it's a regular user, an animation will be shown), and where users can watch the stream, monitor the streamer's bets, and start copying them.

CopyStake admin configuration:

* **Operator settings**. To integrate CopyStake, the operator needs to implement the API contract (provided below) and specify the following information on the Admin CopyStake page:
  * **X-API-KEY**. CopyStake will make requests to the operator with this value in the request’s header.
  * **Session TTL**. It’s needed to synchronize the session duration between the operator and CopyStake.
  * **Callback URL**. It’s required for sending requests from CopyStake.
* **CopyStake settings**. A section necessary for configuring games to be added to the whitelist, creating streamers, scheduling video streams, and more.

API Access:

* All requests from Copystake to the operator contain X-API-KEY with a value issued by the operator.
* All requests from the operator to Copystake contain X-API-KEY with a value issued by Copystake.

Signature:

* All requests from Copystake must be signed by Copystake and verified by the operator.
* All requests from the operator must be signed by the operator and verified by CopyStake.
* The SecretKey for signing the payload in both cases is issued by CopyStake.
* The signature will be placed in the X-REQUEST-SIGNATURE header of each request and uses Base64(HmacSHA512(SecretKey, MD5(request body))).
* Dealing with X-REQUEST-SIGNATURE.

<details>

<summary>Example of how to generate a signature for SecretKey = <code>secret</code></summary>

node \
\
Welcome to Node.js v16.13.0. Type ".help" for more information.\
\
var crypto = require('crypto'); \
undefined \
\
var hasher = crypto.createHash('md5'); \
undefined \
\
var hashed = hasher.update('{"id":"103193","action":"CREDIT","amount":4.123,"currency":"USD","exchangeRate":0.00203250655485,"tokenAmount":2500,"operatorId":123,"operatorUserId":"333"}') \
undefined\
\
var hash = hashed.digest('hex'); \
undefined \
\
var hmac = crypto.createHmac('sha512', 'secret'); \
undefined \
\
hmac.update(hash); \
Hmac { \_options: undefined, \[Symbol(kHandle)]: Hmac {}, \[Symbol(kState)]: { \[Symbol(kFinalized)]: false } } \
\
var sign = hmac.digest('base64'); \
undefined \
\
console.log('signature: ' + sign); \
signature: IifPVbfXptagZ6qSXH9vYrQiqSP4sKIC+hV+39z2K+FlLzRoEboPGTTymjmeuAhN1i0ICDyyrrufYspgAJmxHQ==

</details>

Callback URL:

* `callback_url` - provided by the operator, e.g. `callback_url=https://integration.operator.com/api/v1/copystake`

**IMPORTANT:**&#x20;

Operators need to whitelist the Trueplay IPs to receive transaction status callbacks. Otherwise, Trueplay will return errors for deposit/withdrawal tokens requests.

<details>

<summary><strong>IPs for whitelisting</strong></summary>

18.193.249.95

18.184.86.250

18.196.113.251

</details>

## **Original and Copied transactions**

<figure><img src="/files/5Hv1v8duBG155gj25hNX" alt=""><figcaption></figcaption></figure>

## Get CopyStake page&#x20;

This API method allows operators to obtain a link to the CopyStake page, which can be embedded as an iframe into the operator's website.

*If a user accesses CopyStake, the system loads the lobby as the default entry point. However, when generating a Stream URL, it allows to bypass the CopyStake Lobby and be redirected directly to a broadcast page when a stream is active.*

<mark style="color:green;">`POST`</mark>

`https://integration.trueplay.io/api/v1/copystake/init`

**Headers**

{% code overflow="wrap" %}

```
Content-Type: application/json
X-API-KEY: value issued by CopyStake
X-REQUEST-SIGNATURE: value generated based on the request payload (secret key issued by CopyStake)
```

{% endcode %}

**Request**

<table data-header-hidden><thead><tr><th width="168"></th><th width="92"></th><th width="129"></th><th></th></tr></thead><tbody><tr><td><strong>Field</strong></td><td><strong>Type</strong></td><td><strong>Require</strong></td><td><strong>Description</strong></td></tr><tr><td>userId</td><td>String</td><td>mandatory</td><td>User identifier in the operator system</td></tr><tr><td>sessionId</td><td>String</td><td>optional</td><td>Unique session identifier in the operator system. If not provided, it will be generated by CopyStake</td></tr><tr><td>currency</td><td>String</td><td>mandatory</td><td>User's active currency (ISO 4217)</td></tr><tr><td>balance</td><td>Double</td><td>mandatory</td><td>User's current balance in the operator system, rounded to 8 decimal places</td></tr><tr><td>language</td><td>String</td><td>optional</td><td>User's UI language code from the permitted set in the backoffice (ISO 639)</td></tr><tr><td>streamId</td><td>String</td><td>optional</td><td>If this parameter is included in the request, Trueplay system will recognize it and automatically direct the user to the Broadcast page instead of the Lobby.</td></tr></tbody></table>

**Response success**

<table data-header-hidden><thead><tr><th width="158"></th><th width="118"></th><th width="158"></th><th></th></tr></thead><tbody><tr><td><strong>Field</strong></td><td><strong>Type</strong></td><td><strong>Require</strong></td><td><strong>Description</strong></td></tr><tr><td>url</td><td>String</td><td>mandatory</td><td>CopyStake URL with an authentication token</td></tr><tr><td>sessionId</td><td>String</td><td>mandatory</td><td>Unique session identifier in the operator system. If not provided, it will be generated by CopyStake</td></tr></tbody></table>

**Response error**

HTTP 400 Bad Request

<table data-header-hidden><thead><tr><th width="150"></th><th width="86"></th><th width="118"></th><th></th></tr></thead><tbody><tr><td><strong>Field</strong></td><td><strong>Type</strong></td><td><strong>Require</strong></td><td><strong>Description</strong></td></tr><tr><td>errorCode</td><td>String</td><td>mandatory</td><td><p>ERR_UNKNOWN<br>General error status, for cases without a special error code.</p><p>ERR_COPYSTAKE_OFF<br>CopyStake is disabled</p></td></tr><tr><td>errorMessage</td><td>String</td><td>optional</td><td>more detailed description of the error</td></tr></tbody></table>

<details>

<summary><strong>Request example</strong></summary>

{% code overflow="wrap" %}

```
Request
curl --location 'https://integration.trueplay.io/api/v1/copystake/init' \
--header 'Content-Type: application/json' \
--header 'X-API-KEY: d922bd1c-e7ac-4c98-9b1a-baa724440b0e' \
--header 'X-REQUEST-SIGNATURE: XhNkfy6WCTdiewUu0mSUAGw4LwF7GVBa7GT7drJXCvHkI+ORHJQ07uxVKAi3pA/0vB8/shlzX16x+77DvqCGRw==' \
--data '{
    "userId": "1",
    "sessionId": "10XFTW12",
    "currency": "USDT",
    "balance": 820.00000000,
    "language": "en",
    "streamId": "7b699688-f4ad-407d-baf5-c006ba8d50e2"
}'
```

{% endcode %}

</details>

**Response example**

{% tabs %}
{% tab title="200: OK Request is succesful" %}
{% code overflow="wrap" %}

```
200 OK
{
    "url": "https://copystake.trueplay.io?token=eyJhbGciOiJIUzUxMiJ9.eyJpZF9vcGVyYXRvciI6”,
    "sessionId": "10XFTW12"
}
```

{% endcode %}
{% endtab %}

{% tab title="400: Bad Request " %}

```
400 Bad Request
{
    "errorCode": "ERR_COPYSTAKE_OFF",
    "errorMessage": "CopyStake is disabled"
}
```

{% endtab %}
{% endtabs %}

### **Get CopyStake page DEMO** <a href="#get-copystake-page-demo" id="get-copystake-page-demo"></a>

This API method allows operators to obtain a link to the CopyStake page, which can be opened in demo mode within an iframe on the operator’s website.

<mark style="color:green;">`POST`</mark>

`https://integration.trueplay.io/api/v1/copystake/init-demo`

**Headers**

{% code overflow="wrap" %}

```
Content-Type: application/json
X-API-KEY: value issued by CopyStake
```

{% endcode %}

**Request**

<table data-header-hidden><thead><tr><th width="168"></th><th width="92"></th><th width="129"></th><th></th></tr></thead><tbody><tr><td><strong>Field</strong></td><td><strong>Type</strong></td><td><strong>Require</strong></td><td><strong>Description</strong></td></tr><tr><td>language</td><td>String</td><td>optional</td><td>User's UI language code from the permitted set in the backoffice (ISO 639)</td></tr><tr><td>streamId</td><td>String</td><td>optional</td><td>User identifier for active stream. If present, followers are directed to the Broadcast page, skipping the Lobby</td></tr></tbody></table>

Response success

<table data-header-hidden><thead><tr><th width="183"></th><th width="132"></th><th width="141"></th><th></th></tr></thead><tbody><tr><td><strong>Field</strong></td><td><strong>Type</strong></td><td><strong>Require</strong></td><td><strong>Description</strong></td></tr><tr><td>url</td><td>String</td><td>mandatory</td><td>CopyStake url with auth token</td></tr></tbody></table>

Response error HTTP&#x20;

400 Bad Request

<table data-header-hidden><thead><tr><th width="156"></th><th width="126"></th><th width="161"></th><th></th></tr></thead><tbody><tr><td><strong>Field</strong></td><td><strong>Type</strong></td><td><strong>Require</strong></td><td><strong>Description</strong></td></tr><tr><td>errorCode</td><td>String</td><td>mandatory</td><td><p>ERR_UNKNOWN<br>General error status, for cases without a special error code.</p><p>ERR_COPYSTAKE_OFF<br>CopyStake is disabled</p></td></tr><tr><td>errorMessage</td><td>String</td><td>optional</td><td>more detailed description of the error</td></tr></tbody></table>

<details>

<summary><strong>Request example</strong></summary>

{% code overflow="wrap" %}

```
Request
curl --location 'https://integration.trueplay.io/api/v1/copystake/init-demo' \
--header 'Content-Type: application/json' \
--header 'X-API-KEY: d922bd1c-e7ac-4c98-9b1a-baa724440b0e' \
--header 'X-REQUEST-SIGNATURE: XhNkfy6WCTdiewUu0mSUAGw4LwF7GVBa7GT7drJXCvHkI+ORHJQ07uxVKAi3pA/0vB8/shlzX16x+77DvqCGRw==' \
--data '{
    "language": "en",
    "streamId": "7b699688-f4ad-407d-baf5-c006ba8d50e2"
}'
```

{% endcode %}

</details>

**Response example**

{% tabs %}
{% tab title="200: OK Request is succesful" %}
{% code overflow="wrap" %}

```
200 OK
{
    "url": "https://copystake.trueplay.io?token=eyJhbGciOiJIUzUxMiJ9.eyJpZF9vcGVyYXRvciI6”
}
```

{% endcode %}
{% endtab %}

{% tab title="400: Bad Request " %}

```
400 Bad Request
{
    "errorCode": "ERR_COPYSTAKE_OFF",
    "errorMessage": "CopyStake is disabled"
}
```

{% endtab %}
{% endtabs %}

## Send game transaction

This method allows operators to send transaction data to Trueplay. Operators must provide the relevant details of each transaction.

<mark style="color:green;">`POST`</mark>&#x20;

`https://{operator-name}.proxy.trueplay.io/api/v1/accept`

**Headers**

```
Content-Type: application/json
X-API-KEY: value issued by CopyStake
```

<table data-header-hidden><thead><tr><th width="217"></th><th width="102"></th><th width="115"></th><th></th></tr></thead><tbody><tr><td><strong>Field</strong></td><td><strong>Type</strong></td><td><strong>Require</strong></td><td><strong>Description</strong></td></tr><tr><td>userId</td><td>String</td><td>mandatory</td><td>User identifier in the operator system</td></tr><tr><td>transactionId</td><td>String</td><td>mandatory</td><td>A unique identifier for each transaction type (BET, WIN, ROLLBACK). For every new action (BET, WIN, ROLLBACK) a distinct transactionId is generated to track that specific action.</td></tr><tr><td>referenceTransactionId</td><td>String</td><td>optional</td><td><p>Used to link related transactions.</p><p>• For BET transactions, this will be null as they are the starting point of the sequence.</p><p>• For WIN transactions, this field should reference the transactionId of the corresponding BET.</p><p>• For ROLLBACK transactions, this field should reference the transactionId of the original BET being rolled back. This also applies to rolling back WIN transactions; the referenceTransactionId should point to the transactionId of the WIN being rolled back.</p></td></tr><tr><td>type</td><td>String</td><td>mandatory</td><td>Type of game transaction (BET | WIN | ROLLBACK)</td></tr><tr><td>gameProvider</td><td>String</td><td>mandatory</td><td>Indicates the provider of the game or platform associated with the transaction </td></tr><tr><td>gamePublisher</td><td>String</td><td>mandatory</td><td>Specifies the game’s publisher</td></tr><tr><td>gameCode</td><td>String</td><td>mandatory</td><td>Unique game code</td></tr><tr><td>gameName</td><td>String</td><td>mandatory</td><td>An identifier for the specific game associated with the transaction</td></tr><tr><td>gameType</td><td>String</td><td>mandatory</td><td>The type of game</td></tr><tr><td>currency</td><td>String</td><td>mandatory</td><td>User's active currency (ISO 4217)</td></tr><tr><td>amount</td><td>Double</td><td>mandatory</td><td><ul><li>Fiat: Although fiat currencies typically allow up to two decimal places (e.g., USD: 10.25)</li><li>Cryptocurrency: The amount parameter adheres to a maximum of 8 decimal places</li></ul></td></tr></tbody></table>

<details>

<summary><strong>Request example</strong></summary>

{% code overflow="wrap" %}

```
Request
curl --location 'https://integration.trueplay.io/api/v1/copystake/init' \
--header 'Content-Type: application/json' \
--header 'X-API-KEY: d922bd1c-e7ac-4c98-9b1a-baa724440b0e' \
--header 'X-REQUEST-SIGNATURE: XhNkfy6WCTdiewUu0mSUAGw4LwF7GVBa7GT7drJXCvHkI+ORHJQ07uxVKAi3pA/0vB8/shlzX16x+77DvqCGRw==' \
--data '{
    "userId": "1",
    "transactionId": "111",
    "type": "WIN",
    "gameProvider": "NOLIMIT_CITY"
    "gameProvider": "EVOLUTION",
    "gameCode": "Mental",
    "gameName": "Mental",
    "gameType": "Slots",
    "currency": "USDT",
    "amount": 25.00000000,
}
```

{% endcode %}

</details>

{% tabs %}
{% tab title="200: OK Request is successful" %}
{% code overflow="wrap" %}

```
200 OK
```

{% endcode %}
{% endtab %}

{% tab title="400: Bad Request " %}
{% code overflow="wrap" %}

```
400 Bad Request
{
    "errorCode": "ERR_COPYSTAKE_OFF",
    "errorMessage": "CopyStake is disabled"
}
```

{% endcode %}
{% endtab %}
{% endtabs %}

## **Get user balance**

This method retrieves the current available balance of a user during the opening of the broadcast page and before starting the copy round.

<mark style="color:green;">`POST`</mark>&#x20;

```
{callback_url}/user-balance
```

**Headers**

{% code overflow="wrap" %}

```
Content-Type: application/json
X-API-KEY: value issued by Operator
X-REQUEST-SIGNATURE: value generated based on the request payload (secret key issued by CopyStake)
```

{% endcode %}

**Request**

<table data-header-hidden><thead><tr><th width="154"></th><th width="110"></th><th width="144"></th><th></th></tr></thead><tbody><tr><td><strong>Field</strong></td><td><strong>Type</strong></td><td><strong>Require</strong></td><td><strong>Description</strong></td></tr><tr><td>sessionId</td><td>String</td><td>mandatory</td><td>unique session identifier</td></tr></tbody></table>

**Response**

<table data-header-hidden><thead><tr><th width="135"></th><th width="112"></th><th width="143"></th><th></th></tr></thead><tbody><tr><td><strong>Field</strong></td><td><strong>Type</strong></td><td><strong>Require</strong></td><td><strong>Description</strong></td></tr><tr><td>balance</td><td>Double</td><td>mandatory</td><td><ul><li>rounding to 8 decimal places</li><li>current available user's balance</li></ul></td></tr><tr><td>currency</td><td>String</td><td>mandatory</td><td>user's active currency ISO 4217</td></tr></tbody></table>

<details>

<summary><strong>Request example</strong></summary>

{% code overflow="wrap" %}

```
curl --location '{callback_url}/user-balance' \
--header 'Content-Type: application/json' \
--header 'X-API-KEY: e58d1990-31b9-41f5-aa39-fd150643a8fe' \
--header 'X-REQUEST-SIGNATURE: FbS2341Uu0mSUAGw4LwF7GVBa7GT7drJXCvHkI+ORHJQ07uxVKAi3pA/0vB8/shlzX16x234FD==' \
--data '{
    "sessionId": "10XFTW12"

```

{% endcode %}

</details>

{% tabs %}
{% tab title="Response example" %}

```
200 OK
{
    "balance": 830.00000000,
    "currency": "USDT",
}
```

{% endtab %}
{% endtabs %}

## **Send copied transactions (BET/WIN/ROLLBACK)**

### **Send BET copied transaction**

This API method sends a BET copied transaction to the operator immediately after receiving the transaction from the streamer during an active game round.

<mark style="color:green;">`POST`</mark>&#x20;

```
{callback_url}/bet
```

**Headers**

{% code overflow="wrap" %}

```
Content-Type: application/json
X-API-KEY: value issued by Operator
X-REQUEST-SIGNATURE: value generated based on the request payload (secret key issued by CopyStake)
```

{% endcode %}

**Request**

<table data-header-hidden><thead><tr><th width="222"></th><th width="98"></th><th width="119"></th><th></th></tr></thead><tbody><tr><td><strong>Field</strong></td><td><strong>Type</strong></td><td><strong>Require</strong></td><td><strong>Description</strong></td></tr><tr><td>sessionId</td><td>String</td><td>mandatory</td><td>Unique session identifier</td></tr><tr><td>roundId</td><td>String</td><td>mandatory</td><td>Unique round identifier representing one copy cycle (some number of game actions)</td></tr><tr><td>transactionId</td><td>String</td><td>mandatory</td><td>Unique CopyStake transaction identifier</td></tr><tr><td>transactionIdOriginal</td><td>String</td><td>mandatory</td><td>Original transaction identifier (the streamer's transaction)</td></tr><tr><td>currency</td><td>String</td><td>mandatory</td><td>User's active currency (ISO 4217)</td></tr><tr><td>amount</td><td>Double</td><td>mandatory</td><td><p></p><ul><li>Fiat: Although fiat currencies typically allow up to two decimal places (e.g., USD: 10.25)</li><li>Cryptocurrency: The amount parameter adheres to a maximum of 8 decimal places</li></ul></td></tr></tbody></table>

**Response**

<table data-header-hidden><thead><tr><th width="157"></th><th width="100"></th><th width="122"></th><th></th></tr></thead><tbody><tr><td><strong>Field</strong></td><td><strong>Type</strong></td><td><strong>Require</strong></td><td><strong>Description</strong></td></tr><tr><td>balance</td><td>Double</td><td>mandatory</td><td>Current available user's balance, rounded to 8 decimal places</td></tr><tr><td>currency</td><td>String</td><td>mandatory</td><td>User's active currency (ISO 4217)</td></tr></tbody></table>

**Response error**\
HTTP 400 Bad Request

<table data-header-hidden><thead><tr><th width="158"></th><th width="88"></th><th width="117"></th><th></th></tr></thead><tbody><tr><td><strong>Field</strong></td><td><strong>Type</strong></td><td><strong>Require</strong></td><td><strong>Description</strong></td></tr><tr><td>errorCode</td><td>String</td><td>mandatory</td><td>ERR_UNKNOWN<br>General error status, for cases without a special error code.</td></tr><tr><td>errorMessage</td><td>String</td><td>optional</td><td>More detailed description of the error</td></tr></tbody></table>

<details>

<summary><strong>Request example</strong></summary>

{% code overflow="wrap" %}

```
curl --location'https://{operator_base_url}/integration/copystake/bet'\
--header 'Content-Type: application/json' \
--header 'X-API-KEY: e58d1990-31b9-41f5-aa39-fd150643a8fe' \
--header 'X-REQUEST-SIGNATURE: FbS2341Uu0mSUAGw4LwF7GVBa7GT7drJXCvHkI+ORHJQ07uxVKAi3pA/0vB8/shlzX16x234FD==' \
--data '{
    "sessionId": "10XFTW12",
    "roundId": "1",
    "transactionId": "1",
    "transactionIdOriginal": "111",
    "currency": "USDT",
    "amount": 25.00000000
}'
```

{% endcode %}

</details>

**Response example**

{% tabs %}
{% tab title="200: OK Request is successful" %}

```
200 OK
{
    "balance": 795.00000000,
    "currency": "USDT",
}
```

{% endtab %}

{% tab title="400: Bad Request " %}

```
400 Bad Request
{
    "errorCode": "ERR_COPYSTAKE_OFF",
    "errorMessage": "CopyStake is disabled"
}
```

{% endtab %}
{% endtabs %}

### **Send WIN copied transaction**

This API method sends a WIN copied transaction to the operator immediately after receiving the transaction from the streamer during an active game round.

<mark style="color:green;">`POST`</mark>&#x20;

```
{callback_url}/win
```

**Headers**

{% code overflow="wrap" %}

```
Content-Type: application/json
X-API-KEY: value issued by Operator
X-REQUEST-SIGNATURE: value generated based on the request payload (secret key issued by CopyStake)
```

{% endcode %}

**Request**

<table data-header-hidden><thead><tr><th width="229"></th><th width="100"></th><th width="120"></th><th></th></tr></thead><tbody><tr><td><strong>Field</strong></td><td><strong>Type</strong></td><td><strong>Require</strong></td><td><strong>Description</strong></td></tr><tr><td>sessionId</td><td>String</td><td>mandatory</td><td>Unique session identifier</td></tr><tr><td>roundId</td><td>String</td><td>mandatory</td><td>Unique round identifier representing one copy cycle (some number of game actions)</td></tr><tr><td>transactionId</td><td>String</td><td>mandatory</td><td>Unique CopyStake transaction identifier</td></tr><tr><td>transactionIdOriginal</td><td>String</td><td>mandatory</td><td>Original transaction identifier (the streamer's transaction)</td></tr><tr><td>currency</td><td>String</td><td>mandatory</td><td>User's active currency (ISO 4217)</td></tr><tr><td>amount</td><td>Double</td><td>mandatory</td><td><p></p><ul><li>Fiat: Although fiat currencies typically allow up to two decimal places (e.g., USD: 10.25)</li><li>Cryptocurrency: The amount parameter adheres to a maximum of 8 decimal places</li></ul></td></tr></tbody></table>

**Response**

<table data-header-hidden><thead><tr><th width="143"></th><th width="112"></th><th width="146"></th><th></th></tr></thead><tbody><tr><td><strong>Field</strong></td><td><strong>Type</strong></td><td><strong>Require</strong></td><td><strong>Description</strong></td></tr><tr><td>balance</td><td>Double</td><td>mandatory</td><td>Current available user's balance, rounded to 8 decimal places</td></tr><tr><td>currency</td><td>String</td><td>mandatory</td><td>User's active currency (ISO 4217)</td></tr></tbody></table>

<details>

<summary><strong>Request example</strong></summary>

{% code overflow="wrap" %}

```
curl --location https://{operator_base_url}/integration/copystake/win \
--header 'Content-Type: application/json' \
--header 'X-API-KEY: e58d1990-31b9-41f5-aa39-fd150643a8fe' \
--header 'X-REQUEST-SIGNATURE: FbS2341Uu0mSUAGw4LwF7GVBa7GT7drJXCvHkI+ORHJQ07uxVKAi3pA/0vB8/shlzX16x234FD==' \
--data '{
    "sessionId": "10XFTW12",
    "roundId": "1",
    "transactionId": "1",
    "transactionIdOriginal": "111",
    "currency": "USDT",
    "amount": 25.00000000
}'
```

{% endcode %}

</details>

**Response example**

{% tabs %}
{% tab title="200: OK Request is successful" %}

```
200 OK
{
    "balance": 805.00000000,
    "currency": "USDT",
}
```

{% endtab %}

{% tab title="400: Bad Request " %}

```
400 Bad Request
{
    "errorCode": "ERR_COPYSTAKE_OFF",
    "errorMessage": "CopyStake is disabled"
}
```

{% endtab %}
{% endtabs %}

### **Send ROLLBACK BET copied transaction**

This API method sends a ROLLBACK BET copied transaction to the operator immediately after receiving the transaction from the streamer during an active game round.

<mark style="color:green;">`POST`</mark>&#x20;

```
{callback_url}/rollback-bet
```

**Headers**

{% code overflow="wrap" %}

```
Content-Type: application/json
X-API-KEY: value issued by Operator
X-REQUEST-SIGNATURE: value generated based on the request payload (secret key issued by CopyStake)
```

{% endcode %}

**Request**

<table data-header-hidden><thead><tr><th width="295"></th><th width="86"></th><th width="120"></th><th></th></tr></thead><tbody><tr><td><strong>Field</strong></td><td><strong>Type</strong></td><td><strong>Require</strong></td><td><strong>Description</strong></td></tr><tr><td>sessionId</td><td>String</td><td>mandatory</td><td>Unique session identifier</td></tr><tr><td>roundId</td><td>String</td><td>mandatory</td><td>Unique round identifier representing one copy cycle (some number of game actions)</td></tr><tr><td>transactionId</td><td>String</td><td>mandatory</td><td>Unique CopyStake transaction identifier</td></tr><tr><td>referenceTransactionId</td><td>String</td><td>mandatory</td><td>Reference to BET CopyStake transaction identifier</td></tr><tr><td>transactionIdOriginal</td><td>String</td><td>mandatory</td><td>Original transaction identifier (the streamer's transaction)</td></tr><tr><td>referenceTransactionIdOriginal</td><td>String</td><td>mandatory</td><td>Reference to original BET transaction identifier</td></tr><tr><td>currency</td><td>String</td><td>mandatory</td><td>User's active currency (ISO 4217)</td></tr><tr><td>amount</td><td>Double</td><td>mandatory</td><td><p></p><ul><li>Fiat: Although fiat currencies typically allow up to two decimal places (e.g., USD: 10.25)</li><li>Cryptocurrency: The amount parameter adheres to a maximum of 8 decimal places</li></ul></td></tr></tbody></table>

**Response**

<table data-header-hidden><thead><tr><th width="137"></th><th width="133"></th><th width="143"></th><th></th></tr></thead><tbody><tr><td><strong>Field</strong></td><td><strong>Type</strong></td><td><strong>Require</strong></td><td><strong>Description</strong></td></tr><tr><td>balance</td><td>Double</td><td>mandatory</td><td>Current available user's balance, rounded to 8 decimal places</td></tr><tr><td>currency</td><td>String</td><td>mandatory</td><td>User's active currency (ISO 4217)</td></tr></tbody></table>

<details>

<summary><strong>Request example</strong></summary>

{% code overflow="wrap" %}

```
curl --location 'https://{operator_base_url}/integration/copystake/rollback-bet' \
--header 'Content-Type: application/json' \
--header 'X-API-KEY: e58d1990-31b9-41f5-aa39-fd150643a8fe' \
--header 'X-REQUEST-SIGNATURE: FAE3S2341Uu0mSUAGw4LwF7GVBa7GT7drJXCvHkI+ORHJQ07uxVKAi3pA/0vB8/shlzX16x23d23d==' \
--data '{
    "sessionId": "10XFTW12",                   
    "roundId": "1",                            
    "transactionId": "3",                      
    "referenceTransactionId": "1",              
    "transactionIdOriginal": "333",             
    "referenceTransactionIdOriginal": "111",
    "currency": "USDT",                        
    "amount": 25.00000000                        
}'
```

{% endcode %}

</details>

**Response example**

{% tabs %}
{% tab title="200: OK Request is successful" %}

```
200 OK
{
    "balance": 830.00000000,
    "currency": "USDT"
}
```

{% endtab %}

{% tab title="400: Bad Request " %}

```
400 Bad Request
{
    "errorCode": "ERR_COPYSTAKE_OFF",
    "errorMessage": "CopyStake is disabled"
}
```

{% endtab %}
{% endtabs %}

### **Send ROLLBACK WIN copied transaction**

This API method sends a ROLLBACK WIN copied transaction to the operator immediately after receiving the transaction from the streamer during an active game round.

<mark style="color:green;">`POST`</mark>&#x20;

```
{callback_url}/rollback-win
```

**Headers**

{% code overflow="wrap" %}

```
Content-Type: application/json
X-API-KEY: value issued by Operator
X-REQUEST-SIGNATURE: value generated based on the request payload (secret key issued by CopyStake)
```

{% endcode %}

**Request**

<table data-header-hidden><thead><tr><th width="276"></th><th width="92"></th><th width="117"></th><th></th></tr></thead><tbody><tr><td><strong>Field</strong></td><td><strong>Type</strong></td><td><strong>Require</strong></td><td><strong>Description</strong></td></tr><tr><td>sessionId</td><td>String</td><td>mandatory</td><td>Unique session identifier</td></tr><tr><td>roundId</td><td>String</td><td>mandatory</td><td>Unique round identifier representing one copy cycle (some number of game actions)</td></tr><tr><td>transactionId</td><td>String</td><td>mandatory</td><td>Unique CopyStake transaction identifier</td></tr><tr><td>referenceTransactionId</td><td>String</td><td>mandatory</td><td>Reference to WIN CopyStake transaction identifier</td></tr><tr><td>transactionIdOriginal</td><td>String</td><td>mandatory</td><td>Original transaction identifier (the streamer's transaction)</td></tr><tr><td>referenceTransactionIdOriginal</td><td>String</td><td>mandatory</td><td>Reference to original BET transaction identifier</td></tr><tr><td>currency</td><td>String</td><td>mandatory</td><td>User's active currency (ISO 4217)</td></tr><tr><td>amount</td><td>Double</td><td>mandatory</td><td><p></p><ul><li>Fiat: Although fiat currencies typically allow up to two decimal places (e.g., USD: 10.25)</li><li>Cryptocurrency: The amount parameter adheres to a maximum of 8 decimal places</li></ul></td></tr></tbody></table>

**Response**

<table data-header-hidden><thead><tr><th width="137"></th><th width="133"></th><th width="143"></th><th></th></tr></thead><tbody><tr><td><strong>Field</strong></td><td><strong>Type</strong></td><td><strong>Require</strong></td><td><strong>Description</strong></td></tr><tr><td>balance</td><td>Double</td><td>mandatory</td><td>Current available user's balance, rounded to 8 decimal places</td></tr><tr><td>currency</td><td>String</td><td>mandatory</td><td>User's active currency (ISO 4217)</td></tr></tbody></table>

<details>

<summary><strong>Request example</strong></summary>

<pre data-overflow="wrap"><code><strong>curl --location 'https://{operator_base_url}/integration/copystake/rollback-win' \
</strong>--header 'Content-Type: application/json' \
--header 'X-API-KEY: e58d1990-31b9-41f5-aa39-fd150643a8fe' \
--header 'X-REQUEST-SIGNATURE: FAE3S2341Uu0mSUAGw4LwF7GVBa7GT7drJXCvHkI+ORHJQ07uxVKAi3pA/0vB8/shlzX16x23d23d==' \
--data '{
    "sessionId": "10XFTW12",                   
    "roundId": "1",                            
    "transactionId": "4",                      
    "referenceTransactionId": "2",              
    "transactionIdOriginal": "444",             
    "referenceTransactionIdOriginal": "222",
    "currency": "USDT",                        
    "amount": 10.00000000                        
}'
</code></pre>

</details>

**Response example**

{% tabs %}
{% tab title="200: OK Request is successful" %}

```
200 OK
{
    "balance": 830.00,
    "currency": "USDT"
}
```

{% endtab %}

{% tab title="400: Bad Request " %}

```
400 Bad Request
{
    "errorCode": "ERR_COPYSTAKE_OFF",
    "errorMessage": "CopyStake is disabled"
}
```

{% endtab %}
{% endtabs %}

## Update User account type

This API method allows you to update the user account type to either TEST or REGULAR. It is commonly used to mark a user as a test user.

<mark style="color:red;">`PUT`</mark>&#x20;

```
https://integration.trueplay.io/api/v1/copystake/user
```

**Headers**

{% code overflow="wrap" %}

```
Content-Type: application/json
X-API-KEY: value issued by CopyStake
```

{% endcode %}

**Request**

| **Field** | **Type** | **Require** | **Description**  |
| --------- | -------- | ----------- | ---------------- |
| userId    | String   | mandatory   | operator user id |
| userType  | String   | mandatory   | TEST REGULAR     |

**Response success**

```
204 OK
```

<details>

<summary><strong>Request example</strong></summary>

{% code overflow="wrap" %}

```
curl -X 'PUT' \
  'https://integration.trueplay.io/api/v1/copystake/user' \
  -H 'accept: */*' \
  -H 'X-API-KEY: d922bd1c-e7ac-4c98-9b1a-baa724440b0e' \
  -H 'Content-Type: application/json' \
  -d '{
  "userId": "local_user",
  "userType": "TEST"
}'
```

{% endcode %}

</details>

**Response example**

{% tabs %}
{% tab title="204: OK Request is successful" %}

```
204 OK
```

{% endtab %}

{% tab title="404: Not Found " %}

```
404 Not Found
{
    "message": "User [operatorUserId] not found"
}
```

{% endtab %}
{% endtabs %}

## Copystake Reward URL

The reward endpoint allows CopyStake to notify the operator’s bonus engine about a reward granted to a user as part of gamified features like Jackpot, Wheel of Fortune, or Sign-up Reward.

<mark style="color:green;">`POST`</mark>&#x20;

```
https://{callback_url}/reward
```

**Headers**

{% code overflow="wrap" %}

```
Content-Type: application/json
X-API-KEY: value issued by Operator
X-REQUEST-SIGNATURE: value generated based on the request payload (secret key issued by CopyStake)
```

{% endcode %}

<table data-header-hidden><thead><tr><th width="135.86328125"></th><th width="100.93359375"></th><th width="146.078125"></th><th></th></tr></thead><tbody><tr><td><strong>Field</strong></td><td><strong>Type</strong></td><td><strong>Require</strong></td><td><strong>Description</strong></td></tr><tr><td>userId</td><td>String</td><td>mandatory</td><td>user identifier in the operator system</td></tr><tr><td>transactionId</td><td>String</td><td>mandatory</td><td>unique CopyStake transaction identifier</td></tr><tr><td>rewardType</td><td>String</td><td>mandatory</td><td>COPYSTAKE_REWARD<br>the reward type sent by CopyStake</td></tr><tr><td>eventType</td><td>String</td><td>mandatory</td><td>JACKPOT, WHEEL_OF_FORTUNE, SIGN_UP_REWARD<br>the event type sent by CopyStake</td></tr><tr><td>bonusType</td><td>String</td><td>mandatory</td><td>CASINO, SPORT<br>the bonus type sent by CopyStake to identify Operator’s bonus engine</td></tr><tr><td>wager</td><td>Integer</td><td>mandatory</td><td><p>0: the charge is made to the main user’s account</p><p>1 and greater: coefficient used to calculate the amount a user must wager to transfer winnings from the bonus user’s account to the main user’s account (e.g. wager=5 and amount=100, then after wagering the amount of 5*100, the user will receive a bonus of 100 to to the main user’s account)</p></td></tr><tr><td>bonusTtlDays</td><td>Integer</td><td>mandatory</td><td><p>0: in case the wager=0</p><p>1 and greater: number of days during which the user can wager the bonus</p></td></tr><tr><td>currency</td><td>String</td><td>mandatory</td><td>user's active currency ISO 4217</td></tr><tr><td>amount</td><td>Double</td><td>mandatory</td><td><ul><li>fiat: two decimal places (e.g., USD: 10.25)</li><li>cryptocurrency: the amount parameter adheres to a maximum of 8 decimal places</li></ul></td></tr></tbody></table>

<details>

<summary>Request example</summary>

```
curl --location '{callback_url}/reward' \
--header 'Content-Type: application/json' \
--header 'X-API-KEY: e58d1990-31b9-41f5-aa39-fd150643a8fe' \
--header 'X-REQUEST-SIGNATURE: FbS2341Uu0mSUAGw4LwF7GVBa7GT7drJXCvHkI+ORHJQ07uxVKAi3pA/0vB8/shlzX16x234FD==' \
--data '{
    "userId": "29e946a1-e801-4e27-952d-1d6ebbd19525",
    "transactionId": "01944be7-a260-7209-b16f-0d2d953e8c9f",
    "rewardType": "COPYSTAKE_REWARD",
    "eventType": "WHEEL_OF_FORTUNE",
    "bonusType": "CASINO",
    "wager": 0,
    "currency": "USD",
    "amount": 5.00
}'
```

</details>

{% tabs %}
{% tab title="200: OK Request is successful" %}

{% endtab %}

{% tab title="400 Bad Request" %}

```
{
    "errorCode": "ERR_REWARD_FORBIDDEN",
    "errorMessage": "user fraud detected"
}
```

{% endtab %}
{% endtabs %}

## **Error handling and retry mechanism**

CopyStake is very sensitive to the stability and speed of the operator's response and cannot allow long delays in response or a classic retry mechanism with several attempts. In the case of prolonged latency, CopyStack will **interrupt the game round** but keep the session active. If an error differs from those below, there will be **one retry attempt**.

**Response**

<table data-header-hidden><thead><tr><th width="150"></th><th width="96"></th><th width="116"></th><th></th></tr></thead><tbody><tr><td><strong>Field</strong></td><td><strong>Type</strong></td><td><strong>Require</strong></td><td><strong>Description</strong></td></tr><tr><td>errorCode</td><td>String</td><td>mandatory</td><td><p>ERR_UNKNOWN<br>General error status, for cases without a special error code.</p><p>ERR_INSUFFICIENT_FUNDS<br>Not enough money on the user's balance to process a transaction</p><p>ERR_DUPLICATE_TRANSACTION<br>A transaction with the same identifier was sent</p><p>ERR_USER_DISABLED<br>User is disabled/locked (cannot interact with user's balance)</p><p>ERR_INVALID_SIGNATURE<br>Operator couldn't verify the signature on request from</p></td></tr><tr><td>errorMessage</td><td>String</td><td>optional</td><td>more detailed description of the error</td></tr></tbody></table>

**Response example**

{% tabs %}
{% tab title="400: Bad Request " %}

```
400 Bad Request
{
    "errorCode": "ERR_INSUFFICIENT_FUNDS",
    "errorMessage": "Insufficient balance"
}
```

{% endtab %}
{% endtabs %}


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.trueplay.io/tech/copystake-integration.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
