White-Label Platform Setup: Tenants, Access, Accounting
You run a platform — vertical SaaS, a marketplace, a franchise back office — and your customers need books. They should never see our name on them. This guide walks the setup that makes that work: one isolated company per customer, roles that match your product's permission model, tokens scoped so nobody can read anyone else's ledger.
One thing to be clear about before we start, because vendors are routinely vague here. White-label on Paprel today means two delivery models:
- Headless API under your brand. Your UI, your domain, your design system. Paprel is the accounting engine behind your endpoints; your customers interact only with surfaces you built.
- Hosted surfaces. Paprel-hosted workspace views your customers can be handed access to, for the parts of accounting you don't want to rebuild.
Embeddable UI components — drop-in React/Vue widgets you mount inside your own app — are on the roadmap, not shipping today. If a component library is a hard requirement for your launch, talk to us before you commit; don't design around something that doesn't exist yet. Commercial terms for white-label and private deployment are scoped in the Custom tier — see pricing.
What follows is the headless path, end to end.
What you need:
- Sandbox keys — self-serve signup
curlor any HTTP client- The API reference open in another tab for exact schemas
export ACCESS_TOKEN="<from the credential exchange>"
export BASE="https://api-sandbox.paprel.com"
export AUTH="authorization: Bearer $ACCESS_TOKEN"
export CT="content-type: application/json"
First time? The five-minute setup — token exchange and request signing — is on the Authentication page. The curl examples below show the auth header only; write requests also require an x-data-signature header, which the SDK computes for you.
Step 1 — Provision a tenant
A company is the tenant boundary in Paprel: the books, the reporting entity, the isolation unit. In a white-label setup you create one per customer of your platform, at whatever moment in your onboarding makes sense — signup, first paid plan, first invoice, your call.
The field that makes this white-label rather than just multi-tenant is partner_id: it associates the company with your partner account, so every tenant you provision is attributed to your platform rather than floating as an independent signup.
curl -X POST $BASE/v1/company \
-H "$AUTH" -H "$CT" \
-d '{
"company_name": "Harbor Coffee Roasters LLC",
"company_type": "…",
"contact_email": "owner@harborcoffee.example",
"contact_first_name": "Mei",
"contact_last_name": "Tan",
"currency_id": "…",
"timezone": "America/New_York",
"locale": "en",
"address_country": "US",
"address_city": "Portland",
"partner_id": "<your partner id>"
}'
Endpoint reference: POST /v1/company →
The response returns the created company record — persist its id against your own customer record. It scopes every call you make on this tenant's behalf from here on.
Behind the scenes: the new company comes up with isolated books — its own ledger, its own document numbering, and a default chart of accounts seeded from a standard taxonomy (assets, liabilities, equity, income, expenses). Isolation is enforced at the data layer, not just in application code: there is no query a token scoped to one tenant can write that reads another tenant's rows. For a platform operator this is the property that matters most, because the failure mode it prevents — one customer seeing another customer's revenue — is the one you don't recover from.
GET /v1/company/{id} reads the record back; PUT /v1/company/{id} updates it (renames, address changes, contact handoffs).
Step 2 — Define roles and grant access
Your product almost certainly has its own idea of who can do what — owner, bookkeeper, read-only accountant, branch manager. Map that onto Paprel roles per tenant instead of giving everyone the keys.
Start by reading what's grantable. Permissions are organized by module (invoicing, expenses, banking, reports, and so on):
curl $BASE/v1/acl/permission -H "$AUTH"
Endpoint reference: GET /v1/acl/permission →
(There's also GET /v1/acl/module for the module list on its own.)
Then create a role. role_permission carries the permission grants you selected from the previous call — the exact serialization is in the API reference:
curl -X POST $BASE/v1/acl/role \
-H "$AUTH" -H "$CT" \
-d '{
"role_name": "Bookkeeper",
"role_description": "Full invoicing and expenses, read-only reports, no banking",
"is_active": true,
"role_permission": "…"
}'
Endpoint reference: POST /v1/acl/role →
Keep the returned role id. GET /v1/acl/role/{id} reads it back, PUT /v1/acl/role/{id} updates it as your product's permission model evolves, DELETE /v1/acl/role/{id} retires it.
Behind the scenes: roles live inside the tenant, which is the right place for them. A "Bookkeeper" role in Harbor Coffee's company says nothing about, and grants nothing in, any other tenant. When your product adds a new permission tier, you update role definitions per company — and because the permission list is module-shaped, the mapping from your product's roles to accounting capabilities stays legible: this role can touch invoices, that one can only read reports.
The design thinking behind least-privilege role shapes for accounting — why "can create invoices" and "can see the P&L" should be separable, what an agent-facing role looks like — is covered in OAuth scopes and permissions for embedded accounting platforms.
Step 3 — Scoped API access
Auth is OAuth 2.0/2.1 with PKCE. We're not going to paste token-endpoint curl here, because the flow you use depends on whether the caller is your backend, a user-facing session, or an agent — the API reference walks each one. What matters for white-label architecture is the shape of the model:
- Tokens are tenant-scoped. A token is minted for one company. It cannot reach across tenants, regardless of what your application code does with it.
- Grants are per-route. Within the tenant, the token carries the permissions of the role behind it — a token for the Bookkeeper role from step 2 can create invoices and cannot touch banking, enforced at the API boundary, not in your middleware.
- PKCE everywhere. Authorization-code interception doesn't work even for public clients, which matters once your mobile or browser-based surfaces talk to the API directly.
The practical consequence: your platform backend holds whatever broad credentials it needs for provisioning, and everything downstream — user sessions, embedded agents, third-party tools your customers connect — runs on narrow, per-tenant, per-role tokens. When one leaks, the blast radius is one role's view of one company's books.
Behind the scenes: this is the same model that makes the MCP/agent story safe. An AI agent acting for one of your customers gets a scoped token like any other client; the things it can't do are the things the token can't do. You don't have to trust prompt engineering to protect the ledger.
Step 4 — What every tenant gets
At this point each customer of yours has a company, roles, and scoped access. Here's what's actually inside the box you handed them:
- A seeded chart of accounts. Working from minute one, extensible over the API — see steps 2 and 8 of the 20-minute guide for the create-account and read-the-ledger flow. You can layer your vertical's account structure on top of the default taxonomy at provisioning time.
- An isolated double-entry ledger. Every invoice, payment, and expense posts balanced journals into this tenant's books and nobody else's. Debits equal credits or the posting is rejected.
- Reports derived from the ledger. P&L, balance sheet, trial balance — generated live from journal lines, never reconciled against them. Your customers' accountants can tie everything out.
- An audit trail. Document versioning and activity history per tenant: who created what, when it was sent, what each action posted.
- Connections.
GET /v1/company/connectionslists a company's connection mappings to other companies, enriched with company metadata — the read you'll want when building an admin view of how a tenant's books relate to other entities. (PUT /v1/company/connections/{id}updates a mapping.)
All of it is API-first, which means all of it can sit behind your brand: your customers see your invoice screens, your reports page, your domain — and underneath it, real books.
Honest scope: what's today vs. roadmap
Worth restating plainly, because this page will get cited in your vendor evaluation:
Today: headless API under your brand, hosted surfaces, tenant provisioning with partner_id attribution, per-tenant roles and permissions, tenant-scoped OAuth tokens with per-route grants, data-layer isolation, seeded COA and full ledger per tenant.
Roadmap: embeddable UI components (drop-in widgets for invoices, reports, and the rest). If anyone — including a salesperson — tells you these ship today, they're wrong.
Commercial: white-label and private-deployment terms are scoped per engagement in the Custom tier. Sandbox is free and needs no sales call; run this whole guide there first.
Where to go next
- Build embedded accounting in 20 minutes — run the full invoice-to-ledger loop inside one of the tenants you just provisioned
- Embedded accounting — the platform overview, including the MCP/agent surface
- OAuth scopes and permissions for embedded accounting platforms — designing least-privilege roles for books
- Pricing — Custom tier covers white-label and private deployment terms