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
- An initialized
Portalclient with a wallet and API access. - Noah enabled for your Portal environment and configured in the dashboard.
- For payins and payouts, the end user must complete Noah KYC with approved status before those flows succeed.
Architecture
Prefer
portal.ramps.noah over lower-level APIs. All request and response types are exported from PortalSwift.
Supported networks
Everynetwork 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
Unsupported network for Noah integration. A non-CAIP-2 string such as "ethereum" is rejected earlier still, with Network must be a "[namespace]:[reference]".
Types and responses
Successful Client API responses use an envelope{ data: T, metadata: NoahResponseMetadata? }, where NoahResponseMetadata is a type alias for [String: AnyCodable]. For example, NoahInitiateKycResponse is { data: { hostedUrl: String } }.
All nine methods are async throws. Failures surface as thrown errors — network problems, TLS issues, or an API error payload — and are handled with do/catch like any other async Portal call. See Error handling.
initiateKyc
Starts hosted Noah onboarding. Opendata.hostedUrl in the system browser. Validate HTTPS and the hostname against the checkout domains Noah documents for your environment (extend the example allowlist accordingly).
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.NoahInitiateKycResponse: { data: { hostedUrl: String } }.
This call only starts onboarding; KYC outcome and status changes arrive asynchronously via Noah
Customer webhooks. See Noah webhooks.initiatePayin
Creates a fiat-to-stablecoin payin and returns bank instructions and apayinId. Use a supported network and the user’s wallet address as destinationAddress.
Returns —
NoahInitiatePayinResponse: { data: { payinId: String, bankDetails: NoahBankDetails } }.
NoahBankDetails includes:
paymentMethodId— payment method identifierpaymentMethodType— payment rail type, for exampleBankSepaorIdentifierPixaccountNumber— bank account numbercryptoCurrency— crypto currency for this payinnetwork— network identifierfee— fee breakdown (NoahFeeDetails:fiatCurrencyCode,totalFeePct,totalFeeBase,totalFeeMin)accountHolderName— optional account holder namebankCode— optional bank routing or sort codebankName— optional bank namebankAddress— optionalNoahBankAddresswithstreet,street2,city,postCode,state,countryreference— optional payment referencerelatedPaymentMethods— optional[NoahBankToAddressRelatedPaymentMethod]
Payin lifecycle updates are asynchronous; track them with Noah
FiatDeposit and Transaction webhooks, not by polling this SDK response. See Noah webhooks.simulatePayin
Simulates a fiat deposit against a payment method so you can exercise the payin flow end to end without moving real money.
Returns —
NoahSimulatePayinResponse: { data: { fiatDepositId: String, reference: String? } }.
getPayoutCountries
Lists countries available for fiat payouts, keyed by country code with the supported fiat currency codes for each.NoahGetPayoutCountriesResponse: { data: { countries: [String: [String]] } }.
getPayoutChannels
Returns payout rails for a given crypto asset. OnlycryptoCurrency is required; country and fiatCurrency narrow the results, and fiatAmount refines channel availability.
Returns —
NoahGetPayoutChannelsResponse: { data: { items: [NoahChannel], pageToken: String? } }.
Each NoahChannel includes:
id— channel identifierpaymentMethodCategory— broad grouping such asBank,Card, orIdentifierpaymentMethodType— payment rail type, for exampleBankSepaorIdentifierPixfiatCurrency— fiat currency codecountry— ISO country codelimits—NoahChannelLimitswithminLimitand optionalmaxLimitrate— exchange rate as a stringprocessingSeconds— estimated settlement timecalculated— optionalNoahChannelCalculatedwithtotalFeepaymentMethods— optional[NoahChannelPaymentMethodDisplay], only populated when a customer id was suppliedprocessingTier— optional settlement speed tier such asStandardorPriorityformSchema— optional inline JSON Schema for the channel’s payout formformMetadata— optionalNoahFormMetadatawithcontentHashissuer— optional issuer identifier
getPayoutChannelForm
Loads the dynamic form schema for a channel so you can collect recipient fields before requesting a quote. ThechannelId is percent-encoded as a single path segment.
Returns —
NoahGetPayoutChannelFormResponse: { data: { formSchema: [String: AnyCodable]?, formMetadata: NoahFormMetadata? } }.
getPayoutQuote
Requests fees and crypto amount estimates for a payout. Includeform when the channel requires recipient data.
fiatAmount and cryptoAmount are mutually exclusive, and NoahGetPayoutQuoteRequest enforces that in the type system rather than at runtime: there are two initializers, one taking fiatAmount and one taking cryptoAmount. Choosing an initializer chooses the denomination, and passing both or neither will not compile.
NoahGetPayoutQuoteResponse: 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.
Returns —
NoahInitiatePayoutResponse: { data: { destinationAddress: String?, conditions: [NoahDepositSourceTriggerCondition]?, ruleId: String? } }.
destinationAddress is optional because it is derived from conditions[0].destinationAddress and falls back to nil 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 viapaymentMethodId — when trigger is omitted and the saved method is a single on-chain deposit source, a default trigger is synthesised from the sourceAddress, expiry, and nonce you passed.
NoahOnchainDepositSourceTrigger is an enum with three cases:
The wire format is discriminated by a
Type field, so the enum encodes its wrapped value transparently — no extra nesting is added to the JSON. Each trigger input defaults its type property to the correct discriminator, so you do not need to set it:
Type, Conditions, SourceAddress, Expiry, Nonce) to match the Noah API wire format.
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.getPaymentMethods
Returns payment methods available to the customer, including a pagination token when more results are available.
Returns —
NoahGetPaymentMethodsResponse: { data: { paymentMethods: [NoahPaymentMethod], pageToken: String? } }.
Each NoahPaymentMethod includes id, paymentMethodCategory, country, and displayDetails, plus optional customerId, capabilities, accountHolderDetails, and issuerDetails.
Testing and mocking
Ramps.noah is a var typed as NoahProtocol, so you can substitute your own conformer in tests:
Ramps.init(api:) is internal, so replace the noah property rather than the Ramps instance.
Noah.init(api: PortalNoahApiProtocol) and PortalNoahApi.init(apiKey:apiHost:requests:) are both public, so you can also stay on the real domain type and swap the transport layer instead:
Error handling
All Noah methods are async throwing functions. Wrap calls in ado/catch block. 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.
The following errors may be thrown: