Admin

The Admin client manages organizations, projects, API keys, users, invites, service accounts, and role bindings. It uses OAuth2 client credentials (service account) rather than an API key, and is the right tool for control-plane operations such as creating projects and rotating keys. It is synchronous only — there is no async form of this client.

The users, invites, service-account, and role-binding namespaces are new in 2026-07. See the Admin and OAuth section of Migrating to V10 for the per-operation release notes and an end-to-end RBAC walkthrough.

class pinecone.admin.Admin(*, client_id=None, client_secret=None, additional_headers=None, proxy_url=None, ssl_verify=True, source_tag=None, host=None, oauth_url=None)[source]

Bases: object

Manage Pinecone organizations, projects, and the credentials that reach them.

Admin authenticates with OAuth2 client credentials — a service account’s client_id and client_secret — never with an API key, and it reaches only control-plane resources: organizations, projects, API keys, and the role bindings that grant access to them. Pinecone is the other half of the SDK: it takes an API key and does index and vector work. Constructing Admin exchanges your credentials for a token straight away, so bad credentials fail here rather than on the first call. This client is synchronous only — there is no async form.

Projects are created inside the organization the credentials belong to.

Operations are grouped into seven namespaces:

  • organizations — the organizations these credentials can reach

  • projects — create, configure, and delete projects

  • api_keys — the project-scoped keys Pinecone authenticates with

  • users — the organization’s members

  • invites — pending and expired invitations to join the organization

  • service_accounts — the OAuth principals this client itself authenticates as

  • role_bindings — every grant of a role to a principal, at organization or project scope; nothing else confers permissions

Parameters:
  • client_id (str | None) – OAuth2 client ID. Falls back to PINECONE_CLIENT_ID env var.

  • client_secret (str | None) – OAuth2 client secret. Falls back to PINECONE_CLIENT_SECRET env var.

  • additional_headers (dict[str, str] | None) – Extra headers included in every admin API request. Merged last, so an entry keyed exactly "Authorization" or "X-Pinecone-Api-Version" replaces the header the SDK would otherwise send for that name. Matching is case-sensitive: any other spelling is sent alongside the SDK’s own header rather than replacing it.

  • proxy_url (str | None) – HTTP proxy URL for outgoing requests.

  • ssl_verify (bool) – Whether to verify SSL certificates. Defaults to True.

  • source_tag (str | None) – Tag appended to the User-Agent string for request attribution.

  • host (str | None) – Admin API host. Falls back to PINECONE_CONTROLLER_HOST env var, then defaults to https://api.pinecone.io. A value with no scheme is prefixed with https://, matching Pinecone. Intended for pointing the client at a local simulator in tests or at a private Pinecone deployment; leave it unset against production.

  • oauth_url (str | None) – Full URL of the OAuth2 token endpoint, including its path. Defaults to https://login.pinecone.io/oauth/token. A value with no scheme is prefixed with https://. Intended for pointing the token exchange at a local simulator in tests or at a private Pinecone deployment; leave it unset against production. There is no environment-variable fallback for this parameter.

Raises:
  • PineconeValueError – If client_id or client_secret resolves to nothing, from either the argument or the environment.

  • ApiError – If the credential exchange is rejected — usually a wrong or revoked client_secret.

Examples

Every namespace hangs off one client:

>>> from pinecone import Admin
>>> admin = Admin(client_id="your-client-id", client_secret="your-client-secret")
>>> for org in admin.organizations.list():
...     print(org.name)

An API key minted here is what Pinecone authenticates with, so the two clients chain: create the project, create a key scoped to it, then hand that key’s secret to Pinecone.

>>> from pinecone import Pinecone
>>> project = admin.projects.create(name="product-search")
>>> key = admin.api_keys.create(project_id=project.id, name="prod-search-key")
>>> key.value
'pcsk_abc123_secretvalue'
>>> pc = Pinecone(api_key=key.value)
>>> index = pc.indexes.create(
...     name="product-catalog",
...     schema={"fields": {"embedding": {
...         "type": "dense_vector", "dimension": 1536, "metric": "cosine"}}},
...     deployment={"deployment_type": "managed", "cloud": "aws",
...                 "region": "us-east-1"},
... )

Note

Where the credentials come from — a service account’s client_id and client_secret come either from admin.service_accounts.create(), once you already hold admin credentials, or from the Pinecone console under organization settings, which is how the first pair is obtained: nothing can authenticate an Admin until one service account exists. Either way the client_secret is shown exactly once, and rotate_secret() is the only way to get another.

Note

Token refreshAdmin renews its OAuth token before it expires, so a long-lived instance keeps working with no action on your part. Supply your own Authorization entry in additional_headers to manage the token yourself instead.

See also

Pinecone — index and vector operations, authenticated with an API key created here.

Error Handling — what each exception an admin call can raise means, and what to do about it.

__init__(*, client_id=None, client_secret=None, additional_headers=None, proxy_url=None, ssl_verify=True, source_tag=None, host=None, oauth_url=None)[source]
Parameters:
  • client_id (str | None)

  • client_secret (str | None)

  • additional_headers (dict[str, str] | None)

  • proxy_url (str | None)

  • ssl_verify (bool)

  • source_tag (str | None)

  • host (str | None)

  • oauth_url (str | None)

Return type:

None

property organizations: Organizations

Access the Organizations namespace for organization operations.

An organization is the top-level account boundary in Pinecone: it holds projects, users, and billing.

Returns:

The Organizations namespace. Call list() or describe() to look up organizations reachable with the current credentials.

Examples

>>> for org in admin.organizations.list():
...     print(org.name)
property projects: Projects

Access the Projects namespace for project operations.

A project is the boundary for quotas and API keys inside an organization: indexes, collections, backups, and keys each belong to exactly one project.

Returns:

The Projects namespace. Call create() or list() to create or look up projects.

Examples

>>> for project in admin.projects.list():
...     print(project.name)
property api_keys: ApiKeys

Access the ApiKeys namespace for API key operations.

API keys are the project-scoped credentials Pinecone authenticates with.

Returns:

The ApiKeys namespace. Call create() or list() to create or look up keys for a project.

Examples

>>> keys = admin.api_keys.list(project_id="proj-abc123")
>>> for key in keys:
...     print(key.key.id)
property users: Users

Access the Users namespace for organization-member operations.

Users are people who already belong to the organization; invites covers those who have been asked and have not joined yet.

Returns:

The Users namespace. Call list() to look up the organization’s members.

Examples

>>> for user in admin.users.list():
...     print(user.email)
alice@example.com
property invites: Invites

Access the Invites namespace for organization-invite operations.

An invite is a pending or expired request for someone to join the organization; it becomes a users entry once accepted.

Returns:

The Invites namespace. Call create() or list() to invite someone to the organization or look up pending invites.

Examples

>>> for invite in admin.invites.list():
...     print(invite.email, invite.status)
newhire@acme.com pending
property service_accounts: ServiceAccounts

Access the ServiceAccounts namespace for service-account operations.

Service accounts are the OAuth principals Admin clients authenticate as, including the one behind this client’s own client_id/client_secret — rotating or deleting that account breaks this client.

Returns:

The ServiceAccounts namespace. Call create() or list() to create or look up service accounts.

Examples

>>> for account in admin.service_accounts.list():
...     print(account.name, account.client_id)
ci-prod l3Ow0CmFyc4jOONcwiKUCRqQKN0tiCAn
property role_bindings: RoleBindings

Access the RoleBindings namespace for role-binding operations.

Role bindings are the only thing that confers permissions in Pinecone, so this is where any principal’s access — user, service account, API key, or pending invite — is read and changed.

Returns:

