Starship Rewards API

Create Payout API

Initiate money transfers to beneficiaries via bank transfers, mobile money, and other payment methods

Create Payout API

Create a new payout to transfer money to a beneficiary. The payout amount is immediately deducted from your wallet, and the funds are sent through the appropriate payment provider.

Endpoint

POST /api/v1/payouts

Authentication: API Key + Secret required

Request Headers

X-API-Key: sk_live_abc123def456
X-API-Secret: your-secret-here
Content-Type: application/json
Idempotency-Key: pay_xyz789_1705689660
HeaderRequiredDescription
X-API-KeyYesYour API key
X-API-SecretYesYour API secret
Content-TypeYesMust be application/json
Idempotency-KeyYesUnique key (8-256 chars) to prevent duplicate payouts. Learn more →

Critical: The Idempotency-Key header is required for all payout creation requests. This prevents duplicate payouts from network retries or application crashes. Keys are valid for 24 hours.

Request Body

Required Fields

{
  "reference_id": "PAY_2024_001",
  "wallet_id": 1234,
  "amount": 100.00,
  "currency": "USD",
  "beneficiary_email": "john.doe@example.com",
  "beneficiary_first_name": "John",
  "beneficiary_last_name": "Doe"
}

Complete Request Schema

{
  "reference_id": "PAY_2024_001",
  "wallet_id": 1234,
  "amount": 100.00,
  "currency": "USD",
  "beneficiary_id": "ben_abc123xyz",
  "beneficiary_email": "john.doe@example.com",
  "beneficiary_first_name": "John",
  "beneficiary_last_name": "Doe",
  "beneficiary_country": "US",
  "beneficiary_phone": "+1234567890",
  "beneficiary_identifiers": {
    "account_number": "123456789",
    "routing_number": "987654321"
  },
  "description": "Q1 2024 Bonus Payment",
  "metadata": {
    "employee_id": "EMP001",
    "department": "Engineering"
  },
  "notify_beneficiary": true
}

Field Descriptions

FieldTypeRequiredDescription
reference_idstringYesUnique client reference for idempotency
wallet_idnumberYesSource wallet ID for the payout
amountnumberYesPayout amount (must be > 0)
currencystringYesCurrency code (ISO 4217)
beneficiary_idstringNoID of saved beneficiary (alternative to inline info)
beneficiary_emailstringYes*Beneficiary email (*required if no beneficiary_id)
beneficiary_first_namestringYes*First name (*required if no beneficiary_id)
beneficiary_last_namestringYes*Last name (*required if no beneficiary_id)
beneficiary_countrystringNoCountry code (ISO 3166-1 alpha-2)
beneficiary_phonestringNoPhone number with country code
beneficiary_identifiersobjectNoPayment identifiers (bank account, mobile number, etc.)
descriptionstringNoInternal description for the payout
metadataobjectNoCustom key-value pairs for your reference
notify_beneficiarybooleanNoSend email notification to beneficiary (default: false)

Validation Rules

  • reference_id: Must be unique across all your payouts (idempotency key)
  • amount: Must be positive and within provider min/max limits
  • currency: Must be supported by an available payout provider
  • wallet_id: Must belong to your client and have sufficient balance (including fees)
  • beneficiary_email: Must be valid email format when provided

Response

Success Response

Status Code: 201 Created

{
  "id": "pyt_xyz789abc",
  "reference_id": "PAY_2024_001",
  "status": "created",
  "amount": 100.00,
  "currency": "USD",
  "fees": 1.50,
  "total_amount": 101.50,
  "beneficiary": {
    "email": "john.doe@example.com",
    "first_name": "John",
    "last_name": "Doe",
    "country": "US",
    "phone": "+1234567890"
  },
  "description": "Q1 2024 Bonus Payment",
  "created_at": "2024-01-15T10:30:00Z",
  "queued_at": null,
  "submitted_at": null,
  "completed_at": null,
  "failed_at": null,
  "cancelled_at": null
}

Response Fields

FieldTypeDescription
idstringUnique payout identifier (use for tracking)
reference_idstringYour client reference
statusstringCurrent payout status
amountnumberPayout amount to beneficiary
currencystringCurrency code
feesnumberFees charged for this payout
total_amountnumberTotal deducted from wallet (amount + fees)
beneficiaryobjectBeneficiary information
failure_codestringError code if failed (nullable)
failure_reasonstringError description if failed (nullable)
descriptionstringPayout description (nullable)
created_atstringCreation timestamp (ISO 8601)
queued_atstringWhen queued for processing (nullable)
submitted_atstringWhen submitted to provider (nullable)
completed_atstringWhen completed successfully (nullable)
failed_atstringWhen failed (nullable)
cancelled_atstringWhen cancelled (nullable)

