Skip to content

Status Codes Reference

Complete reference for all transaction status codes in the FLUID Network API. Understanding these statuses is crucial for tracking transaction lifecycle and building robust integrations.

Quick Reference

StatusDescriptionFinal State?Customer Action Required?
pendingTransaction queued for processingNoWait
processingBank is processing the transactionNoWait
completedTransaction successful✅ YesNone
failedTransaction failed✅ YesRetry or check account
reversedTransaction refunded✅ YesNone (informational)

Transaction Status Flow

Valid Transitions: Status transitions must follow the flow above. Attempting invalid transitions (e.g., pendingcompleted) will result in error code 3001 (Invalid status transition).


Status Definitions

pending

Description

Transaction has been created and validated, awaiting submission to the bank's API.

Typical Duration

  • 1-5 seconds under normal load
  • Up to 30 seconds during high traffic periods

When This Status Occurs

  1. Transaction passes API validation
  2. Transaction is queued in internal processing system
  3. Waiting for bank connector to pick up the transaction

Webhook Events

  • transaction.created
  • transaction.pending

What Your System Should Do

  • Display "Payment initiated" or "Processing" to customer
  • Store transaction reference for tracking
  • Do not fulfill orders yet
  • Do not mark payment as complete

Expected Next Status

  • processing (normal flow)
  • failed (if validation fails before bank submission)

Example Response

json
{
  "success": true,
  "data": {
    "id": "dt_12345",
    "reference": "FLU-20250128-ABC123",
    "status": "pending",
    "amount": 100.00,
    "currency": "GHS",
    "created_at": "2025-01-28T10:15:00Z",
    "processed_at": null,
    "completed_at": null
  }
}

processing

Description

Transaction has been successfully submitted to the bank and is being processed. The bank has received the request and is awaiting customer approval or performing internal checks.

Typical Duration

  • USSD approval: 30 seconds - 2 minutes (customer must approve)
  • Direct debit: 5-30 seconds (automated)
  • Web/App approval: 1-5 minutes (customer interaction required)

When This Status Occurs

  1. Bank API accepts the transaction request
  2. Bank returns a bank_reference (tracking ID)
  3. For USSD: Customer receives USSD prompt on their phone
  4. For direct debit: Bank validates account and processes automatically

Webhook Events

  • transaction.processing

What Your System Should Do

  • Update UI to "Awaiting bank confirmation"
  • For USSD: Instruct customer to check phone for prompt
  • Display estimated completion time (2-3 minutes)
  • Do not fulfill orders yet
  • Monitor for webhook completion events

Expected Next Status

  • completed (successful bank confirmation)
  • failed (bank rejection, timeout, or customer decline)

Example Response

json
{
  "success": true,
  "data": {
    "id": "dt_12345",
    "reference": "FLU-20250128-ABC123",
    "status": "processing",
    "amount": 100.00,
    "currency": "GHS",
    "bank_reference": "BNK-REF-789012",
    "approval_method": "ussd",
    "created_at": "2025-01-28T10:15:00Z",
    "processed_at": "2025-01-28T10:15:12Z",
    "completed_at": null
  }
}

Bank Reference Available: Once a transaction reaches processing, the bank_reference field is populated. Use this for bank-level inquiries or disputes.


completed

Description

Final success state. The bank has confirmed successful processing. Funds have been debited from the customer's account.

When This Status Occurs

  1. Bank sends success callback or confirmation
  2. Customer approves USSD prompt (for USSD approval method)
  3. Bank validates and processes direct debit successfully
  4. All bank-side validations pass

Webhook Events

  • transaction.completed

What Your System Should Do ✅

  • Mark payment as successful
  • Fulfill order or deliver service
  • Send confirmation to customer (SMS/email)
  • Update inventory (if applicable)
  • Generate receipt
  • Update accounting records

⚠️ This Is Your Trigger: completed status is the ONLY status where you should fulfill orders or mark payments as successful. Do not fulfill on any other status.

Example Response

