1. 7. Payment Providers (PSPs)
Mozarto - The Payments Orchestration Platform
  • 1. Introduction
  • 2. Quick Start
  • 3. Authentication
  • 4. Webhook Setup
  • 5. Errors
  • 6. Redirect flow
    • Pay-In Flow
    • Pay-Out Flow
  • 7. Payment Providers (PSPs)
    • Overview
    • Brite
    • CashLib
    • Cleo
    • Contiant
    • emerchantpay
    • Flexepin
    • ForumPay
    • Gigadat
    • Neosurf
    • OFApay
    • PayModum
    • PayOne
    • PawaPay
    • Trust Payments
    • Worldpay
  • 8. Redirect Flow APIs
    • Redirect Flow APIs - overview
    • Pay-In
      • Brite
      • CashLib
      • Cleo
      • Contiant
      • emerchantpay
      • Flexepin
      • Forumpay
      • Gigadat
      • Neosurf
      • OFApay
      • PayOne
      • PawaPay
      • Trust Payments
      • WorldPay
    • Pay-Out
      • Cleo
      • Forumpay
      • Gigadat withdraw ACH
      • Gigadat withdraw ETO
      • PawaPay
      • PayOne
      • Trust Payments
  • Webhook
    • ForumPay status webhook
    • PayOne status webhook
  • Schemas
    • TransactionData
  1. 7. Payment Providers (PSPs)

PayModum

US bank transfer (ACH) via PayModum, using PayModum Hosted Link. The payer verifies their identity, links their bank, and is screened for risk on PayModum-hosted pages, so no identity documents or bank credentials pass through the Mozarto API. A single Pay-In call returns a hosted-page URL; the multi-step flow (identity verification → AML screening → bank linking → ACH risk decision → processor token) runs server-side and reports progress and the final outcome by webhook.
Operations: Pay-In ✓ | Pay-Out ✓ | Webhook ✓
Processing: Asynchronous - redirect the payer to the hosted page, then receive step and final events by webhook
Payment type: Bank transfer (US ACH), fund movement handed off to Sponsor Bank
Fields marked with * are required.

How the flow works#

A Pay-In moves through these steps. The payer only ever sees PayModum-hosted pages; Mozarto orchestrates the steps and emits a webhook at each meaningful point.
#StepWhat happensMerchant webhook
1Identity VerificationPayer verifies identity on PayModum Hosted Link. Skipped when the same payer's latest verification already passed and their first name, last name, email, and phone matchidentity_verification.failed on decline
2AML / watchlist screeningStandalone PayModum Monitor screening (runs on every transaction)watchlist.failed if not cleared
3Bank linkingMozarto creates the bank Hosted Link and hands its URL to the merchant; payer links their bank on PayModumbank_link.required (action needed), bank_link.failed on definitive link failure
4ACH risk decision (Signal)PayModum Signal scores the transaction against the configured ruleset - ACCEPT proceeds, REROUTE / REVIEW declinerisk_check.declined on decline
5Processor tokenOn accept, Mozarto creates a Sponsor Bank processor token and hands off. Fund movement is executed by Sponsor Bank (separate)payment.initiated
Every failure path also has the generic payment.failed fallback. See Webhook.

Configuration (Mozarto back office)#

FieldDescription
clientId *PayModum client_id (stored encrypted)
secret *PayModum secret for the environment (stored encrypted)
idvTemplateId *PayModum Identity Verification template id (idvtmp_...)
watchlistProgramIdPayModum Monitor program id (prg_...) for AML/watchlist screening. When empty, screening is skipped
accountName *Display name shown on the PayModum hosted pages
webhookUrl *Your HTTPS endpoint for step and final webhooks
siteUrlFallback return URL for the payer after the hosted flow
Signal ruleset: the ACH risk decision uses the PayModum Signal ruleset whose key is default. Create a Signal Transaction Score ruleset in the PayModum Dashboard and assign it to your environment; otherwise /signal/evaluate returns scores but no ACCEPT/REROUTE/REVIEW decision and the transaction proceeds on scores alone.

Pay-In#

