> ## Documentation Index
> Fetch the complete documentation index at: https://docs.singapay.id/llms.txt
> Use this file to discover all available pages before exploring further.

# Trigger Top-Up

> Initiates an e-wallet fund transfer to a beneficiary. This endpoint performs comprehensive validation, deducts funds from merchant balance atomically, creates a transaction record, and queues processing through the job system. The response indicates transaction has been queued with status typically Pending until background processing completes. **CRITICAL:** This is a money-out operation requiring **X-Signature** and **X-Timestamp** authentication per internal signing protocol. Middleware validation includes kill-switch check, request signature verification, rate limiting, and anomaly detection. Duplicate reference numbers within 24 hours are rejected with error code SP004.



## OpenAPI

````yaml https://payment-b2b.singapay.id/api/docs/merchant-api.json post /api/v2.0/ewallet/trigger-topup
openapi: 3.1.0
info:
  title: Singa Merchant API
  description: >-
    OpenAPI specification for the merchant/partner HTTP API. All routes below
    are additionally protected by `ip.whitelisted.merchant` — the caller IP must
    be registered for the credential or merchant. Obtain a JWT using `POST
    /api/v1.0/access-token/b2b` (Basic auth) or `POST
    /api/v1.1/access-token/b2b` (X-Signature) before calling secured endpoints.
  version: 1.0.0
servers:
  - url: https://sandbox-payment-b2b.singapay.id
    description: >-
      API host. Paths include `/api` prefix (see `RouteServiceProvider`).
      Replace scheme/host with your environment.
security: []
tags:
  - name: Security
    description: >-
      Merchant authentication (`OauthMerchantTokenController`). **v1.1** B2B
      token uses `X-CLIENT-ID`, `X-PARTNER-ID`, and `X-Signature` (no Basic
      auth). Secured routes also require the issued Bearer JWT plus
      `X-PARTNER-ID`.
  - name: Accounts
    description: >-
      Account management (`routes/merchantApiRoute.php`, `v1.0`). Path parameter
      `{id}` is always the account ULID.
  - name: Balance Inquiry
    description: >-
      Merchant and per-account balance inquiry (`BalanceController`, `v1.0`).
      Path `account_id` is the account ULID.
  - name: Statements
    description: >-
      Per-account statement list and detail
      (`AccountController::accountStatements`, `accountStatementDetail`, prefix
      `v1.0/statements`). Flugger responses; detail path param `{statement_id}`
      maps to `statements.transaction_id`.
  - name: Payment Link
    description: >-
      Payment link CRUD and payment-method catalog (`PaymentLinkApiController`,
      prefix `v1.0/payment-link-manage`). `account_id` is ULID;
      `payment_link_id` is numeric `payment_links.id`.
  - name: Payment Link History
    description: >-
      Payment link transaction/history listing and detail
      (`PaymentLinkApiController`, prefix `v1.0/payment-link-histories`).
      `history_id` is numeric `payment_link_histories.id`.
  - name: Virtual Account
    description: >-
      Native VA CRUD (`VirtualAccountController`, prefix
      `v1.0/virtual-accounts`). `account_id` and `virtual_account_id` are ULIDs.
  - name: VA Transaction
    description: >-
      VA money-in transaction listing and detail (`VirtualAccountController`,
      prefix `v1.0/va-transactions`).
  - name: QRIS (Money In)
    description: >-
      MPM dynamic QRIS list, show, and generate (`QrisMpmDynamicApiController`,
      prefix `v1.0/qris-dynamic`).
  - name: QRIS (Money Out)
    description: >-
      Issuer MPM decode/inquiry, payment credit (money out), and transaction
      status (`QrisIssuerMpmController`, `QrisApiV2Controller::checkStatus`,
      prefix `v2.0/qris`). Related list/detail: `GET
      /api/v2.0/qris/transaction/...`.
  - name: E-Wallet (Money In)
    description: >-
      E-Wallet Native checkout and transactions (`EwalletNativeApiController`,
      `EwalletNativeTransactionApiController`, `EwalletNativeV2ApiController`).
      Paths include `v1.0/ewallet-native`, `v1.0/ewallet-native-transactions`,
      and `v2.0/ewallet-native`.
  - name: E-Wallet (Money Out)
    description: >-
      E-wallet disbursement / top-up to beneficiary wallets
      (`EWalletTopUpController`, prefix `v2.0/ewallet`). Responses use the
      custom **`MerchantV2ApiEnvelope`** with **`MerchantV2ResponseCode`**
      (SP000–SP020).
  - name: Card (Money In)
    description: >-
      One-time card payment, cancel, and inquiry
      (`CardPaymentMerchantApiController`, prefix `v2.0/card`).
  - name: Subscription (Recurring)
    description: >-
      Credit-card recurring subscription plans (`SubscriptionPlanApiController`,
      prefix `v2.0/recurring`). Plan `{id}` is UUID (`sub_plans.id`).
  - name: Direct Debit
    description: >-
      Direct Debit — bind a customer bank account once via hosted webview, then
      charge it host-to-host (`DirectDebitMerchantController`, prefix
      `v2.0/direct-debit`). Binding `{binding_id}` and transaction
      `{transaction_id}` are UUIDs. Responses use the
      **`MerchantV2ApiEnvelope`** with both standard SP000–SP020 codes and
      Direct-Debit-specific codes (`SP_DD_*`). The `/charge` endpoint is
      additionally protected by **`X-Signature`** + **`X-Timestamp`**
      (`verify.signature-internal`); see operation parameters for the signing
      scheme.
  - name: Account Transfer
    description: >-
      Move funds between sub-accounts within the same merchant
      (`AnotherAccountApiController`, prefix `v1.0/account-transfer`). The
      transfer endpoint requires request signature headers
      (`verify.signature-internal`). Both accounts must belong to the
      authenticated merchant and be accessible to the credential.
  - name: Disbursement (Money Out)
    description: >-
      Bank disbursement (`DisbursementController` **v1.0**,
      `DisbursementV2Controller` **v2.0**). **v1.0**
      list/show/fee/beneficiary/transfer use Flugg envelopes; v1 inquiry-status
      uses the **custom v2 envelope** (`MerchantV2ApiEnvelope`, codes
      **SP000–SP020**). **v2.0** check-beneficiary, transfer, and inquiry-status
      use the same custom envelope — see component schema
      **`MerchantV2ResponseCode`** for the full response code table.
  - name: Cardless Withdrawal
    description: >-
      Cardless withdrawal API for initiating ATM cash withdrawals without a
      physical card. Supports creating withdrawals with OTP generation, listing
      transaction history, viewing transaction details, canceling pending
      withdrawals, and deleting canceled records. All endpoints use the
      `v1.0/cardless-withdrawals` route prefix and the standard success/error
      response envelope. Path parameter `{id}` refers to the `transaction_id`
      (platform-assigned business identifier).