json
{
  "success": true,
  "data": {
    "id": "dt_12345",
    "reference": "FLU-20250128-ABC123",
    "status": "completed",
    "amount": 100.00,
    "currency": "GHS",
    "fee": 1.50,
    "bank_reference": "BNK-REF-789012",
    "approval_method": "ussd",
    "created_at": "2025-01-28T10:15:00Z",
    "processed_at": "2025-01-28T10:15:12Z",
    "completed_at": "2025-01-28T10:15:45Z"
  }
}

Typical Timeline

PhaseDurationCumulative
pendingprocessing2-5 seconds2-5s
processingcompleted30s - 2 minutes32s - 2m 5s
Total30s - 2 minutes30s - 2m 5s

failed

Description

Final failure state. Transaction processing failed at some point. No funds were debited from the customer.

Common Failure Reasons

ReasonError CodeDescriptionCan Retry?
Insufficient funds3009Customer account balance too low✅ Yes (after customer adds funds)
Transaction declined3002Customer declined USSD prompt✅ Yes (customer can retry approval)
Invalid account-Phone number or account invalid❌ No (fix account details first)
Timeout2408Bank or network timeout✅ Yes (retry immediately)
Bank API error2002Bank system unavailable or error✅ Yes (retry with exponential backoff)
Duplicate reference3003Transaction with this reference already exists❌ No (use existing transaction)
Amount limit exceeded3005Amount exceeds configured limits❌ No (reduce amount)

When This Status Occurs

  • Customer declines USSD prompt
  • Insufficient funds in customer account
  • Invalid phone number or account details
  • Bank system timeout or error
  • Network connectivity issues
  • Transaction validation fails

Webhook Events

  • transaction.failed

What Your System Should Do ❌

  • Do NOT fulfill order
  • Display error message to customer
  • Log failure reason from error_message field
  • Suggest remediation based on error code (see table above)
  • Allow retry for retryable errors
  • Update transaction status to failed

Example Response

json
{
  "success": true,
  "data": {
    "id": "dt_12346",
    "reference": "FLU-20250128-DEF456",
    "status": "failed",
    "amount": 500.00,
    "currency": "GHS",
    "error_message": "Insufficient funds in customer account",
    "bank_reference": "BNK-REF-789013",
    "created_at": "2025-01-28T10:15:50Z",
    "processed_at": "2025-01-28T10:16:05Z",
    "completed_at": null
  }
}

Handling Failed Transactions:

  1. Parse the error_message field for user-friendly error details
  2. Check Error Codes Reference for specific error code handling
  3. For retryable errors (insufficient funds, timeout), allow customer to retry with same reference
  4. For non-retryable errors (invalid account), prompt customer to update details before retrying

reversed

Description

Final state. A previously completed transaction has been reversed (refunded). Funds have been returned to the customer's account.

When This Status Occurs

  • Merchant initiates a refund
  • Bank reverses transaction due to error or fraud
  • Dispute resolution requires reversal
  • Regulatory or compliance requirement

Typical Timeline

  • Reversal request: Instant
  • Funds return to customer: 1-5 business days (bank-dependent)

Webhook Events

  • transaction.reversed

What Your System Should Do

  • Reverse order fulfillment (if possible)
  • Update accounting records to reflect refund
  • Send reversal notification to customer
  • Mark order as refunded
  • Update inventory (restore items if applicable)

Example Response

json
{
  "success": true,
  "data": {
    "id": "dt_12345",
    "reference": "FLU-20250128-ABC123",
    "status": "reversed",
    "amount": 100.00,
    "currency": "GHS",
    "bank_reference": "BNK-REF-789012",
    "metadata": {
      "reversal_reason": "Customer requested refund"
    },
    "created_at": "2025-01-28T10:15:00Z",
    "processed_at": "2025-01-28T10:15:12Z",
    "completed_at": "2025-01-28T10:15:45Z"
  }
}

Reversal Metadata: Reversed transactions include a reversal_reason in the metadata field explaining why the reversal occurred. This is informational only and not standardized.


Status Transition Rules

Valid Transitions

