| 1 | --- |
| 2 | title: Organization governance |
| 3 | description: Manage existing Neon organization access, billing alerts, and private-network associations with explicit ownership and restoration. |
| 4 | --- |
| 5 | |
| 6 | These resources configure an **existing** Neon organization. They use management |
| 7 | API credentials, separate from Postgres roles, application users, and branch |
| 8 | service credentials. Include `Neon.providers()` in your stack as described in |
| 9 | [Setup](/neon/setup). |
| 10 | |
| 11 | | Resource | Controls | On destroy | |
| 12 | | --- | --- | --- | |
| 13 | | [OrganizationApiKey](/providers/neon/organizationapikey) | An organization key, optionally project-restricted | Revoke the exact recorded key | |
| 14 | | [OrganizationMemberRole](/providers/neon/organizationmemberrole) | An existing member's organization role | Restore their original role, never remove membership | |
| 15 | | [ProjectMemberRole](/providers/neon/projectmemberrole) | An existing member's direct project grant | Restore the original direct grant, or remove the new grant | |
| 16 | | [OrganizationSpendingLimit](/providers/neon/organizationspendinglimit) | Monthly spending-alert threshold | Restore the original threshold, or remove the new threshold | |
| 17 | | [OrganizationVPCEndpoint](/providers/neon/organizationvpcendpoint) | Registration of an existing AWS PrivateLink endpoint | Restore an adopted label, or unregister a new registration | |
| 18 | | [ProjectVPCEndpoint](/providers/neon/projectvpcendpoint) | A project's association with an organization endpoint | Restore an adopted label, or remove a new association | |
| 19 | |
| 20 | ## Prepare the deployment identity |
| 21 | |
| 22 | Choose an existing organization and have an administrator approve the specific |
| 23 | access, spending, or network controls this stack will manage. For key creation |
| 24 | and role controls, use a personal management API key belonging to an organization |
| 25 | admin: key creation requires a personal admin key, and Alchemy's role safety |
| 26 | checks must identify the authenticated actor. Avoid making the deployment actor |
| 27 | the member whose role is being changed. |
| 28 | |
| 29 | An organization-wide key has broad administrative authority but is not accepted |
| 30 | by every operation. A project-restricted key has Editor access to that project; |
| 31 | it cannot manage project access or delete the project. Check Neon's |
| 32 | [operation/key matrix](https://neon.com/docs/manage/orgs-api#matrix-of-operations-and-key-types) |
| 33 | and [permissions model](https://neon.com/docs/manage/user-permissions) rather than |
| 34 | assuming any valid key can administer the organization. Configure the deployment |
| 35 | key using [Setup](/neon/setup), never as a Function environment variable. |
| 36 | |
| 37 | Use a separate non-admin test member for access experiments, and obtain explicit |
| 38 | approval before changing a real member's role or a shared billing threshold. |
| 39 | Plan and account entitlements remain Neon's responsibility; Alchemy does not |
| 40 | upgrade the organization. |
| 41 | |
| 42 | ## Find the organization and project IDs |
| 43 | |
| 44 | In the Neon Console, open **Organization Settings → General information** and |
| 45 | copy the organization ID. Select the intended project and copy its ID from |
| 46 | project settings, or use the `projectId` output of its completed Alchemy |
| 47 | deployment. Verify it belongs to that organization. |
| 48 | |
| 49 | Role and spending controls need resolved properties on their first deployment so |
| 50 | the original configuration can be persisted before mutation. Deploy a project |
| 51 | first and then pass its resolved ID for an initial `ProjectMemberRole` control; |
| 52 | do not use an unresolved project output to skip that checkpoint. |
| 53 | |
| 54 | ## Find an existing membership ID |
| 55 | |
| 56 | Have the user join the organization through Neon's normal invitation process |
| 57 | first. Alchemy's role resources do not invite users. With `NEON_API_KEY` supplied |
| 58 | securely to this terminal and `NEON_ORG_ID` set to the selected organization, |
| 59 | make a read-only request: |
| 60 | |
| 61 | ```sh |
| 62 | curl --fail-with-body --get \ |
| 63 | "https://console.neon.tech/api/v2/organizations/$NEON_ORG_ID/members" \ |
| 64 | --header "Authorization: Bearer $NEON_API_KEY" \ |
| 65 | --data-urlencode "limit=100" \ |
| 66 | --data-urlencode "sort_by=email" \ |
| 67 | --data-urlencode "sort_order=asc" |
| 68 | ``` |
| 69 | |
| 70 | Match the intended person's `user.email`, verify the account is active, and copy |
| 71 | `members[].member.id`. This is the membership ID, not `member.user_id`. If the |
| 72 | response includes `pagination.next`, repeat the request with |
| 73 | `--data-urlencode "cursor=<returned cursor>"`, keeping the sort settings, until |
| 74 | you find the member or exhaust the pages. Do not infer absence from the first |
| 75 | page. These results contain personal information; keep them out of public logs. |
| 76 | |
| 77 | ## Issue a project-restricted management key |
| 78 | |
| 79 | ```typescript |
| 80 | import * as Neon from "alchemy/Neon"; |
| 81 | |
| 82 | const project = yield* Neon.Project("Application", { |
| 83 | orgId: "org-example", |
| 84 | }); |
| 85 | |
| 86 | const automation = yield* Neon.OrganizationApiKey("Automation", { |
| 87 | orgId: "org-example", |
| 88 | projectId: project.projectId, |
| 89 | }); |
| 90 | ``` |
| 91 | |
| 92 | The `automation.key` output is a `Redacted<string>`. Neon returns this secret only |
| 93 | at creation: preserve and protect the Alchemy state store. A matching key name |
| 94 | cannot recover a missing secret or prove ownership. The resource fails rather |
| 95 | than silently adopting another key, rotating a revoked key, or substituting your |
| 96 | deployment credential. |
| 97 | |
| 98 | :::caution |
| 99 | Omitting `projectId` issues an organization-wide management key. Prefer a |
| 100 | project-restricted key. For Function storage and backend bindings, use |
| 101 | [`Neon.Credential`](/providers/neon/credential) and the runtime bindings instead; |
| 102 | a management key is not a branch service token. |
| 103 | ::: |
| 104 | |
| 105 | ## Manage an existing member's role |
| 106 | |
| 107 | ```typescript |
| 108 | import * as Alchemy from "alchemy"; |
| 109 | |
| 110 | const membership = yield* Neon.OrganizationMemberRole("DeveloperRole", { |
| 111 | orgId: "org-example", |
| 112 | memberId: authorizedMemberId, |
| 113 | role: "editor", |
| 114 | }).pipe(Alchemy.adopt(true)); |
| 115 | ``` |
| 116 | |
| 117 | Use the **membership ID**, not an email address or user ID. Resource-scoped |
| 118 | adoption authorizes managing this existing role and saves its original value |
| 119 | before mutation. Destroy restores that value; it never removes the person from |
| 120 | the organization. The resource refuses changes to the authenticated actor's own |
| 121 | role. Neon determines which roles your organization supports. Initial role-control |
| 122 | properties must be resolved. To change the member or project being controlled, |
| 123 | remove the old control first so its original role is restored, then declare the |
| 124 | new control. |
| 125 | |
| 126 | ## Manage a direct project grant |
| 127 | |
| 128 | ```typescript |
| 129 | const reviewer = yield* Neon.ProjectMemberRole("ReviewerAccess", { |
| 130 | orgId: "org-example", |
| 131 | memberId: authorizedMemberId, |
| 132 | project: { projectId: deployedProjectId }, |
| 133 | role: "viewer", |
| 134 | }); |
| 135 | ``` |
| 136 | |
| 137 | Deploy the project first and pass its resolved ID when initially creating this |
| 138 | control. The provider must capture the original grant before writing. An existing |
| 139 | direct grant requires resource-scoped adoption, as in the organization-role |
| 140 | example. Organization-inherited access and administrator overrides are not |
| 141 | owned by this resource. Removing a direct grant can leave inherited access in |
| 142 | place; it is not equivalent to removing every permission. |
| 143 | |
| 144 | ## Set a monthly spending alert |
| 145 | |
| 146 | ```typescript |
| 147 | const spending = yield* Neon.OrganizationSpendingLimit("SpendingAlert", { |
| 148 | orgId: "org-example", |
| 149 | spendingLimitCents: 10000, |
| 150 | }).pipe(Alchemy.adopt(true)); |
| 151 | ``` |
| 152 | |
| 153 | This sets a $100 monthly alert threshold. Neon sends notifications at 80% and |
| 154 | 100%; it **does not suspend compute or enforce a hard spending cap**. Supply a |
| 155 | positive integer number of cents. An existing threshold requires adoption and |
| 156 | is restored on destroy; a newly created threshold is removed. Setting a |
| 157 | threshold below current spending can trigger a notification. Plan and admin |
| 158 | requirements are enforced by Neon. Initial properties must be resolved so Alchemy |
| 159 | can persist the original threshold before writing. Remove the control and restore |
| 160 | its original threshold before declaring a control for another organization. |
| 161 | |
| 162 | ## Register an existing private endpoint |
| 163 | |
| 164 | ```typescript |
| 165 | const network = yield* Neon.OrganizationVPCEndpoint("PrivateNetwork", { |
| 166 | orgId: "org-example", |
| 167 | regionId: "aws-us-east-2", |
| 168 | vpcEndpointId: "vpce-0123456789abcdef0", |
| 169 | label: "Application network", |
| 170 | }); |
| 171 | ``` |
| 172 | |
| 173 | Create the AWS endpoint separately. This resource registers it with Neon; it |
| 174 | never provisions or deletes the AWS endpoint, configures DNS, or blocks public |
| 175 | connections. Private networking requires an entitled organization and a |
| 176 | supported AWS region. Follow [Private networking](/neon/guides/private-networking) |
| 177 | for entitlement, AWS endpoint creation, registration order, private DNS, and |
| 178 | inside/outside-network connection checks. |
| 179 | |
| 180 | ## Associate a project with the endpoint |
| 181 | |
| 182 | ```typescript |
| 183 | const access = yield* Neon.ProjectVPCEndpoint("PrivateAccess", { |
| 184 | project, |
| 185 | endpoint: network, |
| 186 | label: "Application access", |
| 187 | }); |
| 188 | ``` |
| 189 | |
| 190 | The project and organization registration must share an organization and AWS |
| 191 | region. This manages only this endpoint association, leaving other associations |
| 192 | and the project's public-connectivity setting unchanged. Removing the last |
| 193 | association can broaden connectivity. Existing associations require scoped |
| 194 | adoption and have their original label restored on destroy. |
| 195 | |
| 196 | :::caution |
| 197 | Neon prevents re-registering an endpoint in the same organization after it is |
| 198 | unregistered. Destroying a newly managed registration can therefore be |
| 199 | irreversible for that endpoint ID. Remove dependent project associations first. |
| 200 | An adopted organization registration is preserved and its original label |
| 201 | restored. |
| 202 | ::: |
| 203 | |
| 204 | ## Ownership and recovery |
| 205 | |
| 206 | Keep governance state backed up. The original role, alert threshold, or network |
| 207 | configuration is part of the restoration record, not something that can safely |
| 208 | be reconstructed from desired properties. Cleanup refuses conflicting external |
| 209 | changes rather than overwriting another administrator's settings. |
| 210 | |
| 211 | Serialize changes to each member, spending threshold, and endpoint association. |
| 212 | These APIs do not provide conditional writes, so observe-before-write checks |
| 213 | cannot prevent every concurrent edit. An ambiguous interrupted role change |
| 214 | fails closed and needs operator reconciliation of the retained state. |
| 215 | |
| 216 | Before deployment, record the intended member/grant, alert threshold, or network |
| 217 | association with the responsible administrator. After deployment, independently |
| 218 | check the selected organization/project in the Console. After destroy, confirm |
| 219 | the original role, grant, threshold, or adopted label was restored; deletion of a |
| 220 | resource declaration is not proof that the expected access policy is in effect. |
| 221 | A spending threshold can send notifications immediately if current spending is |
| 222 | already above it, so coordinate that test with the billing owner. |
| 223 | |
| 224 | For missing state, reveal-once secrets, or conflicting external edits, follow |
| 225 | [State and recovery](/neon/guides/state-recovery) before another mutation. |
| 226 | |
| 227 | Organization creation, membership invitations, and organization deletion are |
| 228 | outside these resources. Invitations are deliberately excluded because the |
| 229 | public API does not expose a corresponding revoke lifecycle. |