Coins (Hubble Managed)
Complete coin management system for managing digital coin wallets, transactions, and rewards.
Hubble’s managed coin system provides a complete solution for your digital coin wallets. Credit coins to users, debit coins for purchases, and track all transactions with full audit trails.
This is ideal if you don’t have your own in-app currency infrastructure and want to quickly implement a robust currency system.
Base URLs
| Environment | Base URL |
|---|---|
| Development | https://api.dev.myhubble.money |
| Production | https://api.myhubble.money |
Authentication
All API requests require authentication using a Bearer token obtained from the login API.
Required Headers:
| Header | Description | Required |
|---|---|---|
Authorization | Bearer token obtained from login API | Yes |
Content-Type | application/json | Yes |
X-Request-Id | Request tracking ID | Recommended |
Endpoints
1. Get Coin Balance
Retrieve the coin balance for a specific user.
HTTP Method: GET
Endpoint: /v1/partners/coins/{userId}/balance
Path Parameters:
| Parameter | Type | Description |
|---|---|---|
userId | string | Your unique user identifier |
Response:
Status Code: 200 OK
{ "userId": "USR-001", "available": 1500.00, "held": 0.00, "consumed": 500.00, "expired": 100.00, "total": 1500.00}Response Fields:
| Field | Type | Description |
|---|---|---|
userId | string | User identifier |
available | decimal | Coins available for use |
held | decimal | Coins reserved (for future use) |
consumed | decimal | Total coins spent |
expired | decimal | Total coins expired |
total | decimal | Sum of available + held |
2. Credit Coins
Credit coins to a user’s wallet.
HTTP Method: POST
Endpoint: /v1/partners/coins/credit
Request Body:
{ "userId": "USR-001", "idempotencyKey": "REWARD-2025-Q1-001", "amount": 500.00, "remarks": "Q1 Performance Bonus", "expiresOn": "2025-12-31"}Request Fields:
| Field | Type | Required | Description |
|---|---|---|---|
userId | string | Yes | Your unique user identifier |
idempotencyKey | string | Yes | Unique key to prevent duplicates |
amount | decimal | Yes | Coins to credit (> 0) |
remarks | string | No | Transaction notes |
expiresOn | date | No | Expiry date (YYYY-MM-DD). Uses default if not provided |
Response:
Status Code: 201 Created
{ "transactionId": "01HKXYZ123ABC456DEF789GHI", "userId": "USR-001", "type": "CREDIT", "status": "SUCCESS", "amount": 500.00, "remarks": "Q1 Performance Bonus", "transactedAt": "2025-01-16T10:30:00"}3. Bulk Credit Coins
Credit coins to multiple users in a single request. Maximum 100 credits per request.
HTTP Method: POST
Endpoint: /v1/partners/coins/bulk-credit
Request Body:
{ "credits": [ { "userId": "USR-001", "idempotencyKey": "WELCOME-2025-001", "amount": 100.00, "remarks": "Welcome bonus", "expiresOn": "2025-12-31" }, { "userId": "USR-002", "idempotencyKey": "WELCOME-2025-002", "amount": 150.00, "remarks": "Referral reward" } ]}Request Fields:
| Field | Type | Required | Description |
|---|---|---|---|
credits | array | Yes | List of credit operations (max 100) |
credits[].userId | string | Yes | Your unique user identifier |
credits[].idempotencyKey | string | Yes | Unique key per credit |
credits[].amount | decimal | Yes | Coins to credit (> 0) |
credits[].remarks | string | No | Transaction notes |
credits[].expiresOn | date | No | Expiry date for this credit |
Response:
Status Code: 200 OK
{ "totalOperations": 2, "successfulOperations": 2, "results": [ { "transactionId": "01HKXYZ123ABC456DEF789GHI", "userId": "USR-001", "type": "CREDIT", "status": "SUCCESS", "amount": 100.00, "remarks": "Welcome bonus", "transactedAt": "2025-01-16T10:30:00" }, { "transactionId": "01HKXYZ789XYZ012PQR345STU", "userId": "USR-002", "type": "CREDIT", "status": "SUCCESS", "amount": 150.00, "remarks": "Referral reward", "transactedAt": "2025-01-16T10:30:01" } ], "failedOperations": []}Partial Failure Response:
{ "totalOperations": 3, "successfulOperations": 2, "results": [...], "failedOperations": [ { "idempotencyKey": "WELCOME-2025-003", "userId": "USR-003", "error": "Credit amount 15000.00 exceeds maximum allowed 10000.00" } ]}4. Debit Coins
Debit coins from a user’s wallet. Coins are consumed in FIFO order (oldest expiring coins first).
HTTP Method: POST
Endpoint: /v1/partners/coins/debit
Request Body:
{ "userId": "USR-001", "idempotencyKey": "ORDER-2025-ORD456", "amount": 200.00, "remarks": "Gift card purchase"}Request Fields:
| Field | Type | Required | Description |
|---|---|---|---|
userId | string | Yes | Your unique user identifier |
idempotencyKey | string | Yes | Unique key to prevent duplicates |
amount | decimal | Yes | Coins to debit (> 0) |
remarks | string | No | Transaction notes |
Response:
Status Code: 200 OK
{ "transactionId": "01HKXYZ123ABC456DEF789GHI", "userId": "USR-001", "type": "DEBIT", "status": "SUCCESS", "amount": 200.00, "remarks": "Gift card purchase", "transactedAt": "2025-01-16T10:30:00"}5. Reverse Transaction
Reverse a completed (SUCCESS) transaction.
HTTP Method: POST
Endpoint: /v1/partners/coins/reverse
Request Body:
{ "transactionId": "01HKXYZ123ABC456DEF789GHI", "reason": "Order refund"}Request Fields:
| Field | Type | Required | Description |
|---|---|---|---|
transactionId | string | Yes | Transaction ID to reverse |
reason | string | No | Reason for reversal |
Response:
Status Code: 200 OK
{ "transactionId": "01HKXYZ123ABC456DEF789GHI", "userId": "USR-001", "type": "DEBIT", "status": "REVERSED", "amount": 200.00, "remarks": "Gift card purchase", "transactedAt": "2025-01-16T10:30:00"}Reversal Behavior:
- CREDIT reversed: Deducts coins from available balance. Only allowed if coins are fully intact (not consumed or expired).
- DEBIT reversed: Restores coins back to available balance.
6. Get Transaction History
Retrieve transaction history for a user with optional filtering.
HTTP Method: GET
Endpoint: /v1/partners/coins/{userId}/transactions
Path Parameters:
| Parameter | Type | Description |
|---|---|---|
userId | string | Your unique user identifier |
Query Parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
type | enum | All | Filter: CREDIT or DEBIT |
pageNo | integer | 0 | Page number (0-indexed) |
limit | integer | 20 | Page size (max: 100) |
Response:
Status Code: 200 OK
{ "data": [ { "transactionId": "01HKXYZ123ABC456DEF789GHI", "userId": "USR-001", "type": "CREDIT", "status": "SUCCESS", "amount": 500.00, "remarks": "Q1 Performance Bonus", "transactedAt": "2025-01-15T10:30:00" }, { "transactionId": "01HKXYZ456DEF789ABC012GHI", "userId": "USR-001", "type": "DEBIT", "status": "SUCCESS", "amount": 200.00, "remarks": "Gift card purchase", "transactedAt": "2025-01-16T14:20:00" } ], "nextCursor": { "pageNo": "1", "limit": 20, "totalElements": 45 }}Transaction States
CREDIT Flow
Request ──────────────────────────▶ SUCCESS │ ▼ (reverse) REVERSEDDEBIT Flow
Request ──────────────────────────▶ SUCCESS │ ▼ (reverse) REVERSEDTransaction Status Definitions
| Status | Description |
|---|---|
SUCCESS | Transaction completed successfully |
REVERSED | Completed transaction has been reversed |
Error Responses
All error responses follow this structure:
{ "code": "INSUFFICIENT_BALANCE", "message": "Insufficient balance. Required: 200.00, Available: 150.00", "requestId": "req-abc123"}Common Error Codes
| Code | HTTP Status | Description |
|---|---|---|
INVALID_INPUT | 400 | Request validation failed |
INSUFFICIENT_BALANCE | 400 | Not enough coins for debit |
ENTITY_NOT_FOUND | 404 | User, transaction, or resource not found |
INVALID_OPERATION | 400 | Operation not allowed for current state |
UNAUTHORIZED | 401 | Invalid credentials |
INTERNAL_ERROR | 500 | Server error |
Best Practices
1. Use Descriptive Idempotency Keys
Format your keys for easy tracking and debugging:
{TYPE}-{DATE}-{UNIQUE_ID}Examples:
WELCOME-2025-Q1-USR001REWARD-20250116-USR042REFUND-ORDER123-USR007
2. Store Transaction IDs
Always store transaction IDs for:
- Reconciliation
- Reversals
- Support inquiries
- Audit trails
3. Set Appropriate Expiry Dates
Benefits of setting expiry dates:
- Creates urgency for users to spend coins
- Manages your liability
- Drives engagement
4. Implement Retry with Backoff
Handle transient failures gracefully:
async function creditWithRetry(userId, amount, maxRetries = 3) { for (let i = 0; i < maxRetries; i++) { try { return await creditCoins(userId, amount); } catch (error) { if (i === maxRetries - 1) throw error; await sleep(Math.pow(2, i) * 1000); // Exponential backoff } }}5. Check Balance Before Debit
Provide better UX by checking balance before attempting debit:
const balance = await getBalance(userId);if (balance.available < amount) { // Show user-friendly message return;}await debitCoins(userId, amount);6. Test in Development First
Always validate your integration in the development environment before deploying to production.
Usage Examples
Example 1: Welcome Bonus
Credit new users with welcome bonus coins:
curl -X POST 'https://api.myhubble.money/v1/partners/coins/credit' \ -H 'Authorization: Bearer your-access-token' \ -H 'Content-Type: application/json' \ -H 'X-Request-Id: req-welcome-001' \ -d '{ "userId": "USR-001", "idempotencyKey": "WELCOME-2025-USR001", "amount": 100.00, "remarks": "Welcome bonus for new user", "expiresOn": "2025-12-31" }'Example 2: Bulk Reward Campaign
Reward multiple users at once:
curl -X POST 'https://api.myhubble.money/v1/partners/coins/bulk-credit' \ -H 'Authorization: Bearer your-access-token' \ -H 'Content-Type: application/json' \ -H 'X-Request-Id: req-bulk-001' \ -d '{ "credits": [ { "userId": "USR-001", "idempotencyKey": "Q1REWARD-USR001", "amount": 500.00, "remarks": "Q1 top performer" }, { "userId": "USR-002", "idempotencyKey": "Q1REWARD-USR002", "amount": 300.00, "remarks": "Q1 achievement" } ] }'Example 3: Purchase Flow with Debit
Debit coins for a purchase:
curl -X POST 'https://api.myhubble.money/v1/partners/coins/debit' \ -H 'Authorization: Bearer your-access-token' \ -H 'Content-Type: application/json' \ -H 'X-Request-Id: req-order-001' \ -d '{ "userId": "USR-001", "idempotencyKey": "ORDER-2025-ORD123", "amount": 250.00, "remarks": "Amazon gift card purchase" }'Example 4: Refund Scenario
Reverse a transaction for refund:
curl -X POST 'https://api.myhubble.money/v1/partners/coins/reverse' \ -H 'Authorization: Bearer your-access-token' \ -H 'Content-Type: application/json' \ -H 'X-Request-Id: req-refund-001' \ -d '{ "transactionId": "01HKXYZ123ABC456DEF789GHI", "reason": "Customer requested refund" }'Example 5: Check Balance
Get user’s current coin balance:
curl -X GET 'https://api.myhubble.money/v1/partners/coins/balance/USR-001' \ -H 'Authorization: Bearer your-access-token' \ -H 'X-Request-Id: req-balance-001'Example 6: Transaction History with Filters
Get credit transactions:
curl -X GET 'https://api.myhubble.money/v1/partners/coins/transactions/USR-001?type=CREDIT&pageNo=0&limit=50' \ -H 'Authorization: Bearer your-access-token' \ -H 'X-Request-Id: req-history-001'Troubleshooting
Error: “Insufficient Balance”
Problem: User doesn’t have enough coins for the debit operation.
Solution: Check the user’s balance first using the Get Coin Balance endpoint before initiating a debit.
const balance = await getBalance(userId);if (balance.available < requestedAmount) { throw new Error(`Insufficient coins. Available: ${balance.available}`);}Error: “Duplicate Key” / Idempotency Key Already Used
Problem: The idempotency key has already been used for another transaction.
Solution:
- If retrying a failed request, use the same idempotency key (you’ll get the original response)
- For new transactions, use a unique idempotency key with transaction-specific identifiers
Note: Duplicate requests with the same idempotency key return the original transaction response, not an error.
Error: “Invalid Operation”
Problem: Attempting an operation that’s not allowed for the transaction’s current state.
Common scenarios:
- Trying to reverse an already reversed transaction
- Trying to reverse a credit where coins have been partially consumed
- Trying to reverse a credit where coins have expired
Solution: Check transaction status before performing operations.
Error: “Entity Not Found”
Problem: The specified user or transaction doesn’t exist.
Solution:
- Verify the userId matches your system’s user identifier
- Verify the transactionId is correct (copy from original response)
Coins Not Appearing Immediately
Problem: User doesn’t see credited coins in their balance.
Solution:
- Check the transaction status via Get Transaction History
- Ensure the transaction status is
SUCCESS - Verify you’re checking the correct userId
Credit Reversal Blocked
Problem: Cannot reverse a credit transaction.
Solution: Credit reversal is only allowed if:
- The lot has not been partially consumed (no debits from those coins)
- The lot has not expired
If coins have been spent or expired, reversal is not possible.
Rate Limits
| Endpoint | Limit |
|---|---|
| All endpoints | 1000 requests/minute per client |
| Bulk credit | 100 credits per request |
| Transaction history | 100 records per page (max limit) |
Two-Phase Debit (Coming Soon)
A two-phase debit flow is planned for scenarios requiring hold-then-confirm semantics (e.g., checkout flows where the order needs confirmation before finalizing the debit).
Planned Endpoints:
| Endpoint | Method | Description |
|---|---|---|
/v1/partners/coins/hold | POST | Create a hold on coins |
/v1/partners/coins/hold/{holdId}/confirm | POST | Confirm the hold and complete debit |
/v1/partners/coins/hold/{holdId}/cancel | POST | Cancel the hold and release coins |
Planned Flow:
Request ──► INITIATED (hold created) │ ┌───────┴───────┐ ▼ ▼ CONFIRMED CANCELLED (debit done) (coins released)This will be useful for:
- E-commerce checkout flows where payment confirmation is separate from order placement
- Reservation systems where coins are held pending final confirmation
- Any scenario requiring a “soft lock” before committing the debit
Contact support to express interest or for early access.
Webhooks (Coming Soon)
Webhook notifications for transaction events will be available in a future release.
SDK Support
Official SDKs are planned for:
- JavaScript/TypeScript
- Python
- Java
- Go
Contact support for early access.