FromToAllowed?Description
pendingprocessing✅ YesNormal flow - submitted to bank
pendingfailed✅ YesValidation failed before bank submission
processingcompleted✅ YesBank confirms success
processingfailed✅ YesBank rejects or times out
completedreversed✅ YesRefund initiated

Invalid Transitions

FromToAllowed?Error Code
pendingcompleted❌ No3001
pendingreversed❌ No3001
processingpending❌ No3001
completedpending❌ No3001
completedprocessing❌ No3001
completedfailed❌ No3001
failedcompleted❌ No3001
failedprocessing❌ No3001
reversedAny❌ No3001

Status Finality: The statuses completed, failed, and reversed are terminal states. Once a transaction reaches these states, it cannot transition to any other status (except completedreversed for refunds).


Querying by Status

API Endpoint

http
GET /api/v1/payment-providers/debit-requests?status=completed

Example Queries

To filter transactions by status, add the status query parameter to the API endpoint:

http
GET /api/v1/payment-providers/debit-requests?status=completed
GET /api/v1/payment-providers/debit-requests?status=failed
GET /api/v1/payment-providers/debit-requests?status=processing

Implementation approach:

  1. Send GET request to the debit requests endpoint
  2. Include status query parameter with desired status value
  3. Include authentication header with your API key
  4. Parse the JSON response containing matching transactions

Common use cases:

  • Get all completed transactions for reconciliation
  • Monitor failed transactions for customer support
  • Track processing transactions to identify stuck payments

Best Practices

1. Use Webhooks Instead of Polling

❌ Inefficient approach: Polling for status changes

  • Repeatedly queries the API in a loop to check if status changed
  • Causes unnecessary API calls and hits rate limits
  • Adds latency (e.g., 5-second polling interval means 5-second delay in detecting changes)
  • Wastes server resources and bandwidth

✅ Recommended approach: Subscribe to webhooks

  • FLUID sends real-time notifications when status changes
  • No polling required - instant updates
  • Reduces API calls by 95%+
  • More efficient and reliable

Implementation:

  • Configure a webhook endpoint on your server (e.g., POST /webhooks/fluid)
  • Subscribe to transaction.completed and transaction.failed events in the dashboard
  • Verify webhook signatures for security
  • Return HTTP 200 response quickly (process async)

2. Only Fulfill on completed Status

✅ Correct approach:

  • Wait for completed status before fulfilling orders
  • Never fulfill on pending or processing status
  • Handle each status appropriately:
    • completed: Fulfill order, send confirmation, update accounting
    • failed: Notify customer, log reason, allow retry
    • processing: Update UI to "awaiting confirmation"
    • pending: Show "processing" indicator
    • reversed: Reverse fulfillment, refund customer, update inventory

❌ Common mistake:

  • Fulfilling orders when status reaches processing
  • This is incorrect because the transaction may still fail
  • Customer gets the product/service but payment fails
  • Results in revenue loss and reconciliation issues

Why this matters:

  • completed is the ONLY status that guarantees funds were debited
  • Bank may reject transaction even after processing status
  • Customer may decline USSD prompt or have insufficient funds

3. Handle Failed Transactions Gracefully

When a transaction fails, provide user-friendly error messages and appropriate retry options:

Implementation approach:

  1. Parse error message from transaction.error_message field
  2. Map to user-friendly text - technical errors should be translated to plain language
  3. Provide context - explain what went wrong and what the customer should do
  4. Enable retry - for retryable errors (insufficient funds, timeout), show retry button
  5. Block retry - for non-retryable errors (invalid account), prompt to fix details first

Error message mapping examples:

  • Insufficient funds → "Your account balance is too low. Please add funds and try again."
  • Transaction declined → "You declined the payment prompt. Would you like to try again?"
  • Timeout → "The transaction timed out. Please try again."
  • Invalid account → "We couldn't find your account. Please verify your phone number."

Retry logic:

  • Allow retry for: insufficient funds, timeout, network errors, declined approval
  • Prevent retry for: invalid account, amount exceeds limit, duplicate reference
  • Use the same reference when retrying (for idempotency)

