# API V2

### Authentication & Intents

* **API Token:** All endpoints (except `/healthy`) require an API token in the request header:
  * `TOKEN: <your-api-token>`
* **Intents:** Each endpoint requires specific intents. If your token lacks the required intent, you will receive an error response with a list of accepted intents.

***

### Error Handling

All error responses follow this structure:

```json
{
  "message": "invalid token or missing required intent",
  "accepted_intents": ["PRODUCTS_READ", "PRODUCTS_READ_WRITE", ...]
}
```

* Always check the `message` and `accepted_intents` fields to understand why a request failed.

***

### Update (PUT) Operations Warning

**Important:**

> For every update (PUT) call, always perform a GET first and pass all fields (including unchanged ones) in your PUT request. Fields not included in the PUT body may be lost.

***

### Endpoints

#### 1. Health & Ping

**`GET /api/v2/healthy`**

* **Description:** Health check endpoint. No authentication required.
* **Response:**
  * `200 OK` — `"healthy"`

**`GET /api/v2/ping`**

* **Description:** Ping endpoint to test authentication.
* **Headers:**
  * `TOKEN: <api-token>`
* **Response:**
  * `200 OK` — `"pong"`
  * `401 Unauthorized` — Error response

***

#### 2. Product API

**`GET /api/v2/products`**

* **Intents:** `PRODUCTS_READ`, `PRODUCTS_READ_WRITE`, `ALL_READ`, `ALL_READ_WRITE`
* **Response:** List of products

**`GET /api/v2/products/{id}`**

* **Intents:** Same as above
* **Response:** Product object

**`POST /api/v2/products`**

* **Intents:** `PRODUCTS_READ_WRITE`, `ALL_READ_WRITE`
* **Body:**

```json
{
  "name": "Test Product",
  "description": "1",
  "productType": "OTHER",
  "productTypeOther": "Test Type",
  "defaultMaxIPs": 4,
  "defaultMaxHWIDs": 3
}
```

* **Note:** Do not include `id`, `createdDate`, or `updatedDate`.

**`PUT /api/v2/products/{id}`**

* **Intents:** Same as POST
* **Body:** All product fields (see GET response)
* **Warning:** See update warning above.

**`DELETE /api/v2/products/{id}`**

* **Intents:** Same as POST

**Sample Product Response:**

```json
{
  "id": 602,
  "name": "Test Product",
  "description": "1",
  "productType": "OTHER",
  "productTypeOther": "Test Type",
  "createdDate": "2024-11-16T09:54:23.048+00:00",
  "updatedDate": "2024-11-16T09:54:23.048+00:00",
  "defaultMaxIPs": 4,
  "defaultMaxHWIDs": 3,
  "roleId": null,
  "purchaseRoleId": null,
  "url": null,
  "imageUrl": null
}
```

***

#### 3. License API

**`GET /api/v2/licenses`**

* **Intents:** `LICENSES_READ`, `LICENSES_READ_WRITE`, `ALL_READ`, `ALL_READ_WRITE`

**`GET /api/v2/licenses/{id}`**

* **Intents:** Same as above

**`POST /api/v2/licenses`**

* **Intents:** `LICENSES_READ_WRITE`, `ALL_READ_WRITE`
* **Body:**

```json
{
  "productId": 1552,
  "licenseType": "TEMPORARY",
  "licensePlatform": null,
  "licensePlatformOther": "",
  "ownerPlatformId": "",
  "expiryDate": "2025-05-31T18:30:00.000+00:00",
  "maxIPs": 5,
  "maxHWIDs": 5,
  "ownerDiscordId": "754054589690544250",
  "ownerDiscordUsername": "kaxun",
  "notes": ""
}
```

* **Note:** Do not include `id`, `createdDate`, `updatedDate`, `customer`, or `licenseKey` (license key is generated automatically).

**`GET /api/v2/licenses/by-license-key/{licenseKey}`**

* **Intents:** Same as above
* **Description:** Get a single license by its license key
* **Response:** License object or 404 if not found

**`GET /api/v2/licenses/by-customer-email/{email}`**

* **Intents:** Same as above
* **Description:** Get all licenses associated with a customer email address
* **Response:** Array of license objects