The RoleBindings namespace. Call create() or list() to grant a role or look up existing grants.

Examples

>>> for binding in admin.role_bindings.list():
...     print(binding.principal_type, binding.role, binding.resource_type)
user OrgMember organization
close()[source]

Close the underlying HTTP client, releasing its connections.

Call this when you’re done with an Admin instance that isn’t used as a context manager. Further calls through any of its namespaces will fail.

Examples

>>> from pinecone import Admin
>>> throwaway = Admin(client_id="your-client-id", client_secret="your-client-secret")
>>> throwaway.close()
Return type:

None

Organizations

class pinecone.admin.organizations.Organizations(*, http)[source]

Bases: object

Operations on Pinecone organizations.

An organization is the top-level account boundary in Pinecone: it holds projects, users, and billing, and everything else an Admin client touches lives inside one. Where a project scopes indexes and API keys, an organization scopes projects, members, and the bill. Not constructed directly — reach it as admin.organizations.

Examples

>>> from pinecone import Admin
>>> admin = Admin(client_id="your-client-id", client_secret="your-client-secret")
>>> for org in admin.organizations.list():
...     print(org.name)

See also

Projects — the projects inside an organization.

Parameters:

http (HTTPClient)

__init__(*, http)[source]
Parameters:

http (HTTPClient)

Return type:

None

list()[source]

List the organizations your credentials can reach.

Returns:

An OrganizationList of every reachable organization, supporting iteration, len(), and index access. Returned whole — there is no paging.

Return type:

OrganizationList

Examples

>>> for org in admin.organizations.list():
...     print(org.name, org.plan)
describe(*, organization_id)[source]

Get details for one organization.

Parameters:

organization_id (str) – The organization’s identifier, e.g. "org-abc123".

Returns:

An OrganizationModel with the organization’s name, plan, payment status, support tier, and creation time.

Raises:

PineconeValueError – If organization_id is empty or whitespace-only. Checked before the request is sent.

Return type:

OrganizationModel

Examples

>>> org = admin.organizations.describe(organization_id="org-abc123")
>>> org.name
'Acme Corp'
update(*, organization_id, name)[source]

Rename an organization.

The name is the only organization field this SDK can change; plan, payment status, and support tier are read-only here.

Parameters:
  • organization_id (str) – The organization’s identifier, e.g. "org-abc123".

  • name (str) – The new display name, e.g. "Acme Corporation". Unlike organization_id, it is not checked client-side.

Returns:

An OrganizationModel carrying the name as it was stored.

Raises:

PineconeValueError – If organization_id is empty or whitespace-only. Checked before the request is sent.

Return type:

OrganizationModel

Examples

>>> org = admin.organizations.update(
...     organization_id="org-abc123", name="Acme Corporation"
... )
delete(*, organization_id)[source]

Delete an organization permanently.

There is no undo and no soft-delete window. An organization must meet three conditions before it can be deleted:

  • It is not on a paid plan (downgrade first).

  • Its payment status is active, with no open invoices.

  • It contains no projects (see Projects.delete).

All three must hold at once — an organization with no projects can still be blocked by its plan or payment status.

Parameters:

organization_id (str) – The organization’s identifier, e.g. "org-abc123".

Raises:
  • PineconeValueError – If organization_id is empty or whitespace-only. Checked before the request is sent.

  • FailedPreconditionError – If the organization is on a paid plan, its payment status is not active, or it still contains projects. The error message names the blocker.

Return type:

None

Examples

>>> admin.organizations.delete(organization_id="org-abc123")

Projects

class pinecone.admin.projects.Projects(*, http, admin=None)[source]

Bases: object

Operations on Pinecone projects.

A project is the quota and credential boundary inside an organization: every index, collection, backup, assistant, and API key belongs to exactly one project. Where an organization scopes members and billing, a project scopes the resources you actually query. Not constructed directly — reach it as admin.projects.

Examples

>>> from pinecone import Admin
>>> admin = Admin(client_id="your-client-id", client_secret="your-client-secret")
>>> for project in admin.projects.list():
...     print(project.name)

See also

ApiKeys — the keys that let Pinecone reach a project’s data.

Error Handling — what each exception these calls raise means.

Parameters:
  • http (HTTPClient)

  • admin (Admin | None)

__init__(*, http, admin=None)[source]
Parameters:
  • http (HTTPClient)

  • admin (Admin | None)

Return type:

None

list()[source]

List the projects your credentials can reach.

Returns:

A ProjectList of every reachable project, supporting iteration, len(), and index access. Returned whole — there is no paging.

Return type:

ProjectList

Examples

>>> for project in admin.projects.list():
...     print(project.name, project.id)
create(*, name, max_pods=None, force_encryption_with_cmek=None)[source]

Create a project in the organization your credentials belong to.

The project starts empty; create an API key in it with create() before any index work can reach it.

Parameters:
  • name (str) – Name for the project, e.g. "product-search"; 1-512 characters and no null bytes, both checked client-side. Names need not be unique within an organization, which is why describe_by_name() can find more than one.

  • max_pods (int | None) – Pod ceiling for the project. Pod-based capacity is legacy and a non-zero value is rejected unless the organization has pod access; 0 means serverless-only. Omitted from the request if None.

  • force_encryption_with_cmek (bool | None) – Require customer-managed encryption keys for everything in the project. Requesting True needs CMEK enabled for the organization, and it cannot be turned back off later — see update(). Omitted from the request if None.

Returns:

A ProjectModel with the new project’s id — the value every other project and API-key call takes — plus its name, quotas, and organization_id.

Raises:
  • PineconeValueError – If name is empty, longer than 512 characters, or contains a null byte, or if max_pods is negative. All checked before the request is sent.

  • PaymentRequiredError – If the organization’s billing state does not permit creating a project.

  • ForbiddenError – If the organization has reached its project quota, or if force_encryption_with_cmek was requested without CMEK enabled for the organization.

  • ApiError – If a non-zero max_pods was requested without pod access.

Return type:

ProjectModel

Examples

>>> project = admin.projects.create(name="product-search")
>>> project.id
'proj-abc123'
describe(*, project_id)[source]

Get details for one project.

Parameters:

project_id (str) – The project’s identifier, e.g. "proj-abc123".

Returns:

A ProjectModel with the project’s name, quotas, and organization.

Raises:

PineconeValueError – If project_id is empty or whitespace-only. Checked before the request is sent.

Return type:

ProjectModel

Examples

>>> project = admin.projects.describe(project_id="proj-abc123")
>>> project.name
'my-project'
describe_by_name(*, name)[source]

Get details for one project by name.

Project names are not unique, so this is a client-side convenience, not a lookup the API offers: it fetches every reachable project and filters for an exact, case-sensitive name match. Prefer describe() with a project_id in code that runs often or in an organization with many projects.

Parameters:

name (str) – The project’s name, e.g. "my-project".

Returns:

A ProjectModel with the project’s name, quotas, and organization.

Raises:
  • PineconeValueError – If name is empty or whitespace-only. Checked before the request is sent.

  • NotFoundError – If no reachable project has that exact name. Raised by the client after the listing comes back, so the message names name rather than any URL.

  • PineconeError – If more than one project shares name. Nothing disambiguates them here — use describe() with the project_id you want.

Return type:

ProjectModel

Examples

project = admin.projects.describe_by_name(name="product-search")
print(project.id)
exists(*, project_id=None, name=None)[source]

Check whether a project exists.

Pass exactly one of project_id or name. Only a definite “no such project” gives False; every other failure the lookup hits — no permission to read the project, a connection problem, a server error — is currently reported as True as well, so treat a True as “not known to be absent” and do not use this as an authorization check. Several projects sharing name also give True, since they all exist.

