Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.tagada.io/llms.txt

Use this file to discover all available pages before exploring further.

Sub-merchant provisioning

Every merchant your platform onboards becomes a TagadaPay Account (TPA) — identifier prefix tpa_xxx. The TPA is the unit of KYB, payment routing, settlement, and authorization scope.

What gets created

When you call partners.accounts.create(), three database rows are inserted in one atomic operation:
accounts (acc_xxx)            ← the merchant's private namespace
  ├─ ownerUserId   = NULL         (no Clerk user — never invited automatically)
  ├─ orgClerkName  = legalName
  ├─ kind          = 'partner_managed'
  └─ partner       = 'your-slug'  (provenance tag)

       └─ stores (store_xxx)   ← the auto-provisioned store
              ├─ accountId  → acc_xxx
              ├─ name       = legalName
              ├─ baseCurrency = currency
              └─ type       = 'tagadapay'

                    └─ tagadapay_accounts (tpa_xxx)   ← the TPA
                           ├─ accountId  → acc_xxx
                           ├─ storeId    → store_xxx  (1:1 invariant, mutable)
                           ├─ partnerId  → par_xxx
                           ├─ partnerExternalRef = your_id
                           └─ status     = 'pending_operator_assignment'
The merchant is never auto-invited. No email is sent, no Clerk user is created, no portal access link generated. The accounts row exists only as the data-ownership anchor — it’s invisible to the merchant unless your platform later decides to graduate them to direct TagadaPay access.

Create a sub-merchant

const tpa = await partner.partners.accounts.create({
  legalName: 'Acme SAS',
  country: 'FR',                  // ISO 3166-1 alpha-2
  currency: 'EUR',                // ISO 4217 settlement currency
  externalRef: 'merchant_42',     // YOUR id — used for idempotency
  metadata: {
    plan: 'pro',
    signupSource: 'in-app',
  },
});

// →
// {
//   id: 'tpa_xxx',
//   accountId: 'acc_xxx',
//   storeId: 'store_xxx',
//   partnerId: 'par_xxx',
//   partnerExternalRef: 'merchant_42',
//   status: 'pending_operator_assignment',
//   applicationStatus: 'created',
//   mode: 'live',
//   createdAt: '2026-04-29T...',
// }

Idempotency

If you call create() again with the same externalRef, you get back the same TPA. No duplicates. This makes retries safe:
// Network blip on first call? Just retry.
const tpa = await partner.partners.accounts.create({ legalName, externalRef: 'merchant_42' });
const tpaAgain = await partner.partners.accounts.create({ legalName, externalRef: 'merchant_42' });
console.log(tpa.id === tpaAgain.id);  // true
The idempotency window is unlimited — once a (partnerId, externalRef) pair is recorded, it’s permanent. Choose a stable, unique ref (your own merchant id is the natural choice).

Retrieve / list

const tpa = await partner.partners.accounts.retrieve('tpa_xxx');
const tpaByExternalRef = await partner.partners.accounts.retrieveByExternalRef('merchant_42');

const list = await partner.partners.accounts.list({
  status: ['active', 'pending_operator_assignment'],
  page: 1,
  pageSize: 50,
});

Update a sub-merchant

await partner.partners.accounts.update('tpa_xxx', {
  metadata: { plan: 'enterprise' },
});
You can update metadata, partnerExternalRef, and merchantContractSnapshot fields. Status transitions and KYB outcomes are operator-driven (your account manager moves them, or our auto-router does).

Re-pointing a TPA to a different store

Coming soon. TPA re-pointing — moving a TPA from its auto-provisioned store to a different store inside the same accounts row — is on the roadmap but not yet exposed via the public API. If you have an operational need (rebrand, post-launch storefront swap, …) contact your account manager and we’ll move the binding for you. The SDK method partner.partners.accounts.repointStore() is intentionally stubbed to throw route_not_implemented until the backend route ships, so no partner code can rely on it silently.

KYB requirements

Every newly-created TPA is seeded with a default set of KYB requirements:
CodeCategoryDescription
business.legal_namebusinessExact legal name
business.registration_numberbusinessCompany registration number
business.countrybusinessCountry of registration
business.vat_numberbusinessVAT number (where applicable)
business.websitebusinessPublic URL
representative.full_namerepresentativeLegal representative
representative.dobrepresentativeDate of birth
representative.addressrepresentativeResidential address
documents.identity_proofdocumentsGovernment ID
documents.proof_of_addressdocuments< 3 months old
documents.company_registrationdocumentsRegistration certificate
banking.ibanbankingSettlement IBAN (must match company)
You can customize this set per-partner via applicationPreferences.defaultRequirements — talk to your account manager.

List + satisfy requirements

const requirements = await partner.partners.requirements.list('tpa_xxx');

// Submit a document linked to a specific requirement
await partner.partners.documents.record({
  tagadapayAccountId: 'tpa_xxx',
  requirementCode: 'documents.identity_proof',
  kind: 'identity_proof',
  filename: 'passport-jane-doe.pdf',
  storageUrl: 's3://your-bucket/passport-jane-doe.pdf',  // pre-uploaded
});
Submitting 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.

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 (advanced reporting, dispute responses, etc.), three policy modes exist:
ModeBehavior
'never'Merchant never gets a TagadaPay login. Operators see a warning if they try to invite.
'on_request' (default)Stub accounts row exists, no auto-invite. Invitations are dispatched out-of-band by your account manager — the public partners.accounts.invite() method is on the roadmap and currently throws route_not_implemented.
'auto_invite'Send the merchant a magic link the moment the TPA is created. Use only if the merchant expects a TagadaPay-branded portal.
Set the partner-wide default via applicationPreferences.merchantPortalAccess (talk to your account manager), or override per-account 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 even if an operator clicks the wrong button.

What you cannot do as a partner

ActionWhy
Read another partner’s TPAsPartner key scope: you only see TPAs where partnerId = your_par_xxx
Move a TPA across accounts rowsWould cross merchant entities — refused server-side
Delete a TPATPAs are status-archived (offboarded), never deleted — required for audit/payout history
Edit KYB-locked fields after activationlegalName, country, currency become immutable once the TPA reaches active