{
  "item": [
    {
      "name": "Registration",
      "description": "Expand this topic and run only the request needed for the current customer state.",
      "item": [
        {
          "request": {
            "method": "POST",
            "url": "{{urlHost}}/api/v1/integrator/onboardings?companyUuid={{companyUuid}}",
            "header": [
              {
                "value": "{{apiKey}}",
                "key": "X-Integrator-Key"
              },
              {
                "value": "application/json",
                "key": "Content-Type"
              }
            ],
            "body": {
              "raw": "{\n  \"identityNumber\": \"{{identityNumber}}\",\n  \"surname\": \"{{surname}}\",\n  \"firstNames\": \"{{firstNames}}\",\n  \"email\": \"{{email}}\",\n  \"mobileNumber\": \"{{mobileNumber}}\"\n}",
              "mode": "raw",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            },
            "description": "Create/recover registration with the current project apiKey. Omit externalReference so MiCash generates it. Existing customers return 409 EXISTING_PROFILE_REQUIRES_LINKING with a project-scoped onboardingId; the collection captures it. Do not reuse an onboardingId from Leo/GMT under Kindred’s key. After an uncertain initial response, recover before sending another create."
          },
          "name": "01 Create or recover onboarding"
        },
        {
          "name": "02 Verify registration OTP",
          "request": {
            "method": "POST",
            "url": "{{urlHost}}/api/v1/integrator/onboardings/{{onboardingId}}/otp-verification?companyUuid={{companyUuid}}",
            "header": [
              {
                "value": "{{apiKey}}",
                "key": "X-Integrator-Key"
              },
              {
                "value": "application/json",
                "key": "Content-Type"
              }
            ],
            "body": {
              "raw": "{ \"otp\": \"{{otp}}\" }",
              "mode": "raw",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            },
            "description": "Run manually when the guide and current onboarding status call for this step. Recovery uses the same onboardingId and externalReference. Do not run the entire collection as a batch."
          }
        },
        {
          "name": "03 Validate profile (no provider write)",
          "request": {
            "body": {
              "mode": "raw",
              "raw": "{\n  \"displayName\": \"{{displayName}}\",\n  \"email\": \"{{email}}\",\n  \"photo\": \"{{selfieDataUrl}}\",\n  \"identity\": {\n    \"identityNumber\": \"{{identityNumber}}\",\n    \"firstNames\": \"{{firstNames}}\",\n    \"surname\": \"{{surname}}\",\n    \"genderType\": \"{{genderType}}\",\n    \"countryOfBirth\": \"{{countryOfBirth}}\",\n    \"dateOfBirth\": \"{{dateOfBirth}}\"\n  }\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            },
            "header": [
              {
                "key": "X-Integrator-Key",
                "value": "{{apiKey}}"
              },
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": "{{urlHost}}/api/v1/integrator/onboardings/{{onboardingId}}/profile-validation?companyUuid={{companyUuid}}",
            "method": "POST",
            "description": "Run manually when the guide and current onboarding status call for this step. Recovery uses the same onboardingId and externalReference. Do not run the entire collection as a batch."
          }
        },
        {
          "name": "04 Create profile",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "X-Integrator-Key",
                "value": "{{apiKey}}"
              },
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"displayName\": \"{{displayName}}\",\n  \"email\": \"{{email}}\",\n  \"photo\": \"{{selfieDataUrl}}\",\n  \"identity\": {\n    \"identityNumber\": \"{{identityNumber}}\",\n    \"firstNames\": \"{{firstNames}}\",\n    \"surname\": \"{{surname}}\",\n    \"genderType\": \"{{genderType}}\",\n    \"countryOfBirth\": \"{{countryOfBirth}}\",\n    \"dateOfBirth\": \"{{dateOfBirth}}\"\n  }\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            },
            "url": "{{urlHost}}/api/v1/integrator/onboardings/{{onboardingId}}/profile?companyUuid={{companyUuid}}",
            "description": "Run manually when the guide and current onboarding status call for this step. Recovery uses the same onboardingId and externalReference. Do not run the entire collection as a batch."
          }
        },
        {
          "name": "05 Request wallet PIN OTP",
          "request": {
            "header": [
              {
                "key": "X-Integrator-Key",
                "value": "{{apiKey}}"
              },
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "method": "POST",
            "url": "{{urlHost}}/api/v1/integrator/onboardings/{{onboardingId}}/wallet-pin/otp?companyUuid={{companyUuid}}",
            "description": "Run manually when the guide and current onboarding status call for this step. Recovery uses the same onboardingId and externalReference. Do not run the entire collection as a batch."
          }
        },
        {
          "name": "06 Set wallet PIN",
          "request": {
            "header": [
              {
                "key": "X-Integrator-Key",
                "value": "{{apiKey}}"
              },
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "method": "POST",
            "body": {
              "raw": "{\"otp\":\"{{pinOtp}}\",\"pin\":\"{{walletPin}}\"}",
              "mode": "raw",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            },
            "url": "{{urlHost}}/api/v1/integrator/onboardings/{{onboardingId}}/wallet-pin?companyUuid={{companyUuid}}",
            "description": "Run manually when the guide and current onboarding status call for this step. Recovery uses the same onboardingId and externalReference. Do not run the entire collection as a batch."
          }
        }
      ]
    },
    {
      "name": "Existing customers",
      "description": "Expand this topic and run only the request needed for the current customer state.",
      "item": [
        {
          "name": "12 Link existing profile",
          "request": {
            "url": "{{urlHost}}/api/v1/integrator/onboardings/{{onboardingId}}/profile-link?companyUuid={{companyUuid}}",
            "method": "POST",
            "body": {
              "raw": "{\n  \"mobileNumber\": \"{{mobileNumber}}\",\n  \"pin\": \"{{walletPin}}\"\n}",
              "mode": "raw",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            },
            "header": [
              {
                "value": "{{apiKey}}",
                "key": "X-Integrator-Key"
              },
              {
                "value": "application/json",
                "key": "Content-Type"
              }
            ],
            "description": "Link or relink within the current project scope. After 18 returns UNLINKED, retain the same onboardingId/reference and send the registered mobileNumber and actual PIN string (not its hash). Success returns PROFILE_CREATED. Then run 07 to open a new session; old sessions/quotes remain invalid. A Kindred key cannot relink an old Leo/GMT record. To associate that same customer with Kindred, run 01 once using Kindred’s own key and use its newly returned onboardingId; this links the existing customer profile without creating another provider profile."
          }
        }
      ]
    },
    {
      "name": "Wallets",
      "description": "Expand this topic and run only the request needed for the current customer state.",
      "item": [
        {
          "name": "07 Open wallet session",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "X-Integrator-Key",
                "value": "{{apiKey}}"
              },
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\"pin\":\"{{walletPin}}\"}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            },
            "url": "{{urlHost}}/api/v1/integrator/onboardings/{{onboardingId}}/wallet-sessions?companyUuid={{companyUuid}}",
            "description": "Run manually when the guide and current onboarding status call for this step. Recovery uses the same onboardingId and externalReference. Do not run the entire collection as a batch."
          }
        },
        {
          "name": "08 Read wallet balance",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "X-Integrator-Key",
                "value": "{{apiKey}}"
              },
              {
                "key": "Content-Type",
                "value": "application/json"
              },
              {
                "key": "X-Wallet-Session",
                "value": "{{walletSession}}"
              }
            ],
            "url": "{{urlHost}}/api/v1/integrator/onboardings/{{onboardingId}}/wallet?companyUuid={{companyUuid}}",
            "description": "Run manually when the guide and current onboarding status call for this step. Recovery uses the same onboardingId and externalReference. Do not run the entire collection as a batch."
          }
        },
        {
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "X-Integrator-Key",
                "value": "{{apiKey}}"
              }
            ],
            "description": "Read-only paginated wallets linked through this integrator to its authorized merchant. No merchant ID or wallet session. One entry per beneficiary; unlinked associations are excluded. Use onboardingId for login/balance/cashout/unlink.",
            "url": "{{urlHost}}/api/v1/integrator/wallets?page=1&pageSize=25&companyUuid={{companyUuid}}"
          },
          "name": "19 List merchant wallets"
        }
      ]
    },
    {
      "name": "Withdrawals",
      "description": "Expand this topic and run only the request needed for the current customer state.",
      "item": [
        {
          "name": "15 Quote bank cashout (no payout)",
          "request": {
            "url": "{{urlHost}}/api/v1/integrator/onboardings/{{onboardingId}}/bank-cashout-quotes?companyUuid={{companyUuid}}",
            "header": [
              {
                "value": "{{apiKey}}",
                "key": "X-Integrator-Key"
              },
              {
                "value": "application/json",
                "key": "Content-Type"
              },
              {
                "value": "{{walletSession}}",
                "key": "X-Wallet-Session"
              }
            ],
            "method": "POST",
            "body": {
              "mode": "raw",
              "options": {
                "raw": {
                  "language": "json"
                }
              },
              "raw": "{ \"amount\": {{cashoutAmount}}, \"accountName\": \"{{bankAccountName}}\", \"accountNumber\": \"{{bankAccountNumber}}\", \"branchCode\": \"{{bankBranchCode}}\" }"
            },
            "description": "Requires genuine activated wallet and a live session. amount is the TOTAL debit budget; display bankAmount and both fees before customer confirmation. Five-minute quote. MiCash localFee is zero while the platform fee switch is disabled; provider fees can remain. Display the API-returned amounts. Existing quotes retain their original terms. quote.breakdown uses PascalCase: ProviderFee, MiCashFee, TotalFees, WalletDebit and RecipientAmount, AmountModel INCLUSIVE. WalletDebit equals the input amount; never subtract fees twice. See /docs/fees-and-amounts."
          },
          "response": [
            {
              "name": "Illustrative quote: MiCash fees enabled (not a configured tariff)",
              "originalRequest": {
                "url": "{{urlHost}}/api/v1/integrator/onboardings/{{onboardingId}}/bank-cashout-quotes?companyUuid={{companyUuid}}",
                "header": [
                  {
                    "value": "{{apiKey}}",
                    "key": "X-Integrator-Key"
                  },
                  {
                    "value": "application/json",
                    "key": "Content-Type"
                  },
                  {
                    "value": "{{walletSession}}",
                    "key": "X-Wallet-Session"
                  }
                ],
                "method": "POST",
                "body": {
                  "mode": "raw",
                  "options": {
                    "raw": {
                      "language": "json"
                    }
                  },
                  "raw": "{ \"amount\": {{cashoutAmount}}, \"accountName\": \"{{bankAccountName}}\", \"accountNumber\": \"{{bankAccountNumber}}\", \"branchCode\": \"{{bankBranchCode}}\" }"
                },
                "description": "Requires genuine activated wallet and a live session. amount is the TOTAL debit budget; display bankAmount and both fees before customer confirmation. Five-minute quote. MiCash localFee is zero while the platform fee switch is disabled; provider fees can remain. Display the API-returned amounts. Existing quotes retain their original terms. quote.breakdown uses PascalCase: ProviderFee, MiCashFee, TotalFees, WalletDebit and RecipientAmount, AmountModel INCLUSIVE. WalletDebit equals the input amount; never subtract fees twice. See /docs/fees-and-amounts."
              },
              "status": "OK",
              "code": 200,
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/json"
                }
              ],
              "body": "{\n  \"success\": true,\n  \"error\": null,\n  \"quote\": {\n    \"id\": \"00000000-0000-4000-8000-000000000001\",\n    \"expiresAt\": \"<returned expiry>\",\n    \"amount\": 100,\n    \"bankAmount\": 93,\n    \"providerFee\": 5,\n    \"localFee\": 2,\n    \"currency\": \"ZAR\",\n    \"breakdown\": {\n      \"Currency\": \"ZAR\",\n      \"AmountModel\": \"INCLUSIVE\",\n      \"RequestedAmount\": 100,\n      \"ProviderFee\": 5,\n      \"MiCashFee\": 2,\n      \"TotalFees\": 7,\n      \"WalletDebit\": 100,\n      \"RecipientAmount\": 93\n    }\n  }\n}"
            }
          ]
        },
        {
          "name": "16 Submit confirmed bank cashout (moves money)",
          "request": {
            "url": "{{urlHost}}/api/v1/integrator/onboardings/{{onboardingId}}/bank-cashouts?companyUuid={{companyUuid}}",
            "header": [
              {
                "value": "{{apiKey}}",
                "key": "X-Integrator-Key"
              },
              {
                "value": "application/json",
                "key": "Content-Type"
              },
              {
                "value": "{{walletSession}}",
                "key": "X-Wallet-Session"
              }
            ],
            "method": "POST",
            "body": {
              "mode": "raw",
              "options": {
                "raw": {
                  "language": "json"
                }
              },
              "raw": "{ \"quoteId\": \"{{cashoutQuoteId}}\", \"externalReference\": \"{{cashoutReference}}\" }"
            },
            "description": "ONLY after customer confirms quote/bank details. Reuse cashoutReference and cashoutQuoteId on timeout. Never use a new reference to retry. SUBMITTED means provider accepted, not bank settled."
          }
        },
        {
          "name": "17 Check bank cashout by reference",
          "request": {
            "url": "{{urlHost}}/api/v1/integrator/onboardings/{{onboardingId}}/bank-cashouts/by-reference?externalReference={{cashoutReference}}&companyUuid={{companyUuid}}",
            "header": [
              {
                "value": "{{apiKey}}",
                "key": "X-Integrator-Key"
              }
            ],
            "method": "GET",
            "description": "Read-only durable status. No wallet session or provider request is needed. PROCESSING/FEE_PROCESSING/REQUIRES_REVIEW must be reconciled; never resubmit with a new reference."
          }
        }
      ]
    },
    {
      "name": "Recovery and access",
      "description": "Expand this topic and run only the request needed for the current customer state.",
      "item": [
        {
          "request": {
            "method": "GET",
            "url": "{{urlHost}}/api/v1/integrator/onboardings/{{onboardingId}}?companyUuid={{companyUuid}}",
            "header": [
              {
                "value": "{{apiKey}}",
                "key": "X-Integrator-Key"
              }
            ],
            "description": "Run manually when the guide and current onboarding status call for this step. Recovery uses the same onboardingId and externalReference. Do not run the entire collection as a batch."
          },
          "name": "09 Check status by ID"
        },
        {
          "request": {
            "method": "GET",
            "url": "{{urlHost}}/api/v1/integrator/onboardings/by-reference?externalReference={{externalReference}}&companyUuid={{companyUuid}}",
            "header": [
              {
                "value": "{{apiKey}}",
                "key": "X-Integrator-Key"
              }
            ],
            "description": "Run manually when the guide and current onboarding status call for this step. Recovery uses the same onboardingId and externalReference. Do not run the entire collection as a batch."
          },
          "name": "10 Check status by reference"
        },
        {
          "name": "11 Resume interrupted onboarding",
          "request": {
            "url": "{{urlHost}}/api/v1/integrator/onboardings/{{onboardingId}}/resume?companyUuid={{companyUuid}}",
            "method": "POST",
            "body": {
              "raw": "{\"mobileNumber\":\"{{mobileNumber}}\"}",
              "mode": "raw",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            },
            "header": [
              {
                "value": "{{apiKey}}",
                "key": "X-Integrator-Key"
              },
              {
                "value": "application/json",
                "key": "Content-Type"
              }
            ],
            "description": "Run manually when the guide and current onboarding status call for this step. Recovery uses the same onboardingId and externalReference. Do not run the entire collection as a batch."
          }
        },
        {
          "name": "18 Unlink beneficiary (revoke integrator access)",
          "request": {
            "url": "{{urlHost}}/api/v1/integrator/onboardings/{{onboardingId}}/unlink?companyUuid={{companyUuid}}",
            "header": [
              {
                "value": "{{apiKey}}",
                "key": "X-Integrator-Key"
              }
            ],
            "method": "POST",
            "description": "Disconnect ONLY the association owned by the current project apiKey. No body and no PIN. Success returns UNLINKED; collection clears walletSession and cashoutQuoteId but retains onboardingId, externalReference and cashoutReference for recovery/status. Does not delete the customer, provider account or money, and does not cancel an already submitted withdrawal. Run manually after customer confirmation."
          }
        }
      ]
    },
    {
      "name": "Optional testing",
      "description": "Expand this topic and run only the request needed for the current customer state.",
      "item": [
        {
          "request": {
            "method": "POST",
            "url": "{{urlHost}}/api/v1/integrator/onboardings?companyUuid={{companyUuid}}",
            "header": [
              {
                "value": "{{apiKey}}",
                "key": "X-Integrator-Key"
              },
              {
                "value": "application/json",
                "key": "Content-Type"
              }
            ],
            "body": {
              "raw": "{\r\n  \"identityNumber\": \"{{identityNumber}}\",\r\n  \"surname\": \"{{surname}}\",\r\n  \"externalReference\": \"{{externalReference}}\",\r\n  \"firstNames\": \"{{firstNames}}\",\r\n  \"email\": \"{{email}}\",\r\n  \"mobileNumber\": \"{{mobileNumber}}\"\r\n}",
              "mode": "raw",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            },
            "description": "Run manually when the guide and current onboarding status call for this step. Recovery uses the same onboardingId and externalReference. Do not run the entire collection as a batch."
          },
          "name": "13 Replay same reference (optional)"
        },
        {
          "request": {
            "method": "GET",
            "url": "{{urlHost}}/api/v1/integrator/onboardings/by-reference?externalReference={{externalReference}}&companyUuid={{companyUuid}}",
            "header": [
              {
                "value": "invalid-test-key",
                "key": "X-Integrator-Key"
              }
            ],
            "description": "Run manually when the guide and current onboarding status call for this step. Recovery uses the same onboardingId and externalReference. Do not run the entire collection as a batch."
          },
          "name": "14 Reject invalid API key (optional)"
        }
      ]
    },
    {
      "name": "Project funds",
      "description": "Project-level donation/payment allocations held under the sponsor. Separate from individual customer wallet accounts.",
      "item": [
        {
          "name": "20 Read project funds balance (BiggPull / Kindred / GMP)",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "X-Integrator-Key",
                "value": "{{apiKey}}"
              },
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "url": "{{urlHost}}/micash/{{companyUuid}}/integrator-wallet",
            "description": "Read the authenticated project own collected funds held under its sponsor, for example BiggPull under LEO. Use the project apiKey and matching sponsor companyUuid. No onboardingId, customer PIN or X-Wallet-Session is needed. The key derives the project scope; do not substitute the shared LEO key. This is a read-only allocation/settlement summary, not the sponsor entire provider balance or a customer wallet balance. availableForWithdrawal is matured collections less pending and approved/paid withdrawals. Example values are illustrative, not a verified account balance. Available customer funding is capped by actual owning-project merchant category-pocket balances after payout holds. Gross collected is historical, not spendable. balanceBasis=PROJECT_POCKETS_V1, projectWalletBalance and fundingCategories expose this distinction; R4 collected with an actual Donation pocket of R3.95 allows at most R3.95."
          },
          "response": [
            {
              "name": "Project allocation example (illustrative)",
              "originalRequest": {
                "method": "GET",
                "header": [
                  {
                    "key": "X-Integrator-Key",
                    "value": "{{apiKey}}"
                  },
                  {
                    "key": "Accept",
                    "value": "application/json"
                  }
                ],
                "url": "{{urlHost}}/micash/{{companyUuid}}/integrator-wallet",
                "description": "Read the authenticated project own collected funds held under its sponsor, for example BiggPull under LEO. Use the project apiKey and matching sponsor companyUuid. No onboardingId, customer PIN or X-Wallet-Session is needed. The key derives the project scope; do not substitute the shared LEO key. This is a read-only allocation/settlement summary, not the sponsor entire provider balance or a customer wallet balance. availableForWithdrawal is matured collections less pending and approved/paid withdrawals. Example values are illustrative, not a verified account balance."
              },
              "status": "OK",
              "code": 200,
              "_postman_previewlanguage": "json",
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/json"
                }
              ],
              "body": "{\n  \"success\": true,\n  \"siteName\": \"Kindred\",\n  \"responsibleMerchant\": \"LEO DEVELOPMENT GROUP\",\n  \"currency\": \"ZAR\",\n  \"grossCollected\": 4,\n  \"pendingMaturation\": 4,\n  \"pendingWithdrawals\": 0,\n  \"paidOrApprovedWithdrawals\": 0,\n  \"availableForWithdrawal\": 0,\n  \"pockets\": [\n    {\n      \"PocketName\": \"Kindred - Give\",\n      \"GrossCollected\": 4,\n      \"MaturedCollected\": 0,\n      \"PendingMaturation\": 4\n    }\n  ],\n  \"customerFundingHeld\": 0,\n  \"availableForCustomerFunding\": 3.95,\n  \"projectWalletBalance\": 3.95,\n  \"balanceBasis\": \"PROJECT_POCKETS_V1\",\n  \"fundingCategories\": [\n    {\n      \"Category\": \"Donation\",\n      \"Balance\": 3.95,\n      \"AvailableForCustomerFunding\": 3.95\n    },\n    {\n      \"Category\": \"Payment\",\n      \"Balance\": 0,\n      \"AvailableForCustomerFunding\": 0\n    },\n    {\n      \"Category\": \"Subscription\",\n      \"Balance\": 0,\n      \"AvailableForCustomerFunding\": 0\n    }\n  ]\n}"
            }
          ],
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "if (pm.response.code === 200) { const funds = pm.response.json(); pm.test(\"Project balance response\", () => { pm.expect(funds.success).to.eql(true); pm.expect(funds.currency).to.eql(\"ZAR\"); pm.expect(funds.availableForWithdrawal).to.be.a(\"number\"); pm.expect(funds.pockets).to.be.an(\"array\"); }); }"
                ]
              }
            }
          ]
        }
      ]
    },
    {
      "name": "Customer funding",
      "description": "Real project-to-customer funding. Disabled until explicitly authorized. Read status before recovering any uncertain result.",
      "item": [
        {
          "name": "21 Fund a linked customer from project funds",
          "request": {
            "method": "POST",
            "url": "{{urlHost}}/micash/{{companyUuid}}/integrator-wallet/customer-fundings",
            "header": [
              {
                "key": "X-Integrator-Key",
                "value": "{{apiKey}}"
              },
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "description": "Sends real money when customer funding is explicitly enabled by Super Admin. Only this project confirmed funds and ACTIVE customer associations can be used. No maturation wait applies to customer funding. No customer PIN/session is needed; restrict this action to your own authenticated project administrators on your backend. Generate and save one UUID fundingRequestId per intended transfer BEFORE submitting. Reuse that exact ID and body after an uncertain HTTP response; read status first and never create a replacement until reconciled. Choose Donation, Payment or Subscription; payer, payee and category pocket are resolved by MiCash. Confirm amount and customer before sending. A success envelope can contain PROCESSING or REQUIRES_REVIEW, which do not prove delivery. SUBMITTED means confirmed provider acceptance and local pocket credit.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"requestId\": \"{{fundingRequestId}}\",\n  \"onboardingId\": \"{{onboardingId}}\",\n  \"amount\": {{fundingAmount}},\n  \"category\": \"{{fundingCategory}}\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "if (!/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(pm.variables.get(\"fundingRequestId\") || \"\")) throw new Error(\"Set and persist a UUID fundingRequestId for this transfer. Never replace it to retry an uncertain transfer.\");"
                ]
              }
            }
          ]
        },
        {
          "name": "22 Read customer funding status",
          "request": {
            "method": "GET",
            "url": "{{urlHost}}/micash/{{companyUuid}}/integrator-wallet/customer-fundings/{{fundingRequestId}}",
            "header": [
              {
                "key": "X-Integrator-Key",
                "value": "{{apiKey}}"
              }
            ],
            "description": "Read the durable record by the original fundingRequestId. No provider call, new transfer or customer session. Remains available after customer unlink or funding permission is turned off, provided the project key remains active and authorized. PROCESSING and REQUIRES_REVIEW keep funds held. Do not submit a replacement."
          }
        }
      ]
    }
  ],
  "info": {
    "description": "Three-value integration: urlHost, companyUuid and apiKey. Super Admin creates external projects under their sponsor, without requiring a separate merchant signup. Each project gets its own key; MiCash derives project scope from that key. No application header is required. siteName remains optional legacy display metadata; it cannot override project-key ownership. Save the API-generated registration reference and onboarding ID. Run financial requests manually after review. Relink test: within one project use 18 → 12 → 07 → 08 → 19. Keep the same ID/reference across 18/12. When moving from legacy shared credentials to a distinct project key, clear previous onboardingId/externalReference/walletSession/cashoutQuoteId locally and run 01 under the new key; keep old withdrawal references for reconciliation. Old associations are not transferred or deleted automatically. Reviewed 2026-10-15: use each project own approved key with its matching sponsor companyUuid. Existing GMP application code GMT remains valid. Linking does not move funds. MiCash charging defaults off in the fee-policy release; new quotes have localFee zero while disabled, with provider charges separate. Keep original submitted withdrawal references for reconciliation. Added request 20 on 2026-10-16: project own revenue allocation under its sponsor (separate from customer balances). Customer funding added 2026-10-16: explicitly enabled, project-scoped, no maturity wait, durable request IDs and reconciliation. Fee update 2026-10-20: incoming donations have no MiCash deduction even when enabled; customer funding currently has no separate fee. Bank quotes include quote.breakdown with PascalCase fee and debit/net fields. See /docs/fees-and-amounts for caps, thresholds, exact amount models and dev tests.",
    "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json",
    "name": "MiCash360 Integrator Beneficiary Onboarding"
  },
  "variable": [
    {
      "value": "",
      "type": "string",
      "key": "firstNames"
    },
    {
      "value": "",
      "type": "string",
      "key": "mobileNumber"
    },
    {
      "value": "",
      "type": "string",
      "key": "apiKey"
    },
    {
      "value": "",
      "type": "string",
      "key": "externalReference"
    },
    {
      "value": "",
      "type": "string",
      "key": "email"
    },
    {
      "value": "",
      "type": "string",
      "key": "identityNumber"
    },
    {
      "value": "https://localhost:44372",
      "type": "string",
      "key": "urlHost"
    },
    {
      "value": "",
      "type": "string",
      "key": "surname"
    },
    {
      "value": "",
      "type": "string",
      "key": "onboardingId"
    },
    {
      "key": "otp",
      "value": "",
      "type": "string"
    },
    {
      "key": "walletPin",
      "value": "",
      "type": "string"
    },
    {
      "key": "walletSession",
      "value": "",
      "type": "string"
    },
    {
      "key": "pinOtp",
      "value": "",
      "type": "string"
    },
    {
      "type": "string",
      "value": "",
      "key": "displayName"
    },
    {
      "type": "string",
      "value": "",
      "key": "selfieDataUrl"
    },
    {
      "type": "string",
      "value": "",
      "key": "dateOfBirth"
    },
    {
      "type": "string",
      "value": "MALE",
      "key": "genderType"
    },
    {
      "type": "string",
      "value": "ZA",
      "key": "countryOfBirth"
    },
    {
      "type": "string",
      "value": "",
      "key": "onboardingStatus"
    },
    {
      "key": "cashoutAmount",
      "type": "string",
      "value": ""
    },
    {
      "key": "bankAccountName",
      "type": "string",
      "value": ""
    },
    {
      "key": "bankAccountNumber",
      "type": "string",
      "value": ""
    },
    {
      "key": "bankBranchCode",
      "type": "string",
      "value": ""
    },
    {
      "key": "cashoutQuoteId",
      "type": "string",
      "value": ""
    },
    {
      "key": "cashoutReference",
      "type": "string",
      "value": ""
    },
    {
      "key": "companyUuid",
      "value": ""
    },
    {
      "key": "fundingRequestId",
      "value": "",
      "type": "string"
    },
    {
      "key": "fundingAmount",
      "value": "",
      "type": "string"
    },
    {
      "key": "fundingCategory",
      "value": "Donation",
      "type": "string"
    }
  ],
  "event": [
    {
      "listen": "prerequest",
      "script": {
        "type": "text/javascript",
        "exec": [
          "const unresolved = pm.variables.replaceIn(pm.request.url.toString()).match(/\\{\\{[^}]+\\}\\}/); if (unresolved) throw new Error(\"Set the required local variable: \" + unresolved[0]);",
          "if (pm.request.body && pm.request.body.mode === \"raw\") { const raw = pm.variables.replaceIn(pm.request.body.raw); if (/\\{\\{[^}]+\\}\\}/.test(raw)) throw new Error(\"Fill all body variables locally before sending.\"); try { JSON.parse(raw); } catch (_) { throw new Error(\"Body must resolve to valid JSON. Fill numeric amount variables and use single-line image data URLs where required.\"); } }"
        ]
      }
    },
    {
      "listen": "test",
      "script": {
        "type": "text/javascript",
        "exec": [
          "let result; try { result = pm.response.json(); } catch (_) { pm.test(\"API returns JSON\", () => pm.expect.fail(\"Non-JSON response: check host/service availability\")); }",
          "if (result) { pm.test(\"Controlled API envelope\", () => pm.expect(result.success).to.be.a(\"boolean\")); if (result.onboarding) { pm.collectionVariables.set(\"onboardingId\", result.onboarding.id); pm.collectionVariables.set(\"onboardingStatus\", result.onboarding.status); if (result.onboarding.externalReference) pm.collectionVariables.set(\"externalReference\", result.onboarding.externalReference); const customer = result.onboarding.customer; if (customer && result.onboarding.status !== \"UNLINKED\") { pm.collectionVariables.set(\"canonicalEId\", customer.eId); pm.collectionVariables.set(\"profileUuid\", customer.profileUuid); pm.collectionVariables.set(\"accountUuid\", customer.accountUuid); } } if (result.success && result.walletSession) pm.collectionVariables.set(\"walletSession\", result.walletSession); if (result.error === \"WALLET_SESSION_EXPIRED\" || result.error === \"WALLET_SESSION_INVALID\") pm.collectionVariables.unset(\"walletSession\"); }",
          "if (result && result.success && result.quote) pm.collectionVariables.set(\"cashoutQuoteId\", result.quote.id);",
          "if (result) { [\"error\", \"failureStage\", \"providerHttpStatus\", \"providerCode\"].forEach(name => { if (result[name] != null) console.log(name + \": \" + result[name]); }); }",
          "if (result && result.success && result.onboarding && result.onboarding.status === \"UNLINKED\") { pm.collectionVariables.unset(\"walletSession\"); pm.collectionVariables.unset(\"cashoutQuoteId\"); pm.collectionVariables.unset(\"canonicalEId\"); pm.collectionVariables.unset(\"profileUuid\"); pm.collectionVariables.unset(\"accountUuid\"); }"
        ]
      }
    }
  ]
}
