Managing Multiple Organizations (B2B2B)
If you’re a platform (ERP, POS, commerce, fintech) that onboards multiple businesses onto Yona, this guide explains multi-tenancy.
The model
Your platform is a primary organization. Each business you onboard is a secondary (child) organization with isolated data, its own API key, and its own billing.
┌──────────────────────────────────────────────────────────────┐
│ │
│ PRIMARY ORGANIZATION (Your Platform) │
│ API Key: sk_live_partner_... │
│ │
│ Permissions: │
│ ✓ Create and manage child organizations │
│ ✓ Create, rotate, and revoke child API keys │
│ ✓ Manage child org users and webhooks │
│ ✓ Configure credit pool and transfer credits │
│ ✗ Cannot access child invoices, sellers, or buyers │
│ ✗ Cannot read child billing data │
│ │
│ Access methods: │
│ partner.forOrganization(childOrgId) │
│ → API keys, users, webhooks only │
│ partner.forApiKey(childKey, childOrgId) │
│ → Invoices, sellers, buyers, billing │
│ │
├──────────────────┬──────────────────┬────────────────────────┤
│ │ │ │
│ CHILD ORG A │ CHILD ORG B │ CHILD ORG C │
│ │ │ │
│ Own API key │ Own API key │ Own API key │
│ Own sellers │ Own sellers │ Own sellers │
│ Own buyers │ Own buyers │ Own buyers │
│ Own invoices │ Own invoices │ Own invoices │
│ Own billing │ Own billing │ Own billing │
│ │ │ │
│ Data is fully │ Data is fully │ Data is fully │
│ isolated from │ isolated from │ isolated from │
│ other children │ other children │ other children │
│ │ │ │
└──────────────────┴──────────────────┴────────────────────────┘
Credit flow:
Primary ──── transferCredits() ───→ Child A
Primary ──── transferCredits() ───→ Child B
Primary ──── transferCredits() ───→ Child C
OR set primaryCoversSecondary: true
(primary pool pays for all child usage automatically)1. Initialize your partner client
const partner = new EInvoice({
apiKey: 'sk_live_partner_key',
organizationId: 'your_partner_org_id',
});2. Onboard a child organization
const { organization, apiKey } = await partner.createOrganizationWithApiKey(
{ businessName: 'Acme Nigeria', email: 'admin@acme.ng' },
{ mode: 'production', name: 'acme-primary-key' },
);
3. Operate as the child org
Use forApiKey() for data operations (invoices, sellers, buyers, billing):
const acme = partner.forApiKey(apiKey.key, organization.id);
const seller = await acme.sellers.create({ /* ... */ });
const buyer = await acme.buyers.create({ /* ... */ });
const invoice = await acme.invoices.create({ /* ... */ });
await acme.invoices.submit(invoice.data.id);4. Admin operations with your partner key
Use forOrganization() for admin tasks (API keys, users, webhooks):
const acmeAdmin = partner.forOrganization(organization.id);
await acmeAdmin.apiKeys.list();
await acmeAdmin.users.list();
await acmeAdmin.webhooks.create({ /* ... */ });forOrganization() only works for API keys, users, and webhooks. For invoices, sellers, buyers, and billing, use forApiKey() instead.
5. Configure credit pool
await partner.organizations.updateCreditPoolConfig({
primaryCoversSecondary: true,
minimumReserve: 500,
});When set to true, the primary organization’s credit pool pays for all child org usage. When false, each child must have its own credits.
| Config | Behavior |
|---|---|
primaryCoversSecondary: true | Primary’s pool pays for everything |
primaryCoversSecondary: false, fallbackToPrimary: false | Child uses own pool only |
primaryCoversSecondary: false, fallbackToPrimary: true | Child first, primary covers shortfall |
6. Transfer credits
await partner.billing.transferCredits({
targetOrganizationId: organization.id,
amount: 1000,
description: 'Monthly credit allocation',
});Both organizations must be in payg or hybrid billing mode.