Skip to main content

Send payouts

Use this component to let your users send international payouts without leaving your platform. Users pick a beneficiary, see a locked foreign exchange (FX) rate, and confirm the payout. Nium hosts the component and renders it in an iFrame.

This component:

  • Locks a live FX rate and keeps the source and destination amounts in sync.
  • Shows only the remittance-enabled source currencies for your client, with the live wallet balance.
  • Lets you pre-fill and lock fields for guided flows.
  • Shows a Transaction Placed confirmation in the form. There is no redirect.

Live preview​

This is the component running against the Nium sandbox with test data. Pick a beneficiary and enter an amount to try the payout flow.

Live preview · sandbox

Before you begin​

  • Complete the Quick start to set up PKCE authorization. Use the scope ['payout-form'] when your server requests the authorization code.
  • Have the customerHashId of the user who starts the payout.
  • Know how beneficiaries are chosen. The dropdown lists the customer's active beneficiaries. When you don't pass beneficiaryHashId, users can also select Add beneficiary. That opens the hosted beneficiary form inside the iFrame and returns with the new beneficiary preselected. The payout-form scope covers this flow, so you need no extra scope. You can also create beneficiaries with Manage beneficiaries or the Nium API.

Quick example​

After you call init(), create the payoutForm element, mount it, and clean up when you're done.

index.html
<div id="payout-container"></div>
client.ts
const { createElement } = await import('@nium/nium-sdk')

const element = await createElement('payoutForm', {
customerHashId: 'cust-hash-id', // required
beneficiaryHashId: 'bene-abc123', // optional: pre-select and lock a beneficiary
})

element.on('submit', (e) => recordPayout(e.result.transactionId)) // RT number
element.on('error', (e) => console.error(e.error.code, e.error.message))

element.mount('#payout-container')

// When you're done, for example on a route change
element.destroy()

On mount, the hosted form loads the customer's beneficiaries, a live FX rate, the purpose codes, the source wallet balance, and the estimated transfer time for the beneficiary's corridor.

Choose how users reach the form​

Pick the options that match how users reach this component. You can combine options.

  • Pre-selected beneficiary (most common). The user selects Pay on a beneficiary row. Pass beneficiaryHashId. The form skips beneficiary selection and opens at amount entry.
  • Fully guided. Your platform starts a fixed-amount payout. Pass beneficiaryHashId, sourceAmount, and purposeCode. The user reviews and confirms.
  • Open. The user selects everything. Pass customerHashId only. The user picks a beneficiary, enters amounts, and selects the purpose of transfer.

Options​

createElement('payoutForm', options) accepts these options (type NiumPayoutFormOptions). Only customerHashId is required. A pre-filled option locks its field to read-only, except walletHashId.

OptionTypeLocks fieldDescription
customerHashIdstringRequired. Customer whose beneficiary list and wallet the form loads.
intent'create_payout' | 'return_payload'create_payout (default) places the remittance and returns { transactionId }. return_payload skips the API call and returns { payload }, the request body, for your server to submit. Any other value throws INVALID_INTENT.
beneficiaryHashIdstringYesPre-selects a beneficiary and skips the selection step.
walletHashIdstringSource wallet for the payout. When you pass it, the form skips the wallet lookup and uses it. If omitted, the form uses the customer's first wallet. It isn't a visible field, so it isn't locked.
defaultSourceCurrencystringYesISO 4217 code, for example 'AUD'. Must be exactly 3 letters, or the SDK throws INVALID_CURRENCY_CODE.
sourceAmountstringYesFixes the You Send amount.
destinationAmountstringYesFixes the They Receive amount.
purposeCodestringYesPre-selects the purpose of transfer. Must be a valid Nium purpose code for the corridor.
commentstringYesPre-fills the notes or customer comment field.
themeNiumThemeVisual customizations. See Theming.
Set one amount option

Set sourceAmount or destinationAmount, not both. Setting both makes createElement() reject with CONFLICTING_AMOUNT_FIELDS before any iFrame is created. Set neither to let the user enter amounts freely.

Nium configures the purpose codes per client, not per createElement call. To add, remove, or reorder them, contact your Nium account team.

Events​

Listen to form events with element.on(eventName, handler). You can register listeners before or after mount(). element.on() returns a function that removes that listener.

  • ready: The iFrame loaded and the form is interactive. Use it to hide a loading spinner.
  • resize: The form's content height changed. The SDK adjusts the iFrame height for you. Payload: { height: number }.
  • submit: The user confirmed the payout. The form shows the Transaction Placed confirmation, including the reference number. With create_payout, result.transactionId is the transaction's system reference number (RT number). With return_payload, nothing is submitted and result.payload holds the remittance request body. Payload: { result: { transactionId } | { payload } }.
  • error: Authentication expired, the network failed, or the payout failed. Payload: { error: { message, code } }. Branch on code. Use message for display only.
Store the RT number

Store e.result.transactionId in your system. Then query the Nium Transactions API from your server to get the authoritative status.

Common error codes
CodeAction
AUTH_EXPIREDRe-initialize with a fresh authorization code.
NETWORK_ERRORShow a retry UI.
REMITTANCE_ERRORThe payout request failed. The form shows the reason to the user.
READY_TIMEOUT or IFRAME_LOAD_FAILEDThe iFrame didn't load. Show a retry UI.

CONFLICTING_AMOUNT_FIELDS and INVALID_CURRENCY_CODE aren't events. createElement() rejects with them, so catch them where you create the element.

Submit from your own button​

The payout form doesn't support programmatic submission. Users place the payout with the form's own Send button, and you react to the submit event.

Don't build a Send button in your page

Keep the payout button inside the iFrame. If you call element.submit(), the promise rejects with SUBMIT_UNSUPPORTED and an error event fires. Only the Manage beneficiaries form supports programmatic submit today.

client.ts
// Not supported for payoutForm: this always rejects
try {
await element.submit()
} catch (e) {
// e.code === 'SUBMIT_UNSUPPORTED'
}

// Instead, react to the user's own submit
element.on('submit', (e) => {
recordPayout(e.result.transactionId)
})

Clean up​

Call element.destroy() on route change, modal close, or component unmount. It removes the iFrame, detaches all listeners, and releases the SDK's internal state.

  • element.mount(selector): Call after createElement resolves. Mounting again on the same container is a no-op. After unmount(), you can mount the same instance again.
  • element.unmount(): Removes the iFrame and keeps the element. Call mount() to show it again.
  • element.destroy(): Removes the iFrame and all listeners permanently.

Theming​

Pass a NiumTheme object in the theme option to control fonts, colors, input styles, and border radius. The Theme customization reference documents all 15 theme properties and 4 ready-to-use example themes.

Next steps​

  • Track payouts: Show payout history. Pass beneficiaryHashId to scope the list to one beneficiary.
  • Manage beneficiaries: Let users create and manage beneficiaries.
  • Respond to RFIs: Let users answer a Request for Information (RFI) that compliance raises on a submitted payout.
LLM-ready documentation:: llms.txt and llms-full.txt
Was this page helpful?