Workspaces
A workspace is a Plugipay merchant account — the top-level isolation boundary for customers, plans, invoices, ledger entries, everything. Each accountId is a workspace. Most direct integrators have one workspace and never touch this namespace; platform integrators (Storlaunch, Fulkruma, etc. — anyone using X-Plugipay-On-Behalf-Of) manage many workspaces and use these endpoints to provision and manage them. The Python SDK exposes four methods behind plugipay.workspaces. For the wire format, see API → Workspaces.
Namespace
plug.workspaces # _Workspaces
Methods
list
plug.workspaces.list() -> list[Workspace]
Returns every workspace this key can act on. For a single-workspace integrator, this is a one-element list (your own). For a platform key with broad scope, it can be every merchant you've onboarded. Not paginated — if the list is genuinely large, drop to the HTTP API with cursor pagination.
for ws in plug.workspaces.list():
print(ws["id"], ws.get("brandName"), ws.get("businessEmail"))
create
plug.workspaces.create(
*,
brand_name: str | None = None,
business_email: str | None = None,
) -> Workspace
Creates a new workspace under the caller's account. For most direct integrators this is a one-time bootstrap operation. Platform integrators usually prefer the higher-level plug.onboarding.provision_managed(...) (which also kicks off managed-adapter onboarding) or plug.admin.provision_workspace(...) (for partner-billing). SDK auto-generates an idempotency key.
ws = plug.workspaces.create(
brand_name="Acme Indonesia",
business_email="billing@acme.example",
)
print(ws["id"]) # "acc_01HX..."
update
plug.workspaces.update(
workspace_id: str,
*,
brand_name: str | None = None,
business_email: str | None = None,
) -> Workspace
Partial update of workspace-level metadata. Pass only the fields you want to change — omitted fields are not modified. The SDK currently exposes only brand_name and business_email as kwargs; for richer fields (billing address, tax id, default currency, etc.), drop to plug.request("PATCH", "/api/v1/workspaces/<id>", body={...}). No auto idempotency key.
plug.workspaces.update(
"acc_01HX...",
brand_name="Acme Indonesia (Holdings)",
)
delete
plug.workspaces.delete(workspace_id: str) -> None
Deletes the workspace. Soft-delete with a cooling-off period server-side — the workspace becomes inaccessible immediately but underlying data is retained for an operator-defined window for support / GDPR / accounting purposes before hard deletion.
plug.workspaces.delete("acc_01HX...")
Workspaces are NOT for multi-tenancy inside your own app. If you run a SaaS and you want to isolate customer A's payments from customer B's, don't use one Plugipay workspace per tenant — that's a
customer_id+metadatapattern, not a workspace pattern. Workspaces are heavyweight (their own ledger, adapter config, KYC). Reserve them for genuinely separate businesses (partner brands, legal entities, marketplace merchants).
Types
from plugipay import Workspace
Workspace is a Resource subclass. Likely-read fields:
ws["id"]—acc_+ ULID. Often calledaccountIdin HTTP payloads.ws.get("brandName")— display name.ws.get("businessEmail")— primary contact.ws.get("livemode")—True/False.ws.get("currency")— default currency.ws.get("status")—"active" | "suspended" | "pending_deletion".ws["createdAt"].
Full reference at API → Workspaces.
Common patterns
Pick the right provisioning entry point. The SDK exposes three ways to create a workspace; choose by use case:
| Method | Use case |
|---|---|
plug.workspaces.create(...) |
One-time bootstrap of your own workspace; basic shell only. |
plug.onboarding.provision_managed(...) |
New merchant + managed Xendit onboarding in one call. |
plug.admin.provision_workspace(...) |
Partner-billing scenarios (Storlaunch/Fulkruma flows) — see Admin. |
Acting on behalf of a workspace. Most platform calls don't need an explicit "switch workspace" step — set on_behalf_of on the client and every subsequent call carries X-Plugipay-On-Behalf-Of:
merchant_client = plug.for_merchant("acc_merchantA")
customers = merchant_client.customers.list() # Acts inside merchant A's workspace
for_merchant returns a new PlugipayClient — your original plug is unchanged.
Workspace inventory for a platform dashboard.
rows = []
for ws in plug.workspaces.list():
rows.append({
"id": ws["id"],
"name": ws.get("brandName", "—"),
"email": ws.get("businessEmail", "—"),
"status": ws.get("status", "unknown"),
})
render_table(rows)
Idempotent workspace rename. Read first, only update if different:
ws = plug.workspaces.list()[0] # assuming single-workspace key
if ws.get("brandName") != "Acme Indonesia 2026":
plug.workspaces.update(ws["id"], brand_name="Acme Indonesia 2026")
Workspace delete is rare. For a merchant who wants to "close their account" but keep history, prefer update to mark the workspace as inactive in your own systems and stop creating new resources — actual delete is permanent (after the cooling-off window) and cascades through every customer / invoice / ledger entry.
Errors
err.status |
err.code |
Cause |
|---|---|---|
400 |
validation_error |
Bad business_email, name too long. |
404 |
not_found |
Workspace id doesn't exist or caller can't see it. |
409 |
conflict |
A workspace with this business_email already exists under this account. |
409 |
idempotency_key_reused |
Same Idempotency-Key against a different body. |
403 |
insufficient_scope |
Key lacks plugipay:workspace:write. |
409 |
delete_blocked |
delete called on a workspace with open subscriptions / unsettled balance. |
All raise PlugipayError. See Errors.
Next
- API → Workspaces — full field reference and lifecycle states.
- Onboarding —
provision_managedflow that also wires the Xendit adapter. - Admin — partner-billing workspace provisioning.
- Authentication —
on_behalf_ofandfor_merchant.