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

# Simulate a pay-in (sandbox only)

> Fund your sandbox balance by pretending a customer paid you. Sandbox only — in production this returns `403 sandbox_only`, because real money can only arrive by being sent to your account number.

The simulated pay-in is a real pay-in in every way that matters to your integration: it becomes an ordinary collection, it credits your balance net of the fee, and it fires the `collection.settled` webhook. Nothing marks it as simulated, so the code you write against it is the code that runs in production.

**This request is not idempotent.** A `202` means the pay-in exists at the provider but has not credited yet — poll `GET /collections` rather than sending it again, or you will create a second pay-in. Only `4xx` and `503` mean nothing was created.

Otherwise, repeat it as often as you like — each call is an independent collection.



## OpenAPI

````yaml /openapi.json post /collections/simulate
openapi: 3.1.0
info:
  title: Jenzy Hermes Client API
  version: 1.0.0
  description: >-
    Draw down your prefunded MWK balance as payouts to Malawian banks and mobile
    money. Authenticate with your `jz_live_…` API key as a bearer token.
servers:
  - url: https://api.jenzy.com/v1
    description: Production
security: []
paths:
  /collections/simulate:
    post:
      summary: Simulate a pay-in (sandbox only)
      description: >-
        Fund your sandbox balance by pretending a customer paid you. Sandbox
        only — in production this returns `403 sandbox_only`, because real money
        can only arrive by being sent to your account number.


        The simulated pay-in is a real pay-in in every way that matters to your
        integration: it becomes an ordinary collection, it credits your balance
        net of the fee, and it fires the `collection.settled` webhook. Nothing
        marks it as simulated, so the code you write against it is the code that
        runs in production.


        **This request is not idempotent.** A `202` means the pay-in exists at
        the provider but has not credited yet — poll `GET /collections` rather
        than sending it again, or you will create a second pay-in. Only `4xx`
        and `503` mean nothing was created.


        Otherwise, repeat it as often as you like — each call is an independent
        collection.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SimulatedPayInCreate'
      responses:
        '201':
          description: The collection your simulated pay-in became — already credited.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Collection'
        '202':
          description: >-
            Created at the provider, not yet credited. Poll `GET /collections`;
            do not retry.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SimulatedPayInPending'
        '400':
          description: >-
            The body is malformed, the amount is not positive or is too large,
            or the provider refused it. Nothing was created.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
        '401':
          description: Missing, malformed, or revoked API key.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
        '403':
          description: >-
            This is a sandbox-only endpoint and this is not the sandbox, or your
            org has no provider sub-account linked yet.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
        '503':
          description: >-
            Nothing was created and the request is safe to retry.
            `service_incident` / `service_maintenance` mean the API shutter is
            closed and the whole `/v1` surface is paused.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
      security:
        - ApiKeyAuth: []
components:
  schemas:
    SimulatedPayInCreate:
      type: object
      properties:
        amount_mwk:
          anyOf:
            - type: number
            - type: string
          description: >-
            What the pretend sender pays IN — the gross, before fees. You are
            credited less: the fee comes out of it, exactly as it does on a real
            pay-in. Either a JSON integer of whole kwacha or an exact decimal
            string with up to 2 decimal places (`"1000.55"`). Must be positive
            and no greater than 5000000000 MWK.
          example: 50000
        rail:
          type: string
          enum:
            - momo
            - bank
          description: >-
            Where the pretend sender pays from. Decides the `rail` and
            `source_institution` on the resulting collection; it does not change
            the price, which is the same on both. Defaults to `momo`.
          example: momo
      required:
        - amount_mwk
    Collection:
      type: object
      properties:
        id:
          type: string
          format: uuid
          description: Collection id — your handle for polling and support.
        status:
          type: string
          enum:
            - settled
            - reversed
          description: >-
            `settled` means the money credited your balance. `reversed` means
            the provider then clawed the pay-in back — treat the
            `collection.reversed` event as authoritative; your balance catches
            up.
          example: settled
        amount_mwk:
          type: string
          description: >-
            The gross amount the sender sent — exact decimal string at 2 decimal
            places. Parse with a decimal library, never a float.
          example: '1000.00'
        fee:
          type: string
          description: >-
            The transaction fee, as one combined figure — an exact decimal
            string. Always equal to `amount_mwk` minus `amount_credited`. Use
            `POST /fees/quote` to know it in advance.
          example: '15.00'
        amount_credited:
          type: string
          description: >-
            What your balance moved by: `amount_mwk` minus `fee`, as an exact
            decimal string.
          example: '985.00'
        rail:
          type:
            - string
            - 'null'
          enum:
            - bank
            - momo
            - null
          description: >-
            How the money arrived: `momo` from a mobile wallet, `bank` from a
            bank account. Null when the rail could not be determined.
        source_customer_name:
          type:
            - string
            - 'null'
          description: Who sent the money, as reported by the provider.
          example: PETER BANDA
        source_account_number:
          type:
            - string
            - 'null'
          description: >-
            The account or wallet the money came from, verbatim — your handle
            for matching the pay-in to your own customer.
          example: '265991234567'
        source_institution:
          type:
            - string
            - 'null'
          description: The institution the money came from, as reported by the provider.
          example: Airtel Money
        rtp_reference_number:
          type:
            - string
            - 'null'
          description: >-
            The reference you supplied when requesting this payment, echoed back
            for matching. Null when the payer sent the money directly.
        created_at:
          type: string
          format: date-time
      required:
        - id
        - status
        - amount_mwk
        - fee
        - amount_credited
        - rail
        - source_customer_name
        - source_account_number
        - source_institution
        - rtp_reference_number
        - created_at
    SimulatedPayInPending:
      type: object
      properties:
        status:
          type: string
          enum:
            - pending
          description: >-
            The pay-in was created at the provider but has not credited yet.
            **Do not send this request again** — it is not idempotent, and a
            second call creates a second pay-in. Poll `GET /collections`; it
            will appear there, usually within a minute.
      required:
        - status
    ErrorEnvelope:
      type: object
      properties:
        error:
          type: object
          properties:
            code:
              type: string
              enum:
                - validation_error
                - unauthorized
                - forbidden
                - not_found
                - conflict
                - rate_limited
                - internal_error
                - unknown_institution
                - invalid_mobile_number
                - invalid_account_number
                - mobile_number_institution_mismatch
                - service_unavailable
                - sandbox_only
                - insufficient_funds
                - rail_disabled
                - amount_limit_exceeded
                - org_limit_exceeded
                - service_incident
                - service_maintenance
              description: >-
                Stable identifier a client may branch on — never renamed or
                removed.
            message:
              type: string
          required:
            - code
            - message
      required:
        - error
  securitySchemes:
    ApiKeyAuth:
      type: http
      scheme: bearer
      description: 'Your Jenzy API key: `Authorization: Bearer jz_live_…`'

````