Skip to main content

Manage beneficiaries

Use this component to create and update beneficiaries with a form that Nium hosts. The form collects beneficiary and bank account details. It renders in an iFrame inside your platform, with no redirect.

This component:

  • Shows the countries, currencies, and transfer methods supported for your auth context.
  • Works in two modes. With create_beneficiary or update_beneficiary, the form calls Nium's API on submit, so you need no extra backend step. With return_payload, the form returns the request payload and your server makes the API call.
  • Lets you pre-fill known user data and hide or disable fields and sections.
  • Uses the same API version as your client. When you upgrade Nium APIs, the component reflects the updated requirements.

Live preview​

This is the component running against the Nium sandbox with test data. Select a destination country and fill in the beneficiary details to try the form.

Live preview · sandbox

Before you begin​

  • Complete the Quick start to set up PKCE authorization. Use the scope ['beneficiary-onboarding-form'] when your server requests the authorization code.
  • Have the customerHashId of the end user. It scopes the form to that customer's data.

Quick example​

After you call init(), create the payment element, mount it, and capture the IDs when the user submits.

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

const element = await createElement('payment', {
customerHashId,
intent: 'create_beneficiary',
})

element.on('submit', async (e) => {
// The IDs sit on e.result. e.result.payload is the request body.
const { beneficiaryId, paymentAccountId, payload } = e.result
await saveBeneficiary({ beneficiaryId, paymentAccountId, payload })
})
element.on('error', (e) => console.error(e.error.code, e.error.message))

element.mount('#sdk-container')

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

You need beneficiaryId and paymentAccountId to initiate payouts server-side through Nium's transfer creation API. Together they identify where payouts go.

Mount once per instance

Mount only once per instance. To mount again, create a new element or call unmount() first.

Choose an integration path​

The intent option controls whether the form calls Nium's APIs or returns the payload to your server. Both paths are supported.

  • Path A: Nium makes the API call. This path has fewer moving parts. With create_beneficiary or update_beneficiary, the iFrame calls Nium's beneficiary API on submit. Your client receives the new or updated IDs, plus the request body that the form sent.
  • Path B: You make the API call (recommended). With return_payload, the iFrame validates all fields and returns the raw request payload to your client. Your server calls the Nium CreateBeneficiary or UpdateBeneficiary API. This path gives you full control over error handling, retries, enrichment, and auditing.

The intent values:

  • create_beneficiary (Path A): Nium's API creates the beneficiary and payment account on submit. The client receives { beneficiaryId, paymentAccountId, payload }.
  • update_beneficiary (Path A): Nium's API updates the beneficiary and payment account on submit. Requires beneficiaryHashId, or createElement() rejects with UPDATE_REQUIRES_BENEFICIARY_ID. The form loads the existing data automatically, so prefill is rejected with PREFILL_FORBIDDEN_FOR_UPDATE. Demographic fields and email are read-only. The client receives { beneficiaryId, paymentAccountId, payload }.
  • return_payload (Path B): The iFrame returns { payload }, the raw request body. Your server makes the Nium API call.

Options​

Set these options in the object that you pass to createElement('payment', options) (type NiumPaymentFormOptions).

OptionTypeDescription
customerHashIdstringRequired. Customer the beneficiary belongs to.
intent'create_beneficiary' | 'update_beneficiary' | 'return_payload'Required. Form behavior on submit. See Choose an integration path.
beneficiaryHashIdstringRequired for update_beneficiary.
prefillNiumPaymentPrefillPre-fills form fields. Not allowed with update_beneficiary. See Pre-fill the form.
customizationsobjectiFrame dimensions and field and section controls. See Customize the form.
themeNiumThemeVisual customizations. See Theming.

Pre-fill the form​

Use prefill to populate form fields with known user data.

types.ts
interface NiumPaymentPrefill {
name?: string;
email?: string;
entityType?: 'individual' | 'corporate';
contactNumber?: string;
communicationPreference?: 'email' | 'phone';
/** Corporate only */
businessType?: NiumBusinessType;
/** Corporate only */
businessRegistrationNumber?: string;
address?: {
line1?: string;
line2?: string;
city?: string;
state?: string;
postalCode?: string;
countryCode?: string; // also pre-selects the payout country
};
}

type NiumBusinessType =
| 'sole_proprietorship'
| 'partnership'
| 'privately_owned_company'
| 'publicly_owned_company'
| 'government_owned_entity'
| 'financial_institution';
How prefill behaves

Prefilled values are editable unless you also disable the field with customizations.fields. The exception is email: prefilling it locks the email field automatically. Use prefill only with create_beneficiary and return_payload. With update_beneficiary, createElement() rejects with PREFILL_FORBIDDEN_FOR_UPDATE. businessType and businessRegistrationNumber apply only when entityType is 'corporate'. Bank and payment-account fields can't be prefilled.

Events​

