Starship Rewards API

Create Order API

Submit new voucher orders for digital products with automatic wallet deduction and voucher link generation

Create Order API

Submit a new voucher order for digital products including gift cards, mobile top-ups, and digital services. Orders are processed with immediate wallet deduction and delivered based on quantity and availability.

Endpoint

POST /api/v1/orders

Authentication: Bearer token required

Request Headers

Authorization: Bearer <access_token>
Content-Type: application/json

Request Body

Required Fields

{
  "product_id": 6,
  "denomination": 25.00,
  "quantity": 5
}

Complete Request Schema

{
  "product_id": 6,
  "denomination": 25.00,
  "quantity": 5,
  "wallet_id": 1234,
  "ref": "CLIENT_REF_123456",
  "email": "customer@example.com"
}

Field Descriptions

FieldTypeRequiredDescription
product_idnumberYesProduct ID from the catalog (must be > 0)
denominationnumberYesAmount per voucher (must be > 0.00)
quantitynumberYesNumber of vouchers to create (min: 1)
wallet_idnumberNoSpecific wallet for payment (auto-selected if omitted)
refstringNoClient reference for tracking (auto-generated UUID if omitted)
emailstringNoCustomer email for voucher delivery (must be valid email format)

Validation Rules

  • product_id: Must reference an active product in the catalog
  • denomination: Must match available denominations for the product
  • quantity: Maximum varies by product and client limits (bulk limit)
  • wallet_id: Must belong to authenticated client and have sufficient balance
  • ref: Must be unique if provided (no duplicate reference codes)
  • email: Must be valid email format when provided

Response

Success Response

Status Code: 200 OK

{
  "id": 1234,
  "ref": "1757321938LIGPWWSL7JKOKVPADF422WSXTA",
  "transaction_id": 5678,
  "amount": 127.50,
  "product_id": 6,
  "denomination": 25.00,
  "quantity": 5,
  "discount": 2.50,
  "vouchers": [],
  "status": "PENDING",
  "placed_at": "2024-01-15T10:30:00Z"
}

For orders that are immediately delivered, the vouchers array will contain voucher details:

{
  "id": 1234,
  "ref": "1757321938LIGPWWSL7JKOKVPADF422WSXTA",
  "transaction_id": 5678,
  "amount": 127.50,
  "product_id": 6,
  "denomination": 25.00,
  "quantity": 2,
  "discount": 2.50,
  "vouchers": [
    {
      "card_number": "1234-5678-9012-3456",
      "pin_code": "ABC123",
      "claim_url": "{{host}}/vouchers/claim/abc123...",
      "expires_at": "2025-12-31T23:59:59Z",
      "voucher_reference_number": "VOUCHER_REF_001"
    }
  ],
  "status": "DELIVERED",
  "placed_at": "2024-01-15T10:30:00Z"
}

Response Fields

FieldTypeDescription
idnumberUnique order identifier
refstringReference code for the order
transaction_idnumberWallet transaction ID
amountnumberTotal order amount (after discounts and fees)
product_idnumberProduct ID from the request
denominationnumberIndividual voucher amount
quantitynumberNumber of vouchers in the order
discountnumberTotal discount applied
vouchersarrayArray of voucher codes (empty for PENDING orders)
statusstringOrder status (see Order Statuses)
placed_atstringOrder creation timestamp in ISO 8601 format

Voucher Object Structure

{
  "card_number": "1234-5678-9012-3456",
  "pin_code": "1234",
  "code": "GIFT-CODE-123",
  "pin": "5678",
  "claim_url": "{{host}}/vouchers/claim/token_hash",
  "expires_at": "2025-12-31T23:59:59Z",
  "voucher_reference_number": "VOUCHER_REF_123"
}

Voucher Fields:

FieldTypeDescription
card_numberstringGift card or voucher number
pin_codestringPIN code for redemption
codestringAlternative code field
pinstringAlternative PIN field
claim_urlstringWeb URL to claim the voucher
expires_atstringExpiration date in ISO 8601 format
voucher_reference_numberstringReference number for the voucher