4. Store Status History

Track all status changes in your database for auditing, debugging, and analytics:

Benefits:

  • Audit trail: Complete history of transaction state changes
  • Debugging: Identify when and why status changed
  • Analytics: Measure conversion rates, failure patterns, and processing times
  • Reconciliation: Verify transaction flow matches expected patterns

Data to store:

  • Transaction ID and reference
  • Previous status
  • New status
  • Timestamp of change
  • Webhook event type
  • Any error messages or metadata

Use cases:

  • Investigate customer complaints about failed payments
  • Analyze average time spent in processing status
  • Identify patterns in failed transactions
  • Generate reports on transaction success rates
  • Debug webhook delivery issues

5. Set Timeouts for Long-Running Transactions

Monitor transactions that remain in processing status for too long:

Why this matters:

  • Normal processing time: 30 seconds - 2 minutes
  • If stuck >5 minutes, likely an issue (network timeout, webhook missed, bank system issue)
  • Proactive monitoring helps identify problems early

Implementation approach:

  1. Query database periodically (e.g., every 5 minutes) for transactions in processing status
  2. Filter by time - find transactions where processed_at is older than 5 minutes
  3. Fetch latest status from FLUID API to check if webhook was missed
  4. Update local record if status changed remotely
  5. Alert support if still stuck after verification

Monitoring thresholds:

  • processing > 5 minutes → Check status via API
  • processing > 10 minutes → Alert support team
  • pending > 2 minutes → Check for system issues

Recovery actions:

  • If webhook was missed, update local status from API response
  • If truly stuck, contact support with transaction reference
  • Notify customer about delay if stuck >5 minutes

Status Code Comparison

FLUID vs Other Payment Processors

FLUID StatusProvider AProvider BProvider CDescription
pendingrequires_actionCREATEDpendingAwaiting processing
processingprocessingPENDINGongoingBank is processing
completedsucceededCOMPLETEDsuccessSuccessfully processed
failedfailedFAILEDfailedProcessing failed
reversedrefundedREFUNDEDreversedRefunded/reversed

Each status has corresponding webhook events:

StatusWebhook EventDescription
pendingtransaction.pendingTransaction queued for processing
processingtransaction.processingSubmitted to bank, awaiting response
completedtransaction.completedTransaction successful ✅
failedtransaction.failedTransaction failed ❌
reversedtransaction.reversedTransaction refunded

See Webhook Event Types for complete event documentation.


Troubleshooting

Transaction Stuck in pending

Symptoms: Transaction status remains pending for >1 minute

Possible Causes:

  • High system load
  • Bank connector queue backlog
  • Network connectivity issues

Solutions:

  1. Wait 2-3 minutes before escalating
  2. Contact support with transaction reference if stuck >5 minutes

Transaction Stuck in processing

Symptoms: Transaction status remains processing for >5 minutes

Possible Causes:

  • Customer hasn't approved USSD prompt
  • Bank system slow or unresponsive
  • Network timeout

Solutions:

  1. For USSD: Verify customer received prompt and remind them to approve
  2. Wait 5 minutes before escalating
  3. Fetch latest status via API: GET /debit-requests/:reference
  4. If stuck >10 minutes, contact support

Unexpected Status Transition

Symptoms: Status transition doesn't match expected flow

Example: Transaction goes from pending directly to completed (skipping processing)

Explanation: This is not possible - you may be:

  1. Missing webhook events (check webhook logs)
  2. Caching old status (refresh from API)
  3. Experiencing a race condition (webhooks delivered out of order)

Solutions:

  1. Always use previous_status field in webhooks to detect missed events
  2. Implement idempotent webhook handling using event_id
  3. Query API for current status if webhooks seem out of order

Support

For status-related issues:

  1. Check Error Codes Reference for error details
  2. Review Webhook Event Types for event flow
  3. Contact support with:
    • Transaction reference
    • Current status
    • Expected status
    • Timestamp of issue