POST /v1/api/mozarto/cashier
Authentication is required on every cashier call. See Authentication.
The cashier call does not move money. It creates a transaction and returns a paymentUrl (a PayModum Hosted Link URL). Redirect the payer to that URL; the identity, bank-linking, and risk steps run on PayModum-hosted pages and are orchestrated by Mozarto. Track progress and the outcome through the webhooks.

Request body#

FieldTypeDescription
pspType *stringAlways "PAYMODUM"
type *stringAlways "PAYIN"
baseamount *numberAmount in the currency's main unit, up to 2 decimals (e.g. 200 = 200 USD)
currency *stringISO 4217 currency code ("USD")
country *stringISO 3166-1 alpha-2 country ("US")
firstName *stringPayer first name - used to prefill Identity Verification and to match a prior verification
lastName *stringPayer last name
email *stringPayer email
phonestringPayer phone in E.164 (e.g. "+14155550123")
dobstringDate of birth, YYYY-MM-DD
ssnstringUS SSN (9 digits) - prefills the KYC data-source check
addressstringStreet address
citystringCity
statestringState / region
zipCodestringZIP / postal code
merchantReferencestringYour order reference, echoed back on every webhook
webhookUrlstringHTTPS endpoint for this transaction's webhooks. Overrides the configured webhookUrl
siteUrlstringReturn URL for the payer after the hosted flow. Overrides the configured siteUrl
Identity re-use / skip: a returning payer whose latest Identity Verification already passed skips step 1 - but only when firstName, lastName, email, and phone all match the previously verified identity. Any mismatch (or a first-time payer) runs verification.

Example request#

{
  "pspType": "PAYMODUM",
  "type": "PAYIN",
  "baseamount": 200,
  "currency": "USD",
  "country": "US",
  "firstName": "Leslie",
  "lastName": "Knope",
  "email": "leslie.knope@example.com",
  "phone": "+14155550123",
  "dob": "1975-01-18",
  "ssn": "123456789",
  "address": "123 Main St",
  "city": "San Francisco",
  "state": "CA",
  "zipCode": "94111",
  "merchantReference": "ORDER-4821",
  "webhookUrl": "https://example.com/f2e721ba-0ceb-40ea-a388-56e2f584bb7a"
}

Response#

{
  "status": "success",
  "message": "Successfully",
  "isSuccess": true,
  "data": {
    "transaction_id": "6a95bf514824638671769f64",
    "merchantReference": "ORDER-4821",
    "amount": 200,
    "transaction_status": "Initiated",
    "paymentUrl": "https://link.paymodum.com/link/..."
  }
}
FieldTypeDescription
paymentUrlstringPayModum Hosted Link URL - redirect the payer here
transaction_idstringMozarto transaction identifier. Use it to match webhooks
merchantReferencestringYour reference from the request, or an empty string if none was sent
amountnumberThe transaction baseamount
transaction_statusstringAlways "Initiated" at this point. Progress and the final outcome arrive by webhook
Redirect the payer to paymentUrl. When Identity Verification passes and the bank Hosted Link is ready, Mozarto also sends a bank_link.required webhook carrying the bank URL (see below).

Pay-Out#

POST /v1/api/mozarto/cashier
Set type to "PAYOUT". Pay-Out skips Identity Verification (the payee is an existing user) and links the payee's bank to produce a Sponsor Bank processor token; the actual credit is executed by Sponsor Bank. The request returns a paymentUrl and transaction_status: "Initiated" exactly like Pay-In.

Request body#

FieldTypeDescription
pspType *stringAlways "PAYMODUM"
type *stringAlways "PAYOUT"
baseamount *numberAmount to credit, in the currency's main unit (e.g. 200 = 200 USD)
currency *stringISO 4217 currency code ("USD")
country *stringPayer's country - full name, ISO 3166-1 alpha-2, or alpha-3 (e.g. "Germany", "DE", "DEU"). Matched against the account's Processing Scope. Omit it and Mozarto geo-locates the caller's IP instead - see Country
merchantReferencestringYour reference, echoed back on every webhook
webhookUrlstringHTTPS endpoint for this transaction's webhooks. Overrides the configured webhookUrl
siteUrlstringReturn URL for the payee after linking their bank. Overrides the configured siteUrl

Example request#