Note: Not all fields are present for every voucher. The fields returned depend on the product type.

Error Responses

400 Bad Request - Invalid Request

{
  "error": "BadRequest",
  "message": "Invalid request body format"
}

400 Bad Request - Validation Errors

{
  "error": "BadRequest",
  "message": "Invalid Quantity: min"
}

400 Bad Request - Insufficient Funds

{
  "error": "BadRequest",
  "message": "Insufficient funds in your wallet"
}

400 Bad Request - Quantity Limit Exceeded

{
  "error": "BadRequest",
  "message": "Invalid quantity, allowed max quantity: 1000"
}

400 Bad Request - Denomination Not Available

{
  "error": "BadRequest",
  "message": "Denomination not available for this product"
}

400 Bad Request - Duplicate Reference

{
  "error": "BadRequest",
  "message": "Duplicate reference code"
}

401 Unauthorized

{
  "error": "UnauthorizedAccess",
  "message": "User is not authorised to perform this action"
}

404 Not Found - Product Not Available

{
  "error": "NotFound",
  "message": "Product not found"
}

404 Not Found - Wallet Not Found

{
  "error": "NotFound",
  "message": "Wallet not found"
}

500 Internal Server Error

{
  "error": "InternalServerError",
  "message": "Failed to create order"
}

Order Statuses

Orders progress through different statuses during processing:

StatusDescriptionWallet DeductionVouchers Available
PENDINGOrder created, processing in progressYes (Deducted)No (Not yet)
DELIVEREDOrder completed successfullyYes (Deducted)Yes (Available)
PARTIALLY_DELIVEREDSome vouchers deliveredYes (Deducted)Yes (Some available)
FAILEDOrder processing failedNo (Refunded)No (None)
CANCELLEDOrder was cancelledNo (Refunded)No (None)

Status Flow

Order Creation → PENDING → DELIVERED

                 FAILED (if processing fails)

Important: Wallet deduction happens immediately upon order creation. The wallet balance is deducted as soon as the order is created successfully, even while the order is still in PENDING status.

Wallet Deduction Process

Automatic Wallet Selection

If wallet_id is not provided, the system automatically selects the most appropriate wallet:

  1. Checks for wallets matching the product's currency
  2. Selects wallet with sufficient balance
  3. Applies forex conversion if necessary

Immediate Deduction

The wallet balance is deducted immediately when the order is created:

  • Transaction is created with type ORDER
  • Balance is reduced by the total order amount
  • Transaction is linked to the order for audit trail

Multi-Currency Support

The system handles currency conversion automatically:

  • If wallet currency differs from product currency, forex rates are applied
  • Conversion charges may be added based on client configuration
  • All conversions are logged in the transaction history

Claim URLs

For products that provide web-based claim links:

{
  "claim_url": "{{host}}/vouchers/claim/token_hash",
  "expires_at": "2025-12-31T23:59:59Z",
  "voucher_reference_number": "WEB_VOUCHER_12345"
}

Direct Voucher Codes

For products that provide voucher codes directly:

{
  "card_number": "1234-5678-9012-3456",
  "pin_code": "1234",
  "expires_at": "2025-12-31T23:59:59Z",
  "voucher_reference_number": "AMZN_GIFT_12345"
}

Alternative Code Format

Some vouchers provide codes in alternative formats:

{
  "code": "GIFT-ABC-123-XYZ",
  "pin": "9876",
  "expires_at": "2025-12-31T23:59:59Z",
  "voucher_reference_number": "ALT_CODE_12345"
}

Order Processing

Orders are processed automatically after creation:

  • Small Orders (quantity ≤ 10): Processed immediately and may include vouchers in the response
  • Large Orders (quantity > 10): Processed asynchronously in the background with PENDING status initially

