Skip to main content

Processing provisioning (TPAs)

To process payments through TagadaPay, a merchant needs a TagadaPay Account (TPA) — identifier prefix tpa_xxx. The TPA is the unit of KYB, payment routing, settlement, and authorization scope on the Processing domain (/api/tagadapay/v1/partner/*).
CRM-only partners (subscribed_products has crm but not payfac) cannot call tpas.create() (403 payfac_required). Submit an entity application instead — jump to Who assigns the acquirer → CRM-only, or the shared field reference in Apply for TagadaPay Processing.
A merchant (acc_xxx) can own 0..N TPAs (tpa_xxx). One TPA per settlement region is the common pattern (e.g. an EU TPA and a US TPA under the same merchant). Provision a merchant first, then attach TPAs to it via accountId.

What gets created

When you create a TPA, a convenience store is auto-provisioned for the merchant. Store ↔ processor routing is handled by the CRM payment orchestration layer — the TPA and store are independent entities. Discover the store via stores.list({ accountId }). If you pass an accountId, the TPA attaches to that existing merchant; otherwise the surface infers/creates the owning merchant from the calling key.
The merchant is never auto-invited to TagadaPay. No welcome email and no portal login are created automatically. Your platform remains the primary interface unless you later request merchant portal access through your account manager.

The onboarding sequence

Driving a TPA from creation to active is always the same five steps, in order:
Step 4 is not optional. Creating the TPA, filling requirements and uploading documents only stages the data on Tagada’s side — nothing is sent to the payment processor until you call tpas.provision(). A TPA that never receives this call stays pending_provisioning forever. provision() is idempotent: re-calling it is always safe and only replays incomplete steps.
Step 5 is not optional either. A live TPA cannot activate without a signed Merchant Service Agreement — this cannot be waived for platform merchants. The signing request is generated and emailed automatically (no API call needed), branded to your partnership tier (see The merchant agreement below). The provision() response exposes the state in contract: relay contract.signingUrl to your merchant instead of relying on their inbox. Test-mode TPAs and MoR sub-merchants are exempt.
Two requirements deserve special attention because forgetting them is the most common cause of stuck onboardings:
  • banking.* (bank account) — without it the merchant can never be paid out. Submit it with the other requirements, don’t defer it.
  • Documents — after provision() runs, call documents.list() and look for status: 'rejected': the rejectionReason tells you exactly what to fix.

Create a TPA

US businesses are supported. Pass country: 'US' (and typically currency: 'USD') — the KYB requirement set adapts to the entity’s country (e.g. EIN + US bank details instead of an EU VAT number + IBAN). The rest of the flow (requirements → documents → provision()) is identical.

Who assigns the acquirer — two partner situations

What happens after processing intake depends on whether you have signed payfac / processing terms with TagadaPay:
You have an active buy-rate annex + PSP schedule for at least one acquirer (acquirers.list() returns them). Two options:
  • Pin the acquirer yourself: pass acquirer: 'adyen' at create(). The TPA skips the operator queue and goes straight to pending_provisioning — you own the full self-serve path to active.
  • Let the router pick: omit acquirer and, if your partner profile has a default routing config, the TPA is auto-assigned from your signed set.
Your economics apply: Tagada’s buy rate + your markup.For KYB UX you can still use Embed onboarding (iframe) — same draft/entityId/submit lifecycle as tagadapay.io/apply, partner-branded — or push data yourself via requirements / documents (API intake).

Capability matrix — CRM-only vs processing partner

The SDK surface is mostly shared. What changes for CRM-only is that entity applications replace shell tpas.create:

Idempotency

Calling create() again with the same externalRef returns the same TPA — no duplicates, safe to retry:
The (partnerId, externalRef) pair is permanent. Use a stable, unique ref — when a merchant has multiple TPAs, suffix your own id (merchant_42_eu, merchant_42_us).

Per-merchant pricing (markup)

Each merchant’s price is Tagada’s buy rate (your signed annex) + a markup — your margin. By default the markup is your partner-level grid (see acquirers.list()), but you can price any merchant individually:
  • Pass markup at tpas.create() (above), or
  • Set / change it at any time — the new price applies to the very next charge:
Rules to know:
  • Wholesale replacement, not a merge. A per-TPA markup replaces your partner-level grid entirely for that merchant — fields you omit mean “no margin on that fee”. updateMarkup('tpa_xxx', {}) prices the merchant at Tagada’s buy rate (zero margin for you).
  • Additive only. Every field must be ≥ 0 — you can never price a merchant below Tagada’s signed buy rate.
  • Instant. The call refreshes the TPA’s charge-time price snapshot before returning; sellRate in the response is what the merchant pays from the next charge on.
  • Your margin is carved out per charge and lands on your partner balance account automatically — see Payouts, balances & fees.
REST: GET | PUT | DELETE https://api.tagadapay.io/api/tagadapay/v1/partner/tpas/:id/markup.

Retrieve / list

There is no general “update TPA fields” endpoint. Status transitions and KYB outcomes are operator-driven (your account manager moves them, or our auto-router does), so they are not settable from the SDK. Store ↔ processor routing is handled by the CRM payment orchestration layer.

Mint a Processing Key

A Processing Key (tp_sk_…) is scoped to this one TPA and used for charging and reporting.
See API keys & authentication for the full key model, and Server-to-server payments to charge with it.

KYB requirements

Every newly-created TPA is seeded with a default set of KYB requirements:
US merchants: the US has no separate company registration number, so the federal EIN (e.g. 98-1923816) is both business.tax_id and business.registration_number — submit the same 9-digit value for both.
You can customize this set per-partner via applicationPreferences.defaultRequirements — talk to your account manager.

List + satisfy requirements

The requirement flips to pending_verification — never directly to satisfied. Tagada ops (or a downstream provider) verify the value before it becomes satisfied. Document requirements (documents.*) are satisfied by documents.record(), not this call. The most reliable way to submit a document is to send the bytes directly — no storage URL to keep alive:

Record a document by URL

Alternatively, record() a storageUrl. The URL must be fetchable server-to-server at the moment you call record() — we download and pin a copy immediately. Private buckets or already-expired presigned URLs are rejected with 422 document_unreachable (a presigned URL that is valid now is fine, even if it expires later). The signature is record(tpaId, params):

Accepted kind values

kind is validated at the API boundary: an unknown value (e.g. your own internal enum like INCORPORATION_CERTIFICATE) is rejected immediately with 400 invalid_document_kind listing the accepted values — it can never sit in the pipeline and fail silently later. Obvious variants are normalized for you (case/separators, certificate_of_incorporationcompany_registration, driving_licensedrivers_license, …), but send the canonical value. Specific identity kinds (passport, drivers_license, national_id) are also accepted when you know the exact document type.

Validation & idempotency

Recording a document linked to a requirement automatically flips that requirement to pending_verification. We don’t auto-approve — manual review (or processor-driven verification) flips it to satisfied.
Check documents.list() after provisioning runs: a document whose delivery to the payment processor failed shows status: 'rejected' with a rejectionReason explaining what to fix (unreachable URL, missing front side, unsupported kind…). A document stuck without an accepted status blocks activation.

Run provisioning

Once requirements and documents are in, call provision() — and treat it as a converge loop, not a one-shot:
Fix what the response points at, then call provision() again — completed steps are never redone.

The merchant agreement (mandatory signature)

Signing the Merchant Service Agreement is a hard activation condition for every live TPA. You never send it yourself: once provisioning completes, Tagada emails the signing request to the merchant representative automatically. How the contract is branded depends on your partnership tier:
The TPA flips to active automatically the moment the required signatures are in — no further API call needed. Test-mode TPAs and MoR sub-merchants never require a signature (required: false).

Common integration mistakes


Merchant portal access policy

By default, sub-merchants have no TagadaPay portal access — they use your platform, not ours. If you ever want a specific merchant to be able to log in directly to TagadaPay, three policy modes exist: Set the partner-wide default via applicationPreferences.merchantPortalAccess (talk to your account manager), or override per-TPA at creation time.
For the typical embedded-payments partner (your merchants don’t know TagadaPay exists), 'never' is the right setting and prevents any accidental email leak.

What you cannot do as a partner