{
  "pspType": "PAYMODUM",
  "type": "PAYOUT",
  "baseamount": 200,
  "currency": "USD",
  "country": "US",
  "merchantReference": "PAYOUT-7723"
}

Response#

{
  "status": "success",
  "message": "Successfully",
  "isSuccess": true,
  "data": {
    "transaction_id": "6a97e94a4608b3c3f6d46d6d",
    "merchantReference": "PAYOUT-7723",
    "amount": 200,
    "transaction_status": "Initiated",
    "paymentUrl": "https://link.paymodum.com/link/..."
  }
}
FieldTypeDescription
paymentUrlstringPayModum Hosted Link URL - redirect the payee here to link their bank
transaction_idstringMozarto transaction identifier. Use it to match webhooks
merchantReferencestringYour reference from the request, or an empty string if none was sent
amountnumberThe transaction baseamount
transaction_statusstringAlways "Initiated" at this point. The final outcome arrives by webhook

Webhook#

Mozarto calls your webhookUrl at each meaningful step and at the final outcome. Every webhook uses one self-describing envelope: read event and step to know what happened, data for step-specific detail, and status / transaction_status for the outcome. The flat transaction fields are always present for backward compatibility.
{
  "event": "bank_link.required",
  "step": "bank_link",
  "psp": "PAYMODUM",
  "timestamp": "2026-09-02T09:16:48.330Z",
  "data": {
    "action": "redirect",
    "hosted_link_url": "https://link.paymodum.com/hl/..."
  },
  "transaction_id": "6a97e94a4608b3c3f6d46d6d",
  "psp_transaction_id": "",
  "user_id": "66f2612b330f684f99a633cb",
  "status": "action_required",
  "transaction_status": "Initiated",
  "amount": 200,
  "currency": "USD",
  "merchantReference": "ORDER-4821"
}

Envelope fields#

FieldDescription
eventThe specific thing that happened - see the event catalog below
stepThe flow stage the event belongs to: identity_verification, watchlist, bank_link, signal, or settlement
pspAlways "PAYMODUM"
timestampISO 8601 time the event was emitted
dataStep-specific detail (varies by event - see catalog)
transaction_idMozarto transaction identifier from the Pay-In/Pay-Out call
psp_transaction_idPayModum Item id once the bank is linked, otherwise empty
user_idPayer identifier resolved from the transaction
statussuccess, failed, or action_required
transaction_statusNormalized outcome - Initiated or Failed. Use this for business logic
amountTransaction amount in the currency's major unit
currencyISO 4217 currency code
merchantReferenceThe reference you passed in the original request - empty string if none

Event catalog#

eventstepstatusdataWhen
bank_link.requiredbank_linkaction_requiredaction, hosted_link_urlIdentity passed; redirect the payer to the bank Hosted Link
payment.initiatedsettlementsuccesspsp_transaction_idBank linked, risk accepted, processor token created - handed off for settlement
identity_verification.failedidentity_verificationfailedreason, idv_statusIdentity verification declined (failed / pending_review / expired)
watchlist.failedwatchlistfailedreason, watchlist_statusAML/watchlist screening not cleared
bank_link.failedbank_linkfailedreason, error_code, error_messageBank Link session ended with an unrecoverable error and no linked account
risk_check.declinedsignalfailedreason, ruleset_result, customer_return_risk, bank_return_riskSignal returned REROUTE / REVIEW
payment.failedsettlementfailedreasonGeneric processing failure
Superseded verifications are silent. When a payer starts a new transaction that supersedes an in-progress verification, PayModum reports the old attempt as canceled. Mozarto closes that old transaction internally but does not send a webhook for it, so you won't receive a spurious failure for an abandoned attempt.

Delivery: synchronous vs webhook#

Not every step produces a webhook - some are decided synchronously and returned in the Pay-In/Pay-Out response instead. Which path a step takes depends on whether Identity Verification runs:
First-time payer (verification runs): the cashier response returns the IDV hosted URL. Identity, screening, and the bank-link hand-off are then decided asynchronously and reported by webhook (identity_verification.failed, watchlist.failed, bank_link.required).
Returning verified payer (verification skipped): identity is re-used and screening runs at cashier time, so the cashier response returns the bank hosted URL directly. There is no identity_verification.failed, watchlist.failed, or bank_link.required webhook - a screening decline is returned synchronously as a cashier error instead.
Both paths converge at the bank step: once the payer links their bank, bank_link.failed, risk_check.declined, payment.initiated, or payment.failed is delivered by webhook.
Pay-Out skips identity, screening, and Signal - the cashier response returns the bank URL, then payment.initiated (or bank_link.failed / payment.failed) by webhook.

