> ## Documentation Index
> Fetch the complete documentation index at: https://neverminedag-sync-api-errors-9d14109.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Organizations & Workspaces

> List the organizations you belong to, target a specific workspace when publishing agents and plans, and read each org's activity feed from the Payments SDK.

## Overview

Every Nevermined account can belong to one or more **organizations** — first-class workspaces with their own members, agents, plans, and billing. The Payments SDK exposes three building blocks for working with them from your code:

* **List your memberships** — every organization you're an active member of, with your role in each.
* **Publish into a specific workspace** — when you register an agent, plan, or API key, tell the SDK which workspace should own it.
* **Read the activity feed** — paginated events for everything happening in an organization (member changes, customers, subscriptions, webhook deliveries).

These map to a small, focused surface on `payments.organizations` and a single optional `organizationId` argument on the publish methods you already use.

<Note>
  Workspace selection is transported by the `X-Current-Org-Id` header. The backend resolves it in priority order: route path parameter → this header → the API key's organization tag → your most-recent active membership → personal account. Pinning a workspace via the SDK is the same control your webapp's workspace switcher uses.
</Note>

## List the organizations you belong to

Use this to drive a workspace picker in your own tools, or to confirm where a publish will land before sending it.

<Tabs>
  <Tab title="TypeScript">
    ```typescript theme={null}
    import { Payments } from '@nevermined-io/payments'

    const payments = Payments.getInstance({
      nvmApiKey: process.env.NVM_API_KEY!,
      environment: 'sandbox',
    })

    const memberships = await payments.organizations.getMyMemberships()

    for (const m of memberships) {
      console.log(`${m.orgName} (${m.orgType}) — ${m.role}${m.isAdmin ? ' (admin)' : ''}`)
    }
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={null}
    import os
    from payments_py import Payments, PaymentOptions

    payments = Payments.get_instance(
        PaymentOptions(
            nvm_api_key=os.environ["NVM_API_KEY"],
            environment="sandbox",
        )
    )

    memberships = payments.organizations.get_my_memberships()

    for m in memberships:
        admin = " (admin)" if m.is_admin else ""
        print(f"{m.org_name} ({m.org_type.value}) — {m.role.value}{admin}")
    ```
  </Tab>
</Tabs>

A typical response (a Premium org and an Enterprise one):

```json theme={null}
[
  {
    "orgId": "org-3f9a...",
    "orgName": "Acme Robotics",
    "role": "Admin",
    "orgType": "Premium",
    "isAdmin": true,
    "hasSubscriptionHistory": true
  },
  {
    "orgId": "org-7d11...",
    "orgName": "Beta Labs",
    "role": "Member",
    "orgType": "Enterprise",
    "isAdmin": false,
    "hasSubscriptionHistory": true
  }
]
```

If you have no active memberships the list is empty — you're operating as a personal account.

## Publish into a specific organization

Two ways to choose where a published agent, plan, or API key lands. Pick whichever fits the calling code.

### Per-call override (recommended for one-off publishes)

Pass `organizationId` (TypeScript) or `organization_id=` (Python) to the publish method. The SDK sends `X-Current-Org-Id` for that call only — your instance-level workspace is untouched.

<Tabs>
  <Tab title="TypeScript">
    ```typescript theme={null}
    // Publish a single agent into Acme Robotics, without leaving Acme as your active workspace.
    const { agentId } = await payments.agents.registerAgent(
      { name: 'Legal Assistant', tags: ['legal', 'rag'] },
      {
        authType: 'bearer',
        endpoints: [{ POST: 'https://api.example.com/legal/tasks' }],
      },
      [planId],
      { organizationId: 'org-3f9a...' },
    )
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={null}
    # Publish a single agent into Acme Robotics, without leaving Acme as your active workspace.
    from payments_py import AgentAPIAttributes, AgentMetadata, AuthType, Endpoint

    result = payments.agents.register_agent(
        AgentMetadata(name="Legal Assistant", tags=["legal", "rag"]),
        AgentAPIAttributes(
            auth_type=AuthType.BEARER,
            endpoints=[Endpoint(verb="POST", url="https://api.example.com/legal/tasks")],
        ),
        [plan_id],
        organization_id="org-3f9a...",
    )
    ```
  </Tab>
</Tabs>

The same `organizationId` / `organization_id` option works on `registerAgentAndPlan` / `register_agent_and_plan` and on the plan-registration helpers (`registerPlan`, `registerCreditsPlan`, `registerTimePlan` and their `register_*_plan` Python equivalents).

### Pin a workspace for the whole session

Set the organization once and every subsequent call from this `Payments` instance — including all sub-APIs (`agents`, `plans`, `requests`, …) — uses it.

<Tabs>
  <Tab title="TypeScript">
    ```typescript theme={null}
    payments.setOrganizationId('org-3f9a...')

    // All of these now land in Acme Robotics:
    await payments.agents.registerAgent(metadata, api, [planId])
    await payments.plans.registerCreditsPlan(planMetadata, price, credits)

    // Clear the pin when you're done — falls back to personal / API-key default.
    payments.setOrganizationId(null)
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={null}
    payments.set_organization_id("org-3f9a...")

    # All of these now land in Acme Robotics:
    payments.agents.register_agent(metadata, api, [plan_id])
    payments.plans.register_credits_plan(plan_metadata, price_config, credits_config)

    # Clear the pin when you're done — falls back to personal / API-key default.
    payments.set_organization_id(None)
    ```
  </Tab>