Parameters:
  • project_id (str | None) – The project’s identifier, e.g. "proj-abc123".

  • name (str | None) – The project’s name, e.g. "product-search". Matched exactly and case-sensitively, as in describe_by_name().

Returns:

True if the project exists, False if nothing matches.

Raises:

PineconeValueError – If both arguments are given, or neither.

Return type:

bool

Examples

>>> admin.projects.exists(project_id="proj-abc123")
True
>>> admin.projects.exists(name="archived-catalog")
False
update(*, project_id, name=None, max_pods=None, force_encryption_with_cmek=None)[source]

Change a project’s name, pod ceiling, or CMEK enforcement.

Omitted arguments are left alone. Renaming a project does not change its id, so API keys and index hosts keep working.

Parameters:
  • project_id (str) – The project to update, e.g. "proj-abc123".

  • name (str | None) – New name, e.g. "product-search-eu"; the same 1-512 characters and no-null-bytes rule create() applies. Left unchanged if omitted.

  • max_pods (int | None) – New pod ceiling, under the same pod-access constraint as create(). Left unchanged if omitted.

  • force_encryption_with_cmek (bool | None) – New CMEK enforcement setting. Enabling it needs the same entitlement as create(), and it is a one-way door — a project with CMEK on cannot have it turned off, while passing False for a project that never had it on is a no-op. Left unchanged if omitted.

Returns:

A ProjectModel reflecting the stored state after the change.

Raises:
  • PineconeValueError – If project_id is empty, if name is given but empty, longer than 512 characters, or contains a null byte, or if max_pods is negative. All checked before the request is sent.

  • ForbiddenError – If force_encryption_with_cmek is True and CMEK is not enabled for the organization.

  • ApiError – If a non-zero max_pods was requested without pod access, or if the call tries to turn CMEK back off.

Return type:

ProjectModel

Examples

>>> project = admin.projects.update(
...     project_id="proj-abc123", name="product-search-eu"
... )
delete_with_cleanup(*, project_id, max_attempts=5, retry_delay=30.0)[source]

Empty a project of every resource, then delete it permanently.

Destructive and unattended: it creates a temporary API key scoped to the project, uses that key to delete every index, collection, assistant, and backup in it, deletes the temporary key, and finally deletes the project. Nothing is recoverable afterwards. It also blocks for as long as the deletions take, retrying failed cleanup passes up to max_attempts times with retry_delay seconds between them.

Creating that temporary key is the first thing it does, so a project whose API-key quota is already full cannot be cleaned up at all — the error names the quota and nothing has been deleted. Free a key slot and call again.

Cleanup is not atomic. It covers every resource kind that blocks a project delete, but anything created in the project while cleanup is running can still leave the final delete blocked.

Parameters:
  • project_id (str) – The project to empty and delete, e.g. "proj-abc123".

  • max_attempts (int) – How many times to retry the whole cleanup pass before giving up. Defaults to 5; the default is fine unless the project is large enough that resources routinely take longer than the retries allow.

  • retry_delay (float) – Seconds to wait between cleanup attempts. Defaults to 30.0, which paces the retries against resources that are still winding down.

Raises:
  • PineconeError – If no admin back-reference is available — call this through admin.projects.delete_with_cleanup(...) rather than constructing Projects directly.

  • PineconeValueError – If project_id is empty or whitespace-only. Checked before anything is deleted.

  • ForbiddenError – If the temporary API key cannot be created — usually the project’s API-key quota. Nothing is deleted in this case, and the message says so and how to clear it.

  • FailedPreconditionError – If the project is still not empty when the final delete runs, which happens when something is created in it after cleanup finishes. The error names what is blocking.

  • ApiError – If the last cleanup attempt failed; the error from that attempt is re-raised, and the project is left partly emptied.

Return type:

None

Examples

admin.projects.delete_with_cleanup(project_id="proj-abc123")
delete(*, project_id)[source]

Delete an empty project permanently.

The project must already be empty. Indexes, collections, assistants, and backups all block the delete, and the error names what is still there; API keys are not a blocker — they go with the project. Use delete_with_cleanup() to clear the blockers first. Nothing here is recoverable.

Parameters:

project_id (str) – The project to delete, e.g. "proj-abc123".

Raises:
  • PineconeValueError – If project_id is empty or whitespace-only. Checked before the request is sent.

  • FailedPreconditionError – If the project still owns indexes, collections, assistants, or backups. The error names what is blocking.

Return type:

None

Examples

>>> admin.projects.delete(project_id="proj-abc123")

See also

delete_with_cleanup() — empties the project first; use that one when you do not already know it is empty.

API Keys

class pinecone.admin.api_keys.ApiKeys(*, http)[source]

Bases: object

Operations on Pinecone API keys.

An API key is a project-scoped credential: it is the thing you pass to Pinecone to read and write indexes in one project. Where a service account authenticates an Admin client against the whole organization, an API key reaches exactly one project. Not constructed directly — reach it as admin.api_keys.

create() is the only call that returns a key’s secret, and it returns it once.

Examples

>>> from pinecone import Admin
>>> admin = Admin(client_id="your-client-id", client_secret="your-client-secret")
>>> for key in admin.api_keys.list(project_id="proj-abc123"):
...     print(key.name, key.roles)

See also

ServiceAccounts — the organization-scoped OAuth credentials an Admin client itself uses.

Error Handling — what each exception these calls raise means.

Parameters:

http (HTTPClient)

__init__(*, http)[source]
Parameters:

http (HTTPClient)

Return type:

None

list(*, project_id)[source]

List the API keys belonging to a project.

Secrets are never returned here; only create() carries one.

Parameters:

project_id (str) – The project’s identifier, e.g. "proj-abc123".

Returns:

An APIKeyList of every key in the project, supporting iteration, len(), and index access. Returned whole — there is no paging.

Raises:

PineconeValueError – If project_id is empty or whitespace-only. Checked before the request is sent.

Return type:

APIKeyList

Examples

>>> for key in admin.api_keys.list(project_id="proj-abc123"):
...     print(key.name, key.roles)
create(*, project_id, name, roles=None)[source]

Create an API key scoped to one project.

The response is the only place the key’s secret ever appears — value is returned here and nowhere else, and no call recovers it later. Store it before doing anything else; if you lose it, delete the key and create another.

Parameters:
  • project_id (str) – The project the key will reach, e.g. "proj-abc123".

  • name (str) – Label for the key, e.g. "prod-search-key"; 1-80 characters, checked client-side.

  • roles (list[APIKeyRole | str] | None) – Roles the key holds. Valid values are "ProjectEditor", "ProjectViewer", "ControlPlaneEditor", "ControlPlaneViewer", "DataPlaneEditor", and "DataPlaneViewer", either as strings or as APIKeyRole members. Defaults to ["ProjectEditor"]. A role the organization is not entitled to grant is refused even though the name is valid; see Raises.

Returns:

An APIKeyWithSecret with value (the secret, this once only) and key (an APIKeyModel carrying the key’s id, name, and roles). Pass value to Pinecone; keep key.id to reach the key again through describe(), update(), or delete().

Raises:
  • PineconeValueError – If project_id or name is empty, if name is longer than 80 characters, or if roles contains a value that is not one of the six role names. All checked before the request is sent.

  • PaymentRequiredError – If the organization’s billing state does not permit creating an API key.

  • ForbiddenError – Either the project has reached its API-key quota or roles names a role the organization cannot grant — the error message distinguishes the two. A full quota surfaces here rather than as RateLimitError, so do not retry it.

Return type:

APIKeyWithSecret

Examples