Webhook sequence by scenario#

Webhooks arrive in the order shown. "(response)" means that step's URL/decision is in the cashier response, not a webhook.
ScenarioWebhooks (in order)
Pay-In accepted - first-time payerbank_link.required → payment.initiated
Pay-In accepted - returning verified payer(bank URL in response) → payment.initiated
Identity verification declinedidentity_verification.failed
Watchlist not cleared - first-time payerwatchlist.failed
Watchlist not cleared - returning verified payer(none - synchronous cashier error)
Bank link failed - first-time payerbank_link.required → bank_link.failed
Bank link failed - returning verified payer(bank URL in response) → bank_link.failed
Risk declined (REROUTE / REVIEW)bank_link.required* → risk_check.declined
Processing error (exchange / token / other)payment.failed
Superseded / abandoned verification(none - closed silently)
Pay-Out accepted(bank URL in response) → payment.initiated
*bank_link.required precedes it only for a first-time payer; for a returning verified payer the bank URL was in the response.

Example payloads (every event)#

bank_link.required - identity passed, redirect the payer to the bank Hosted Link:
{
  "event": "bank_link.required",
  "step": "bank_link",
  "psp": "PAYMODUM",
  "timestamp": "2026-09-02T09:16:48.330Z",
  "data": { "action": "redirect", "hosted_link_url": "https://link.paymodum.com/hl/..." },
  "transaction_id": "6a97e94a4608b3c3f6d46d6d",
  "psp_transaction_id": "",
  "user_id": "66f2612b330f684f99a633cb",
  "status": "action_required",
  "transaction_status": "Initiated",
  "amount": 200,
  "currency": "USD",
  "merchantReference": "ORDER-4821"
}
payment.initiated - bank linked, risk accepted, processor token created (final success):
{
  "event": "payment.initiated",
  "step": "settlement",
  "psp": "PAYMODUM",
  "timestamp": "2026-09-02T09:17:30.512Z",
  "data": { "psp_transaction_id": "aY7Bd... (PayModum Item id)" },
  "transaction_id": "6a97e94a4608b3c3f6d46d6d",
  "psp_transaction_id": "aY7Bd... (PayModum Item id)",
  "user_id": "66f2612b330f684f99a633cb",
  "status": "success",
  "transaction_status": "Initiated",
  "amount": 200,
  "currency": "USD",
  "merchantReference": "ORDER-4821"
}
identity_verification.failed - identity declined (idv_status: failed / pending_review / expired):
{
  "event": "identity_verification.failed",
  "step": "identity_verification",
  "psp": "PAYMODUM",
  "timestamp": "2026-09-02T07:17:06.945Z",
  "data": { "reason": "Identity verification failed", "idv_status": "failed" },
  "transaction_id": "6a97cd204608b3c3f6d46a3c",
  "psp_transaction_id": "",
  "user_id": "66f2612b330f684f99a633cb",
  "status": "failed",
  "transaction_status": "Failed",
  "amount": 200,
  "currency": "USD",
  "merchantReference": "ORDER-4821"
}
watchlist.failed - AML/watchlist screening not cleared (watchlist_status: pending_review / rejected):
{
  "event": "watchlist.failed",
  "step": "watchlist",
  "psp": "PAYMODUM",
  "timestamp": "2026-09-02T08:02:11.004Z",
  "data": { "reason": "Watchlist screening pending_review", "watchlist_status": "pending_review" },
  "transaction_id": "6a97d0604608b3c3f6d46bcc",
  "psp_transaction_id": "",
  "user_id": "66f2612b330f684f99a633cb",
  "status": "failed",
  "transaction_status": "Failed",
  "amount": 200,
  "currency": "USD",
  "merchantReference": "ORDER-4821"
}
bank_link.failed - the bank Link session ended with an unrecoverable error and no linked account:
{
  "event": "bank_link.failed",
  "step": "bank_link",
  "psp": "PAYMODUM",
  "timestamp": "2026-09-07T12:24:11.446Z",
  "data": {
    "reason": "Bank linking failed: INSTANT_MATCH_FAILED",
    "error_code": "INSTANT_MATCH_FAILED",
    "error_message": "Item cannot be verified through Instant Match."
  },
  "transaction_id": "6a9eacabd5324bac369295ee",
  "psp_transaction_id": "",
  "user_id": "66f2612b330f684f99a633cb",
  "status": "failed",
  "transaction_status": "Failed",
  "amount": 200,
  "currency": "USD",
  "merchantReference": "ORDER-4821"
}
risk_check.declined - Signal returned REROUTE or REVIEW:
{
  "event": "risk_check.declined",
  "step": "signal",
  "psp": "PAYMODUM",
  "timestamp": "2026-09-02T09:15:59.959Z",
  "data": {
    "reason": "Signal risk decision: REROUTE",
    "ruleset_result": "REROUTE",
    "customer_return_risk": 72,
    "bank_return_risk": 98
  },
  "transaction_id": "6a97e94a4608b3c3f6d46d6d",
  "psp_transaction_id": "",
  "user_id": "66f2612b330f684f99a633cb",
  "status": "failed",
  "transaction_status": "Failed",
  "amount": 200,
  "currency": "USD",
  "merchantReference": "ORDER-4821"
}
payment.failed - generic processing failure (token exchange, processor token, or other error):
{
  "event": "payment.failed",
  "step": "settlement",
  "psp": "PAYMODUM",
  "timestamp": "2026-09-02T09:18:02.771Z",
  "data": { "reason": "PayModum psp.paymodum.processorTokenCreate failed: ..." },
  "transaction_id": "6a97e94a4608b3c3f6d46d6d",
  "psp_transaction_id": "",
  "user_id": "66f2612b330f684f99a633cb",
  "status": "failed",
  "transaction_status": "Failed",
  "amount": 200,
  "currency": "USD",
  "merchantReference": "ORDER-4821"
}