Testing Realtime Orders

For testing realtime order processing:

  • Product IDs 7 and 8 are specifically configured for testing realtime orders
  • These products will always process orders in realtime regardless of quantity
  • Other products may or may not support realtime processing and might have stocked inventory

Test Request Examples

# Test realtime order with Product ID 7
curl -X POST "{{host}}/api/v1/orders" \
  -H "Authorization: Bearer your_test_token" \
  -H "Content-Type: application/json" \
  -d '{
    "product_id": 7,
    "denomination": 10.00,
    "quantity": 5
  }'

# Test realtime order with Product ID 8 (large quantity)
# This will still process in realtime despite quantity > 10
curl -X POST "{{host}}/api/v1/orders" \
  -H "Authorization: Bearer your_test_token" \
  -H "Content-Type: application/json" \
  -d '{
    "product_id": 8,
    "denomination": 25.00,
    "quantity": 20,
    "ref": "TEST_REALTIME_BULK"
  }'

# Test async order with other product (quantity > 10)
curl -X POST "{{host}}/api/v1/orders" \
  -H "Authorization: Bearer your_test_token" \
  -H "Content-Type: application/json" \
  -d '{
    "product_id": 6,
    "denomination": 50.00,
    "quantity": 15,
    "ref": "TEST_ASYNC_ORDER"
  }'
<?php
// Test realtime order with Product ID 7
function testRealtimeOrder($accessToken) {
    $url = '{{host}}/api/v1/orders';

    // Test with Product 7 - always realtime
    $testData1 = [
        'product_id' => 7,
        'denomination' => 10.00,
        'quantity' => 5
    ];

    $options = [
        'http' => [
            'header' => [
                "Content-Type: application/json",
                "Authorization: Bearer $accessToken"
            ],
            'method' => 'POST',
            'content' => json_encode($testData1)
        ]
    ];

    $context = stream_context_create($options);
    $result = file_get_contents($url, false, $context);
    $response = json_decode($result, true);

    echo "Test 1 - Product 7 (quantity 5):\n";
    echo "Status: " . $response['status'] . "\n";
    echo "Vouchers Available: " . (empty($response['vouchers']) ? 'No' : 'Yes') . "\n\n";

    // Test with Product 8 - large quantity, still realtime
    $testData2 = [
        'product_id' => 8,
        'denomination' => 25.00,
        'quantity' => 20,
        'ref' => 'TEST_REALTIME_BULK'
    ];

    $options['http']['content'] = json_encode($testData2);
    $context = stream_context_create($options);
    $result = file_get_contents($url, false, $context);
    $response = json_decode($result, true);

    echo "Test 2 - Product 8 (quantity 20, realtime despite > 10):\n";
    echo "Status: " . $response['status'] . "\n";
    echo "Vouchers Available: " . (empty($response['vouchers']) ? 'No' : 'Yes') . "\n\n";

    // Test with regular product - async when quantity > 10
    $testData3 = [
        'product_id' => 6,
        'denomination' => 50.00,
        'quantity' => 15,
        'ref' => 'TEST_ASYNC_ORDER'
    ];

    $options['http']['content'] = json_encode($testData3);
    $context = stream_context_create($options);
    $result = file_get_contents($url, false, $context);
    $response = json_decode($result, true);

    echo "Test 3 - Product 6 (quantity 15, should be async):\n";
    echo "Status: " . $response['status'] . "\n";
    echo "Vouchers Available: " . (empty($response['vouchers']) ? 'No' : 'Yes') . "\n";
    echo "Expected: PENDING status with no vouchers initially\n";

    return $response;
}

// Usage
$accessToken = 'your_test_token';
testRealtimeOrder($accessToken);
?>

Asynchronous Processing

Orders will be marked for asynchronous processing and delivered asynchronously when:

  • The order quantity exceeds 10 (except for products 7 and 8 which are always realtime)
  • The product does not support realtime processing
  • Inventory needs to be fetched from stock

