## Conventions
- Base URL: {url}
- Auth: Bearer token in Authorization header (see Authentication)
- Versioning: v1
- Placeholders:
- {url}: base API origin
- {access_token}: token from token generation endpoint
- {member_id}: unique member identifier (e.g., gr_70001)
- {amount}: positive integer hearts to credit or debit
- {reason}: short audit text
- {bit_id}: client correlation ID for the request
- {server_bit_id}: server authoritative transaction ID
- {new_balance}: integer balance after operation
- {occurred_at_iso8601}: UTC timestampx, e.g., 2025-09-04T12:00:00Z
## Authentication – Generate Token
- Method: POST
- URL: {url}/cdn/v1/generate/token
- Body: none
- Required headers:
- client_id: {client_id}
- oneapp_token: {oneapp_token}
#### cURL
```bash
curl -X POST '{url}/cdn/v1/generate/token' \
-H 'client_id: {client_id}' \
-H 'oneapp_token: {oneapp_token}'
```
#### Success response
```json
{
"access_token": "{access_token}",
"expires_in": {expires_in_seconds},
"refresh_expires_in": {refresh_expires_in_seconds},
"refresh_token": {refresh_token_or_null},
"token_type": "{token_type}",
"id_token": "{id_token}",
"not-before-policy": {not_before_epoch_seconds},
"session_state": {session_state_or_null},
"scope": "{scope}"
}
```
#### Status codes
| HTTP | Meaning |
|---|---|
| 200 | Token issued; use access_token until expires_in |
| 400 | Missing/invalid headers (client_id/oneapp_token) |
| 401 | Invalid credentials |
| 403 | Client recognized but not permitted to generate tokens |
| 429 | Rate limited |
| 500 | Server error |
## Balance Check
- Method: GET
- URL: {url}/v1/members/{member_id}/balances
- Required headers:
- Authorization: Bearer {access_token}
- Content-Type: application/json
#### cURL
```bash
curl -X GET '{url}/v1/members/{member_id}/balances' \
-H 'Authorization: Bearer {access_token}' \
-H 'Content-Type: application/json'
```
#### Success response
```json
{
"success": true,
"message": "Balance retrieved.",
"data": {
"memberId": "{member_id}",
"balances": {
"hearts": {hearts_balance}
}
}
}
```
#### Error response (example)
```json
{
"success": false,
"error": {
"code": "MEMBER_NOT_FOUND",
"httpStatus": 404,
"message": "Member not found."
}
}
```
#### Status codes
| HTTP | Meaning |
|---|---|
| 200 | Balance returned |
| 400 | Bad path parameter (member_id) |
| 401 | Missing/invalid Authorization |
| 404 | Member not found |
| 500 | Server error |
## Give Hearts (Credit)
- Method: POST
- URL: {url}/v1/hearts/credits
- Required headers:
- Authorization: Bearer {access_token}
- Content-Type: application/json
- Request body:
- memberId: {member_id}
- amount: {amount} (positive integer)
- reason: {reason}
- bitId: {bit_id}
- metadata: optional object (e.g., {"source":"{source}"})
#### cURL
```bash
curl -X POST '{url}/v1/hearts/credits' \
-H 'Authorization: Bearer {access_token}' \
-H 'Content-Type: application/json' \
-d '{
"memberId": "{member_id}",
"amount": {amount},
"reason": "{reason}",
"bitId": "{bit_id}",
"metadata": { "source": "{source}" }
}'
```
#### Success response
```json
{
"success": true,
"message": "Hearts credited.",
"data": {
"bitId": "{server_bit_id}",
"memberId": "{member_id}",
"transaction": {
"type": "credit",
"amount": {amount},
"heartsAdded": {amount},
"heartsDeducted": 0,
"newBalance": {new_balance},
"occurredAt": "{occurred_at_iso8601}"
}
}
}
```
#### Error response (examples)
- Validation
```json
{
"success": false,
"error": {
"code": "VALIDATION_ERROR",
"httpStatus": 400,
"message": "amount must be a positive integer."
}
}
```
- Authorization
```json
{
"success": false,
"error": {
"code": "UNAUTHORIZED",
"httpStatus": 401,
"message": "Missing or invalid token."
}
}
```
#### Status codes
| HTTP | Meaning |
|---|---|
| 200 | Credit applied |
| 400 | Validation error (e.g., amount <= 0, malformed JSON) |
| 401 | Missing/invalid Authorization |
| 404 | Member not found |
| 500 | Server error |
## Deduct Hearts (Debit)
- Method: POST
- URL: {url}/v1/hearts/debits
- Required headers:
- Authorization: Bearer {access_token}
- Content-Type: application/json
- Request body:
- memberId: {member_id}
- amount: {amount} (positive integer; must be <= available balance)
- reason: {reason}
- bitId: {bit_id}
- metadata: optional object (e.g., {"source":"{source}"})
#### cURL
```bash
curl -X POST '{url}/v1/hearts/debits' \
-H 'Authorization: Bearer {access_token}' \
-H 'Content-Type: application/json' \
-d '{
"memberId": "{member_id}",
"amount": {amount},
"reason": "{reason}",
"bitId": "{bit_id}",
"metadata": { "source": "{source}" }
}'
```
#### Success response
```json
{
"success": true,
"message": "Hearts debited.",
"data": {
"bitId": "{server_bit_id}",
"memberId": "{member_id}",
"transaction": {
"type": "debit",
"amount": {amount},
"heartsAdded": 0,
"heartsDeducted": {amount},
"newBalance": {new_balance},
"occurredAt": "{occurred_at_iso8601}"
}
}
}
```
#### Error response (examples)
- Business rule
```json
{
"success": false,
"error": {
"code": "INSUFFICIENT_BALANCE",
"httpStatus": 400,
"message": "Insufficient balance."
}
}
```
- Validation
```json
{
"success": false,
"error": {
"code": "VALIDATION_ERROR",
"httpStatus": 400,
"message": "amount must be a positive integer."
}
}
```
#### Status codes
| HTTP | Meaning |
|---|---|
| 200 | Debit applied |
| 400 | Validation/business error (e.g., insufficient balance) |
| 401 | Missing/invalid Authorization |
| 404 | Member not found |
| 500 | Server error |
### Validation Rules
- amount must be a positive integer for both credit and debit.
- Debit amount must not exceed current hearts balance.
- bitId is a client correlation ID; the server returns an authoritative bitId in data.bitId.
- memberId must identify an existing member.