Listen to form events with element.on(eventName, handler). It returns a function that removes that listener. Call it when you remove the component to prevent memory leaks.

  • ready: The iFrame is fully rendered. Hide loading states or reveal the container. No payload.
  • submit: The user selects Submit and the operation completes. Payload: { result: { beneficiaryId?: string | null, paymentAccountId?: string | null, payload: object } }. beneficiaryId and paymentAccountId are present only for create_beneficiary and update_beneficiary.
  • error: Submission failed or validation failed. Always handle this event. Payload: { error: { message: string, code: string } }.
  • resize: The form content height changed. The SDK resizes the iFrame for you, so listen only if your layout needs to react. Payload: { height: number }.
events.ts
const offReady = element.on('ready', () => setLoading(false))
const offError = element.on('error', (e) => showError(e.error.message))

// Later, when the component is removed
offReady(); offError()

Submit result​

e.result.payload is always the beneficiary request body that the form built, never the API response. It has the shape { beneficiary, paymentAccount }.

With the create_beneficiary and update_beneficiary intents, the form has already called Nium. e.result also carries:

  • beneficiaryId (string | null): ID of the created or updated beneficiary. It can be null if the create response didn't include one.
  • paymentAccountId (string | null): ID of the beneficiary's payment account. On update, if bank or address details changed, Nium replaces the account. The ID is then the new account's ID, and the old one is deleted.

With the return_payload intent, e.result has no IDs. It holds only payload, the raw request body. Your server makes the Nium API call.

Submit from your own button​

Call element.submit() to trigger submission as if the user selected Submit in the iFrame. It runs all validation. It resolves with the same result as the submit event, or rejects on error. The Beneficiary Management form is currently the only component that supports programmatic submit. Other components reject with SUBMIT_UNSUPPORTED.

Use this when your platform has its own Continue button outside the iFrame. For example, an onboarding flow can have a single Next button that advances across multiple steps.

Validation is not bypassed. If the form is invalid, the promise rejects with validation errors.

submit-programmatic.ts
async function handleContinue() {
try {
const result = await element.submit()
const { beneficiaryId, paymentAccountId } = result

await saveBeneficiary({ beneficiaryId, paymentAccountId })
router.push('/complete')
} catch (error) {
showError(error.message)
}
}

Clean up​

Both destroy() and unmount() remove the iFrame from the DOM and clear all event listeners. The difference is whether you can reuse the element instance.

  • element.destroy(): Permanently removes the iFrame and clears all listeners. You can't reuse the instance. Create a new element to show the form again. Use it when you navigate away from the page.
  • element.unmount(): Removes the iFrame and clears listeners, but keeps the instance. Call mount() again to restore it. Use it when you hide the form temporarily.

Customize the form​

Use customizations.sections to hide entire form sections. Use customizations.fields to hide or disable individual fields. Set customizations.minHeight (default 400) and customizations.maxHeight to control the iFrame height.

  • Sections you can hide ({ hidden: true }): demographicInfo, communicationPreferences, accountType, bankAccountDetails. Sections can only be hidden. To lock a section, disable its fields.
  • Fields you can disable ({ disabled: true }): name, entityType.
  • Fields you can hide or disable ({ hidden: true } or { disabled: true }): businessType, businessRegistrationNumber, line1, line2, city, state, postalCode, countryCode, communicationPreference, email, contactNumber.
  • Fields you can hide ({ hidden: true }): termsCheckbox, helperText.

Hidden fields and sections are skipped by validation. Unknown keys are ignored. businessType and businessRegistrationNumber appear only when entityType is corporate. You can't customize bank-account fields individually. Hide the accountType or bankAccountDetails section instead.

client.ts
const element = await createElement('payment', {
customerHashId,
intent: 'create_beneficiary',
customizations: {
minHeight: 400,
sections: {
communicationPreferences: { hidden: true }
},
fields: {
email: { disabled: true }, // pre-filled from user profile
termsCheckbox: { hidden: true } // T&Cs handled in your onboarding
}
}
})

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, a gating matrix, and four example themes.

Replace a redirect with the embedded form​

If you send users to a third-party page to set up a payment method, replace the redirect with the Nium iFrame. Users stay on your page, for example yourplatform.com/payment-setup. In the form, they choose the country and payment method, such as Local Bank Transfer or SWIFT, and enter bank account details. No page navigation or external redirect occurs.

Next steps​

  • Send payouts: Let the user pay the new beneficiary in the embedded form. To create the transfer yourself, use the captured beneficiaryId and paymentAccountId to call Nium's transfer creation API from your server.
  • View beneficiaries: Show the customer's saved beneficiaries.
Make transfer calls on your server

Make all transfer and payout API calls server-side in a trusted environment. Never call Nium's transfer APIs from the browser.

LLM-ready documentation:: llms.txt and llms-full.txt
Was this page helpful?