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.
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
customerHashIdof 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. Thepayout-formscope 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.
- JavaScript
- React
<div id="payout-container"></div>
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()
import { useEffect } from 'react'
export function PayoutForm({ customerHashId }: { customerHashId: string }) {
useEffect(() => {
let element: { destroy(): void } | undefined
let cancelled = false
;(async () => {
const { createElement } = await import('@nium/nium-sdk')
element = await createElement('payoutForm', { customerHashId })
if (cancelled) return element.destroy()
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')
})()
return () => {
cancelled = true
element?.destroy()
}
}, [customerHashId])
return <div id="payout-container" />
}
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, andpurposeCode. The user reviews and confirms. - Open. The user selects everything. Pass
customerHashIdonly. 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.
| Option | Type | Locks field | Description |
|---|---|---|---|
customerHashId | string | Required. 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. | |
beneficiaryHashId | string | Yes | Pre-selects a beneficiary and skips the selection step. |
walletHashId | string | Source 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. | |
defaultSourceCurrency | string | Yes | ISO 4217 code, for example 'AUD'. Must be exactly 3 letters, or the SDK throws INVALID_CURRENCY_CODE. |
sourceAmount | string | Yes | Fixes the You Send amount. |
destinationAmount | string | Yes | Fixes the They Receive amount. |
purposeCode | string | Yes | Pre-selects the purpose of transfer. Must be a valid Nium purpose code for the corridor. |
comment | string | Yes | Pre-fills the notes or customer comment field. |
theme | NiumTheme | Visual customizations. See Theming. |
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. Withcreate_payout,result.transactionIdis the transaction's system reference number (RT number). Withreturn_payload, nothing is submitted andresult.payloadholds the remittance request body. Payload:{ result: { transactionId } | { payload } }.error: Authentication expired, the network failed, or the payout failed. Payload:{ error: { message, code } }. Branch oncode. Usemessagefor display only.
Store e.result.transactionId in your system. Then query the Nium Transactions API from your server to get the authoritative status.
Common error codes
| Code | Action |
|---|---|
AUTH_EXPIRED | Re-initialize with a fresh authorization code. |
NETWORK_ERROR | Show a retry UI. |
REMITTANCE_ERROR | The payout request failed. The form shows the reason to the user. |
READY_TIMEOUT or IFRAME_LOAD_FAILED | The 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.
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.
// 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 aftercreateElementresolves. Mounting again on the same container is a no-op. Afterunmount(), you can mount the same instance again.element.unmount(): Removes the iFrame and keeps the element. Callmount()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
beneficiaryHashIdto 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.