@canopy-io/node 0.12.0 → 0.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # @canopy-io/node
2
2
 
3
+ ## 0.13.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 51fd41e: Organization invitations name a place and a window. `organizations.createInvite()` takes an optional `role_id` (none admits a member holding nothing, only at the organization node), a `node_id` for an office or team beneath the organization, and `effective_from` / `effective_to`, copied onto the assignment the invitation grants on acceptance. `organizations.addMember()` takes the same `node_id` and dates. Someone already in the Environment, invited into an organization they don't belong to, receives a request to join (`intent: "grant"`) and accepts it with their own sign-in or password. Invitation responses gain `node_name`, `organization_node_id`, `effective_from`, `effective_to` and `invited_by_identity_id`. Types regenerated from the API spec.
8
+
3
9
  ## 0.12.0
4
10
 
5
11
  ### Minor Changes
package/README.md CHANGED
@@ -186,6 +186,15 @@ await canopy.organizations.addMember(acme.id, {
186
186
  role_id: ownerRoleId,
187
187
  });
188
188
 
189
+ // Invite by email straight to an office, for a fixed window. Someone already
190
+ // in the Environment receives a request to join, and accepting is their consent.
191
+ await canopy.organizations.createInvite(acme.id, {
192
+ email: "lee@acme.example",
193
+ role_id: agentRoleId,
194
+ node_id: euOfficeId,
195
+ effective_to: "2026-12-31T23:59:59.999Z",
196
+ });
197
+
189
198
  // An organization only tightens the Environment: a second factor where the
190
199
  // Environment leaves it optional, and only some of the Environment's ways in.
