Manage your Plany account, the people who belong to it and your own user profile. An account is what owns projects, holds credit and receives the bill. Invite teammates by email, set each member's role, and read back who is signed in with GetCurrentUser.
Creates an account. The calling user becomes its owner.
A new account starts on plans/hobby and is granted that plan's included
credit immediately, so a casual user can launch a server without ever
entering card details.
display_namestringRequirednamestringIdentifieraccounts/{account}, where {account} is an opaque generated id. Not the
user's email or chosen name, so both can change freely.
account_idstringImmutableShort human-readable id unique across Plany, e.g. "northstar-games". Usable in place of the generated id in resource names, so URLs read well.
ACCOUNT_TYPE_INDIVIDUALACCOUNT_TYPE_ORGANIZATIONdefault_locationstringDefault location for new projects, locations/{location}. Set from the
signup region so a casual user never has to pick one.
labelsmap<string, string>Free-form labels for the account holder's own organisation.
billing_emailstringContact address for billing and service notices.
country_codestringISO 3166-1 alpha-2 country, used for tax treatment and to pick a default region.
tax_idstringTax identifier such as a VAT number, for organisations.
account_idstringDesired short id. Must be globally unique, 3-40 chars, lowercase alphanumeric and hyphens. Empty generates one from display_name.
request_idstringIdempotency key. Repeating a create with the same key returns the original account rather than creating a second one — signup pages retry.
grpcurl -H "authorization: Bearer $PLANY_API_KEY" \ -d '{ "account": { "display_name": "My server" } }' \ api.plany.cloud:443 plany.account.v1.AccountService/CreateAccount{ "name": "accounts/", "account_id": "my-account", "display_name": "My server", "type": "ACCOUNT_TYPE_INDIVIDUAL", "state": "STATE_ACTIVE", "owner": "", "plan": "", "credit_balance": { "currency_code": "USD", "units": "5", "nanos": 0 }, "default_location": "", "labels": { "key": "" }, "billing_email": "", "country_code": "", "tax_id": "my-tax", "create_time": "2026-10-04T12:00:00Z", "update_time": "2026-10-04T12:00:00Z", "purge_time": "2026-10-04T12:00:00Z", "etag": "W/\"1\"", "state_reason": ""}grpcurl -H "authorization: Bearer $PLANY_API_KEY" \ -d '{ "name": "accounts/" }' \ api.plany.cloud:443 plany.account.v1.AccountService/GetAccount{ "name": "accounts/", "account_id": "my-account", "display_name": "My server", "type": "ACCOUNT_TYPE_INDIVIDUAL", "state": "STATE_ACTIVE", "owner": "", "plan": "", "credit_balance": { "currency_code": "USD", "units": "5", "nanos": 0 }, "default_location": "", "labels": { "key": "" }, "billing_email": "", "country_code": "", "tax_id": "my-tax", "create_time": "2026-10-04T12:00:00Z", "update_time": "2026-10-04T12:00:00Z", "purge_time": "2026-10-04T12:00:00Z", "etag": "W/\"1\"", "state_reason": ""}Lists the accounts you are a member of.
page_sizeint32page_tokenstringfilterstringAIP-160 filter, e.g. state = "ACTIVE" AND type = "ORGANIZATION".
order_bystringgrpcurl -H "authorization: Bearer $PLANY_API_KEY" \ -d '{ "page_size": 50 }' \ api.plany.cloud:443 plany.account.v1.AccountService/ListAccounts{ "accounts": [ { "name": "accounts/", "account_id": "my-account", "display_name": "My server", "type": "ACCOUNT_TYPE_INDIVIDUAL", "state": "STATE_ACTIVE", "owner": "", "plan": "", "credit_balance": { "currency_code": "USD", "units": "5", "nanos": 0 }, "default_location": "", "labels": { "key": "" }, "billing_email": "", "country_code": "", "tax_id": "my-tax", "create_time": "2026-10-04T12:00:00Z", "update_time": "2026-10-04T12:00:00Z", "purge_time": "2026-10-04T12:00:00Z", "etag": "W/\"1\"", "state_reason": "" } ], "next_page_token": ""}display_namestringRequirednamestringIdentifieraccounts/{account}, where {account} is an opaque generated id. Not the
user's email or chosen name, so both can change freely.
account_idstringImmutableShort human-readable id unique across Plany, e.g. "northstar-games". Usable in place of the generated id in resource names, so URLs read well.
ACCOUNT_TYPE_INDIVIDUALACCOUNT_TYPE_ORGANIZATIONdefault_locationstringDefault location for new projects, locations/{location}. Set from the
signup region so a casual user never has to pick one.
labelsmap<string, string>Free-form labels for the account holder's own organisation.
billing_emailstringContact address for billing and service notices.
country_codestringISO 3166-1 alpha-2 country, used for tax treatment and to pick a default region.
tax_idstringTax identifier such as a VAT number, for organisations.
Fields to change. Required: a blanket overwrite of an account is never what the caller means.
grpcurl -H "authorization: Bearer $PLANY_API_KEY" \ -d '{ "account": { "display_name": "My server" }, "update_mask": "display_name" }' \ api.plany.cloud:443 plany.account.v1.AccountService/UpdateAccount{ "name": "accounts/", "account_id": "my-account", "display_name": "My server", "type": "ACCOUNT_TYPE_INDIVIDUAL", "state": "STATE_ACTIVE", "owner": "", "plan": "", "credit_balance": { "currency_code": "USD", "units": "5", "nanos": 0 }, "default_location": "", "labels": { "key": "" }, "billing_email": "", "country_code": "", "tax_id": "my-tax", "create_time": "2026-10-04T12:00:00Z", "update_time": "2026-10-04T12:00:00Z", "purge_time": "2026-10-04T12:00:00Z", "etag": "W/\"1\"", "state_reason": ""}Begins account deletion.
The account enters STATE_PENDING_DELETION and can be restored with UndeleteAccount until its purge_time.
namestringRequiredetagstringFail unless the stored etag matches, guarding against deleting an account that changed since it was read.
validate_onlyboolValidate and report what would happen without doing it.
grpcurl -H "authorization: Bearer $PLANY_API_KEY" \ -d '{ "name": "accounts/" }' \ api.plany.cloud:443 plany.account.v1.AccountService/DeleteAccount{}Cancels a pending deletion and returns the account to ACTIVE. Fails once purge_time has passed.
namestringRequiredgrpcurl -H "authorization: Bearer $PLANY_API_KEY" \ -d '{ "name": "accounts/" }' \ api.plany.cloud:443 plany.account.v1.AccountService/UndeleteAccount{ "name": "accounts/", "account_id": "my-account", "display_name": "My server", "type": "ACCOUNT_TYPE_INDIVIDUAL", "state": "STATE_ACTIVE", "owner": "", "plan": "", "credit_balance": { "currency_code": "USD", "units": "5", "nanos": 0 }, "default_location": "", "labels": { "key": "" }, "billing_email": "", "country_code": "", "tax_id": "my-tax", "create_time": "2026-10-04T12:00:00Z", "update_time": "2026-10-04T12:00:00Z", "purge_time": "2026-10-04T12:00:00Z", "etag": "W/\"1\"", "state_reason": ""}Invites a user to the account by email. If no Plany user holds that address the invitation waits for them to sign up.
parentstringRequiredemailstringRequiredAddress the invitation was sent to. Retained after acceptance so the invite trail stays auditable.
namestringIdentifieraccounts/{account}/memberships/{membership}.
rolesrepeated stringAccount-level roles granted, as roles/{role} names. Written through to
the IAM policy on the account.
grpcurl -H "authorization: Bearer $PLANY_API_KEY" \ -d '{ "parent": "accounts/", "membership": { "email": "" } }' \ api.plany.cloud:443 plany.account.v1.AccountService/CreateMembership{ "name": "accounts//memberships/my-membership", "user": "", "email": "", "state": "STATE_INVITED", "roles": [ "" ], "inviter": "", "create_time": "2026-10-04T12:00:00Z", "invite_expire_time": "2026-10-04T12:00:00Z", "invitation_token": ""}namestringRequiredgrpcurl -H "authorization: Bearer $PLANY_API_KEY" \ -d '{ "name": "accounts//memberships/my-membership" }' \ api.plany.cloud:443 plany.account.v1.AccountService/GetMembership{ "name": "accounts//memberships/my-membership", "user": "", "email": "", "state": "STATE_INVITED", "roles": [ "" ], "inviter": "", "create_time": "2026-10-04T12:00:00Z", "invite_expire_time": "2026-10-04T12:00:00Z", "invitation_token": ""}parentstringRequiredpage_sizeint32page_tokenstringfilterstringgrpcurl -H "authorization: Bearer $PLANY_API_KEY" \ -d '{ "parent": "accounts/", "page_size": 50 }' \ api.plany.cloud:443 plany.account.v1.AccountService/ListMemberships{ "memberships": [ { "name": "accounts//memberships/my-membership", "user": "", "email": "", "state": "STATE_INVITED", "roles": [ "" ], "inviter": "", "create_time": "2026-10-04T12:00:00Z", "invite_expire_time": "2026-10-04T12:00:00Z", "invitation_token": "" } ], "next_page_token": ""}emailstringRequiredAddress the invitation was sent to. Retained after acceptance so the invite trail stays auditable.
namestringIdentifieraccounts/{account}/memberships/{membership}.
rolesrepeated stringAccount-level roles granted, as roles/{role} names. Written through to
the IAM policy on the account.
grpcurl -H "authorization: Bearer $PLANY_API_KEY" \ -d '{ "membership": { "email": "" }, "update_mask": "display_name" }' \ api.plany.cloud:443 plany.account.v1.AccountService/UpdateMembership{ "name": "accounts//memberships/my-membership", "user": "", "email": "", "state": "STATE_INVITED", "roles": [ "" ], "inviter": "", "create_time": "2026-10-04T12:00:00Z", "invite_expire_time": "2026-10-04T12:00:00Z", "invitation_token": ""}Removes a member. Refuses to remove the owner; ownership must be transferred first, so an account can never end up with nobody in charge.
namestringRequiredgrpcurl -H "authorization: Bearer $PLANY_API_KEY" \ -d '{ "name": "accounts//memberships/my-membership" }' \ api.plany.cloud:443 plany.account.v1.AccountService/DeleteMembership{}Accepts an invitation. Called by the invited user, who is identified from their own credentials rather than from the request.
invitation_tokenstringRequiredSingle-use token from the invitation email.
grpcurl -H "authorization: Bearer $PLANY_API_KEY" \ -d '{ "invitation_token": "" }' \ api.plany.cloud:443 plany.account.v1.AccountService/AcceptInvitation{ "name": "accounts//memberships/my-membership", "user": "", "email": "", "state": "STATE_INVITED", "roles": [ "" ], "inviter": "", "create_time": "2026-10-04T12:00:00Z", "invite_expire_time": "2026-10-04T12:00:00Z", "invitation_token": ""}Returns the user behind the caller's credentials.
No parameters.
User.
grpcurl -H "authorization: Bearer $PLANY_API_KEY" \ api.plany.cloud:443 plany.account.v1.AccountService/GetCurrentUser{ "name": "users/my-user", "email": "", "email_verified": false, "display_name": "My server", "timezone": "", "locale": "", "mfa_enabled": false, "create_time": "2026-10-04T12:00:00Z", "last_login_time": "2026-10-04T12:00:00Z"}grpcurl -H "authorization: Bearer $PLANY_API_KEY" \ -d '{ "name": "users/my-user" }' \ api.plany.cloud:443 plany.account.v1.AccountService/GetUser{ "name": "users/my-user", "email": "", "email_verified": false, "display_name": "My server", "timezone": "", "locale": "", "mfa_enabled": false, "create_time": "2026-10-04T12:00:00Z", "last_login_time": "2026-10-04T12:00:00Z"}emailstringRequirednamestringIdentifierusers/{user}.
display_namestringtimezonestringIANA timezone, e.g. "Europe/London". Used to render billing periods in the user's own time rather than UTC.
localestringBCP-47 language tag for notification copy.
User.
grpcurl -H "authorization: Bearer $PLANY_API_KEY" \ -d '{ "user": { "email": "", "display_name": "My server" }, "update_mask": "display_name" }' \ api.plany.cloud:443 plany.account.v1.AccountService/UpdateUser{ "name": "users/my-user", "email": "", "email_verified": false, "display_name": "My server", "timezone": "", "locale": "", "mfa_enabled": false, "create_time": "2026-10-04T12:00:00Z", "last_login_time": "2026-10-04T12:00:00Z"}DeleteAccountMetadata reports progress while an account is torn down. The operation can run for a long time when many regions hold resources, so this carries enough detail for a UI to show real progress.
accountstringprojects_totalint32projects_deletedint32resources_totalint32resources_deletedint32current_stepstringWhat the operation is doing right now, e.g. "deleting instances in eu-west-2".
{ "account": "accounts/", "projects_total": 0, "projects_deleted": 0, "resources_total": 0, "resources_deleted": 0, "current_step": ""}Account is the billing and ownership boundary.
display_namestringRequirednamestringIdentifieraccounts/{account}, where {account} is an opaque generated id. Not the
user's email or chosen name, so both can change freely.
account_idstringImmutableShort human-readable id unique across Plany, e.g. "northstar-games". Usable in place of the generated id in resource names, so URLs read well.
ACCOUNT_TYPE_INDIVIDUALACCOUNT_TYPE_ORGANIZATIONSTATE_ACTIVESTATE_PENDING_VERIFICATIONSTATE_SUSPENDED_NO_CREDITSTATE_SUSPENDED_ADMINSTATE_PENDING_DELETIONSTATE_CLOSEDownerstringOutput onlyPrincipal that owns the account, users/{user}. Distinct from the IAM
owner role: the owner cannot be removed and is the last point of contact.
planstringOutput onlyCurrent plan, plans/{plan} in plany.billing.v1 — plans/hobby or
plans/pro. The plan sets the monthly included credit.
Spendable credit right now, mirrored from billing for convenience so a console load does not need a second call. Billing remains authoritative.
default_locationstringDefault location for new projects, locations/{location}. Set from the
signup region so a casual user never has to pick one.
labelsmap<string, string>Free-form labels for the account holder's own organisation.
billing_emailstringContact address for billing and service notices.
country_codestringISO 3166-1 alpha-2 country, used for tax treatment and to pick a default region.
tax_idstringTax identifier such as a VAT number, for organisations.
When a pending deletion becomes irreversible. Set only in STATE_PENDING_DELETION.
etagstringOutput onlyConcurrency token for read-modify-write updates.
state_reasonstringOutput onlyWhy the account is in its current state, when Plany suspended it.
Shown so that a suspension always comes with its reason. Empty for a state nobody chose — a new account awaiting verification, for example.
accountsrepeated Accountdisplay_namestringRequirednamestringIdentifieraccounts/{account}, where {account} is an opaque generated id. Not the
user's email or chosen name, so both can change freely.
account_idstringImmutableShort human-readable id unique across Plany, e.g. "northstar-games". Usable in place of the generated id in resource names, so URLs read well.
ACCOUNT_TYPE_INDIVIDUALACCOUNT_TYPE_ORGANIZATIONSTATE_ACTIVESTATE_PENDING_VERIFICATIONSTATE_SUSPENDED_NO_CREDITSTATE_SUSPENDED_ADMINSTATE_PENDING_DELETIONSTATE_CLOSEDownerstringOutput onlyPrincipal that owns the account, users/{user}. Distinct from the IAM
owner role: the owner cannot be removed and is the last point of contact.
planstringOutput onlyCurrent plan, plans/{plan} in plany.billing.v1 — plans/hobby or
plans/pro. The plan sets the monthly included credit.
Spendable credit right now, mirrored from billing for convenience so a console load does not need a second call. Billing remains authoritative.
default_locationstringDefault location for new projects, locations/{location}. Set from the
signup region so a casual user never has to pick one.
labelsmap<string, string>Free-form labels for the account holder's own organisation.
billing_emailstringContact address for billing and service notices.
country_codestringISO 3166-1 alpha-2 country, used for tax treatment and to pick a default region.
tax_idstringTax identifier such as a VAT number, for organisations.
When a pending deletion becomes irreversible. Set only in STATE_PENDING_DELETION.
etagstringOutput onlyConcurrency token for read-modify-write updates.
state_reasonstringOutput onlyWhy the account is in its current state, when Plany suspended it.
Shown so that a suspension always comes with its reason. Empty for a state nobody chose — a new account awaiting verification, for example.
next_page_tokenstringmembershipsrepeated MembershipemailstringRequiredAddress the invitation was sent to. Retained after acceptance so the invite trail stays auditable.
namestringIdentifieraccounts/{account}/memberships/{membership}.
userstringOutput onlyThe member, users/{user}. Empty until an invitation is accepted.
STATE_INVITEDSTATE_ACTIVESTATE_SUSPENDEDrolesrepeated stringAccount-level roles granted, as roles/{role} names. Written through to
the IAM policy on the account.
inviterstringOutput onlyPrincipal that issued the invitation.
When an unaccepted invitation lapses.
invitation_tokenstringOutput onlyThe invitation token, returned by CreateMembership and never again.
Stored only as a hash, exactly like an API key and for the same reason: anyone with database read access could otherwise join any account. There is nowhere to look it up afterwards because there is nothing stored to look up — a lost invitation is reissued, not recovered.
next_page_tokenstringMembership grants a user access to an account.
Roles themselves live in IAM; this resource exists so an account can enumerate its people, invite new ones and track invitation state without every membership question becoming a policy query.
emailstringRequiredAddress the invitation was sent to. Retained after acceptance so the invite trail stays auditable.
namestringIdentifieraccounts/{account}/memberships/{membership}.
userstringOutput onlyThe member, users/{user}. Empty until an invitation is accepted.
STATE_INVITEDSTATE_ACTIVESTATE_SUSPENDEDrolesrepeated stringAccount-level roles granted, as roles/{role} names. Written through to
the IAM policy on the account.
inviterstringOutput onlyPrincipal that issued the invitation.
When an unaccepted invitation lapses.
invitation_tokenstringOutput onlyThe invitation token, returned by CreateMembership and never again.
Stored only as a hash, exactly like an API key and for the same reason: anyone with database read access could otherwise join any account. There is nowhere to look it up afterwards because there is nothing stored to look up — a lost invitation is reissued, not recovered.
User is a person with Plany credentials. A user may belong to several accounts; the account is the tenancy boundary, the user is not.
emailstringRequirednamestringIdentifierusers/{user}.
email_verifiedboolOutput onlydisplay_namestringtimezonestringIANA timezone, e.g. "Europe/London". Used to render billing periods in the user's own time rather than UTC.
localestringBCP-47 language tag for notification copy.
mfa_enabledboolOutput onlyAccountType shapes defaults, quotas and support expectations. It does not change the pricing model — both types spend the same credit at the same rates.
| Value | Description |
|---|---|
| ACCOUNT_TYPE_UNSPECIFIED | |
| ACCOUNT_TYPE_INDIVIDUAL | A person hosting servers for themselves and their friends. Lower default quotas, simpler console, aggressive idle-shutdown defaults. |
| ACCOUNT_TYPE_ORGANIZATION | A studio or business. Higher default quotas, multi-seat membership, invoicing and support entitlements. |
| Value | Description |
|---|---|
| STATE_UNSPECIFIED | |
| STATE_ACTIVE | Normal operation. |
| STATE_PENDING_VERIFICATION | Email not yet verified. The account can browse but cannot create billable resources. |
| STATE_SUSPENDED_NO_CREDIT | Out of credit. Running workloads are stopped and creation is refused, but data is retained so a top-up restores service. This is the ordinary end state for a lapsed hobby account, not a punishment. |
| STATE_SUSPENDED_ADMIN | Suspended by Plany, for abuse or non-payment. Only Plany can lift it — open a support case. |
| STATE_PENDING_DELETION | Deletion requested and inside the retention window. Everything is stopped; the account can still be restored. |
| STATE_CLOSED | Retention elapsed and resources are gone. The row survives so historical ledger entries still resolve. |
| Value | Description |
|---|---|
| STATE_UNSPECIFIED | |
| STATE_INVITED | Invited, not yet accepted. |
| STATE_ACTIVE | Accepted and active. |
| STATE_SUSPENDED | Access suspended without removing the record. |