Skip to content

HTTP Status Code Standard

Version: 1.0 Last Updated: 2026-04-03 Owner: Platform Team Status: Active

Purpose

Standardise HTTP status code usage across all Simpaisa APIs. Legacy endpoints (v1/v2) return 200 for nearly all responses, bundling success/failure in the response body. The /v3/ API MUST use semantically correct HTTP status codes.

Status Code Reference

Success Codes (2xx)

Code Meaning When to Use
200 OK Request succeeded Successful GET, PUT, PATCH. Idempotent replay of a previously completed request
201 Created Resource created Successful POST that creates a resource: transaction initiated, merchant created
202 Accepted Async processing started Async pay-in flow accepted for processing; final result delivered via webhook
204 No Content Success, no body Successful DELETE, or PUT where no response body is needed

Client Error Codes (4xx)

Code Meaning When to Use Example Scenario
400 Bad Request Malformed request Invalid JSON, wrong data types, missing required fields amount sent as string instead of number
401 Unauthorised Authentication failed Missing, expired, or invalid HMAC/RSA signature Signature mismatch on X-Simpaisa-Signature header
403 Forbidden Authorised but not permitted Valid auth, but merchant lacks access to this resource or product Merchant A querying Merchant B's transaction (IDOR prevention)
404 Not Found Resource does not exist Transaction ID, merchant ID, or endpoint not found GET /v3/payin/transactions/TXN-NONEXISTENT
409 Conflict State conflict Idempotency key reused with a different request body Same Idempotency-Key, different amount
422 Unprocessable Entity Business rule violation Valid JSON, correct types, but business logic rejects it Insufficient balance, unsupported operator, amount below minimum, inactive channel
429 Too Many Requests Rate limit exceeded Merchant exceeds their allocated request rate Include Retry-After header (seconds)

Server Error Codes (5xx)

Code Meaning When to Use Example Scenario
500 Internal Server Error Unexpected failure Unhandled exceptions, bugs Nil pointer dereference, database query failure
502 Bad Gateway Upstream error Channel returned an error response JazzCash API returned HTTP 500
503 Service Unavailable Temporarily down Channel is in maintenance or circuit breaker is open Easypaisa scheduled maintenance window
504 Gateway Timeout Upstream timeout Channel did not respond within the configured timeout bKash did not respond within 30s

Error Response Body

All error responses (4xx, 5xx) MUST return a consistent JSON structure:

{
  "error": {
    "code": "INSUFFICIENT_FUNDS",
    "message": "The source account does not have sufficient balance for this transaction.",
    "traceId": "a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4",
    "details": [
      { "field": "amount", "issue": "Exceeds available balance of 1200.00 PKR" }
    ]
  }
}
Field Required Description
error.code Yes Machine-readable error code (UPPERCASE_SNAKE_CASE)
error.message Yes Human-readable explanation (safe for merchant developers)
error.traceId Yes OpenTelemetry trace ID for support correlation
error.details No Array of field-level errors for validation failures

Simpaisa Error Code Mapping

Scenario HTTP Status Error Code
Missing required field 400 MISSING_FIELD
Invalid field format 400 INVALID_FORMAT
Invalid JSON body 400 MALFORMED_REQUEST
Signature verification failed 401 INVALID_SIGNATURE
Expired authentication 401 AUTH_EXPIRED
Merchant not authorised for resource 403 ACCESS_DENIED
Transaction not found 404 TRANSACTION_NOT_FOUND
Merchant not found 404 MERCHANT_NOT_FOUND
Idempotency key conflict 409 IDEMPOTENCY_CONFLICT
Insufficient funds 422 INSUFFICIENT_FUNDS
Unsupported operator/channel 422 UNSUPPORTED_CHANNEL
Amount below minimum 422 AMOUNT_TOO_LOW
Amount above maximum 422 AMOUNT_TOO_HIGH
Channel inactive 422 CHANNEL_INACTIVE
Merchant account suspended 422 MERCHANT_SUSPENDED
Rate limit exceeded 429 RATE_LIMIT_EXCEEDED
Internal error 500 INTERNAL_ERROR
Channel returned error 502 CHANNEL_ERROR
Channel unavailable 503 CHANNEL_UNAVAILABLE
Channel timeout 504 CHANNEL_TIMEOUT

KrakenD Gateway Transformation

For /v3/ endpoints, KrakenD passes through the correct HTTP status codes from backend services.

For legacy /v1/ and /v2/ endpoints, KrakenD does not alter the existing behaviour — these continue to return 200 with status in the body for backward compatibility.

/v3/payin/transactions (POST)
  → Backend returns 201 → KrakenD returns 201 to merchant
  → Backend returns 422 → KrakenD returns 422 to merchant

/v1/Transaction/initiate (POST)
  → Backend returns 200 (with status in body) → KrakenD returns 200 (unchanged)

Rate Limit Headers

All responses from rate-limited endpoints MUST include:

X-RateLimit-Limit: 100
X-RateLimit-Remaining: 23
X-RateLimit-Reset: 1712153040

When 429 is returned, also include:

Retry-After: 42

Migration Path

  1. Phase 1 (current): /v3/ endpoints use correct status codes. Legacy endpoints unchanged.
  2. Phase 2: SDKs abstract status code handling — merchants using SDKs are insulated.
  3. Phase 3: Deprecation notices on /v1/ and /v2/. Migration guides published.
  4. Phase 4: Legacy endpoints sunset (12-month notice period).

Testing

  • Every error code in the mapping table MUST have a corresponding integration test.
  • Sandbox environment returns all status codes identically to production (see SANDBOX-STANDARD.md).
  • SDK tests verify correct exception types are raised for each status code.