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

# Top Up Service Wallet

> Add funds to your service wallet via M-Pesa STK Push.

Fund your service wallet to keep payments running without interruption.

## How it works

A top-up sends an STK Push to the phone number you specify. When the M-Pesa PIN is entered, the amount is deposited into your service wallet.

* Top-ups appear in your transaction list with type `WALLET_TOPUP`
* Balance updates after the STK callback is received from M-Pesa — usually within 1–2 minutes

## Idempotent retries

Use `Idempotency-Key` to safely retry without creating duplicate top-ups.

```bash theme={null}
curl -X POST https://api.palpluss.com/v1/wallets/service/topups \
  -u "$PALPLUSS_API_KEY:" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: topup-2026-03-01-001" \
  -d '{
    "amount": 1000,
    "phone": "0712345678",
    "accountReference": "TOPUP-001",
    "transactionDesc": "Wallet Top-Up"
  }'
```

If a top-up with the same `Idempotency-Key` was already accepted, the original response is returned without initiating a new STK Push. Use a unique key per top-up attempt (include a date or sequence number).

## When to top up

* Top up before your balance drops below your minimum operating threshold
* For high-volume operations, maintain a buffer of 2–5× your expected daily fee spend
* The result is delivered asynchronously — poll `GET /transactions/{id}` to confirm the balance update


## OpenAPI