Payout Statuses

StatusDescriptionWallet Impact
createdPayout created and queuedDeducted
queuedIn queue for processingDeducted
processingBeing processed by providerDeducted
settlingFunds settling to beneficiaryDeducted
action_requiredNeeds additional actionDeducted
completedSuccessfully deliveredDeducted
failedProcessing failedRefunded
cancelledCancelled by clientRefunded
expiredExpired before completionRefunded

Error Responses

400 Bad Request - Validation Error

{
  "error": "ValidationException",
  "message": "reference_id is required"
}

400 Bad Request - Invalid Amount

{
  "error": "ValidationException",
  "message": "amount must be greater than 0"
}

400 Bad Request - Insufficient Funds

{
  "error": "InternalServerError",
  "message": "insufficient wallet balance"
}

400 Bad Request - Duplicate Reference

{
  "error": "InternalServerError",
  "message": "duplicate reference_id"
}

401 Unauthorized

{
  "error": "UnauthorizedAccess",
  "message": "Authentication required"
}

500 Internal Server Error

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

Examples

Basic Payout with Inline Beneficiary

# IMPORTANT: Always include Idempotency-Key header
curl -X POST "{{host}}/api/v1/payouts" \
  -H "X-API-Key: sk_live_abc123def456" \
  -H "X-API-Secret: your-secret-here" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: pay_2024_001_$(date +%s)" \
  -d '{
    "reference_id": "PAY_2024_001",
    "wallet_id": 1234,
    "amount": 100.00,
    "currency": "USD",
    "beneficiary_email": "john.doe@example.com",
    "beneficiary_first_name": "John",
    "beneficiary_last_name": "Doe",
    "beneficiary_country": "US",
    "beneficiary_identifiers": {
      "account_number": "123456789",
      "routing_number": "987654321"
    },
    "description": "Bonus Payment",
    "notify_beneficiary": true
  }'

Payout with Saved Beneficiary

curl -X POST "{{host}}/api/v1/payouts" \
  -H "X-API-Key: sk_live_abc123def456" \
      -H "X-API-Secret: your-secret-here" \
  -H "Content-Type: application/json" \
  -d '{
    "reference_id": "PAY_2024_002",
    "wallet_id": 1234,
    "amount": 50.00,
    "currency": "USD",
    "beneficiary_id": "ben_abc123xyz",
    "description": "Recurring payment"
  }'

Mobile Money Payout

curl -X POST "{{host}}/api/v1/payouts" \
  -H "X-API-Key: sk_live_abc123def456" \
      -H "X-API-Secret: your-secret-here" \
  -H "Content-Type: application/json" \
  -d '{
    "reference_id": "MM_2024_001",
    "wallet_id": 5678,
    "amount": 1000.00,
    "currency": "KES",
    "beneficiary_email": "jane@example.com",
    "beneficiary_first_name": "Jane",
    "beneficiary_last_name": "Wanjiku",
    "beneficiary_country": "KE",
    "beneficiary_phone": "+254712345678",
    "beneficiary_identifiers": {
      "mobile_number": "+254712345678",
      "provider": "mpesa"
    },
    "notify_beneficiary": true
  }'

Error Handling Example

Idempotency

The reference_id field serves as an idempotency key. If you submit a payout with a reference_id that already exists:

  • The request will fail with a duplicate reference error
  • The original payout is not modified

This prevents accidental duplicate payouts due to network retries or application errors.

Best Practice

Generate unique reference IDs using a combination of:

  • Timestamp or sequential counter
  • Random component
  • Business context (e.g., invoice number)

Wallet Deduction

The payout amount plus fees are deducted immediately when the payout is created:

  1. Validation - Check wallet balance covers amount + fees
  2. Deduction - Immediately deduct total amount from wallet
  3. Processing - Payout enters processing queue
  4. Outcome - If failed/cancelled, amount is refunded to wallet

Important: Ensure your wallet has sufficient balance for both the payout amount and any applicable fees before creating a payout.

Best Practices

Before Creating Payouts

  1. Check provider availability - Verify a provider supports the target currency/country
  2. Calculate total cost - Account for payout amount + fees
  3. Validate wallet balance - Ensure sufficient funds before submission
  4. Verify beneficiary details - Validate bank/mobile details are correct

During Processing

  1. Store payout IDs - Save the returned id for tracking
  2. Monitor status - Poll the Get Payout API for updates
  3. Handle failures gracefully - Check failure_code and failure_reason for details

Production Recommendations

  1. Use webhooks - Set up webhooks for real-time status updates (when available)
  2. Implement reconciliation - Regularly compare your records with API data
  3. Set up alerts - Monitor for unusual failure rates
  4. Test thoroughly - Use sandbox environment before production