paths:
  /api/v2.0/ewallet/trigger-topup:
    post:
      tags:
        - E-Wallet (Money Out)
      summary: Trigger Top-Up
      description: >-
        Initiates an e-wallet fund transfer to a beneficiary. This endpoint
        performs comprehensive validation, deducts funds from merchant balance
        atomically, creates a transaction record, and queues processing through
        the job system. The response indicates transaction has been queued with
        status typically Pending until background processing completes.
        **CRITICAL:** This is a money-out operation requiring **X-Signature**
        and **X-Timestamp** authentication per internal signing protocol.
        Middleware validation includes kill-switch check, request signature
        verification, rate limiting, and anomaly detection. Duplicate reference
        numbers within 24 hours are rejected with error code SP004.
      operationId: ewalletTopUpTrigger
      parameters:
        - name: X-Signature
          in: header
          description: >-
            HMAC-SHA512 hex signature, computed as `HMAC_SHA512(clientSecret,
            "{HTTP_METHOD}:{REQUEST_URI}:{ACCESS_TOKEN}:{SHA256_HEX(MINIFIED_BODY)}:{X_TIMESTAMP}")`.
            `MINIFIED_BODY` is the request body with object keys recursively
            sorted alphabetically and re-serialised with no whitespace (an
            empty/missing body hashes the empty string). `ACCESS_TOKEN` is the
            bearer token without the `Bearer ` prefix. Missing header returns
            **400** (`response_code=4019900`); signature mismatch returns
            **401** (`response_code=4019900`).
          required: true
          schema:
            type: string
        - name: X-Timestamp
          in: header
          description: >-
            ISO-8601 timestamp of the request; used as part of the signed
            string. Missing header returns **400** (`response_code=4019900`).
          required: true
          schema:
            type: string
            format: date-time
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/EwalletTopUpTriggerRequest'
      responses:
        '200':
          description: >-
            Queued — `data` matches `EwalletTriggerTopUpSuccessData` (usually
            **03** Pending)
          content:
            application/json:
              schema:
                $ref: >-
                  #/components/schemas/EwalletMoneyOutTriggerTopUpSuccessResponse
        '401':
          description: >-
            Unauthorized — bad bearer token, missing `X-PARTNER-ID`, or
            `X-Signature`/`X-Timestamp` mismatch (numeric `4019900` from
            `verify.signature-internal`).
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/MerchantV2ApiEnvelope'
              example:
                response_code: '4019900'
                response_message: Unauthorized. Invalid X-SIGNATURE
                data: null
        '404':
          description: '**SP117** beneficiary not found'
          content:
            application/json:
              schema:
                $ref: >-
                  #/components/schemas/EwalletMoneyOutBeneficiaryInfoNotFoundResponse
        '504':
          description: '**SP009** timeout during processing'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/EwalletMoneyOutTimeoutResponse'
      security:
        - BearerAuth: []
          PartnerId: []
