> For the complete documentation index, see [llms.txt](https://docs.wynta.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.wynta.com/playerapi.md).

# Player Platform API

## Event Tracking API — Integration Guide

**Service:** PAM API Service **Version:** 0.1.0&#x20;

**Base URL:** `https://api.wynta.com`

Two endpoints are available:

| Method | Endpoint                  | Purpose                                                                      |
| ------ | ------------------------- | ---------------------------------------------------------------------------- |
| POST   | `/api/v1/events/track`    | Send one or more player events (e.g. login, deposit, bet placed).            |
| POST   | `/api/v1/events/identify` | Create or update a player profile (e.g. country, VIP level, account status). |

Both endpoints return `202 Accepted` once the payload is validated and queued.

***

### 1. Authentication

Send your Client ID and Client Secret as headers on every request. Each credential pair belongs to one brand, and every event and profile you send is stored under that brand.

| Header              | Required | Description                                                                                                                                                                   |
| ------------------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `Content-Type`      | Yes      | Must be `application/json`.                                                                                                                                                   |
| `X-Client-Id`       | Yes      | Your client identifier.                                                                                                                                                       |
| `X-Client-Secret`   | Yes      | Your client secret. Keep it server-side.                                                                                                                                      |
| `X-Idempotency-Key` | No       | Must be a UUID v4. If you repeat a request with the same key within 6 hours, you get the original response back and the request is not processed again. Use it when retrying. |

#### Authentication errors

| Status | Response body                                                                                       | Cause                                             |
| ------ | --------------------------------------------------------------------------------------------------- | ------------------------------------------------- |
| 401    | `{"detail": "Invalid credentials"}`                                                                 | The Client ID or Secret is wrong.                 |
| 401    | `{"detail": {"code": "invalid_token", "message": "Missing or malformed Authorization header"}}`     | `X-Client-Id` or `X-Client-Secret` is missing.    |
| 400    | `{"detail": {"code": "invalid_idempotency_key", "message": "X-Idempotency-Key must be a UUID v4"}}` | `X-Idempotency-Key` was sent but isn't a UUID v4. |

***

### 2. Track Events

**`POST /api/v1/events/track`**

Send a batch of 1–100 events. Each event is checked separately: valid events are accepted, and invalid events are rejected with error details.

#### Request body

| Field    | Type  | Required | Description                                      |
| -------- | ----- | -------- | ------------------------------------------------ |
| `events` | array | Yes      | Array of event objects. Maximum 100 per request. |

#### Event object

| Field         | Type          | Required | Description                                                                                                                                                                                          |
| ------------- | ------------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `event_id`    | string (UUID) | **Yes**  | Unique ID for this event, as a UUID (e.g. `3f6c1a2e-8b4d-4c7a-9e21-5d0b7f1a0001`). If the same `event_id` is sent again within 24 hours, the repeat is dropped, so a retry can't create a duplicate. |
| `user_id`     | string        | Yes      | Your unique player ID. Use the same value in every event for that player. Must not start with `anon` (see the note below).                                                                           |
| `event_name`  | string        | Yes      | Event name in snake\_case, e.g. `registration`, `login_success`, `deposit_success`, `bet_placed`. See Sample Events.                                                                                 |
| `timestamp`   | string        | Yes      | ISO 8601 time when the event happened, e.g. `2026-05-18T14:38:00.000Z`.                                                                                                                              |
| `session_id`  | string        | No       | Player session ID.                                                                                                                                                                                   |
| `device_type` | string        | No       | e.g. `mobile`, `tablet`, `desktop`.                                                                                                                                                                  |
| `platform`    | string        | No       | e.g. `ios`, `android`, `web`.                                                                                                                                                                        |
| `properties`  | object        | No       | Event-specific key-value data. All values must be JSON-serialisable.                                                                                                                                 |

> **Note:** Events whose `user_id` starts with `anon` are ignored. They are not stored, and they are counted as neither accepted nor rejected.

#### Response — `202 Accepted`

| Field      | Type    | Description                                                                  |
| ---------- | ------- | ---------------------------------------------------------------------------- |
| `accepted` | integer | Number of events that were queued.                                           |
| `rejected` | integer | Number of events that were rejected.                                         |
| `errors`   | array   | One entry per rejected event (see below). Empty if every event was accepted. |

**Error entry:**

| Field      | Type           | Description                                                        |
| ---------- | -------------- | ------------------------------------------------------------------ |
| `index`    | integer        | Position of the failed event in the `events` array, starting at 0. |
| `event_id` | string \| null | The event's `event_id`, if one was sent.                           |
| `code`     | string         | `missing_required`, `invalid_type` or `rate_limited` (see below).  |
| `message`  | string         | Description of the error.                                          |

| Code               | Meaning                                                                                                    |
| ------------------ | ---------------------------------------------------------------------------------------------------------- |
| `missing_required` | A required field is missing, e.g. `Missing required field: event_id`.                                      |
| `invalid_type`     | A field has the wrong type or format, e.g. `event_id` is not a valid UUID, or `timestamp` is not ISO 8601. |
| `rate_limited`     | More than 100 events for this player in the current minute. Retry the event later.                         |

```json
{
  "accepted": 1,
  "rejected": 1,
  "errors": [
    {
      "index": 1,
      "event_id": null,
      "code": "missing_required",
      "message": "Missing required field: event_id"
    }
  ]
}
```

> **Note:** A `202` response doesn't mean every event was accepted. Always check `rejected` and `errors`, and retry the failed events.

#### Status codes

| Status | Meaning                                                                     |
| ------ | --------------------------------------------------------------------------- |
| 202    | Request processed. Check `accepted` and `rejected` in the response body.    |
| 400    | More than 100 events in one request (`payload_too_large`).                  |
| 401    | Authentication failed. See Authentication errors.                           |
| 422    | The request body is malformed, e.g. `events` is missing or is not an array. |
| 503    | Temporary server-side problem. Retry the whole request with backoff.        |

***

### 3. Identify Player

**`POST /api/v1/events/identify`**

Create or update a player's profile traits. Call it at registration and whenever traits change, for example after KYC approval or a VIP tier change. New traits are merged into the existing profile. To remove a trait, list its name in `unset_traits`.

#### Request body

| Field          | Type   | Required | Description                                                                                                     |
| -------------- | ------ | -------- | --------------------------------------------------------------------------------------------------------------- |
| `user_id`      | string | Yes      | Your unique player ID. Must match the `user_id` you send in track events.                                       |
| `timestamp`    | string | Yes      | ISO 8601 time of the identify call.                                                                             |
| `traits`       | object | No       | Traits to set or update. Traits you leave out stay unchanged.                                                   |
| `unset_traits` | array  | No       | Names of traits to remove. A name can't appear in both `traits` and `unset_traits`.                             |
| `anonymous_id` | string | No       | ID the player had before login or registration.                                                                 |
| `device`       | object | No       | Push device registration: `{"token": "<push_token>", "platform": "android"}`. `platform` defaults to `android`. |

**Full request body:**

```json
{
  "user_id": "ply_776192",
  "timestamp": "2026-05-18T14:38:00.000Z",
  "traits": {
    "first_name": "Alex",
    "last_name": "Smith",
    "date_of_birth": "1990-04-15",
    "country": "MT",
    "currency": "EUR",
    "language": "en",
    "kyc_status": "pending",
    "vip_level": "bronze",
    "account_status": "active",
    "registration_date": "2026-05-18T14:38:00.000Z"
  },
  "unset_traits": ["promo_segment"],
  "device": { "token": "<push_token>", "platform": "android" }
}
```

#### Common traits

| Trait                      | Type   | Description                                            |
| -------------------------- | ------ | ------------------------------------------------------ |
| `email`                    | string | Email address.                                         |
| `phone`                    | string | Phone number in E.164 format.                          |
| `first_name` / `last_name` | string | Player's name.                                         |
| `date_of_birth`            | string | `YYYY-MM-DD`.                                          |
| `country`                  | string | ISO 3166-1 alpha-2 code, e.g. `MT`, `GB`.              |
| `currency`                 | string | Main currency, e.g. `EUR`.                             |
| `language`                 | string | Language code, e.g. `en`.                              |
| `kyc_status`               | string | e.g. `pending`, `approved`, `rejected`.                |
| `vip_level`                | string | e.g. `bronze`, `silver`, `gold`, `platinum`.           |
| `account_status`           | string | e.g. `active`, `suspended`, `self_excluded`, `closed`. |
| `registration_date`        | string | ISO 8601 registration time.                            |

#### Response — `202 Accepted`

```json
{ "user_id": "ply_776192" }
```

#### Status codes

| Status | Meaning                                                                                                        |
| ------ | -------------------------------------------------------------------------------------------------------------- |
| 202    | Profile updated.                                                                                               |
| 400    | The same key appears in both `traits` and `unset_traits` (`invalid_request`).                                  |
| 401    | Authentication failed. See Authentication errors.                                                              |
| 422    | The request body is malformed, e.g. `user_id` or `timestamp` is missing.                                       |
| 429    | More than 100 requests for this player in the current minute (`rate_limited`). Retry with exponential backoff. |
| 503    | Temporary server-side problem. Retry with backoff.                                                             |

***

### 4. Sample Events

Examples of common player events. Put the event's details inside `properties`. Send money amounts as strings with up to 4 decimal places, e.g. `"100.0000"`.

| Event             | When to send it                  |
| ----------------- | -------------------------------- |
| `registration`    | A new player account is created. |
| `login_success`   | A player logs in successfully.   |
| `deposit_success` | A deposit is completed.          |
| `bet_placed`      | A wager is accepted.             |

#### `registration`

| Property              | Type   | Required | Description                          |
| --------------------- | ------ | -------- | ------------------------------------ |
| `registration_method` | string | Yes      | `email`, `phone`, `google`, `apple`. |
| `referral_code`       | string | No       | Referral code used at sign-up.       |
| `promo_code`          | string | No       | Promo code used at sign-up.          |
| `ip`                  | string | No       | Player IP address.                   |

```json
{
  "event_id": "3f6c1a2e-8b4d-4c7a-9e21-5d0b7f1a0010",
  "user_id": "ply_776192",
  "event_name": "registration",
  "timestamp": "2026-05-18T14:38:00.000Z",
  "session_id": "sess_abc12398",
  "properties": { "registration_method": "email", "referral_code": "REF_XYZ99", "promo_code": "WELCOME100" }
}
```

#### `login_success`

| Property       | Type   | Required | Description                               |
| -------------- | ------ | -------- | ----------------------------------------- |
| `login_method` | string | Yes      | `email`, `phone`, `google`, `apple`, etc. |
| `ip`           | string | No       | Player IP address.                        |

```json
{
  "event_id": "3f6c1a2e-8b4d-4c7a-9e21-5d0b7f1a0011",
  "user_id": "ply_776192",
  "event_name": "login_success",
  "timestamp": "2026-05-18T15:00:00.000Z",
  "session_id": "sess_abc12398",
  "properties": { "login_method": "email" }
}
```

#### `deposit_success`

Set `is_ftd` to `true` on the player's first deposit. First-deposit bonuses depend on it.

| Property         | Type           | Required | Description                                         |
| ---------------- | -------------- | -------- | --------------------------------------------------- |
| `order_id`       | string         | Yes      | Your deposit/order ID. Must be unique per deposit.  |
| `amount`         | decimal string | Yes      | Settled amount, up to 4 decimal places.             |
| `currency`       | string         | Yes      | ISO 4217 code, e.g. `EUR`.                          |
| `payment_method` | string         | Yes      | e.g. `card`, `upi`, `netbanking`, `wallet`.         |
| `is_ftd`         | boolean        | Yes      | `true` if this is the player's first deposit.       |
| `deposit_count`  | integer        | No       | Total deposits for this player, including this one. |
| `promo_code`     | string         | No       | Promo code applied to this deposit.                 |

```json
{
  "event_id": "3f6c1a2e-8b4d-4c7a-9e21-5d0b7f1a0013",
  "user_id": "ply_776192",
  "event_name": "deposit_success",
  "timestamp": "2026-05-18T15:10:00.000Z",
  "session_id": "sess_abc12398",
  "properties": {
    "order_id": "dep_882001",
    "amount": "100.0000",
    "currency": "EUR",
    "payment_method": "card",
    "is_ftd": true,
    "deposit_count": 1,
    "promo_code": "WELCOME100"
  }
}
```

#### `bet_placed`

| Property                   | Type           | Required | Description                                          |
| -------------------------- | -------------- | -------- | ---------------------------------------------------- |
| `transaction_amount`       | decimal string | Yes      | Stake, up to 4 decimal places.                       |
| `bonus_amount`             | decimal string | Yes      | Bonus funds used in this bet (`"0.0000"` if none).   |
| `chip_type`                | string         | Yes      | Wallet used: `CASH`, `LOYALTY_PINTS` or `FUN_CHIPS`. |
| `wager_tnx_id`             | string         | No       | Your wager transaction reference.                    |
| `session_key`              | string         | No       | Client session key at the time of the bet.           |
| `platform_client_id`       | string         | No       | Your platform's client identifier.                   |
| `product`                  | string         | No       | `RUMMY`, `FANTASY`, `POKER`, etc.                    |
| `game_type`                | string         | No       | `GAME`, `TOURNEY` or `CONTEST`.                      |
| `game_variant`             | string         | No       | e.g. `CRICKET`, `FOOTBALL`, `holdem`.                |
| `game_name`                | string         | No       | Table or tournament name, e.g. `Friday Holdem`.      |
| `game_action`              | string         | No       | e.g. `JOIN_TABLE`, `REGISTER_TOURNY`.                |
| `primary_transaction_id`   | integer        | No       | Primary platform transaction ID.                     |
| `secondary_transaction_id` | integer        | No       | Secondary platform transaction ID.                   |
| `tertiary_transaction_id`  | integer        | No       | Tertiary platform transaction ID.                    |
| `base_request_id`          | integer        | No       | Base request ID from your platform.                  |

```json
{
  "event_id": "3f6c1a2e-8b4d-4c7a-9e21-5d0b7f1a0014",
  "user_id": "ply_776192",
  "event_name": "bet_placed",
  "timestamp": "2026-05-18T15:16:00.000Z",
  "session_id": "sess_abc12398",
  "properties": {
    "transaction_amount": "2.0000",
    "bonus_amount": "0.0000",
    "chip_type": "CASH",
    "wager_tnx_id": "WAGER_REF_001",
    "product": "POKER",
    "game_type": "TOURNEY",
    "game_variant": "holdem",
    "game_name": "Friday Holdem",
    "game_action": "REGISTER_TOURNY"
  }
}
```

***

### 5. Sample Requests

#### Track events — cURL

```bash
curl -X POST "https://api.wynta.com/api/v1/events/track" \
  -H "Content-Type: application/json" \
  -H "X-Client-Id: <your_client_id>" \
  -H "X-Client-Secret: <your_client_secret>" \
  -H "X-Idempotency-Key: 9b1c2f4e-7a3d-4e8b-b5c6-1d2e3f4a5b6c" \
  -d '{
    "events": [
      {
        "event_id": "3f6c1a2e-8b4d-4c7a-9e21-5d0b7f1a0001",
        "user_id": "ply_776192",
        "session_id": "sess_abc12398",
        "event_name": "deposit_success",
        "timestamp": "2026-05-18T15:10:00.000Z",
        "device_type": "mobile",
        "platform": "android",
        "properties": { "order_id": "dep_882001", "amount": "100.0000", "currency": "EUR", "payment_method": "card", "is_ftd": true }
      },
      {
        "event_id": "3f6c1a2e-8b4d-4c7a-9e21-5d0b7f1a0002",
        "user_id": "ply_776192",
        "session_id": "sess_abc12398",
        "event_name": "bet_placed",
        "timestamp": "2026-05-18T15:16:00.000Z",
        "device_type": "mobile",
        "platform": "android",
        "properties": { "transaction_amount": "2.0000", "bonus_amount": "0.0000", "chip_type": "CASH", "product": "POKER" }
      }
    ]
  }'
```

#### Identify player — cURL (new registration)

```bash
curl -X POST "https://api.wynta.com/api/v1/events/identify" \
  -H "Content-Type: application/json" \
  -H "X-Client-Id: <your_client_id>" \
  -H "X-Client-Secret: <your_client_secret>" \
  -d '{
    "user_id": "ply_776192",
    "timestamp": "2026-05-18T14:38:00.000Z",
    "traits": {
      "first_name": "Alex",
      "last_name": "Smith",
      "date_of_birth": "1990-04-15",
      "country": "MT",
      "currency": "EUR",
      "language": "en",
      "kyc_status": "pending",
      "vip_level": "bronze",
      "account_status": "active",
      "registration_date": "2026-05-18T14:38:00.000Z"
    }
  }'
```

#### Identify player — cURL (VIP upgrade and remove a trait)

```bash
curl -X POST "https://api.wynta.com/api/v1/events/identify" \
  -H "Content-Type: application/json" \
  -H "X-Client-Id: <your_client_id>" \
  -H "X-Client-Secret: <your_client_secret>" \
  -d '{
    "user_id": "ply_776192",
    "timestamp": "2026-05-18T20:00:00.000Z",
    "traits": { "vip_level": "platinum", "kyc_status": "approved" },
    "unset_traits": ["anonymous_segment"]
  }'
```

***

### 6. Integration Tips

* **Identify before you track.** Call identify at registration so the player's profile exists before their events arrive.
* **Send events in batches.** Send 10–50 events per request. The maximum is 100.
* **Send the real event time in `timestamp`.** Reports use this value, not the time we receive the event, so retried or delayed events still land at the right time.
* **Generate a new UUID for each `event_id`, and keep it when you retry.** A resent event with the same `event_id` is dropped if it arrives within 24 hours.
* **Validation error format (422):**

```json
{
  "detail": [
    { "type": "missing", "loc": ["body", "user_id"], "msg": "Field required", "input": {} }
  ]
}
```