Order Lifecycle

  1. Order Creation: Order is created and wallet balance is deducted immediately
  2. Processing: Vouchers are generated and validated
  3. Delivery: Order status is updated and notifications sent (if email provided)

Tip: Use the Get Order Details API to check processing status and retrieve vouchers when ready.

Examples

Simple Order Creation

# Create a simple voucher order
curl -X POST "{{host}}/api/v1/orders" \
  -H "Authorization: Bearer your_access_token" \
  -H "Content-Type: application/json" \
  -d '{
    "product_id": 6,
    "denomination": 25.00,
    "quantity": 2
  }'
<?php
function createVoucherOrder($accessToken, $productId, $denomination, $quantity) {
    $url = '{{host}}/api/v1/orders';

    $data = [
        'product_id' => $productId,
        'denomination' => $denomination,
        'quantity' => $quantity
    ];

    $options = [
        'http' => [
            'header' => [
                "Content-Type: application/json",
                "Authorization: Bearer $accessToken"
            ],
            'method' => 'POST',
            'content' => json_encode($data)
        ]
    ];

    $context = stream_context_create($options);
    $result = file_get_contents($url, false, $context);

    return json_decode($result, true);
}

// Usage
$response = createVoucherOrder(
    'your_access_token',
    6,      // Amazon Gift Card
    25.00,  // $25 denomination
    2       // 2 vouchers
);

if (isset($response['id'])) {
    echo "Order ID: " . $response['id'] . "\n";
    echo "Status: " . $response['status'] . "\n";
    echo "Reference: " . $response['ref'] . "\n";
}
?>

Advanced Order with Custom Parameters

{
  "product_id": 15,
  "denomination": 50.00,
  "quantity": 10,
  "wallet_id": 1234,
  "ref": "PROMO_BATCH_2024_001",
  "email": "customer@example.com"
}

Bulk Order Creation

# Create multiple orders for different products
curl -X POST "{{host}}/api/v1/orders" \
  -H "Authorization: Bearer your_access_token" \
  -H "Content-Type: application/json" \
  -d '{
    "product_id": 6,
    "denomination": 25.00,
    "quantity": 100,
    "ref": "BULK_AMAZON_CARDS_2024"
  }'

Error Handling Example

async function createOrderWithErrorHandling(orderData) {
    try {
        const response = await fetch('/api/v1/orders', {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json',
                'Authorization': `Bearer ${getAccessToken()}`
            },
            body: JSON.stringify(orderData)
        });

        const result = await response.json();

        if (!response.ok) {
            // Handle different error types
            switch (response.status) {
                case 400:
                    console.error('Bad Request:', result.message);
                    break;
                case 401:
                    console.error('Unauthorized:', result.message);
                    break;
                case 404:
                    console.error('Not Found:', result.message);
                    break;
                case 500:
                    console.error('Internal Server Error:', result.message);
                    break;
                default:
                    console.error('Order creation failed:', result.message);
            }
            throw new Error(result.message);
        }

        return result;

    } catch (error) {
        console.error('Network or parsing error:', error.message);
        throw error;
    }
}

Best Practices

Pre-Order Validation

  1. Check Product Availability - Use the Products API to verify product status and available denominations
  2. Calculate Charges - Use the Charges API to show exact pricing including fees and discounts
  3. Validate Wallet Balance - Ensure sufficient funds before order creation
  4. Verify Denominations - Confirm the requested denomination is supported for the product

Order Creation Best Practices

  1. Use Unique Reference Numbers - Include client reference codes to prevent duplicate processing
  2. Handle Async Processing - Implement polling or webhooks for order status updates
  3. Store Order IDs - Save the returned order ID for tracking, support, and reconciliation
  4. Batch Large Orders - For high quantities, consider splitting into smaller batches