</Tabs>

You can also pin from the constructor:

<Tabs>
  <Tab title="TypeScript">
    ```typescript theme={null}
    const payments = Payments.getInstance({
      nvmApiKey: process.env.NVM_API_KEY!,
      environment: 'sandbox',
      organizationId: 'org-3f9a...',
    })
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={null}
    payments = Payments.get_instance(
        PaymentOptions(
            nvm_api_key=os.environ["NVM_API_KEY"],
            environment="sandbox",
            organization_id="org-3f9a...",
        )
    )
    ```
  </Tab>
</Tabs>

<Warning>
  Targeting an organization you're not a member of (or an inactive membership) is rejected by the backend with a 403. The SDK surfaces this as a `PaymentsError`.
</Warning>

## Read the organization activity feed

Paginated events for member changes, customer additions, subscription transitions, and webhook deliveries. Only members of the organization can read it; the backend enforces a Premium+ entitlement.

<Tabs>
  <Tab title="TypeScript">
    ```typescript theme={null}
    import { OrganizationActivityEventType } from '@nevermined-io/payments'

    const page = await payments.organizations.getOrganizationActivity(
      'org-3f9a...',
      {
        eventType: OrganizationActivityEventType.MemberInvited,
        from: '2026-05-01T00:00:00Z',
        to: '2026-05-31T23:59:59Z',
        page: 1,
        limit: 25,
      },
    )

    console.log(`${page.total} matching events`)
    for (const event of page.items) {
      console.log(event.occurredAt, event.eventType, event.subject.kind, event.subject.id)
    }
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={null}
    from payments_py import OrganizationActivityEventType, OrganizationActivityFilters

    page = payments.organizations.get_organization_activity(
        "org-3f9a...",
        OrganizationActivityFilters(
            event_type=OrganizationActivityEventType.MEMBER_INVITED,
            **{"from": "2026-05-01T00:00:00Z"},
            to="2026-05-31T23:59:59Z",
            page=1,
            limit=25,
        ),
    )

    print(f"{page.total} matching events")
    for event in page.items:
        print(event.occurred_at, event.event_type, event.subject.kind, event.subject.id)
    ```
  </Tab>
</Tabs>

All filters are optional. The `eventType` filter accepts a single value or a list of values (sent as a comma-separated list); `limit` is the page size (backend cap: 200). The known event types you can filter on:

| Event type                                                                                            | Emitted when                                     |
| ----------------------------------------------------------------------------------------------------- | ------------------------------------------------ |
| `member.invited` / `member.joined`                                                                    | An admin invites a teammate; the invitee accepts |
| `member.role_changed`                                                                                 | A member's role flips between Admin and Member   |
| `member.deactivated` / `member.reactivated` / `member.removed`                                        | Membership lifecycle transitions                 |
| `invitation.revoked` / `invitation.expired`                                                           | Pending invitation lifecycle                     |
| `agent.created` / `plan.created` / `plan.purchased`                                                   | Resource lifecycle inside the org                |
| `customer.added` / `customer.blocked` / `customer.unblocked`                                          | External customer lifecycle                      |
| `subscription.upgraded` / `subscription.downgraded` / `subscription.canceled` / `subscription.lapsed` | Tier subscription transitions on the org         |
| `webhook.delivered` / `webhook.failed`                                                                | Webhook delivery outcomes (status in `metadata`) |

New event types are accepted without an SDK upgrade — the `eventType` field is a plain string in both SDKs. Each event also carries a `subject: { kind, id, …extras }` payload describing the resource it's about (e.g. invitations include `role` + `email`, members include `role` + `userId`, subscriptions include `tier`).

## How workspace context is resolved

When the backend handles an authenticated request, it picks the active organization in this order — the first match wins:

<Steps>
  <Step title="Route path">
    If the URL itself includes `:orgId` (for example `/organizations/{orgId}/activity`), that organization is used directly.
  </Step>

  <Step title="X-Current-Org-Id header">
    Set by the SDK from your pin or per-call override. The backend validates that you're an active member of the requested org before honoring it.
  </Step>

  <Step title="API key tag">
    If your NVM API key was minted scoped to a specific organization, that scope is applied.
  </Step>

  <Step title="Fallback membership">
    Your most-recent active membership — preserves backwards compatibility for single-org callers that don't set anything explicitly.
  </Step>

  <Step title="Personal account">
    No active membership → resources you publish are owned by your personal account.
  </Step>
</Steps>

The SDK never picks a workspace silently — if you don't set one, the backend's fallback rules apply.

## Related

<CardGroup cols={2}>
  <Card title="Organizations on Nevermined" icon="building" href="/docs/solutions/organizations/overview">
    Concepts, tiers, and the webapp surface for organization owners.
  </Card>

  <Card title="Quickstart — TypeScript" icon="code" href="/docs/integrate/quickstart/typescript">
    Set up the SDK and publish your first agent.
  </Card>

  <Card title="Quickstart — Python" icon="python" href="/docs/integrate/quickstart/python">
    Set up the Python SDK and publish your first agent.
  </Card>

  <Card title="Payment Models" icon="credit-card" href="/docs/integrate/patterns/payment-models">
    Fixed, usage-based, and outcome-based pricing patterns.
  </Card>
</CardGroup>
