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 + metadata pattern, 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 called accountId in 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.
  • Onboardingprovision_managed flow that also wires the Xendit adapter.
  • Admin — partner-billing workspace provisioning.
  • Authenticationon_behalf_of and for_merchant.
Plugipay — Payments that don't tax your success