Skip to main content
The Android SDK exposes Noah virtual accounts and global payouts through portal.ramps.noah. Each method issues HTTP requests through portal.api to Portal’s Noah integration on the Client API using your client API key. You do not call Noah’s servers directly from the app.
For dashboard setup, signing keys, and supported CAIP-2 networks, see Noah integration overview. For HTTP shapes and webhooks, see the Noah workflow guides and Noah Business API / EMM documentation.

Prerequisites

Architecture

Prefer portal.ramps.noah over lower-level APIs. All request and response types live in io.portalhq.android.api.data.noah.

Supported networks

Every network parameter takes a CAIP-2 chain identifier. Use the constants on NoahNetwork rather than string literals so a typo is a compile error instead of a runtime failure. Any other value is rejected with a 400 Bad Request (PortalException.Api.HttpBadRequest), because the chain is not mapped to a Noah network. A non-CAIP-2 string such as "Ethereum" is rejected earlier still, by the [namespace]:[reference] format check. Read the specific reason from the exception’s rawBody rather than matching on message text — the wording is not a stable API.
Testnet networks pair with sandbox-only test assets such as USDC_TEST.

Types and responses

Every response is an envelope — data class NoahXxxResponse(val data: T, val metadata: NoahResponseMetadata? = null), where NoahResponseMetadata is a type alias for Map<String, Any>. All nine methods are suspend and return Result<T>. They do not throw, and they do not return a bare value. Handle both outcomes with onSuccess / onFailure:
Every example below uses onSuccess / onFailure, getOrNull(), or getOrThrow(). Do not wrap these calls in try/catch — the failure is inside the Result, not on the stack. See Error handling.

initiateKyc