>>> from pinecone import APIKeyRole
>>> result = admin.api_keys.create(
...     project_id="proj-abc123", name="prod-search-key",
...     roles=[APIKeyRole.DATA_PLANE_EDITOR]
... )
>>> result.value
'pcsk_abc123_secretvalue'
>>> result.key.roles
[<APIKeyRole.DATA_PLANE_EDITOR: 'DataPlaneEditor'>]

The secret is what the data-plane client authenticates with, so this is where an admin workflow hands off to Pinecone:

>>> from pinecone import Pinecone
>>> pc = Pinecone(api_key=result.value)
>>> for index in pc.indexes.list():
...     print(index.name)
describe(*, api_key_id)[source]

Get one API key’s metadata.

The secret is not part of it; only create() ever returns that.

Parameters:

api_key_id (str) – The key’s identifier — key.id from create() or list(), e.g. "key-abc123". This is not the secret.

Returns:

An APIKeyModel with the key’s id, name, project_id, and roles.

Raises:

PineconeValueError – If api_key_id is empty or whitespace-only. Checked before the request is sent.

Return type:

APIKeyModel

Examples

>>> key = admin.api_keys.describe(api_key_id="key-abc123")
>>> key.name
'prod-search-key'
>>> key.roles
[<APIKeyRole.DATA_PLANE_EDITOR: 'DataPlaneEditor'>]
update(*, api_key_id, name=None, roles=None)[source]

Change an API key’s name or roles.

Omitted arguments are left alone, but roles is not merged: passing it replaces the whole role set, so include every role the key should keep. The secret does not change, so callers holding it keep working under the new roles.

Parameters:
  • api_key_id (str) – The key’s identifier, e.g. "key-abc123". Left unchanged by this call.

  • name (str | None) – New label for the key, e.g. "prod-search-key-v2". Left unchanged if omitted. Unlike create(), the length limit is not checked client-side — an over-long name is rejected by the server.

  • roles (list[APIKeyRole | str] | None) – The key’s complete new role set, from the same six values create() accepts. Left unchanged if omitted, and subject to the same entitlement restriction.

Returns:

An APIKeyModel reflecting the stored state after the change.

Raises:
  • PineconeValueError – If api_key_id is empty or whitespace-only, or if roles contains a value that is not one of the six role names. Both checked before the request is sent.

  • ForbiddenError – If roles names a role the organization cannot grant. Unlike create(), no API-key quota applies here — the key already exists.

Return type:

APIKeyModel

Examples

>>> key = admin.api_keys.update(
...     api_key_id="key-abc123", roles=["DataPlaneEditor", "DataPlaneViewer"]
... )
delete(*, api_key_id)[source]

Delete an API key permanently.

Anything still authenticating with the key’s secret starts failing, and there is no way to restore it — a replacement is a new create() with a new secret.

Parameters:

api_key_id (str) – The key’s identifier, e.g. "key-abc123", not the secret.

Raises:

PineconeValueError – If api_key_id is empty or whitespace-only. Checked before the request is sent.

Return type:

None

Examples

>>> admin.api_keys.delete(api_key_id="key-abc123")

Users

class pinecone.admin.users.Users(*, http)[source]

Bases: object

The human members of a Pinecone organization.

A user is a person who has accepted an invitation and now belongs to the organization that the Admin client’s OAuth credentials resolve to. Not constructed directly — reach it as admin.users.

What a user is allowed to do is not part of this model. Permissions come only from role bindings, so RoleBindings is where a user’s access is read and changed.

See Error Handling for the exceptions every operation here can raise.

Examples

>>> from pinecone import Admin
>>> admin = Admin(client_id="your-client-id", client_secret="your-client-secret")
>>> for user in admin.users.list():
...     print(user.email)
alice@example.com

See also

  • Invites — the same person before they accept. An invitee is not yet a user and is not listed here.

  • ServiceAccounts — the machine equivalent, for programmatic access rather than a person.

Parameters:

http (HTTPClient)

__init__(*, http)[source]
Parameters:

http (HTTPClient)

Return type:

None

list(*, email=None, limit=None, pagination_token=None)[source]

List the users in the organization, with lazy pagination.

No request is sent until the returned paginator is iterated; see Pagination.

Parameters:
  • email (str | None) – Filter on the user’s email address, e.g. "alice@example.com". Forwarded verbatim — the SDK does not validate or normalize it, so the server decides what matches and rejects a malformed address. Omit to list every user.

  • limit (int | None) – Number of users the server returns per page. It caps each page, not how many users the paginator yields in total; the paginator keeps following cursors until the pages run out, so use itertools.islice() to cap the total. When None the server chooses the page size.

  • pagination_token (str | None) – Cursor from a previous paginator’s pagination_token, to resume where that iteration stopped. Reuse it with the same email and limit.

Returns:

Paginator yielding UserModel objects.

Raises:

PineconeValueError – If limit is outside 1-100. Raised before any network call.

Return type:

Paginator[UserModel]

Examples

>>> from pinecone import Admin
>>> admin = Admin(client_id="your-client-id", client_secret="your-client-secret")
>>> for user in admin.users.list():
...     print(user.id, user.email)
e2e92523-85dc-4142-b8c2-e681be8b78df alice@example.com

Filtering by address still returns a paginator, not a single user:

>>> admin.users.list(email="alice@example.com").to_list()[0].name
'Alice Nakamura'

See also

  • Invites.list() — invitees who have not accepted yet, and so are absent from this list.

  • RoleBindings.list() — with principal_type="user", what each user can do.

describe(*, user_id)[source]

Get one user’s details by their user ID.

Parameters:

user_id (str) – The user’s UUID, as carried by UserModel.id — not their email address, and not the ID of the invite they accepted.

Returns:

UserModel with id, email, and name (None when the user has not set one).

Raises:
  • PineconeValueError – If user_id is empty.

  • NotFoundError – If no such user is a member of the organization. A person who was invited but has not accepted reads back as not found here — look for them under Invites.list() instead.

Return type:

UserModel

Examples

>>> from pinecone import Admin
>>> admin = Admin(client_id="your-client-id", client_secret="your-client-secret")
>>> user = admin.users.describe(user_id="e2e92523-85dc-4142-b8c2-e681be8b78df")
>>> user.email
'alice@example.com'

See also

  • Invites.describe() — the invite a user accepted, which keeps its own ID and status after acceptance.

delete(*, user_id)[source]

Remove a user from the organization.

The user’s role bindings are revoked immediately; their Pinecone account itself is not deleted. This call is not repeatable — once it succeeds, a second call with the same user_id raises NotFoundError, as does describe() for that user.

Parameters:

user_id (str) – The UUID of the user to remove.

Raises:
  • PineconeValueError – If user_id is empty.

  • NotFoundError – If no such user is a member of the organization.

  • ConflictError – If removal would violate an organization invariant, such as dropping the last OrgOwner. Resolve what the server’s message names — usually by granting that role to someone else — then retry.

Return type:

None

Examples

>>> from pinecone import Admin
>>> admin = Admin(client_id="your-client-id", client_secret="your-client-secret")
>>> admin.users.delete(user_id="e2e92523-85dc-4142-b8c2-e681be8b78df")

See also

  • Invites.delete() — how to withdraw access from someone who never accepted; this method cannot reach them.

Invites

class pinecone.admin.invites.Invites(*, http)[source]

Bases: object

Offers of organization membership that have not yet been accepted.

An invite is an emailed offer for someone to join the organization that the Admin client’s OAuth credentials resolve to. It is a principal in its own right — roles can be bound to it before anyone accepts — and accepting it turns the recipient into a user. Not constructed directly — reach it as admin.invites.

An invite’s role bindings are not part of its representation: create() sends them, but no method here returns them. Read or change them through RoleBindings with principal_type="invite".