components:
  schemas:
    EwalletTopUpTriggerRequest:
      description: >-
        Request payload to initiate an e-wallet transfer. Includes validation,
        balance deduction, and asynchronous processing through the job queue.
      required:
        - account_id
        - reference_number
        - ewallet_code
        - customer_number
        - amount
      properties:
        account_id:
          description: Unique identifier (ULID) of the merchant account
          type: string
          maxLength: 64
          example: 01K5G4FZZ18DMK0M5QTR8Y9QY9
        reference_number:
          description: >-
            Unique reference identifier for this transaction. Must be unique per
            account to prevent duplicate submissions.
          type: string
          maxLength: 64
          example: REF-001
        ewallet_code:
          description: E-wallet provider code (DANA, OVO, GOPAY, SHOPEEPAY)
          type: string
          example: DANA
        customer_number:
          description: Beneficiary phone number or account identifier
          type: string
          maxLength: 15
          minLength: 10
          example: '085733347341'
        amount:
          $ref: '#/components/schemas/EwalletMoneyOutMoneyAmount'
          description: Transfer amount (includes platform fees if applicable)
        notes:
          description: >-
            Optional transaction notes or description for internal reference.
            Notes only allows space, alphanumeric, non-latin letter, non-latin
            numeric, and "/" characters.
          type:
            - string
            - 'null'
          maxLength: 50
          example: Top up DANA
      type: object
    EwalletMoneyOutTriggerTopUpSuccessResponse:
      description: >-
        Success response data after balance lock and transaction creation (HTTP
        200). Transaction status is typically Pending until processed by job or
        confirmed via webhook. Funds have been deducted from merchant account.
      properties:
        response_code:
          description: Standardized response code (e.g., SP000 for success)
          type: string
          example: SP000
        response_message:
          description: Human-readable response message
          type: string
          example: Successful
        data:
          $ref: '#/components/schemas/EwalletTriggerTopUpSuccessData'
          description: Detailed data about the initiated transaction
      type: object
    MerchantV2ApiEnvelope:
      description: >-
        SingaPay Merchant API v2 custom response envelope
        (`ApiResponderHelper::responseJson`, `ApiResponseTrait`). Business
        outcome is determined by `response_code` (SP000–SP020), not by HTTP
        status alone. On success (`SP000`), `data` holds the operation payload.
        On errors, `data` often includes a `message` and may echo request
        fields.
      required:
        - response_code
        - response_message
      properties:
        response_code:
          $ref: '#/components/schemas/MerchantV2ResponseCode'
        response_message:
          $ref: '#/components/schemas/MerchantV2ResponseMessage'
        data:
          description: >-
            Endpoint-specific payload on success, or error context (validation
            message, inquiry result with `status` invalid, etc.).
          type:
            - object
            - 'null'
          additionalProperties: true
      type: object
      example:
        response_code: SP000
        response_message: Successfully
        data: []
    EwalletMoneyOutBeneficiaryInfoNotFoundResponse:
      description: >-
        Response when beneficiary information is not found during account
        inquiry.
      properties:
        response_code:
          description: Standardized response code (e.g., SP117 for beneficiary not found)
          type: string
          example: SP117
        response_message:
          description: Human-readable response message
          type: string
          example: Beneficiary not found
        data:
          description: Additional data or null when beneficiary is not found
          properties:
            ewallet_code:
              description: E-wallet provider code that was queried
              type: string
              example: DANA
            customer_number:
              description: Beneficiary account identifier that was queried
              type: string
              example: '085733347341'
          type: object
      type: object
    EwalletMoneyOutTimeoutResponse:
      description: Response when a timeout occurs during account inquiry or status refresh.
      properties:
        response_code:
          description: Standardized response code (e.g., SP009 for timeout)
          type: string
          example: SP009
        response_message:
          description: Human-readable response message
          type: string
          example: Upstream timeout
        data:
          description: Additional data or null when a timeout occurs
          properties:
            reference_number:
              description: Reference number of the transaction that experienced a timeout
              type: string
              example: '176458254538481'
          type: object
      type: object
    EwalletMoneyOutMoneyAmount:
      description: >-
        Monetary amount with currency information. All amounts are displayed
        with exactly 2 decimal places.
      required:
        - value
        - currency
      properties:
        value:
          description: Numeric amount with exactly 2 decimal places (e.g., 10000.50)
          type: string
          pattern: ^\d+\.\d{2}$
          example: '10000.00'
        currency:
          description: Currency code (ISO 4217)
          type: string
          enum:
            - IDR
          example: IDR
      type: object
    EwalletTriggerTopUpSuccessData:
      description: >-
        Success response data after balance lock and transaction creation (HTTP
        200). Transaction status is typically Pending until processed by job or
        confirmed via webhook. Funds have been deducted from merchant account.
      properties:
        transaction_id:
          description: Unique system-generated transaction identifier
          type: string
          example: EWT-20260402-0001
        transaction_status:
          $ref: '#/components/schemas/EwalletTopUpTransactionStatus'
          description: Current transaction processing status
        reference_number:
          description: Your submitted reference identifier for this transaction
          type: string
          example: REF-001
        notes:
          description: Optional notes provided with the transaction
          type:
            - string
            - 'null'
          example: Top up DANA
        ewallet:
          description: Beneficiary e-wallet information
          properties:
            code:
              description: E-wallet provider code
              type: string
              example: DANA
            customer_number:
              description: Beneficiary account identifier
              type: string
              example: '085733347341'
            customer_name:
              description: Beneficiary name
              type:
                - string
                - 'null'
              example: John Doe
          type: object
        post_timestamp:
          description: ISO 8601 timestamp when transaction was initiated (server time)
          type: string
          example: '2026-04-02T10:30:00+07:00'
        processed_timestamp:
          description: >-
            ISO 8601 timestamp when transaction was processed (may be null if
            still pending)
          type: string
          example: '2026-04-02T10:30:05+07:00'
        balance_after:
          $ref: '#/components/schemas/EwalletMoneyOutMoneyAmount'
          description: Merchant account balance after transaction deduction
        net_amount:
          $ref: '#/components/schemas/EwalletMoneyOutMoneyAmount'
          description: Amount received by beneficiary (before any recipient-side fees)
        fee:
          $ref: '#/components/schemas/EwalletMoneyOutMoneyAmount'
          description: Platform or provider fee charged for this transaction
        gross_amount:
          $ref: '#/components/schemas/EwalletMoneyOutMoneyAmount'
          description: Total deducted amount (net + fee)
      type: object
    MerchantV2ResponseCode:
      description: SingaPay custom business response code.
      type: string
      example: SP000
    MerchantV2ResponseMessage:
      description: Human-readable label paired with `response_code`.
      type: string
      example: Successfully
    EwalletTopUpTransactionStatus:
      description: Transaction status information with provider-specific status codes.
      properties:
        code:
          description: >-
            Standardized transaction status code: `00` (Success), `03`
            (Pending), `06` (Failed), or other provider-specific codes
          type: string
          example: '03'
        desc:
          description: Human-readable status description
          type: string
          example: Pending
      type: object
  securitySchemes:
    BearerAuth:
      type: http
      description: >-
        JWT issued by `POST /api/v1.1/access-token/b2b`. Send `Authorization:
        Bearer <token>`.
      bearerFormat: JWT
      scheme: bearer
    PartnerId:
      type: apiKey
      description: Merchant API key (`Credential.api_key`). Required on every request.
      name: X-PARTNER-ID
      in: header

````