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:
objectManage Pinecone organizations, projects, and the credentials that reach them.
Adminauthenticates with OAuth2 client credentials — a service account’sclient_idandclient_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.Pineconeis the other half of the SDK: it takes an API key and does index and vector work. ConstructingAdminexchanges 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 reachprojects— create, configure, and delete projectsapi_keys— the project-scoped keysPineconeauthenticates withusers— the organization’s membersinvites— pending and expired invitations to join the organizationservice_accounts— the OAuth principals this client itself authenticates asrole_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_IDenv var.client_secret (str | None) – OAuth2 client secret. Falls back to
PINECONE_CLIENT_SECRETenv 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_HOSTenv var, then defaults tohttps://api.pinecone.io. A value with no scheme is prefixed withhttps://, matchingPinecone. 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 withhttps://. 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
Pineconeauthenticates with, so the two clients chain: create the project, create a key scoped to it, then hand that key’s secret toPinecone.>>> 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_idandclient_secretcome either fromadmin.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 anAdminuntil one service account exists. Either way theclient_secretis shown exactly once, androtate_secret()is the only way to get another.Note
Token refresh —
Adminrenews its OAuth token before it expires, so a long-lived instance keeps working with no action on your part. Supply your ownAuthorizationentry inadditional_headersto 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]¶
- 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
Organizationsnamespace. Calllist()ordescribe()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.
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
Pineconeauthenticates with.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;
invitescovers those who have been asked and have not joined yet.- Returns:
The
Usersnamespace. Calllist()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
usersentry once accepted.- Returns:
The
Invitesnamespace. Callcreate()orlist()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
Adminclients authenticate as, including the one behind this client’s ownclient_id/client_secret— rotating or deleting that account breaks this client.- Returns:
The
ServiceAccountsnamespace. Callcreate()orlist()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
RoleBindingsnamespace. Callcreate()orlist()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
Admininstance 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:
objectOperations on Pinecone organizations.
An organization is the top-level account boundary in Pinecone: it holds projects, users, and billing, and everything else an
Adminclient 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 asadmin.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)
- list()[source]¶
List the organizations your credentials can reach.
- Returns:
An
OrganizationListof every reachable organization, supporting iteration,len(), and index access. Returned whole — there is no paging.- Return type:
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
OrganizationModelwith 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:
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:
- Returns:
An
OrganizationModelcarrying the name as it was stored.- Raises:
PineconeValueError – If organization_id is empty or whitespace-only. Checked before the request is sent.
- Return type:
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:
objectOperations 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 letPineconereach 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
ProjectListof every reachable project, supporting iteration,len(), and index access. Returned whole — there is no paging.- Return type:
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 whydescribe_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;
0means serverless-only. Omitted from the request ifNone.force_encryption_with_cmek (bool | None) – Require customer-managed encryption keys for everything in the project. Requesting
Trueneeds CMEK enabled for the organization, and it cannot be turned back off later — seeupdate(). Omitted from the request ifNone.
- Returns:
A
ProjectModelwith the new project’sid— the value every other project and API-key call takes — plus itsname, quotas, andorganization_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:
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
ProjectModelwith 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:
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 aproject_idin code that runs often or in an organization with many projects.- Parameters:
name (str) – The project’s name, e.g.
"my-project".- Returns:
A
ProjectModelwith 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 theproject_idyou want.
- Return type:
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 asTrueas well, so treat aTrueas “not known to be absent” and do not use this as an authorization check. Several projects sharing name also giveTrue, 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 indescribe_by_name().
- Returns:
Trueif the project exists,Falseif nothing matches.- Raises:
PineconeValueError – If both arguments are given, or neither.
- Return type:
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 rulecreate()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 passingFalsefor a project that never had it on is a no-op. Left unchanged if omitted.
- Returns:
A
ProjectModelreflecting 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
Trueand 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:
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 constructingProjectsdirectly.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:
objectOperations on Pinecone API keys.
An API key is a project-scoped credential: it is the thing you pass to
Pineconeto read and write indexes in one project. Where a service account authenticates anAdminclient against the whole organization, an API key reaches exactly one project. Not constructed directly — reach it asadmin.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 anAdminclient itself uses.Error Handling — what each exception these calls raise means.
- Parameters:
http (HTTPClient)
- 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
APIKeyListof 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:
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 —
valueis 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 asAPIKeyRolemembers. Defaults to["ProjectEditor"]. A role the organization is not entitled to grant is refused even though the name is valid; see Raises.
- Returns:
An
APIKeyWithSecretwithvalue(the secret, this once only) andkey(anAPIKeyModelcarrying the key’sid,name, androles). PassvaluetoPinecone; keepkey.idto reach the key again throughdescribe(),update(), ordelete().- 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:
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.idfromcreate()orlist(), e.g."key-abc123". This is not the secret.- Returns:
An
APIKeyModelwith the key’sid,name,project_id, androles.- Raises:
PineconeValueError – If api_key_id is empty or whitespace-only. Checked before the request is sent.
- Return type:
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. Unlikecreate(), 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
APIKeyModelreflecting 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:
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:
objectThe human members of a Pinecone organization.
A user is a person who has accepted an invitation and now belongs to the organization that the
Adminclient’s OAuth credentials resolve to. Not constructed directly — reach it asadmin.users.What a user is allowed to do is not part of this model. Permissions come only from role bindings, so
RoleBindingsis 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)
- 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. WhenNonethe 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 sameemailandlimit.
- Returns:
- Raises:
PineconeValueError – If limit is outside 1-100. Raised before any network call.
- Return type:
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()— withprincipal_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:
UserModelwithid,email, andname(Nonewhen 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:
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 doesdescribe()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:
objectOffers of organization membership that have not yet been accepted.
An invite is an emailed offer for someone to join the organization that the
Adminclient’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 asadmin.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 throughRoleBindingswithprincipal_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)
- 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. WhenNonethe 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 samelimit.
- Returns:
PaginatoryieldingInviteModelobjects.- Raises:
PineconeValueError – If limit is outside 1-100. Raised before any network call.
- Return type:
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
Noneonce 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, withstatus == InviteStatus.PROCESSED, and the accepted invitee is now a member reachable throughUsers.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, withexpires_atset 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
RoleBindingInputinstances or plain dicts, mixed freely. Each entry needsresource_type("organization"or"project") androle;projectscope additionally needsresource_id, the project UUID. At least one entry is required, and the server requires at least one of them to be anorganization-scoped membership role (OrgOwner,OrgManager,OrgBillingAdmin, orOrgMember) — a project-only invite is rejected.
- Returns:
The created
InviteModel, whoseidis whatdescribe(),resend(), anddelete()take, and what identifies the invite as aprincipal_idin 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:
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
idis whatresend(),delete(), and role-binding queries take, and the invitee stays absent fromUsers.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 returnstatus == 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_atcarries 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:
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:
PineconeValueError – If invite_id is empty.
NotFoundError – If no such invite exists in the organization.
ConflictError – If the invite has already been accepted. There is no invite left to withdraw; remove the resulting member with
Users.delete()instead.
- 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
pendingagain with a freshexpires_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, withstatusback topendingand a laterexpires_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_aftercarries the server’s cooldown when one is supplied.
- Return type:
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
RateLimitErrorthat reaches you has already survived the SDK’s own retries, which honorRetry-After(see Retries and Resilience) — so an immediate retry of your own will just fail again. Honorexc.retry_afterwhen 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:
objectThe 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
Adminitself authenticates as, so this namespace manages the same species of credential the client is holding. Not constructed directly — reach it asadmin.service_accounts.Two consequences are worth knowing before calling anything here:
create()androtate_secret()are the only operations that ever return aclient_secret, and each returns it exactly once. Capture it, or rotate again — nothing can retrieve it afterwards.rotate_secret()anddelete()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. UseRoleBindingswithprincipal_type="service_account"and the account’sidasprincipal_idto 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)
- 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. WhenNonethe 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 samelimit.
- Returns:
PaginatoryieldingServiceAccountModelobjects. The listed accounts carry noclient_secret— that is returned only bycreate()androtate_secret().- Raises:
PineconeValueError – If limit is outside 1-100. Raised before any network call.
- Return type:
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
Noneonce 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_secretis shown exactly once: it is not stored by the SDK, and neitherdescribe()norlist()can retrieve it. Capture it here or the only recovery isrotate_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’slen().role_bindings (Sequence[RoleBindingInput | Mapping[str, Any]] | None) – Optional initial roles, as
RoleBindingInputinstances or plain dicts, mixed freely. Each entry needsresource_type("organization"or"project") androle;projectscope additionally needsresource_id, the project UUID.Noneand[]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
ServiceAccountWithSecretexposing.service_account(the metadata, including theidevery other method here takes and the OAuthclient_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:
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_secretis readable — hand it straight to whatever stores your credentials, because neitherdescribe()norlist()will return it and the only other way to get a working secret isrotate_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_secretas a credential.repr()of the result masks it, butto_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_secretis never part of this response — it exists in the clear only in thecreate()androtate_secret()results.- Parameters:
service_account_id (str) – The account’s UUID, as carried by
ServiceAccountModel.id. Not the OAuthclient_id, which identifies the account only during token exchange.- Returns:
A
ServiceAccountModelwithid,name,client_id,created_at, andupdated_at.- Raises:
PineconeValueError – If service_account_id is empty.
NotFoundError – If no such service account exists in the organization. Passing the OAuth
client_idinstead of theidlands here too.
- Return type:
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
Users.describe()— the human equivalent, addressed by user ID.
- update(*, service_account_id, name=None)[source]¶
Rename a service account.
Only the name is mutable here. Roles are managed through
RoleBindings, and the OAuthclient_idandclient_secretare not editable at all — rotate the secret withrotate_secret()instead.- Parameters:
- Returns:
The updated
ServiceAccountModel, with a freshupdated_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:
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_idis 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:
PineconeValueError – If service_account_id is empty.
NotFoundError – If no such service account exists in the organization.
- 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_secretbuilt thisAdminclient 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_secretis 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’sidand OAuthclient_idare 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
ServiceAccountWithSecretwhose.client_secretis the newly issued secret and whose.service_accountcarries the unchangedidandclient_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:
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_idabove is the pre-rotation one, unchanged: only the secret is new, so a caller replaces one value. Readrotated.client_secretnow 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
Adminclient 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_secretas a credential.repr()of the result masks it, butto_dict()and JSON encoding return it in full — so never log or serialize the result wholesale.See also
create()— the other operation that returns aclient_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:
objectThe whole of Pinecone’s authorization model.
A role binding grants one
roleto 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()withprincipal_typeandprincipal_idis how a principal’s access is enumerated. Not constructed directly — reach it asadmin.role_bindings.Bindings are immutable: there is no update. Changing a principal’s role means
create()for the new one anddelete()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, andInvites— the principals bindings point at. Each is identified here by its ownidasprincipal_id, andprincipal_typeis what disambiguates them.
- Parameters:
http (HTTPClient)
- 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 whenNone.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 whenNone.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").RoleNamemembers are accepted interchangeably. Omitted whenNone.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. WhenNonethe 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:
PaginatoryieldingRoleBindingModelobjects, each carrying theidthatdelete()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:
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
Noneonce 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 theidoff 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
iddelete()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 byInvites.create()andServiceAccounts.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 aninvitegrants 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").RoleNamemembers 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, whoseresource_idis 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:
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 withresource_idfilled 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, anapi_keyprincipal accepts only the roles a key can hold (seeAPIKeyRole), 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 acreate()result — not the principal’s ID and not the project’s.- Returns:
A
RoleBindingModelwith 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:
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_idrather than by the principal/scope/role triple, so revoking a role means finding the binding first — usually withlist()filtered byprincipal_typeandprincipal_id, or from thecreate()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 withInvites.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.