See Error Handling for the exceptions every operation here can raise.

Examples

>>> from pinecone import Admin
>>> admin = Admin(client_id="your-client-id", client_secret="your-client-secret")
>>> for invite in admin.invites.list():
...     print(invite.email, invite.status)
newhire@acme.com pending

See also

  • Users — the same person after they accept. An invite and the user it produces are separate records with separate IDs, and only one of the two appears in each list.

  • ServiceAccounts — machine identities, which are created directly and never invited.

Parameters:

http (HTTPClient)

__init__(*, http)[source]
Parameters:

http (HTTPClient)

Return type:

None

list(*, limit=None, pagination_token=None)[source]

List the organization’s pending and expired invites, with lazy pagination.

Accepted invites are omitted, so absence from this list does not mean an invite never existed — see the note below. No request is sent until the returned paginator is iterated; see Pagination.

Parameters:
  • limit (int | None) – Number of invites the server returns per page. It caps each page, not how many invites the paginator yields in total; the paginator keeps following cursors until the pages run out, so use itertools.islice() to cap the total. When None the server chooses the page size.

  • pagination_token (str | None) – Cursor from a previous paginator’s pagination_token, to resume where that iteration stopped. Reuse it with the same limit.

Returns:

Paginator yielding InviteModel objects.

Raises:

PineconeValueError – If limit is outside 1-100. Raised before any network call.

Return type:

Paginator[InviteModel]

Examples

>>> from pinecone import Admin
>>> admin = Admin(client_id="your-client-id", client_secret="your-client-secret")
>>> for invite in admin.invites.list():
...     print(invite.email, invite.status)
newhire@acme.com pending

Page-level access exposes the cursor, which is None once there is no further page to fetch:

>>> for page in admin.invites.list(limit=25).pages():
...     print(len(page.items), page.pagination_token)
1 None

Note

An invite missing from this list may simply have been accepted. describe() still returns it, with status == InviteStatus.PROCESSED, and the accepted invitee is now a member reachable through Users.list(). To reconcile who has access, read both lists.

See also

  • Users.list() — the members this list’s invitees become once they accept.

create(*, email, role_bindings)[source]

Invite someone to the organization and grant their initial roles.

The server has already sent the email by the time this returns; the invite comes back pending, with expires_at set to when it lapses. The response does not echo the role bindings.

Parameters:
  • email (str) – The address to invite, e.g. "newhire@acme.com". The SDK checks only that it isn’t empty; the server validates the address itself and rejects a malformed or over-long one.

  • role_bindings (Sequence[RoleBindingInput | Mapping[str, Any]]) – The roles to grant the invitee, as RoleBindingInput instances or plain dicts, mixed freely. Each entry needs resource_type ("organization" or "project") and role; project scope additionally needs resource_id, the project UUID. At least one entry is required, and the server requires at least one of them to be an organization-scoped membership role (OrgOwner, OrgManager, OrgBillingAdmin, or OrgMember) — a project-only invite is rejected.

Returns:

The created InviteModel, whose id is what describe(), resend(), and delete() take, and what identifies the invite as a principal_id in role-binding queries.

Raises:
  • PineconeValueError – If email is empty, if role_bindings is empty, or if any entry is missing resource_type/role, carries an unrecognized key, or names a value this SDK release does not know. The message names the index of the offending entry. Raised before any network call.

  • ConflictError – If a pending invite already exists for the address, or the address already belongs to a member. In the second case there is nothing to invite — manage the existing user’s roles instead.

Return type:

InviteModel

Examples

>>> from pinecone import Admin
>>> admin = Admin(client_id="your-client-id", client_secret="your-client-secret")
>>> invite = admin.invites.create(
...     email="newhire@acme.com",
...     role_bindings=[{"resource_type": "organization", "role": "OrgMember"}],
... )
>>> invite.status
'pending'
>>> invite.processed_at is None
True

The result is a pending principal, not a member: its own id is what resend(), delete(), and role-binding queries take, and the invitee stays absent from Users.list() until they accept.

Typed inputs and dicts are interchangeable, and may be mixed:

>>> from pinecone.models.admin import ResourceType, RoleBindingInput, RoleName
>>> invite = admin.invites.create(
...     email="newhire@acme.com",
...     role_bindings=[
...         RoleBindingInput(
...             resource_type=ResourceType.ORGANIZATION,
...             role=RoleName.ORG_MEMBER,
...         ),
...         {
...             "resource_type": "project",
...             "role": "ProjectViewer",
...             "resource_id": "a2f7dddb-1597-4eff-9f71-535fde243f58",
...         },
...     ],
... )
>>> invite.email
'newhire@acme.com'

See also

  • RoleBindings.create() — how to grant a further role after the invite exists, and the only way to read back the roles this call sent.

  • ServiceAccounts.create() — the machine equivalent, which takes the same binding shape but mints credentials instead of emailing anyone.

describe(*, invite_id)[source]

Get one invite’s details, whatever its status.

Unlike list(), this reaches accepted invites too — it is the only operation that can return status == InviteStatus.PROCESSED, which is how you tell an accepted invite from one that never existed.

Parameters:

invite_id (str) – The invite’s UUID, as carried by InviteModel.id. This is not the ID of the user the invite produced on acceptance; the two records have separate IDs.

Returns:

An InviteModel. On an accepted invite, processed_at carries when it was accepted.

Raises:
  • PineconeValueError – If invite_id is empty.

  • NotFoundError – If no such invite exists in the organization. A deleted invite reads back as not found rather than as a status value, so not-found and accepted are genuinely different answers here.

Return type:

InviteModel

Examples

>>> from pinecone import Admin
>>> admin = Admin(client_id="your-client-id", client_secret="your-client-secret")
>>> invite = admin.invites.describe(
...     invite_id="9c8e3528-b9c0-4358-84ce-84c28e91b566"
... )
>>> invite.email, invite.status
('newhire@acme.com', 'pending')

See also

  • Users.describe() — the member record created when this invite was accepted, addressed by its own user ID.

delete(*, invite_id)[source]

Withdraw a pending or expired invite, along with its role bindings.

The invite and its role bindings are gone by the time this returns — a repeat call, or fetching it by ID afterwards, gets a not-found error.

Parameters:

invite_id (str) – The UUID of the invite to withdraw.

Raises:
Return type:

None

Examples

>>> from pinecone import Admin
>>> admin = Admin(client_id="your-client-id", client_secret="your-client-secret")
>>> admin.invites.delete(invite_id="9c8e3528-b9c0-4358-84ce-84c28e91b566")

See also

  • Users.delete() — the only way to revoke access once an invite has been accepted.

resend(*, invite_id)[source]

Resend an invite’s email and push its expiration back out.

Works on pending and expired invites alike: the returned invite is pending again with a fresh expires_at. Invite emails are rate limited per organization, so this is not safe to call in a tight loop — see the note below.

Parameters:

invite_id (str) – The UUID of the invite to resend.

Returns:

The updated InviteModel, with status back to pending and a later expires_at. Read the new expiry from here rather than computing it.

Raises:
  • PineconeValueError – If invite_id is empty.

  • NotFoundError – If no such invite exists in the organization.

  • ConflictError – If the invite has already been accepted. Never retry this one — there is nothing left to resend.

  • RateLimitError – If the organization’s invite-email budget is exhausted. retry_after carries the server’s cooldown when one is supplied.

Return type:

InviteModel

Examples

>>> from pinecone import Admin
>>> admin = Admin(client_id="your-client-id", client_secret="your-client-secret")
>>> invite = admin.invites.resend(
...     invite_id="9c8e3528-b9c0-4358-84ce-84c28e91b566"
... )
>>> invite.status
'pending'
>>> invite.expires_at
'2026-05-21T03:00:00Z'