Starts hosted Noah onboarding. Open data.hostedUrl in the system browser. Validate HTTPS and the hostname against the checkout domains Noah documents for your environment (extend the example allowlist accordingly).
Signature
The returnUrl must be an HTTPS URL. Custom app schemes (for example myapp://callback) are not supported. For mobile applications, the recommended pattern is to use an HTTPS bridge page that redirects to a deep link after KYC completion.
ReturnsNoahInitiateKycResponse: { data: { hostedUrl: String } }.
This endpoint is idempotent. If a Noah customer record already exists for the client, the previously stored hostedUrl is returned regardless of KYC status (Pending, Submitted, Approved, Declined). Calling it a second time does not restart onboarding and does not mint a fresh URL.
This call only starts onboarding; KYC outcome and status changes arrive asynchronously via Noah Customer webhooks. See Noah webhooks.
See also: Noah KYC guide, Noah hosted flows.

initiatePayin

Creates a fiat-to-stablecoin payin and returns bank instructions and a payinId. Use a supported network and the user’s wallet address as destinationAddress.
Signature
ReturnsNoahInitiatePayinResponse: { data: { payinId: String, bankDetails: NoahBankDetails } }. NoahBankDetails includes:
  • paymentMethodId — payment method identifier
  • paymentMethodType — payment rail type, for example BankSepa or IdentifierPix
  • accountNumber — bank account number
  • cryptoCurrency — crypto currency for this payin
  • network — network identifier
  • fee — fee breakdown (NoahFeeDetails: fiatCurrencyCode, totalFeePct, totalFeeBase, totalFeeMin)
  • accountHolderName — optional account holder name
  • bankCode — optional bank routing or sort code
  • bankName — optional bank name
  • bankAddress — optional NoahBankAddress with street, street2, city, postCode, state, country
  • reference — optional payment reference
  • relatedPaymentMethods — optional List<NoahBankToAddressRelatedPaymentMethod>
NoahBankDetails carries sensitive financial data — accountNumber, accountHolderName, bankCode, and bankAddress. Render it to the user as deposit instructions and keep it in memory; do not write it to Log, crash reports, or analytics.
Payin lifecycle updates are asynchronous; track them with Noah FiatDeposit and Transaction webhooks, not by polling this SDK response. See Noah webhooks.
See also: Payins, FiatDeposit webhooks.

simulatePayin

Simulates a fiat deposit landing against a payment method so you can exercise the payin flow end to end without moving real money.
Signature
ReturnsNoahSimulatePayinResponse: { data: { fiatDepositId: String, reference: String? } }.
This endpoint is sandbox-only. Calling it against a production environment fails with a 400 Bad Request, surfacing as PortalException.Api.HttpBadRequest — not as a 403. Guard the call behind your own environment check rather than shipping it in a production code path.

getPayoutCountries

Lists countries available for fiat payouts, keyed by ISO-3166 country code with the fiat currency codes supported in each.
Signature
ReturnsNoahGetPayoutCountriesResponse: { data: { countries: Map<String, List<String>> } }.

getPayoutChannels

Returns payout rails for a given crypto asset. Only cryptoCurrency is required; country and fiatCurrency narrow the results, and fiatAmount seeds channel-level fee calculations.
Signature
ReturnsNoahGetPayoutChannelsResponse: { data: { items: List<NoahChannel>, pageToken: String? } }. Each NoahChannel includes:
  • id — channel identifier
  • paymentMethodCategory — broad grouping such as Bank, Card, or Identifier
  • paymentMethodType — payment rail type, for example BankSepa or IdentifierPix
  • fiatCurrency — fiat currency code
  • country — ISO country code
  • limitsNoahChannelLimits with minLimit and optional maxLimit
  • rate — exchange rate as a string
  • processingSeconds — estimated settlement time
  • calculated — optional NoahChannelCalculated with totalFee
  • paymentMethods — optional List<NoahChannelPaymentMethodDisplay>, only populated when a customer id was supplied
  • processingTier — optional settlement speed tier such as Standard or Priority
  • formSchema — optional inline JSON Schema for the channel’s payout form
  • formMetadata — optional NoahFormMetadata with contentHash
  • issuer — optional issuer identifier
When a channel already carries a formSchema, use it directly instead of calling getPayoutChannelForm — it saves a round-trip.
See also: Payouts.

getPayoutChannelForm

Loads the dynamic form schema for a channel so you can collect recipient fields before requesting a quote. The channelId is URL-encoded as a single path segment, with spaces normalized to %20.
Signature
ReturnsNoahGetPayoutChannelFormResponse: { data: { formSchema: Map<String, Any>?, formMetadata: NoahFormMetadata? } }.

getPayoutQuote

Requests fees and crypto amount estimates for a payout. Include form when the channel requires recipient data.
Signature

Choosing the amount denomination

fiatAmount and cryptoAmount are mutually exclusive. The primary constructor enforces this at runtime — passing both, or neither, throws IllegalArgumentException at construction, before any network call:
Prefer the withFiatAmount and withCryptoAmount factory helpers over the primary constructor. They make the invalid combination unrepresentable at the call site rather than deferring it to a runtime require check. Both are annotated @JvmStatic, so they are also callable from Java.
ReturnsNoahGetPayoutQuoteResponse: includes payoutId, totalFee, cryptoAmountEstimate, cryptoAuthorizedAmount, formSessionId, and optional cryptoCurrency, fiatCurrency, fiatAmount, rate, breakdown, quote ({ signedQuote, expiry }), and nextStep. Each breakdown entry is a NoahTransactionBreakdownItem with a type of ChannelFee, BusinessFee, or Remaining, plus an amount.
Set quoted = true if you intend to submit the payout with a NoahQuotedOnchainDepositSourceTriggerInput trigger — that variant requires the signedQuote returned here.

initiatePayout

Executes a payout after quoting. Returns the destination address and the on-chain deposit conditions your transfer must satisfy.
java.time requires API 26 or core library desugaring. If your minSdk is lower and desugaring is not enabled, format the expiry with a UTC SimpleDateFormat instead.
Signature
ReturnsNoahInitiatePayoutResponse: { data: { destinationAddress: String?, conditions: List<NoahDepositSourceTriggerCondition>?, ruleId: String? } }. destinationAddress is nullable because it is derived from conditions[0].destinationAddress and falls back to null when that shape is missing. ruleId is returned for permanent and quoted triggers.

Deposit source triggers

The payout must be authorized by a trigger. Either supply one explicitly, or rely on a saved payment method attached to the quote via paymentMethodId — when trigger is omitted and the saved method is a single on-chain deposit source, a default trigger is synthesized from the sourceAddress, expiry, and nonce you passed. NoahOnchainDepositSourceTrigger is a sealed interface with three implementations: Each implementation defaults its type property to the correct discriminator, so you do not need to set it:
Trigger inputs serialize their fields in PascalCase (Type, Conditions, SourceAddress, Expiry, Nonce) to match the Noah API wire format. The same applies to NoahBusinessFee, whose fields serialize as FeeBase, FeePct, and FiatCurrency — visible in any logged request body.
After this call returns destinationAddress and conditions, submit the on-chain transfer to satisfy them. You can do this with any wallet — including Portal’s own send method (portal.sendAsset(...)) on the same Portal instance, which builds, signs, and broadcasts in one call.
This call initiates the payout flow; completion and failures are reported asynchronously via Noah Transaction webhooks. See Noah webhooks.
See also: Transaction events, automated payout recipes.

getPaymentMethods

Returns the saved payment methods available to the customer for payouts, including a pagination token when more results exist.
The request parameter is defaulted, so you can call it with no arguments to use server-side defaults:
Signature
ReturnsNoahGetPaymentMethodsResponse: { data: { paymentMethods: List<NoahPaymentMethod>, pageToken: String? } }. Each NoahPaymentMethod includes id, paymentMethodCategory, country, and displayDetails, plus optional customerId, capabilities, accountHolderDetails, and issuerDetails.

Error handling

Every Noah method returns Result<T>. Failures are captured inside the Result rather than thrown, so handle them with onFailure, fold, or getOrElse — not try/catch. The following exceptions may appear in a failed Result:
HttpBadRequest is a sibling of HttpRequestFailed under PortalException.Api, not a subclass. A branch that only matches HttpRequestFailed will not catch a 400. Match both explicitly, or match the PortalException.Api umbrella.
HttpRequestFailed.statusCode is null when no valid HTTP response was received, or for synthesized failures such as a 200 body that carried an error envelope. A null statusCode therefore means “no status to branch on”, not “unknown error”.
Log or surface errors without printing full API responses in production if they might contain sensitive identifiers. Retry only idempotent reads unless your product team confirms otherwise.