````yaml POST /wallets/service/topups
openapi: 3.1.0
info:
  title: PalPluss API
  version: '1.0'
  summary: >-
    Payment infrastructure for Kenya — STK Push, B2C payouts, and wallet
    management.
  description: >
    The PalPluss API gives developers programmatic access to M-Pesa STK Push
    collections,

    B2C payouts, service wallet top-ups, and payment channel management.


    **Base URL:** `https://api.palpluss.com/v1`


    All requests must be authenticated with an API key using HTTP Basic Auth.

    All responses are wrapped in a standard envelope. See [Errors](#tag/errors)
    for error codes.


    Amounts are in **KES (Kenyan Shillings)** unless stated otherwise. Phone
    numbers

    are normalized to the `254XXXXXXXXX` format internally.
  contact:
    name: PalPluss Developer Support
    email: developer@palpluss.com
    url: https://palpluss.com/developers
servers:
  - url: https://api.palpluss.com/v1
    description: Production
  - url: https://sandbox.palpluss.com/v1
    description: Sandbox
security:
  - BasicAuth: []
tags:
  - name: STK Push
    description: >
      Initiate M-Pesa STK Push payment prompts. The customer receives a PIN
      prompt on their

      phone and confirms payment. PalPluss delivers the result asynchronously to
      your webhook URL.
  - name: Transactions
    description: >-
      Query the status of any transaction by ID, or list all transactions with
      filters.
  - name: B2C Payouts
    description: >
      Send money directly to a customer's M-Pesa number. Requires KYC approval
      and sufficient

      B2C wallet balance. Payouts are processed asynchronously.
  - name: Service Wallet
    description: >
      The service wallet holds pre-funded tokens used to pay PalPluss
      transaction fees.

      Top up via STK Push. Balance is deducted with each API call.
  - name: Payment Channels
    description: >
      Payment channels represent the M-Pesa shortcodes (Paybill / Till)
      associated with your

      account. Channels are used to route STK Push requests to a specific
      shortcode.
paths:
  /wallets/service/topups:
    post:
      tags:
        - Service Wallet
      summary: Top up service wallet
      description: >
        Initiates an M-Pesa STK Push to top up your service wallet. The customer

        (typically yourself or your account admin) receives a PIN prompt and the

        credited amount is added to your service wallet balance on success.


        Top-up transactions appear in your transaction list with type
        `WALLET_TOPUP`.

        The result is delivered asynchronously to your `callbackUrl`.
      operationId: topUpServiceWallet
      parameters:
        - name: Idempotency-Key
          in: header
          required: false
          description: >
            Optional unique key for idempotent retries. If a top-up with the
            same

            key was already processed, the original response is returned.
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ServiceTopupRequest'
            examples:
              basic:
                summary: Top up KES 500
                value:
                  amount: 500
                  phone: '0712345678'
                  accountReference: TOPUP-2024-001
                  transactionDesc: Service wallet top-up
      responses:
        '200':
          description: Top-up STK Push accepted.
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/SuccessEnvelope'
                  - type: object
                    properties:
                      data:
                        $ref: '#/components/schemas/StkInitiateResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '429':
          $ref: '#/components/responses/RateLimited'
components:
  schemas:
    ServiceTopupRequest:
      type: object
      required:
        - amount
        - phone
      properties:
        amount:
          type: number
          minimum: 1
          description: Top-up amount in KES.
          example: 500
        phone:
          type: string
          description: Phone number to send the STK Push prompt to.
          example: '0712345678'
        accountReference:
          type: string
          maxLength: 120
          description: >
            Optional reference label for this top-up. Appears in the M-Pesa
            statement.
          example: TOPUP-001
        transactionDesc:
          type: string
          maxLength: 255
          description: Optional description shown in your transaction history.
          example: Service wallet top-up
    SuccessEnvelope:
      type: object
      required:
        - success
        - data
        - requestId
      properties:
        success:
          type: boolean
          example: true
        data:
          type: object
          description: Response payload. Shape varies by endpoint.
        requestId:
          type: string
          format: uuid
          description: Unique identifier for this API request. Include in support tickets.
          example: c1b2a3d4-e5f6-7890-abcd-ef1234567890
    StkInitiateResponse:
      type: object
      properties:
        transactionId:
          type: string
          format: uuid
        tenantId:
          type: string
          format: uuid
        channelId:
          type: string
          format: uuid
          nullable: true
        type:
          type: string
          example: STK
        status:
          type: string
          example: PENDING
        amount:
          type: number
          example: 1000
        currency:
          type: string
          example: KES
        phone:
          type: string
          example: '254712345678'
        accountReference:
          type: string
          example: INV-2024-001
        transactionDesc:
          type: string
          example: Payment
        providerRequestId:
          type: string
          nullable: true
        providerCheckoutId:
          type: string
          nullable: true
        transactionFee:
          type: number
          description: >
            Service wallet fee charged for this request in KES.

            `0` when no pricing rule is configured or the transaction did not
            succeed.
          example: 2.5
        resultCode:
          type: string
          nullable: true
        resultDescription:
          type: string
          nullable: true
        createdAt:
          type: string
          format: date-time
        updatedAt:
          type: string
          format: date-time
    ErrorEnvelope:
      type: object
      required:
        - success
        - error
        - requestId
      properties:
        success:
          type: boolean
          example: false
        error:
          type: object
          required:
            - message
            - code
          properties:
            message:
              type: string
              description: Human-readable error description.
              example: Invalid API key
            code:
              type: string
              description: Machine-readable error code.
              example: INVALID_API_KEY
            details:
              type: object
              description: Optional structured details about the error.
        requestId:
          type: string
          format: uuid
          example: c1b2a3d4-e5f6-7890-abcd-ef1234567890
  responses:
    BadRequest:
      description: Validation error — check the request body or parameters.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorEnvelope'
          examples:
            invalid_phone:
              value:
                success: false
                error:
                  message: Invalid phone number format.
                  code: INVALID_PHONE
                  details: {}
                requestId: a1b2c3d4-0000-0000-0000-000000000000
    Unauthorized:
      description: Missing, invalid, or revoked API key.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorEnvelope'
          examples:
            invalid_key:
              value:
                success: false
                error:
                  message: Invalid or revoked API key.
                  code: INVALID_API_KEY
                  details: {}
                requestId: a1b2c3d4-0000-0000-0000-000000000000
    RateLimited:
      description: Rate limit exceeded — 60 requests per minute per API key.
      headers:
        x-ratelimit-limit:
          schema:
            type: integer
          description: Maximum requests per minute.
        x-ratelimit-remaining:
          schema:
            type: integer
          description: Remaining requests in the current window.
        x-ratelimit-reset:
          schema:
            type: integer
          description: Unix timestamp when the window resets.
        Retry-After:
          schema:
            type: integer
          description: Seconds to wait before retrying.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorEnvelope'
          examples:
            rate_limited:
              value:
                success: false
                error:
                  message: Rate limit exceeded. Try again in 12 seconds.
                  code: RATE_LIMIT_EXCEEDED
                  details:
                    limit: 60
                    remaining: 0
                    resetInSeconds: 12
                requestId: a1b2c3d4-0000-0000-0000-000000000000
  securitySchemes:
    BasicAuth:
      type: http
      scheme: basic
      description: >
        Paste your PalPluss API key in the **username** field and leave the

        **password** field empty — it is ignored.


        Get your key from **console → Settings → API Keys** (starts with
        `pk_live_` or `pk_test_`).


        In your own code, send the key directly:

        `Authorization: Basic YOUR_API_KEY`

````