Note

A RateLimitError that reaches you has already survived the SDK’s own retries, which honor Retry-After (see Retries and Resilience) — so an immediate retry of your own will just fail again. Honor exc.retry_after when the server supplies one, and back off generously otherwise.

Service Accounts

The OAuth principals the Admin client itself authenticates as. create and rotate_secret are the only operations that return a client_secret, and each returns it exactly once — capture it or rotate again.

class pinecone.admin.service_accounts.ServiceAccounts(*, http)[source]

Bases: object

The organization’s machine identities, and the credentials they authenticate with.

A service account is a non-human principal for programmatic API access. It is also the kind of principal Admin itself authenticates as, so this namespace manages the same species of credential the client is holding. Not constructed directly — reach it as admin.service_accounts.

Two consequences are worth knowing before calling anything here:

  • create() and rotate_secret() are the only operations that ever return a client_secret, and each returns it exactly once. Capture it, or rotate again — nothing can retrieve it afterwards.

  • rotate_secret() and delete() aimed at the account whose credentials built this client will break it. See those methods.

Role bindings are not part of a service account’s representation: create() can send initial ones, but no method here returns them. Use RoleBindings with principal_type="service_account" and the account’s id as principal_id to read or change them afterwards.

See Error Handling for the exceptions every operation here can raise.

Examples

>>> from pinecone import Admin
>>> admin = Admin(client_id="your-client-id", client_secret="your-client-secret")
>>> for account in admin.service_accounts.list():
...     print(account.name, account.client_id)
ci-prod l3Ow0CmFyc4jOONcwiKUCRqQKN0tiCAn

See also

  • Users — the human members. A person is invited and accepts; a service account is created outright and holds its own OAuth credentials, so the two are never interchangeable.

  • ApiKeys — the other machine credential. An API key authorizes data-plane and control-plane calls within one project; a service account authenticates the Admin API across the organization.

Parameters:

http (HTTPClient)

__init__(*, http)[source]
Parameters:

http (HTTPClient)

Return type:

None

list(*, limit=None, pagination_token=None)[source]

List the organization’s service accounts, with lazy pagination.

No request is sent until the returned paginator is iterated; see Pagination.

Parameters:
  • limit (int | None) – Number of service accounts the server returns per page. It caps each page, not how many accounts the paginator yields in total; the paginator keeps following cursors until the pages run out, so use itertools.islice() to cap the total. When None the server chooses the page size.

  • pagination_token (str | None) – Cursor from a previous paginator’s pagination_token, to resume where that iteration stopped. Reuse it with the same limit.

Returns:

Paginator yielding ServiceAccountModel objects. The listed accounts carry no client_secret — that is returned only by create() and rotate_secret().

Raises:

PineconeValueError – If limit is outside 1-100. Raised before any network call.

Return type:

Paginator[ServiceAccountModel]

Examples

>>> from pinecone import Admin
>>> admin = Admin(client_id="your-client-id", client_secret="your-client-secret")
>>> for account in admin.service_accounts.list():
...     print(account.name, account.client_id)
ci-prod l3Ow0CmFyc4jOONcwiKUCRqQKN0tiCAn

Page-level access exposes the cursor, which is None once there is no further page to fetch:

>>> for page in admin.service_accounts.list(limit=25).pages():
...     print(len(page.items), page.pagination_token)
1 None

See also

  • Users.list() — the human members, which this list deliberately excludes.

create(*, name, role_bindings=None)[source]

Create a service account and receive its OAuth secret, once.

The returned client_secret is shown exactly once: it is not stored by the SDK, and neither describe() nor list() can retrieve it. Capture it here or the only recovery is rotate_secret(), which mints a different one. The server does not deduplicate on name, so repeating this call creates another, separate account with its own credentials rather than returning the first.

Parameters:
  • name (str) – Human-readable label for the account, e.g. "ci-prod". Sent verbatim — the SDK checks only that it is non-empty and leaves length and content to the server. The server measures length in UTF-8 bytes rather than codepoints, so a name of multi-byte characters can be rejected while looking short to Python’s len().

  • role_bindings (Sequence[RoleBindingInput | Mapping[str, Any]] | None) – Optional initial roles, as RoleBindingInput instances or plain dicts, mixed freely. Each entry needs resource_type ("organization" or "project") and role; project scope additionally needs resource_id, the project UUID. None and [] both create an account with no roles at all — it can obtain a token but do nothing with it until roles are granted. The bindings are not echoed in the response.

Returns:

A ServiceAccountWithSecret exposing .service_account (the metadata, including the id every other method here takes and the OAuth client_id) and .client_secret.

Raises:
  • PineconeValueError – If name is empty, or if any role_bindings entry is missing resource_type/role, carries an unrecognized key, or names a value this SDK release does not know. The message names the index of the offending entry. Raised before any network call.

  • ForbiddenError – If the caller lacks permission to create service accounts, or the organization’s plan does not include them. The two cases are distinguishable only by the server’s message, so read it before concluding which one you hit.

Return type:

ServiceAccountWithSecret

Examples

>>> from pinecone import Admin
>>> admin = Admin(client_id="your-client-id", client_secret="your-client-secret")
>>> created = admin.service_accounts.create(name="ci-prod")
>>> created.service_account.name
'ci-prod'
>>> bool(created.client_secret)
True

That is the only moment created.client_secret is readable — hand it straight to whatever stores your credentials, because neither describe() nor list() will return it and the only other way to get a working secret is rotate_secret().

With initial roles, typed or as dicts:

>>> from pinecone.models.admin import ResourceType, RoleBindingInput, RoleName
>>> created = admin.service_accounts.create(
...     name="ci-prod",
...     role_bindings=[
...         RoleBindingInput(
...             resource_type=ResourceType.PROJECT,
...             role=RoleName.DATA_PLANE_EDITOR,
...             resource_id="a2f7dddb-1597-4eff-9f71-535fde243f58",
...         ),
...         {"resource_type": "organization", "role": "OrgMember"},
...     ],
... )
>>> bool(created.client_secret)
True

The roles are not echoed back, so read them through RoleBindings.list().

Warning

Treat client_secret as a credential. repr() of the result masks it, but to_dict() and JSON encoding return it in full, so a result logged or serialized wholesale leaks the secret.

See also

  • rotate_secret() — the only way to obtain a working secret for an account whose creation result was dropped.

  • Invites.create() — the human equivalent, which takes the same binding shape but emails an offer instead of minting credentials.

describe(*, service_account_id)[source]

Get one service account’s metadata.

The client_secret is never part of this response — it exists in the clear only in the create() and rotate_secret() results.

Parameters:

service_account_id (str) – The account’s UUID, as carried by ServiceAccountModel.id. Not the OAuth client_id, which identifies the account only during token exchange.

Returns:

A ServiceAccountModel with id, name, client_id, created_at, and updated_at.

Raises:
  • PineconeValueError – If service_account_id is empty.

  • NotFoundError – If no such service account exists in the organization. Passing the OAuth client_id instead of the id lands here too.

Return type:

ServiceAccountModel

Examples

>>> from pinecone import Admin
>>> admin = Admin(client_id="your-client-id", client_secret="your-client-secret")
>>> account = admin.service_accounts.describe(
...     service_account_id="f8a3b2c1-4d5e-6f7a-8b9c-0d1e2f3a4b5c"
... )
>>> account.name
'ci-prod'
>>> "client_secret" in account.to_dict()
False

See also

update(*, service_account_id, name=None)[source]

Rename a service account.

Only the name is mutable here. Roles are managed through RoleBindings, and the OAuth client_id and client_secret are not editable at all — rotate the secret with rotate_secret() instead.