**`GET /api/v2/licenses/by-platform/{platform}/{platformId}`**

* **Intents:** Same as above
* **Description:** Get all licenses for a specific platform and platform ID
* **Query Parameters:**
  * `productId` (optional): Filter by product ID
* **Path Parameters:**
  * `platform`: One of SPIGOT, FORGE, BUKKIT, BUILT\_BY\_BIT, SOURCE\_EXCHANGE, DISCORD, OTHER
  * `platformId`: The platform-specific user ID
* **Response:** Array of license objects

**`GET /api/v2/licenses/by-customer-id/{customerId}`**

* **Intents:** Same as above
* **Description:** Get all licenses for a specific customer ID
* **Response:** Array of license objects

**`GET /api/v2/licenses/by-product-id/{productId}`**

* **Intents:** Same as above
* **Description:** Get all licenses for a specific product
* **Response:** Array of license objects

**`GET /api/v2/licenses/by-discord-id/{discordId}`**

* **Intents:** Same as above
* **Description:** Get all licenses associated with a Discord user ID
* **Response:** Array of license objects

**`PUT /api/v2/licenses/{id}`**

* **Intents:** Same as POST
* **Body:** All license fields (see GET response)
* **Warning:** See update warning above.

**`DELETE /api/v2/licenses/{id}`**

* **Intents:** Same as POST

**Sample License Response:**

```json
{
  "id": 4353,
  "licenseKey": "U12BPHB1VO1CB73N773B",
  "productId": 1552,
  "licenseType": "TEMPORARY",
  "licensePlatform": null,
  "licensePlatformOther": "",
  "ownerPlatformId": "",
  "createdDate": "2025-06-15T15:00:37.243+00:00",
  "updatedDate": "2025-06-23T05:31:36.707+00:00",
  "expiryDate": "2025-05-31T18:30:00.000+00:00",
  "maxIPs": 5,
  "maxHWIDs": 5,
  "totalRequests": null,
  "ownerDiscordId": "754054589690544250",
  "ownerDiscordUsername": "kaxun",
  "licenseStatus": "EXPIRED",
  "hwids": [],
  "ips": [],
  "notes": "",
  "customer": { ... },
  "productIdString": "1552"
}
```

***

#### 4. Blacklist API

**`GET /api/v2/blacklists`**

* **Intents:** `BLACKLISTS_READ`, `BLACKLISTS_READ_WRITE`, `ALL_READ`, `ALL_READ_WRITE`

**`GET /api/v2/blacklists/{id}`**

* **Intents:** Same as above

**`POST /api/v2/blacklists`**

* **Intents:** `BLACKLISTS_READ_WRITE`, `ALL_READ_WRITE`
* **Body:**

```json
{
  "licenseDataType": "HWID",
  "data": "unique-hardware-id3",
  "isForAllProducts": true,
  "reason": "x"
}
```

* **Note:** Do not include `id`, `createdAt`, or `updatedAt`.

**`PUT /api/v2/blacklists/{id}`**

* **Intents:** Same as POST
* **Body:** All blacklist fields (see GET response)
* **Warning:** See update warning above.

**`DELETE /api/v2/blacklists/{id}`**

* **Intents:** Same as POST

**Sample Blacklist Response:**

```json
{
  "id": 1,
  "licenseDataType": "HWID",
  "data": "unique-hardware-id3",
  "isForAllProducts": true,
  "productId": null,
  "reason": "x",
  "createdAt": "2025-02-13T07:41:53.226+00:00",
  "updatedAt": "2025-02-13T07:41:53.226+00:00"
}
```

***

#### 5. License Request API

**`GET /api/v2/requests`**

* **Intents:** `REQUESTS_READ`, `ALL_READ`, `ALL_READ_WRITE`

**`GET /api/v2/requests/{id}`**

* **Intents:** Same as above

**Sample License Request Response:**

```json
{
  "id": 802,
  "licenseId": null,
  "licenseKey": "FE72URFX4RKWM38T0ARO",
  "productId": 602,
  "productVersion": "1.0.0",
  "ip": "0:0:0:0:0:0:0:1",
  "hwid": "unique-hardware-id7",
  "macAddress": "00:1A:2B:3C:4D:5E",
  "operatingSystem": "Windows",
  "operatingSystemVersion": "10",
  "operatingSystemArchitecture": "x64",
  "javaVersion": "17",
  "requestDate": "2025-01-27T17:28:13.173+00:00",
  "requestType": "Valid",
  "responseType": "Success",
  "blockReason": null,
  "notes": null,
  "remarks": null,
  "valid": true,
  "successful": true,
  "rateLimited": false,
  "blocked": false,
  "spam": false
}
```