Idempotency & retries#

Every step transition is guarded by an atomic claim, so each event is emitted once even when PayModum delivers overlapping webhooks (e.g. STEP_UPDATED + STATUS_UPDATED) or the payer's browser and PayModum's webhook race.
Treat webhooks as at-least-once: dedupe on transaction_id + event, and use transaction_status as the source of truth for the outcome.
bank_link.required carries an action_required status - it is an interim prompt, not a terminal state; the transaction is still Initiated.
The request carries an Authorization header with a Mozarto-generated signature token. Verify it before applying any balance or state change. See Webhook setup.

Transaction statuses#

transaction_statusMeaning
InitiatedCreated and progressing (verified/tokenized). Real settlement statuses arrive from the fund-movement layer (Sponsor Bank) separately
FailedDeclined at any step (identity, screening, bank link, or risk) or a processing error
Risk scores from the Signal step are stored on the transaction and shown in the transaction grid and detail view as Customer Return Risk and Bank Return Risk.

Sandbox testing#

ItemValue
Test identityLeslie Knope - DOB 1975-01-18, phone +12345678909, SSN 123456789
Bank login (Auth-friendly)Institution First Platypus Bank / Tartan Bank, username user_good, password pass_good, OTP 1234
Forced Auth failureInstitution First Gingham Credit Union triggers INSTANT_MATCH_FAILED (tests the bank_link.failed path)
Gated products: PayModum Monitor (watchlist screening) and Signal rulesets must be enabled/created for your environment. Leave watchlistProgramId empty to skip screening while Monitor access is pending.

Error codes#

Configuration and provider transport failures are mapped to normalized Mozarto errorCode values.
Mozarto errorCodeWhen
PSP_NOT_CONFIGUREDclientId, secret, or idvTemplateId not set for this brand
Step-level declines (identity, screening, bank link, risk) are reported through the webhook event catalog above rather than as cashier error codes. For the full list of errorCode values and retryability guidance, see Errors.
Modified at 2026-09-14 11:35:04
Previous
OFApay
Next
PayOne
Built with