Parameters:
  • service_account_id (str) – The account’s UUID.

  • name (str | None) – The new name, e.g. "ci-prod-eu". Sent verbatim; the server owns the length and content rules, and measures length in UTF-8 bytes rather than codepoints.

Returns:

The updated ServiceAccountModel, with a fresh updated_at. No secret is returned.

Raises:
  • PineconeValueError – If service_account_id is empty, or if no updatable field was given. The server would accept a fieldless patch as a success that merely bumps updated_at, hiding a caller bug — usually a misspelled keyword — behind an apparent success, so the SDK rejects it first. Raised before any network call.

  • NotFoundError – If no such service account exists in the organization.

  • ForbiddenError – If the caller lacks the update permission, or the organization’s plan does not include service accounts.

Return type:

ServiceAccountModel

Examples

>>> from pinecone import Admin
>>> admin = Admin(client_id="your-client-id", client_secret="your-client-secret")
>>> account = admin.service_accounts.update(
...     service_account_id="f8a3b2c1-4d5e-6f7a-8b9c-0d1e2f3a4b5c",
...     name="ci-prod-eu",
... )
>>> account.client_id
'l3Ow0CmFyc4jOONcwiKUCRqQKN0tiCAn'

The OAuth client_id is untouched by a rename, so anything already authenticating as this account keeps working.

delete(*, service_account_id)[source]

Delete a service account, its role bindings, and its credentials.

The account and its role bindings are gone by the time this returns; a repeat of this call raises NotFoundError, like any other reference to a deleted account.

Parameters:

service_account_id (str) – The UUID of the service account to delete.

Raises:
Return type:

None

Examples

>>> from pinecone import Admin
>>> admin = Admin(client_id="your-client-id", client_secret="your-client-secret")
>>> admin.service_accounts.delete(
...     service_account_id="f8a3b2c1-4d5e-6f7a-8b9c-0d1e2f3a4b5c"
... )

Warning

Deleting the service account whose client_id/client_secret built this Admin client revokes the credentials that client authenticates with. Tokens it already minted stop working and no new one can be obtained, so the client cannot undo this — recovery needs another account’s credentials.

See also

  • rotate_secret() — replaces the secret without destroying the account or its role bindings.

rotate_secret(*, service_account_id)[source]

Issue a new OAuth client secret for a service account, revoking the old one.

The new client_secret is shown exactly once, in this response: it is not stored by the SDK and no later request can retrieve it, so a rotation whose result is dropped can only be recovered by rotating again. The account’s id and OAuth client_id are unchanged, so callers replace one value rather than reconfiguring the client identity.

Parameters:

service_account_id (str) – The UUID of the service account whose secret should be rotated.

Returns:

A ServiceAccountWithSecret whose .client_secret is the newly issued secret and whose .service_account carries the unchanged id and client_id.

Raises:
  • PineconeValueError – If service_account_id is empty.

  • NotFoundError – If no such service account exists in the organization.

  • ForbiddenError – If the caller lacks the rotate permission, or the organization’s plan does not include service accounts.

Return type:

ServiceAccountWithSecret

Examples

>>> from pinecone import Admin
>>> admin = Admin(client_id="your-client-id", client_secret="your-client-secret")
>>> rotated = admin.service_accounts.rotate_secret(
...     service_account_id="f8a3b2c1-4d5e-6f7a-8b9c-0d1e2f3a4b5c"
... )
>>> rotated.service_account.client_id
'l3Ow0CmFyc4jOONcwiKUCRqQKN0tiCAn'
>>> bool(rotated.client_secret)
True

The client_id above is the pre-rotation one, unchanged: only the secret is new, so a caller replaces one value. Read rotated.client_secret now and store it — this response is the only place it exists in the clear.

Warning

Rotating the secret of the service account whose credentials built this Admin client invalidates the secret that client holds. Its current access token keeps working until it expires, but the next token exchange fails until the client is rebuilt with the new secret.

Warning

Treat client_secret as a credential. repr() of the result masks it, but to_dict() and JSON encoding return it in full — so never log or serialize the result wholesale.

See also

  • create() — the other operation that returns a client_secret, and the only one that returns a new account.

Role Bindings

The whole of Pinecone’s authorization model: one role granted to one principal — user, service account, API key, or pending invite — at one scope, either the organization or a single project. Nothing else confers permissions, and bindings are immutable, so a role change is a create followed by a delete.

class pinecone.admin.role_bindings.RoleBindings(*, http)[source]

Bases: object

The whole of Pinecone’s authorization model.

A role binding grants one role to one principal — a user, service account, API key, or pending invite — at one scope, either the organization or a single project. Nothing else confers permissions, so this namespace is where a principal’s access is read and changed. The other admin namespaces deliberately carry no role bindings in their models; list() with principal_type and principal_id is how a principal’s access is enumerated. Not constructed directly — reach it as admin.role_bindings.

Bindings are immutable: there is no update. Changing a principal’s role means create() for the new one and delete() for the old one, in that order — deleting first can strip the principal’s last organization-membership binding, which the server refuses.

The server owns which role may be bound to which scope and principal type, and which roles an organization’s plan includes. Those rules vary by plan, so the SDK does not replicate them: it checks only that a value is one this release knows about and that the filter co-requirements hold, and lets the server’s own error messages — which name the role, the scope, and the plan — explain the rest.

See Error Handling for the exceptions every operation here can raise.

Examples

>>> from pinecone import Admin
>>> admin = Admin(client_id="your-client-id", client_secret="your-client-secret")
>>> for binding in admin.role_bindings.list():
...     print(binding.principal_type, binding.role, binding.resource_type)
user OrgMember organization

See also

  • Users, ServiceAccounts, and Invites — the principals bindings point at. Each is identified here by its own id as principal_id, and principal_type is what disambiguates them.

Parameters:

http (HTTPClient)

__init__(*, http)[source]
Parameters:

http (HTTPClient)

Return type:

None

list(*, principal_type=None, principal_id=None, resource_type=None, resource_id=None, role=None, limit=None, pagination_token=None)[source]

List the organization’s role bindings, with lazy pagination.

Every supplied filter is combined with AND, so list(principal_type="user", role="OrgOwner") returns the bindings that are both. With no filters at all it walks every binding the caller is allowed to see, which for an org owner is the organization’s entire authorization state.

No request is sent until the returned paginator is iterated; see Pagination. The filters and limit are carried onto every later page, so a cursor is always replayed with the query that produced it.

Parameters:
  • principal_type (str | PrincipalType | None) – Restrict to one kind of principal — "user", "service_account", "api_key", or "invite". Required whenever principal_id is given, since an ID alone is ambiguous across principal kinds. Omitted when None.

  • principal_id (str | None) – Restrict to one principal’s bindings — a UUID for every principal type. Requires principal_type. Sent verbatim; an unparseable value is rejected by the server. Omitted when None.

  • resource_type (str | ResourceType | None) – Restrict to one scope kind — "organization" or "project". Required whenever resource_id is given. Omitted when None.

  • resource_id (str | None) – Restrict to one organization or project. Requires resource_type. Omitted when None.

  • role (str | RoleName | None) – Restrict to one role, spelled as the wire name ("ProjectOwner", not "project_owner"). RoleName members are accepted interchangeably. Omitted when None.

  • limit (int | None) – Number of bindings the server returns per page. It caps each page, not how many bindings the paginator yields in total; the paginator keeps following cursors until the pages run out, so use itertools.islice() to cap the total. When None the server chooses the page size.

  • pagination_token (str | None) – Cursor from a previous paginator’s pagination_token, to resume where that iteration stopped. Reuse it with the same filters and limit.