191
200
  await canopy.organizations.updatePolicy(acme.id, {
package/dist/index.d.cts CHANGED
@@ -1522,7 +1522,7 @@ interface paths {
1522
1522
  put?: never;
1523
1523
  /**
1524
1524
  * Add a member to an organization
1525
- * @description Adds a member. Membership is admission, not authority: with `role_id` the member also holds that role at the organization node from the start; without it they belong and hold nothing until a role is assigned. The identity must already hold an active membership in this Environment (invite by email instead for someone who does not). An identity that is already a member returns `409`; roles are added through the assignment routes. Requires the `rbac.manage_assignments` permission at the organization and emits `organization.member.added`. An organization's own administrator, signed in with an identity token and admitted by `canopy:organization.manage` alone, is refused with `403` (`organization.member_add_requires_invite`): the person never agreed to join, so the administrator invites them instead.
1525
+ * @description Adds a member. Membership is admission, not authority: with `role_id` the member also holds that role from the start, at the organization node or at the office or team `node_id` names, for the `effective_from` to `effective_to` window when given; without it they belong and hold nothing until a role is assigned (a place or dates need a role, `400 invite.window_requires_role`). The identity must already hold an active membership in this Environment (invite by email instead for someone who does not). An identity that is already a member returns `409`; roles are added through the assignment routes. Requires the `rbac.manage_assignments` permission at the organization and emits `organization.member.added`. An organization's own administrator, signed in with an identity token and admitted by `canopy:organization.manage` alone, is refused with `403` (`organization.member_add_requires_invite`): the person never agreed to join, so the administrator invites them instead.
1526
1526
  */
1527
1527
  post: operations["ApiOrganizationsController_addMember"];
1528
1528
  delete?: never;
@@ -1586,7 +1586,7 @@ interface paths {
1586
1586
  put?: never;
1587
1587
  /**
1588
1588
  * Invite a member into an organization
1589
- * @description Invites an email into the organization with the role bound to the invitation; acceptance creates the Environment membership and the organization membership in one step. Works for emails with no identity in the Environment — one is created on acceptance. Requires the `identity.manage` permission and emits `invite.created`.
1589
+ * @description Invites an email into the organization. `role_id` is held on acceptance at `node_id`, the organization node (the default) or an office or team beneath it, for the `effective_from` to `effective_to` window when given; with no `role_id` the invitation admits a member holding nothing, which only the organization node allows (`400 invite.malformed_assignment` beneath it). Acceptance creates what is missing in one transaction: the identity, its Environment membership, the organization membership and the role. For someone who already holds an active membership in this Environment the invitation is a request to join (`intent: grant`): accepting is their consent, proven by their password or their own sign-in. Requires the `identity.manage` permission, or `canopy:organization.manage` at the organization or `canopy:access.manage` at the place for the organization's own administrators, and emits `invite.created`.
1590
1590
  */
1591
1591
  post: operations["ApiOrganizationsController_createInvite"];
1592
1592
  delete?: never;
@@ -2968,12 +2968,28 @@ interface components {
2968
2968
  name: string;
2969
2969
  role_id?: string | null;
2970
2970
  node_id?: string | null;
2971
+ /** @description The name of the node at `node_id`, when it has one. */
2972
+ node_name?: string | null;
2973
+ /** @description The organization the invitation joins, when `node_id` is an organization node or sits beneath one. */
2974
+ organization_node_id?: string | null;
2975
+ /**
2976
+ * Format: date-time
2977
+ * @description When the role it grants starts to count.
2978
+ */
2979
+ effective_from?: string | null;
2980
+ /**
2981
+ * Format: date-time
2982
+ * @description When the role it grants stops counting.
2983
+ */
2984
+ effective_to?: string | null;
2971
2985
  has_initial_assignment: boolean;
2972
2986
  /** @enum {string} */
2973
2987
  status: "pending" | "accepted" | "revoked" | "expired";
2974
2988
  /** Format: date-time */
2975
2989
  expires_at: string;
2976
2990
  invited_by: string;
2991
+ /** @description The customer's own administrator who sent it with their identity token; `invited_by` names a platform user instead. */
2992
+ invited_by_identity_id?: string | null;
2977
2993
  /** Format: date-time */
2978
2994
  created_at: string;
2979
2995
  };
@@ -2999,12 +3015,28 @@ interface components {
2999
3015
  name: string;
3000
3016
  role_id?: string | null;
3001
3017
  node_id?: string | null;
3018
+ /** @description The name of the node at `node_id`, when it has one. */
3019
+ node_name?: string | null;
3020
+ /** @description The organization the invitation joins, when `node_id` is an organization node or sits beneath one. */
3021
+ organization_node_id?: string | null;
3022
+ /**
3023
+ * Format: date-time
3024
+ * @description When the role it grants starts to count.
3025
+ */
3026
+ effective_from?: string | null;
3027
+ /**
3028
+ * Format: date-time
3029
+ * @description When the role it grants stops counting.
3030
+ */
3031
+ effective_to?: string | null;
3002
3032
  has_initial_assignment: boolean;
3003
3033
  /** @enum {string} */
3004
3034
  status: "pending" | "accepted" | "revoked" | "expired";
3005
3035
  /** Format: date-time */
3006
3036
  expires_at: string;
3007
3037
  invited_by: string;
3038
+ /** @description The customer's own administrator who sent it with their identity token; `invited_by` names a platform user instead. */
3039
+ invited_by_identity_id?: string | null;
3008
3040
  /** Format: date-time */
3009
3041
  created_at: string;
3010
3042
  /** @description Tokenized URL the invitee would land on. Returned so callers that pass send_email=false can deliver it themselves. */
@@ -3023,7 +3055,7 @@ interface components {
3023
3055
  first_name?: string;
3024
3056
  /** @description Required for `activate`. Ignored for `add_to_app` and `password_reset`. */
3025
3057
  last_name?: string;
3026
- /** @description Role ID — required if node_id is provided */
3058
+ /** @description The role the invitation grants at `node_id`. Required with a `node_id`, except an organization node, where an invitation may admit a member with no role. */
3027
3059
  role_id?: string;
3028
3060
  /** @description Node ID — required if role_id is provided */
3029
3061
  node_id?: string;
@@ -3032,6 +3064,16 @@ interface components {
3032
3064
  * @default true
3033
3065
  */
3034
3066
  send_email: boolean;
3067
+ /**
3068
+ * Format: date-time
3069
+ * @description When the role starts to count (ISO 8601). Copied onto the assignment the invitation grants; needs a role.
3070
+ */
3071
+ effective_from?: string;
3072
+ /**
3073
+ * Format: date-time
3074
+ * @description When the role stops counting (ISO 8601), after `effective_from`. Copied onto the assignment; an invitation whose window has ended by acceptance is refused with `400 invite.window_ended`.
3075
+ */
3076
+ effective_to?: string;
3035
3077
  };
3036
3078
  BulkCreateIdentityInvitesDto: {
3037
3079
  invites: components["schemas"]["CreateIdentityInviteDto"][];
@@ -3495,12 +3537,36 @@ interface components {
3495
3537
  identity_id: string;
3496
3538
  /** @description A role to hold at the organization node from the start, from the Environment's roles. Left out, the member belongs and holds nothing until a role is assigned. */
3497
3539
  role_id?: string;
3540
+ /** @description Where the role is held: the organization node (the default) or an office or team beneath it. Needs a role. */
3541
+ node_id?: string;
3542
+ /**
3543
+ * Format: date-time
3544
+ * @description When the role starts to count (ISO 8601). Needs a role.
3545
+ */
3546
+ effective_from?: string;
3547
+ /**
3548
+ * Format: date-time
3549
+ * @description When the role stops counting (ISO 8601), after `effective_from`. Needs a role.
3550
+ */
3551
+ effective_to?: string;
3498
3552
  };
3499
3553
  CreateOrganizationInviteDto: {
3500
3554
  /** @description Email to invite. Works for people with no identity in this Environment — one is created when they accept. */
3501
3555
  email: string;
3502
- /** @description The role the member will hold in this organization on acceptance. */
3503
- role_id: string;
3556
+ /** @description The role the person holds on acceptance, at `node_id`. Optional at the organization node, where accepting admits a member with no role; required for a place beneath it. */
3557
+ role_id?: string;
3558
+ /** @description Where the role is held: the organization node (the default) or an office or team beneath it. Accepting admits the person to the organization either way. */
3559
+ node_id?: string;
3560
+ /**
3561
+ * Format: date-time
3562
+ * @description When the role starts to count (ISO 8601). Copied onto the assignment the invitation grants; needs a role.
3563
+ */
3564
+ effective_from?: string;
3565
+ /**
3566
+ * Format: date-time
3567
+ * @description When the role stops counting (ISO 8601), after `effective_from`. Copied onto the assignment; an invitation whose window has ended by acceptance is refused with `400 invite.window_ended`.
3568
+ */
3569
+ effective_to?: string;
3504
3570
  first_name?: string;
3505
3571
  last_name?: string;
3506
3572
  /**
@@ -9103,7 +9169,7 @@ interface operations {
9103
9169
  };
9104
9170
  };
9105
9171
  };
9106
- /** @description The request failed validation; `role_id` and `node_id` were not given together; a `password_reset` invite carries a role assignment or names an email with no identity; or `client_id` names no active OAuth client in this Environment, or one with no invite redirect URL; or the request sent both an `X-API-Key` and an `Authorization` header */
9172
+ /** @description The request failed validation; `role_id` came with no `node_id`, or a `node_id` with no `role_id` outside an organization node (`invite.malformed_assignment`); dates came with no role (`invite.window_requires_role`), end before they start, or have already ended (`invite.window_ended`); a `password_reset` invite carries a role assignment or names an email with no identity; or `client_id` names no active OAuth client in this Environment, or one with no invite redirect URL; or the request sent both an `X-API-Key` and an `Authorization` header */
9107
9173
  400: {
9108
9174
  headers: {
9109
9175
  [name: string]: unknown;
@@ -9114,7 +9180,7 @@ interface operations {
9114
9180
  * "error": {
9115
9181
  * "statusCode": 400,
9116
9182
  * "code": null,
9117
- * "message": "The request failed validation; `role_id` and `node_id` were not given together; a `password_reset` invite carries a role assignment or names an email with no identity; or `client_id` names no active OAuth client in this Environment, or one with no invite redirect URL; or the request sent both an `X-API-Key` and an `Authorization` header",
9183
+ * "message": "The request failed validation; `role_id` came with no `node_id`, or a `node_id` with no `role_id` outside an organization node (`invite.malformed_assignment`); dates came with no role (`invite.window_requires_role`), end before they start, or have already ended (`invite.window_ended`); a `password_reset` invite carries a role assignment or names an email with no identity; or `client_id` names no active OAuth client in this Environment, or one with no invite redirect URL; or the request sent both an `X-API-Key` and an `Authorization` header",
9118
9184
  * "timestamp": "2026-04-20T12:00:00.000Z",
9119
9185
  * "path": "/api/v1/identity-invites",
9120
9186
  * "method": "POST"
@@ -9166,7 +9232,7 @@ interface operations {
9166
9232
  "application/json": components["schemas"]["ErrorResponseDto"];
9167
9233
  };
9168
9234
  };
9169
- /** @description A pending invite already exists for this email at this scope, the identity already has access to this Environment, or the node sits inside an organization the identity is not a member of */
9235
+ /** @description A pending invite already exists for this email at this scope, the identity already has access to this Environment and the invitation joins no organization (`invite.identity_already_in_app`), or the identity is already an active member of the organization it joins (`organization.member_exists`) */
9170
9236
  409: {
9171
9237
  headers: {
9172
9238
  [name: string]: unknown;
@@ -9177,7 +9243,7 @@ interface operations {
9177
9243
  * "error": {
9178
9244
  * "statusCode": 409,
9179
9245
  * "code": null,
9180
- * "message": "A pending invite already exists for this email at this scope, the identity already has access to this Environment, or the node sits inside an organization the identity is not a member of",
9246
+ * "message": "A pending invite already exists for this email at this scope, the identity already has access to this Environment and the invitation joins no organization (`invite.identity_already_in_app`), or the identity is already an active member of the organization it joins (`organization.member_exists`)",
9181
9247
  * "timestamp": "2026-04-20T12:00:00.000Z",
9182
9248
  * "path": "/api/v1/identity-invites",
9183
9249
  * "method": "POST"
@@ -14317,7 +14383,7 @@ interface operations {
14317
14383
  };
14318
14384
  };
14319
14385
  };
14320
- /** @description The request failed validation, or the role is a system role, which cannot be assigned; or the request sent both an `X-API-Key` and an `Authorization` header */
14386
+ /** @description The request failed validation, the role is a system role, which cannot be assigned, a place or dates came with no role (`invite.window_requires_role`), or the dates end before they start; or the request sent both an `X-API-Key` and an `Authorization` header */
14321
14387
  400: {
14322
14388
  headers: {
14323
14389
  [name: string]: unknown;
@@ -14328,7 +14394,7 @@ interface operations {
14328
14394
  * "error": {
14329
14395
  * "statusCode": 400,
14330
14396
  * "code": null,
14331
- * "message": "The request failed validation, or the role is a system role, which cannot be assigned; or the request sent both an `X-API-Key` and an `Authorization` header",
14397
+ * "message": "The request failed validation, the role is a system role, which cannot be assigned, a place or dates came with no role (`invite.window_requires_role`), or the dates end before they start; or the request sent both an `X-API-Key` and an `Authorization` header",
14332
14398
  * "timestamp": "2026-04-20T12:00:00.000Z",
14333
14399
  * "path": "/api/v1/organizations/{id}/members",
14334
14400
  * "method": "POST"
@@ -14380,7 +14446,7 @@ interface operations {
14380
14446
  "application/json": components["schemas"]["ErrorResponseDto"];
14381
14447
  };
14382
14448
  };
14383
- /** @description The organization, the identity or the role does not exist in this Environment */
14449
+ /** @description The organization, the identity or the role does not exist in this Environment, or `node_id` is not the organization or a place beneath it */
14384
14450
  404: {
14385
14451
  headers: {
14386
14452
  [name: string]: unknown;
@@ -14391,7 +14457,7 @@ interface operations {
14391
14457
  * "error": {
14392
14458
  * "statusCode": 404,
14393
14459
  * "code": null,
14394
- * "message": "The organization, the identity or the role does not exist in this Environment",
14460
+ * "message": "The organization, the identity or the role does not exist in this Environment, or `node_id` is not the organization or a place beneath it",
14395
14461
  * "timestamp": "2026-04-20T12:00:00.000Z",
14396
14462
  * "path": "/api/v1/organizations/{id}/members",
14397
14463
  * "method": "POST"
@@ -14824,7 +14890,7 @@ interface operations {
14824
14890
  };
14825
14891
  };
14826
14892
  };
14827
- /** @description The request failed validation: a body field, a query parameter or an id in the path is missing, malformed or out of range; or the request sent both an `X-API-Key` and an `Authorization` header */
14893
+ /** @description The request failed validation; a place beneath the organization was named with no role (`invite.malformed_assignment`); dates came with no role (`invite.window_requires_role`), end before they start, or have already ended (`invite.window_ended`); or the request sent both an `X-API-Key` and an `Authorization` header */
14828
14894
  400: {
14829
14895
  headers: {
14830
14896
  [name: string]: unknown;
@@ -14835,7 +14901,7 @@ interface operations {
14835
14901
  * "error": {
14836
14902
  * "statusCode": 400,
14837
14903
  * "code": null,
14838
- * "message": "The request failed validation: a body field, a query parameter or an id in the path is missing, malformed or out of range; or the request sent both an `X-API-Key` and an `Authorization` header",
14904
+ * "message": "The request failed validation; a place beneath the organization was named with no role (`invite.malformed_assignment`); dates came with no role (`invite.window_requires_role`), end before they start, or have already ended (`invite.window_ended`); or the request sent both an `X-API-Key` and an `Authorization` header",
14839
14905
  * "timestamp": "2026-04-20T12:00:00.000Z",
14840
14906
  * "path": "/api/v1/organizations/{id}/invites",
14841
14907
  * "method": "POST"
@@ -14887,7 +14953,7 @@ interface operations {
14887
14953
  "application/json": components["schemas"]["ErrorResponseDto"];
14888
14954
  };
14889
14955
  };
14890
- /** @description Organization not found */
14956
+ /** @description The organization does not exist, or `node_id` is not the organization or a place beneath it */
14891
14957
  404: {
14892
14958
  headers: {
14893
14959
  [name: string]: unknown;
@@ -14898,7 +14964,7 @@ interface operations {
14898
14964
  * "error": {
14899
14965
  * "statusCode": 404,
14900
14966
  * "code": null,
14901
- * "message": "Organization not found",
14967
+ * "message": "The organization does not exist, or `node_id` is not the organization or a place beneath it",
14902
14968
  * "timestamp": "2026-04-20T12:00:00.000Z",
14903
14969
  * "path": "/api/v1/organizations/{id}/invites",
14904
14970
  * "method": "POST"
@@ -14908,7 +14974,7 @@ interface operations {
14908
14974
  "application/json": components["schemas"]["ErrorResponseDto"];
14909
14975
  };
14910
14976
  };
14911
- /** @description A pending invitation already exists for this email, or the identity already has active access to this Environment */
14977
+ /** @description A pending invitation already exists for this email at this place, or the identity is already an active member of the organization (`organization.member_exists`); give a member a role through the assignment routes */
14912
14978
  409: {
14913
14979
  headers: {
14914
14980
  [name: string]: unknown;
@@ -14919,7 +14985,7 @@ interface operations {
14919
14985
  * "error": {
14920
14986
  * "statusCode": 409,
14921
14987
  * "code": null,
14922
- * "message": "A pending invitation already exists for this email, or the identity already has active access to this Environment",
14988
+ * "message": "A pending invitation already exists for this email at this place, or the identity is already an active member of the organization (`organization.member_exists`); give a member a role through the assignment routes",
14923
14989
  * "timestamp": "2026-04-20T12:00:00.000Z",
14924
14990
  * "path": "/api/v1/organizations/{id}/invites",
14925
14991
  * "method": "POST"
package/dist/index.d.ts CHANGED
@@ -1522,7 +1522,7 @@ interface paths {
1522
1522
  put?: never;
1523
1523
  /**
1524
1524
  * Add a member to an organization
1525
- * @description Adds a member. Membership is admission, not authority: with `role_id` the member also holds that role at the organization node from the start; without it they belong and hold nothing until a role is assigned. The identity must already hold an active membership in this Environment (invite by email instead for someone who does not). An identity that is already a member returns `409`; roles are added through the assignment routes. Requires the `rbac.manage_assignments` permission at the organization and emits `organization.member.added`. An organization's own administrator, signed in with an identity token and admitted by `canopy:organization.manage` alone, is refused with `403` (`organization.member_add_requires_invite`): the person never agreed to join, so the administrator invites them instead.
1525
+ * @description Adds a member. Membership is admission, not authority: with `role_id` the member also holds that role from the start, at the organization node or at the office or team `node_id` names, for the `effective_from` to `effective_to` window when given; without it they belong and hold nothing until a role is assigned (a place or dates need a role, `400 invite.window_requires_role`). The identity must already hold an active membership in this Environment (invite by email instead for someone who does not). An identity that is already a member returns `409`; roles are added through the assignment routes. Requires the `rbac.manage_assignments` permission at the organization and emits `organization.member.added`. An organization's own administrator, signed in with an identity token and admitted by `canopy:organization.manage` alone, is refused with `403` (`organization.member_add_requires_invite`): the person never agreed to join, so the administrator invites them instead.
1526
1526
  */
1527
1527
  post: operations["ApiOrganizationsController_addMember"];
1528
1528
  delete?: never;
@@ -1586,7 +1586,7 @@ interface paths {
1586
1586
  put?: never;
1587
1587
  /**
1588
1588
  * Invite a member into an organization
1589
- * @description Invites an email into the organization with the role bound to the invitation; acceptance creates the Environment membership and the organization membership in one step. Works for emails with no identity in the Environment — one is created on acceptance. Requires the `identity.manage` permission and emits `invite.created`.
1589
+ * @description Invites an email into the organization. `role_id` is held on acceptance at `node_id`, the organization node (the default) or an office or team beneath it, for the `effective_from` to `effective_to` window when given; with no `role_id` the invitation admits a member holding nothing, which only the organization node allows (`400 invite.malformed_assignment` beneath it). Acceptance creates what is missing in one transaction: the identity, its Environment membership, the organization membership and the role. For someone who already holds an active membership in this Environment the invitation is a request to join (`intent: grant`): accepting is their consent, proven by their password or their own sign-in. Requires the `identity.manage` permission, or `canopy:organization.manage` at the organization or `canopy:access.manage` at the place for the organization's own administrators, and emits `invite.created`.
1590
1590
  */
1591
1591
  post: operations["ApiOrganizationsController_createInvite"];
1592
1592
  delete?: never;
@@ -2968,12 +2968,28 @@ interface components {
2968
2968
  name: string;
2969
2969
  role_id?: string | null;
2970
2970
  node_id?: string | null;
2971
+ /** @description The name of the node at `node_id`, when it has one. */
2972
+ node_name?: string | null;
2973
+ /** @description The organization the invitation joins, when `node_id` is an organization node or sits beneath one. */
2974
+ organization_node_id?: string | null;
2975
+ /**
2976
+ * Format: date-time
2977
+ * @description When the role it grants starts to count.
2978
+ */
2979
+ effective_from?: string | null;
2980
+ /**
2981
+ * Format: date-time
2982
+ * @description When the role it grants stops counting.
2983
+ */
2984
+ effective_to?: string | null;
2971
2985
  has_initial_assignment: boolean;
2972
2986
  /** @enum {string} */
2973
2987
  status: "pending" | "accepted" | "revoked" | "expired";
2974
2988
  /** Format: date-time */
2975
2989
  expires_at: string;
2976
2990
  invited_by: string;
2991
+ /** @description The customer's own administrator who sent it with their identity token; `invited_by` names a platform user instead. */
2992
+ invited_by_identity_id?: string | null;
2977
2993
  /** Format: date-time */
2978
2994
  created_at: string;
2979
2995
  };
@@ -2999,12 +3015,28 @@ interface components {
2999
3015
  name: string;
3000
3016
  role_id?: string | null;
3001
3017
  node_id?: string | null;
3018
+ /** @description The name of the node at `node_id`, when it has one. */
3019
+ node_name?: string | null;
3020
+ /** @description The organization the invitation joins, when `node_id` is an organization node or sits beneath one. */
3021
+ organization_node_id?: string | null;
3022
+ /**
3023
+ * Format: date-time
3024
+ * @description When the role it grants starts to count.
3025
+ */
3026
+ effective_from?: string | null;
3027
+ /**
3028
+ * Format: date-time
3029
+ * @description When the role it grants stops counting.
3030
+ */
3031
+ effective_to?: string | null;
3002
3032
  has_initial_assignment: boolean;
3003
3033
  /** @enum {string} */
3004
3034
  status: "pending" | "accepted" | "revoked" | "expired";
3005
3035
  /** Format: date-time */
3006
3036
  expires_at: string;
3007
3037
  invited_by: string;
3038
+ /** @description The customer's own administrator who sent it with their identity token; `invited_by` names a platform user instead. */
3039
+ invited_by_identity_id?: string | null;
3008
3040
  /** Format: date-time */
3009
3041
  created_at: string;
3010
3042
  /** @description Tokenized URL the invitee would land on. Returned so callers that pass send_email=false can deliver it themselves. */
@@ -3023,7 +3055,7 @@ interface components {
3023
3055
  first_name?: string;
3024
3056
  /** @description Required for `activate`. Ignored for `add_to_app` and `password_reset`. */
3025
3057
  last_name?: string;
3026
- /** @description Role ID — required if node_id is provided */
3058
+ /** @description The role the invitation grants at `node_id`. Required with a `node_id`, except an organization node, where an invitation may admit a member with no role. */
3027
3059
  role_id?: string;
3028
3060
  /** @description Node ID — required if role_id is provided */
3029
3061
  node_id?: string;
@@ -3032,6 +3064,16 @@ interface components {
3032
3064
  * @default true
3033
3065
  */
3034
3066
  send_email: boolean;
3067
+ /**
3068
+ * Format: date-time
3069
+ * @description When the role starts to count (ISO 8601). Copied onto the assignment the invitation grants; needs a role.
3070
+ */
3071
+ effective_from?: string;
3072
+ /**
3073
+ * Format: date-time
3074
+ * @description When the role stops counting (ISO 8601), after `effective_from`. Copied onto the assignment; an invitation whose window has ended by acceptance is refused with `400 invite.window_ended`.
3075
+ */
3076
+ effective_to?: string;
3035
3077
  };
3036
3078
  BulkCreateIdentityInvitesDto: {
3037
3079
  invites: components["schemas"]["CreateIdentityInviteDto"][];
@@ -3495,12 +3537,36 @@ interface components {
3495
3537
  identity_id: string;
3496
3538
  /** @description A role to hold at the organization node from the start, from the Environment's roles. Left out, the member belongs and holds nothing until a role is assigned. */
3497
3539
  role_id?: string;
3540
+ /** @description Where the role is held: the organization node (the default) or an office or team beneath it. Needs a role. */
3541
+ node_id?: string;
3542
+ /**
3543
+ * Format: date-time
3544
+ * @description When the role starts to count (ISO 8601). Needs a role.
3545
+ */
3546
+ effective_from?: string;
3547
+ /**
3548
+ * Format: date-time
3549
+ * @description When the role stops counting (ISO 8601), after `effective_from`. Needs a role.
3550
+ */
3551
+ effective_to?: string;
3498
3552
  };
3499
3553
  CreateOrganizationInviteDto: {
3500
3554
  /** @description Email to invite. Works for people with no identity in this Environment — one is created when they accept. */
3501
3555
  email: string;
3502
- /** @description The role the member will hold in this organization on acceptance. */
3503
- role_id: string;
3556
+ /** @description The role the person holds on acceptance, at `node_id`. Optional at the organization node, where accepting admits a member with no role; required for a place beneath it. */
3557
+ role_id?: string;
3558
+ /** @description Where the role is held: the organization node (the default) or an office or team beneath it. Accepting admits the person to the organization either way. */
3559
+ node_id?: string;
3560
+ /**
3561
+ * Format: date-time
3562
+ * @description When the role starts to count (ISO 8601). Copied onto the assignment the invitation grants; needs a role.
3563
+ */
3564
+ effective_from?: string;
3565
+ /**
3566
+ * Format: date-time
3567
+ * @description When the role stops counting (ISO 8601), after `effective_from`. Copied onto the assignment; an invitation whose window has ended by acceptance is refused with `400 invite.window_ended`.
3568
+ */
3569
+ effective_to?: string;
3504
3570
  first_name?: string;
3505
3571
  last_name?: string;
3506
3572
  /**
@@ -9103,7 +9169,7 @@ interface operations {
9103
9169
  };
9104
9170
  };
9105
9171
  };
9106
- /** @description The request failed validation; `role_id` and `node_id` were not given together; a `password_reset` invite carries a role assignment or names an email with no identity; or `client_id` names no active OAuth client in this Environment, or one with no invite redirect URL; or the request sent both an `X-API-Key` and an `Authorization` header */
9172
+ /** @description The request failed validation; `role_id` came with no `node_id`, or a `node_id` with no `role_id` outside an organization node (`invite.malformed_assignment`); dates came with no role (`invite.window_requires_role`), end before they start, or have already ended (`invite.window_ended`); a `password_reset` invite carries a role assignment or names an email with no identity; or `client_id` names no active OAuth client in this Environment, or one with no invite redirect URL; or the request sent both an `X-API-Key` and an `Authorization` header */
9107
9173
  400: {
9108
9174
  headers: {
9109
9175
  [name: string]: unknown;
@@ -9114,7 +9180,7 @@ interface operations {
9114
9180
  * "error": {
9115
9181
  * "statusCode": 400,
9116
9182
  * "code": null,
9117
- * "message": "The request failed validation; `role_id` and `node_id` were not given together; a `password_reset` invite carries a role assignment or names an email with no identity; or `client_id` names no active OAuth client in this Environment, or one with no invite redirect URL; or the request sent both an `X-API-Key` and an `Authorization` header",
9183
+ * "message": "The request failed validation; `role_id` came with no `node_id`, or a `node_id` with no `role_id` outside an organization node (`invite.malformed_assignment`); dates came with no role (`invite.window_requires_role`), end before they start, or have already ended (`invite.window_ended`); a `password_reset` invite carries a role assignment or names an email with no identity; or `client_id` names no active OAuth client in this Environment, or one with no invite redirect URL; or the request sent both an `X-API-Key` and an `Authorization` header",
9118
9184
  * "timestamp": "2026-04-20T12:00:00.000Z",
9119
9185
  * "path": "/api/v1/identity-invites",
9120
9186
  * "method": "POST"
@@ -9166,7 +9232,7 @@ interface operations {
9166
9232
  "application/json": components["schemas"]["ErrorResponseDto"];
9167
9233
  };
9168
9234
  };
9169
- /** @description A pending invite already exists for this email at this scope, the identity already has access to this Environment, or the node sits inside an organization the identity is not a member of */
9235
+ /** @description A pending invite already exists for this email at this scope, the identity already has access to this Environment and the invitation joins no organization (`invite.identity_already_in_app`), or the identity is already an active member of the organization it joins (`organization.member_exists`) */
9170
9236
  409: {
9171
9237
  headers: {
9172
9238
  [name: string]: unknown;
@@ -9177,7 +9243,7 @@ interface operations {
9177
9243
  * "error": {
9178
9244
  * "statusCode": 409,
9179
9245
  * "code": null,
9180
- * "message": "A pending invite already exists for this email at this scope, the identity already has access to this Environment, or the node sits inside an organization the identity is not a member of",
9246
+ * "message": "A pending invite already exists for this email at this scope, the identity already has access to this Environment and the invitation joins no organization (`invite.identity_already_in_app`), or the identity is already an active member of the organization it joins (`organization.member_exists`)",
9181
9247
  * "timestamp": "2026-04-20T12:00:00.000Z",
9182
9248
  * "path": "/api/v1/identity-invites",
9183
9249
  * "method": "POST"
@@ -14317,7 +14383,7 @@ interface operations {
14317
14383
  };
14318
14384
  };
14319
14385
  };
14320
- /** @description The request failed validation, or the role is a system role, which cannot be assigned; or the request sent both an `X-API-Key` and an `Authorization` header */
14386
+ /** @description The request failed validation, the role is a system role, which cannot be assigned, a place or dates came with no role (`invite.window_requires_role`), or the dates end before they start; or the request sent both an `X-API-Key` and an `Authorization` header */
14321
14387
  400: {
14322
14388
  headers: {
14323
14389
  [name: string]: unknown;
@@ -14328,7 +14394,7 @@ interface operations {
14328
14394
  * "error": {
14329
14395
  * "statusCode": 400,
14330
14396
  * "code": null,
14331
- * "message": "The request failed validation, or the role is a system role, which cannot be assigned; or the request sent both an `X-API-Key` and an `Authorization` header",
14397
+ * "message": "The request failed validation, the role is a system role, which cannot be assigned, a place or dates came with no role (`invite.window_requires_role`), or the dates end before they start; or the request sent both an `X-API-Key` and an `Authorization` header",
14332
14398
  * "timestamp": "2026-04-20T12:00:00.000Z",
14333
14399
  * "path": "/api/v1/organizations/{id}/members",
14334
14400
  * "method": "POST"
@@ -14380,7 +14446,7 @@ interface operations {
14380
14446
  "application/json": components["schemas"]["ErrorResponseDto"];
14381
14447
  };
14382
14448
  };
14383
- /** @description The organization, the identity or the role does not exist in this Environment */
14449
+ /** @description The organization, the identity or the role does not exist in this Environment, or `node_id` is not the organization or a place beneath it */
14384
14450
  404: {
14385
14451
  headers: {
14386
14452
  [name: string]: unknown;
@@ -14391,7 +14457,7 @@ interface operations {
14391
14457
  * "error": {
14392
14458
  * "statusCode": 404,
14393
14459
  * "code": null,
14394
- * "message": "The organization, the identity or the role does not exist in this Environment",
14460
+ * "message": "The organization, the identity or the role does not exist in this Environment, or `node_id` is not the organization or a place beneath it",
14395
14461
  * "timestamp": "2026-04-20T12:00:00.000Z",
14396
14462
  * "path": "/api/v1/organizations/{id}/members",
14397
14463
  * "method": "POST"
@@ -14824,7 +14890,7 @@ interface operations {
14824
14890
  };
14825
14891
  };
14826
14892
  };
14827
- /** @description The request failed validation: a body field, a query parameter or an id in the path is missing, malformed or out of range; or the request sent both an `X-API-Key` and an `Authorization` header */
14893
+ /** @description The request failed validation; a place beneath the organization was named with no role (`invite.malformed_assignment`); dates came with no role (`invite.window_requires_role`), end before they start, or have already ended (`invite.window_ended`); or the request sent both an `X-API-Key` and an `Authorization` header */
14828
14894
  400: {
14829
14895
  headers: {
14830
14896
  [name: string]: unknown;
@@ -14835,7 +14901,7 @@ interface operations {
14835
14901
  * "error": {
14836
14902
  * "statusCode": 400,
14837
14903
  * "code": null,
14838
- * "message": "The request failed validation: a body field, a query parameter or an id in the path is missing, malformed or out of range; or the request sent both an `X-API-Key` and an `Authorization` header",
14904
+ * "message": "The request failed validation; a place beneath the organization was named with no role (`invite.malformed_assignment`); dates came with no role (`invite.window_requires_role`), end before they start, or have already ended (`invite.window_ended`); or the request sent both an `X-API-Key` and an `Authorization` header",
14839
14905
  * "timestamp": "2026-04-20T12:00:00.000Z",
14840
14906
  * "path": "/api/v1/organizations/{id}/invites",
14841
14907
  * "method": "POST"
@@ -14887,7 +14953,7 @@ interface operations {
14887
14953
  "application/json": components["schemas"]["ErrorResponseDto"];
14888
14954
  };
14889
14955
  };
14890
- /** @description Organization not found */
14956
+ /** @description The organization does not exist, or `node_id` is not the organization or a place beneath it */
14891
14957
  404: {
14892
14958
  headers: {
14893
14959
  [name: string]: unknown;
@@ -14898,7 +14964,7 @@ interface operations {
14898
14964
  * "error": {
14899
14965
  * "statusCode": 404,
14900
14966
  * "code": null,
14901
- * "message": "Organization not found",
14967
+ * "message": "The organization does not exist, or `node_id` is not the organization or a place beneath it",
14902
14968
  * "timestamp": "2026-04-20T12:00:00.000Z",
14903
14969
  * "path": "/api/v1/organizations/{id}/invites",
14904
14970
  * "method": "POST"
@@ -14908,7 +14974,7 @@ interface operations {
14908
14974
  "application/json": components["schemas"]["ErrorResponseDto"];
14909
14975
  };
14910
14976
  };
14911
- /** @description A pending invitation already exists for this email, or the identity already has active access to this Environment */
14977
+ /** @description A pending invitation already exists for this email at this place, or the identity is already an active member of the organization (`organization.member_exists`); give a member a role through the assignment routes */
14912
14978
  409: {
14913
14979
  headers: {
14914
14980
  [name: string]: unknown;
@@ -14919,7 +14985,7 @@ interface operations {
14919
14985
  * "error": {
14920
14986
  * "statusCode": 409,
14921
14987
  * "code": null,
14922
- * "message": "A pending invitation already exists for this email, or the identity already has active access to this Environment",
14988
+ * "message": "A pending invitation already exists for this email at this place, or the identity is already an active member of the organization (`organization.member_exists`); give a member a role through the assignment routes",
14923
14989
  * "timestamp": "2026-04-20T12:00:00.000Z",
14924
14990
  * "path": "/api/v1/organizations/{id}/invites",
14925
14991
  * "method": "POST"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@canopy-io/node",
3
- "version": "0.12.0",
3
+ "version": "0.13.0",
4
4
  "description": "Official TypeScript SDK for Canopy \u2014 hierarchical identity and access management.",
5
5
  "license": "MIT",
6
6
  "author": "Canopy Identity Inc.",