Error Handling Strategies

  1. Implement Exponential Backoff - For 5xx server errors and rate limits
  2. Don't Retry Validation Errors - 4xx errors typically require request modification
  3. Log Order Attempts - Maintain audit trails for troubleshooting and reconciliation
  4. Handle Partial Failures - Account for PARTIALLY_DELIVERED status in bulk orders

Monitoring and Observability

  1. Track Success Rates - Monitor order creation and delivery success metrics
  2. Set Up Alerts - Alert on unusual failure rates or processing delays
  3. Monitor Wallet Balances - Ensure adequate funding for automated systems
  4. Track Voucher Usage - Monitor claim rates and expiration patterns

Security Considerations

API Security

  • Always use HTTPS for all API communications
  • Secure Access Tokens - Store bearer tokens securely, rotate regularly
  • Validate Inputs - Sanitize and validate all request data client-side
  • Implement Request Signing - Use HMAC signatures for additional security

Sensitive Data Handling

  • Never log voucher codes or claim URLs in plain text
  • Encrypt storage of order references and customer data
  • Implement audit trails for all order creation and modification activities
  • Use secure channels for voucher delivery (HTTPS, encrypted email)

Rate Limits

The Create Order API implements multiple rate limiting tiers:

Limit TypeRestrictionReset Period
Per Minute60 requests1 minute
Burst Limit10 requests10 seconds
Daily Orders5,000 orders24 hours
Concurrent3 parallel requestsReal-time

Rate Limit Headers

X-RateLimit-Limit: 60
X-RateLimit-Remaining: 45
X-RateLimit-Reset: 1640995200
Retry-After: 30

Integration Testing

Test Order Creation

# Test with minimal required fields
curl -X POST "{{host}}/api/v1/orders" \
  -H "Authorization: Bearer test_token_123" \
  -H "Content-Type: application/json" \
  -d '{
    "product_id": 6,
    "denomination": 5.00,
    "quantity": 1
  }' | jq '.'

Validate Order Processing

  1. Create Test Order - Use small denomination and quantity
  2. Monitor Status - Check order status progression
  3. Verify Wallet Deduction - Confirm correct amount deducted
  4. Test Voucher Delivery - Ensure vouchers are accessible

Webhooks (Available)

Monitor order status changes with webhook notifications:

Event Types

  • order.created - Order successfully created with PENDING status
  • order.processing - Order processing started
  • order.delivered - Order completed successfully with vouchers available
  • order.partially_delivered - Some vouchers delivered, others still processing
  • order.failed - Order processing failed, wallet refunded
  • order.cancelled - Order cancelled by client or system

Webhook Payload Example

{
  "event": "order.delivered",
  "timestamp": "2024-01-15T10:30:00Z",
  "data": {
    "order_id": 12345,
    "ref": "1757321938LIGPWWSL7JKOKVPADF422WSXTA",
    "status": "DELIVERED",
    "vouchers_ready": 5,
    "total_vouchers": 5
  }
}

Performance Considerations

Optimal Request Patterns

  • Batch Similar Orders - Group orders by product when possible
  • Stagger Large Volumes - Distribute bulk orders over time
  • Use Async Processing - Don't block on immediate voucher availability
  • Implement Caching - Cache product information to reduce API calls

Technical Details

Order Processing

  • Transaction Safety: All order operations are atomic - if any part fails, the entire order is rolled back
  • Immediate Deduction: Wallet balance is deducted when the order is created
  • Status Updates: Orders progress through statuses based on processing completion
  • Performance: Large orders are processed efficiently in the background

Next Steps

Immediate Actions

Advanced Integration

  • Implement Status Polling - Get order details
  • Set Up Webhooks - Monitor order events in real-time
  • Build Reconciliation - List and filter orders
  • Handle Refunds - Process cancelled or failed orders

Production Readiness

  • Load Testing - Test with expected order volumes
  • Monitor Integration - Set up logging and alerting
  • Backup Systems - Implement failover for critical flows
  • Documentation - Document your integration for team reference