Returns:

Paginator yielding RoleBindingModel objects, each carrying the id that delete() needs.

Raises:

PineconeValueError – If principal_id is given without principal_type, or resource_id without resource_type; or if principal_type, resource_type, or role names a value this SDK release does not know. Raised before any network call.

Return type:

Paginator[RoleBindingModel]

Examples

>>> from pinecone import Admin
>>> admin = Admin(client_id="your-client-id", client_secret="your-client-secret")
>>> for binding in admin.role_bindings.list():
...     print(binding.principal_type, binding.role, binding.resource_type)
user OrgMember organization

Every binding reads as that same triple — one principal, one role, one scope — which is the whole of what authorization consists of here. Filters narrow which triples come back:

>>> everything_one_service_account_can_do = admin.role_bindings.list(
...     principal_type="service_account",
...     principal_id="f8a3b2c1-4d5e-6f7a-8b9c-0d1e2f3a4b5c",
... ).to_list()
>>> project_owners = admin.role_bindings.list(
...     resource_type="project",
...     resource_id="a2f7dddb-1597-4eff-9f71-535fde243f58",
...     role="ProjectOwner",
... ).to_list()

Page-level access exposes the cursor, which is None once there is no further page to fetch:

>>> for page in admin.role_bindings.list(limit=25).pages():
...     print(len(page.items), page.pagination_token)
1 None

See also

  • delete() — takes the id off a binding found here; there is no way to revoke by principal, scope, and role.

create(*, principal_type, principal_id, resource_type, role, resource_id=None)[source]

Grant a role to a principal at an organization or project scope.

The binding takes effect immediately and comes back carrying the id delete() needs — the only way to revoke it, since bindings cannot be edited in place. The same scope-and-role pair is accepted as an initial binding by Invites.create() and ServiceAccounts.create(), so a grant expressed once works in all three places.

Parameters:
  • principal_type (str | PrincipalType) – The kind of principal receiving the role — "user", "service_account", "api_key", or "invite". Binding to an invite grants the role to whoever accepts it; once accepted the server refuses further bindings on the invite, and the roles must be managed on the resulting user instead.

  • principal_id (str) – The principal’s UUID. Sent verbatim — an unknown or unparseable principal is rejected by the server.

  • resource_type (str | ResourceType) – The scope — "organization" or "project".

  • role (str | RoleName) – The role to grant, spelled as the wire name ("DataPlaneEditor"). RoleName members are accepted interchangeably. Which roles are legal depends on the scope and principal type; see the note below.

  • resource_id (str | None) – The project UUID. Required when resource_type is "project". For "organization" scope leave it unset — the organization is inferred from the credentials, and naming any organization other than the caller’s own is rejected.

Returns:

The created RoleBindingModel, whose resource_id is always populated: an organization-scoped binding comes back carrying the organization the credentials resolved to, even though the request omitted it.

Raises:
  • PineconeValueError – If principal_id is empty; if principal_type, resource_type, or role names a value this SDK release does not know; or if resource_type is "project" and resource_id is missing. Raised before any network call.

  • NotFoundError – If the principal or the resource does not exist in the caller’s organization.

  • ConflictError – If an identical binding already exists — the grant is already in force, so this is usually safe to treat as success — or the principal is an invite that has already been accepted, in which case re-target the binding at the resulting user.

  • ForbiddenError – If the role cannot be bound to that scope or principal type, the organization’s plan does not include it, or the caller would be granting a permission it does not itself hold. The SDK cannot tell these apart in advance; see the note below.

Return type:

RoleBindingModel

Examples

>>> from pinecone import Admin
>>> admin = Admin(client_id="your-client-id", client_secret="your-client-secret")
>>> binding = admin.role_bindings.create(
...     principal_type="user",
...     principal_id="e2e92523-85dc-4142-b8c2-e681be8b78df",
...     resource_type="organization",
...     role="OrgMember",
... )
>>> binding.principal_type, binding.role, binding.resource_type
('user', 'OrgMember', 'organization')

The grant comes back with its own id, and with resource_id filled in even though an organization-scoped request omits it:

>>> bool(binding.id)
True
>>> bool(binding.resource_id)
True

A project-scoped grant, with enums:

>>> from pinecone.models.admin import PrincipalType, ResourceType, RoleName
>>> binding = admin.role_bindings.create(
...     principal_type=PrincipalType.SERVICE_ACCOUNT,
...     principal_id="f8a3b2c1-4d5e-6f7a-8b9c-0d1e2f3a4b5c",
...     resource_type=ResourceType.PROJECT,
...     resource_id="a2f7dddb-1597-4eff-9f71-535fde243f58",
...     role=RoleName.DATA_PLANE_EDITOR,
... )

Note

Whether a grant is allowed is entirely the server’s call, and it refuses for several distinct reasons that all arrive as ForbiddenError: a project-scoped binding must name a project-scoped role, an api_key principal accepts only the roles a key can hold (see APIKeyRole), some roles are gated behind the organization’s plan, and the caller cannot grant a permission it does not itself hold. Each rejection names the role, the scope, and — for plan gating — the plan required, so read the message rather than pre-flighting the rules.

See also

  • delete() — the second half of a role change, which must run after this call rather than before it.

describe(*, role_binding_id)[source]

Get one role binding’s details.

Parameters:

role_binding_id (str) – The binding’s own UUID, from list() or a create() result — not the principal’s ID and not the project’s.

Returns:

A RoleBindingModel with the principal, the scope, the role, and when it was granted.

Raises:
  • PineconeValueError – If role_binding_id is empty.

  • NotFoundError – If no such role binding is visible to the caller. A binding in another organization, and a project binding the caller cannot see, both look the same as one that does not exist — absence and inaccessibility are deliberately indistinguishable, so do not read this as proof the binding is gone.

Return type:

RoleBindingModel

Examples

>>> from pinecone import Admin
>>> admin = Admin(client_id="your-client-id", client_secret="your-client-secret")
>>> binding = admin.role_bindings.describe(
...     role_binding_id="9a8e3528-b9c0-4358-84ce-84c28e91b566"
... )
>>> binding.principal_type, binding.role, binding.resource_type
('user', 'OrgMember', 'organization')
delete(*, role_binding_id)[source]

Revoke a role binding, by the binding’s own ID.

Deletion is addressed by role_binding_id rather than by the principal/scope/role triple, so revoking a role means finding the binding first — usually with list() filtered by principal_type and principal_id, or from the create() result. The permissions are revoked immediately, after which the binding reads back as not found, including for a repeat of this call: delete is not idempotent in the “second call also succeeds” sense.

Parameters:

role_binding_id (str) – The binding’s own UUID.

Raises:
  • PineconeValueError – If role_binding_id is empty.

  • NotFoundError – If no such role binding is visible to the caller, including a repeat of a successful delete.

  • ConflictError – If deleting the binding would strip the organization of its last owner, remove a principal’s last organization membership while it still holds other roles, or the organization’s user management is delegated to an identity provider. Grant the replacement binding first, or make the change in the identity provider.

Return type:

None

Examples

>>> from pinecone import Admin
>>> admin = Admin(client_id="your-client-id", client_secret="your-client-secret")
>>> admin.role_bindings.delete(
...     role_binding_id="9a8e3528-b9c0-4358-84ce-84c28e91b566"
... )

Note

Some bindings cannot be deleted at all: the organization’s last OrgOwner, and a pending invite’s last organization-membership binding — withdraw the invite with Invites.delete() instead of unpicking its bindings. Organizations whose users are managed by an identity provider refuse user and invite binding changes outright.

See also

  • create() — run it before this call when changing a role, or the delete can be refused for leaving the principal with no organization membership.