Merchant onboarding
Collect identity, underwriting, and bank data to approve a Vortex merchant account.
Merchant onboarding
Vortex Payments handles merchant onboarding, processing, and settlement. Before a merchant account can charge or settle, its identity, underwriting, and banking information must be approved.
The flow at a glance
- Create a merchant account —
POST /v1/merchant-accounts. The account starts indraft. - Create a hosted onboarding link —
POST /v1/merchant-accounts/{merchantAccountId}/onboarding-link. Send the URL to the merchant, or use it yourself in sandbox. - Collect and submit data — either through the hosted page or by calling the onboarding API with the complete merchant profile and consent attestation.
- Track requirements — Vortex returns a list of open requirements
(documents, forms, data). Each moves through
pending→submitted→satisfied/failed/waived. - Wait for approval — the session status moves
draft→submitted→under_review→approved. Once approved, the merchant account becomesactive.
If the session lands on action_required, refresh it to see the new
requirements, provide them, and refresh again. rejected and restricted are
terminal states that need operator support.
Required merchant identity
The merchant account create request already captures the basics:
| Field | Purpose |
|---|---|
displayName |
Legal business name |
legalEntityType |
CORPORATION, LLC, PARTNERSHIP, etc. |
country |
ISO country code, e.g. USA |
merchantMode |
processing, payout_only, or hybrid |
defaultCurrency |
Three-letter currency, e.g. USD |
For a live submission you also want:
businessAddressandpersonalAddressbusinessTaxId(EIN) andtaxIddoingBusinessAs,businessPhone,emailfirstName,lastName,dateOfBirthincorporationDatemccandurlsettlementBankAccount(account number, account type, bank code, name)
Required underwriting
processing and hybrid merchants need additional underwriting data.
These fields are required and the percentage distributions must sum to 100.
| Field | Notes |
|---|---|
achMaxTransactionAmount |
Largest ACH transaction you expect |
annualCardVolume |
Estimated annual card volume |
underwriting.annualAchVolume |
Estimated annual ACH volume |
underwriting.averageAchTransferAmount |
Average ACH transfer |
underwriting.averageCardTransferAmount |
Average card transaction |
underwriting.businessDescription |
What the business does |
underwriting.cardVolumeDistribution |
cardPresentPercentage + mailOrderTelephoneOrderPercentage + ecommercePercentage = 100 |
underwriting.refundPolicy |
One of NO_REFUNDS, MERCHANDISE_EXCHANGE_ONLY, REFUNDS_WITHIN_30_DAYS, OTHER |
underwriting.volumeDistributionByBusinessType |
otherVolumePercentage + consumerToConsumerVolumePercentage + businessToConsumerVolumePercentage + businessToBusinessVolumePercentage + personToPersonVolumePercentage = 100 |
Associated identities / beneficial owners
The people behind the business are required. Each associated identity
includes identityRoles and an optional relationType:
control_personbeneficial_ownerrepresentative
Each identity can also carry firstName, lastName, title, email,
phone, taxId, dateOfBirth, and personalAddress.
Consent attestation
An explicit merchant agreement consent is required. The attestation is captured at submission and includes:
merchantAgreementAccepted: truemerchantAgreementAcceptedAt— ISO 8601 UTC timestampmerchantAgreementIpAddress— the merchant’s IPmerchantAgreementUserAgent— the merchant’s user agent
Optional fields include the payment terms, privacy policy, fee disclosure, and terms URLs that the merchant accepted.
Example create a complete merchant
import {
createMerchantAccount,
createVortex,
} from "@vortex-api/vortex/2026-08";
const vortex = createVortex({ apiKey: process.env.VORTEX_API_KEY });
const { data, error } = await createMerchantAccount({
client: vortex,
body: {
environment: "sandbox",
externalMerchantRef: "acme-sandbox",
displayName: "Acme Inc.",
legalEntityType: "CORPORATION",
merchantMode: "processing",
country: "USA",
defaultCurrency: "USD",
businessTaxId: "12-3456789",
businessPhone: "+1 555 0100",
email: "finance@acme.example",
doingBusinessAs: "Acme",
mcc: "5411",
url: "https://acme.example",
achMaxTransactionAmount: 100000,
annualCardVolume: 1000000,
businessAddress: {
line1: "123 Market St",
city: "San Francisco",
region: "CA",
postalCode: "94105",
country: "USA",
},
settlementBankAccount: {
accountNumber: "000123456789",
accountType: "CHECKING",
bankCode: "121000248",
name: "Acme Inc.",
},
associatedIdentities: [
{
identityRoles: ["owner"],
relationType: "beneficial_owner",
firstName: "Alice",
lastName: "Example",
principalPercentageOwnership: 50,
personalAddress: {
line1: "123 Market St",
city: "San Francisco",
region: "CA",
postalCode: "94105",
country: "USA",
},
},
],
underwriting: {
businessDescription: "Retail grocery",
annualAchVolume: 500000,
averageAchTransferAmount: 25000,
averageCardTransferAmount: 75,
refundPolicy: "REFUNDS_WITHIN_30_DAYS",
cardVolumeDistribution: {
cardPresentPercentage: 90,
mailOrderTelephoneOrderPercentage: 0,
ecommercePercentage: 10,
},
volumeDistributionByBusinessType: {
otherVolumePercentage: 0,
consumerToConsumerVolumePercentage: 0,
businessToConsumerVolumePercentage: 100,
businessToBusinessVolumePercentage: 0,
personToPersonVolumePercentage: 0,
},
},
},
});
if (error) throw new Error(JSON.stringify(error));
console.log(data.data.id); // ma_...
Create a hosted onboarding link
Send the merchant a link so they can review and consent in a browser:
import {
createMerchantOnboardingLink,
createVortex,
} from "@vortex-api/vortex/2026-08";
const vortex = createVortex({ apiKey: process.env.VORTEX_API_KEY });
const { data, error } = await createMerchantOnboardingLink({
client: vortex,
path: { merchantAccountId: "ma_..." },
body: {
environment: "sandbox",
createdByRef: "operator:my-user-id",
},
});
if (error) throw new Error(JSON.stringify(error));
console.log(data.data.hostedMerchantOnboardingUrl);
The hosted page shows the merchant the session status, open requirement count, and a refresh control. In sandbox it is the fastest way to verify the end-to-end flow.
Document uploads
When a requirement asks for a document, upload it through Vortex. Vortex relays the bytes upstream, so the merchant never resolves a provider hostname.
- Allowed types:
image/jpeg,image/png,image/tiff,application/pdf - Maximum size: 10 MB
For each requirement, request a pre-signed upload link, PUT the file, then
refresh the requirement status. The exact upload shapes are in the
API reference under the merchant-accounts onboarding routes.
Status reference
Session status
| Status | Meaning |
|---|---|
draft |
Data collection in progress |
submitted |
Submitted for underwriting |
under_review |
Underwriting review in progress |
action_required |
More information needed |
approved |
Merchant can process and settle |
rejected |
Merchant rejected; contact support |
restricted |
Approved with restrictions; contact support |
Requirement status
| Status | Meaning |
|---|---|
pending |
Not yet provided |
submitted |
Provided and awaiting review |
satisfied |
Accepted |
failed |
Rejected; provide again |
waived |
Not required for this merchant |
Sandbox vs. production
Sandbox onboarding runs on test rails. Approval is usually fast, and test merchant data is accepted as long as the fields are well-formed. Production onboarding follows live underwriting and can take longer.
The request body always includes environment: "sandbox" or
environment: "production". The API rejects a body whose environment does not
match the key (vp_test_ or vp_live_), which keeps sandbox and live data
strictly separated.