***

#### 6. Customer API

**`GET /api/v2/customers`**

* **Intents:** `CUSTOMERS_READ`, `ALL_READ`, `ALL_READ_WRITE`

**`GET /api/v2/customers/{id}`**

* **Intents:** Same as above

**Sample Customer Response:**

```json
{
  "id": 503,
  "firstName": null,
  "lastName": null,
  "username": "chinezu_1221",
  "email": null,
  "phoneNumber": null,
  "profilePictureUrl": "https://cdn.discordapp.com/avatars/1203762369625464832/a_7a39928788f5ee51dd60b53a8f04ed4a.gif",
  "passwordHash": null,
  "role": null,
  "status": null,
  "createdAt": null,
  "lastLoginAt": null,
  "discordId": 1203762369625464832,
  "discordUsername": "chinezu_1221",
  "discordAvatarUrl": null,
  "discordAccessToken": null,
  "discordRefreshToken": null,
  "discordTokenExpiry": null,
  "spigotId": null,
  "spigotUsername": null,
  "forgeId": null,
  "forgeUsername": null,
  "bukkitId": null,
  "bukkitUsername": null,
  "builtByBitId": "477048",
  "builtByBitUsername": null,
  "sourceExchangeId": null,
  "sourceExchangeUsername": null,
  "ploymartId": null,
  "ploymartUsername": null,
  "modrinthId": null,
  "modrinthUsername": null,
  "curseforgeId": null,
  "curseforgeUsername": null,
  "notes": null
}
```

***

#### 7. License Validation API

**`POST /api/v2/validate`**

* **Intents:** `LICENSE_VALIDATE`, `ALL_READ_WRITE`
* **Body:** See v1 LicenseRequestController for request structure.
* **Response:** Same as v1 LicenseRequestController.

***

### Enum Values Used in API Models

#### ProductType (Product)

* `MINECRAFT_JAVA_PLUGIN`
* `MINECRAFT_JAVA_MOD`
* `MINECRAFT_BEDROCK_PLUGIN`
* `MINECRAFT_BEDROCK_MOD`
* `MINECRAFT_SERVER_SETUP`
* `WEB_APPLICATION`
* `DESKTOP_APPLICATION`
* `MOBILE_APPLICATION`
* `OTHER`

#### LicenseType (License)

* `TEMPORARY`
* `PERMANENT`

#### LicenseStatus (License)

* `ACTIVE`
* `EXPIRED`
* `DEACTIVATED`
* `DELETED`

#### LicensePlatform (License)

* `SPIGOT`
* `FORGE`
* `BUKKIT`
* `BUILT_BY_BIT`
* `SOURCE_EXCHANGE`
* `DISCORD`
* `OTHER`

#### LicenseDataType (Blacklist)

* `IP`
* `HWID`

#### ApiIntent (Token/Permissions)

* `PRODUCTS_READ`
* `PRODUCTS_READ_WRITE`
* `LICENSE_VALIDATE`
* `LICENSES_READ`
* `LICENSES_READ_WRITE`
* `BLACKLISTS_READ`
* `BLACKLISTS_READ_WRITE`
* `REQUESTS_READ`
* `CUSTOMERS_READ`
* `ALL_READ`
* `ALL_READ_WRITE`

***

### General Notes

* Always include the `TOKEN` header for all endpoints except `/healthy`.
* For all update (PUT) operations, always fetch the current object first and send all fields in your update request.
* Error responses will always include a `message` and a list of `accepted_intents`.
* Do not send fields like `id`, `createdDate`, `updatedDate`, or system-generated fields when creating new objects.
* For creating licenses, do not send the license key or customer object; these are handled by the system.

***

For further details or questions, contact the SunLicense [support team](https://dsc.gg/sundevs).


---

# 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.sunlicense.hapangama.com/api-documentation/api-v2.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.
