@picked/client 0.0.61 → 0.0.62-next.10

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.
Files changed (135) hide show
  1. package/README.md +0 -8
  2. package/dist/{api-CjGYS0lP.js → api-C11HFYrl.js} +1 -1
  3. package/dist/api-DXosW8uz.cjs +1 -0
  4. package/dist/cjs/form/index.js +1 -1
  5. package/dist/cjs/index.js +4 -4
  6. package/dist/cjs/plugins/create-experience/index.js +1 -1
  7. package/dist/cjs/plugins/dom-observer/index.js +1 -0
  8. package/dist/cjs/plugins/dynamic-experiences/index.js +1 -1
  9. package/dist/cjs/plugins/experience-browser-storage/index.js +1 -1
  10. package/dist/cjs/plugins/history-browser-storage/index.js +1 -1
  11. package/dist/cjs/plugins/responses-browser-storage/index.js +1 -1
  12. package/dist/cjs/plugins/single-page-app/index.js +1 -1
  13. package/dist/cjs/plugins/web-components/index.js +1 -1
  14. package/dist/cjs/server/index.js +1 -1
  15. package/dist/docs/actions.mdx +0 -1
  16. package/dist/docs/api/authentication.md +376 -0
  17. package/dist/docs/api/authorization-and-scopes.md +262 -0
  18. package/dist/docs/api/error-handling.md +211 -0
  19. package/dist/docs/api/index.md +172 -11
  20. package/dist/docs/api/pagination.md +176 -0
  21. package/dist/docs/image-optimisation-service.md +72 -0
  22. package/dist/docs/index.md +0 -2
  23. package/dist/docs/pickedql/data-sources.md +2 -2
  24. package/dist/docs/pickedql/dimensions.md +2 -0
  25. package/dist/docs/pickedql/schema.md +2 -2
  26. package/dist/docs/pickedql/virtual-functions.md +27 -0
  27. package/dist/docs/sdk/actions.md +0 -1
  28. package/dist/docs/sdk/content-security-policy.md +8 -7
  29. package/dist/docs/sdk/features.md +0 -1
  30. package/dist/docs/sdk/fields.md +2 -2
  31. package/dist/docs/sdk/index.md +0 -1
  32. package/dist/docs/sdk/plugins/create-experience.md +0 -1
  33. package/dist/docs/sdk/plugins/dynamic-experiences.md +2 -3
  34. package/dist/docs/sdk/plugins/window-object.md +0 -1
  35. package/dist/docs/sdk/predicates.md +0 -1
  36. package/dist/docs/workspaces.mdx +60 -1
  37. package/dist/dom-C3C6Fmwz.cjs +1 -0
  38. package/dist/dom-DGePXPpM.js +84 -0
  39. package/dist/esm/form/index.js +41 -34
  40. package/dist/esm/index.js +1020 -494
  41. package/dist/esm/plugins/create-experience/index.js +1 -1
  42. package/dist/esm/plugins/dom-observer/index.js +87 -0
  43. package/dist/esm/plugins/dynamic-experiences/index.js +133 -60
  44. package/dist/esm/plugins/experience-browser-storage/index.js +9 -6
  45. package/dist/esm/plugins/history-browser-storage/index.js +1 -1
  46. package/dist/esm/plugins/responses-browser-storage/index.js +1 -1
  47. package/dist/esm/plugins/single-page-app/index.js +1 -1
  48. package/dist/esm/plugins/web-components/index.js +1 -1
  49. package/dist/esm/server/index.js +23 -13
  50. package/dist/experience-instance-DImym3m_.js +419 -0
  51. package/dist/experience-instance-DygAdzbn.cjs +1 -0
  52. package/dist/rules-logic-D_CVBuv7.js +13 -0
  53. package/dist/rules-logic-H6P8xwHF.cjs +1 -0
  54. package/dist/types/client.d.ts +2 -2
  55. package/dist/types/entities/action/action.d.ts +6 -3
  56. package/dist/types/entities/action/action.d.ts.map +1 -1
  57. package/dist/types/entities/component/component.d.ts +39 -2
  58. package/dist/types/entities/component/component.d.ts.map +1 -1
  59. package/dist/types/entities/content/content.d.ts +37 -0
  60. package/dist/types/entities/content/content.d.ts.map +1 -0
  61. package/dist/types/entities/experience/experience.d.ts +69 -113
  62. package/dist/types/entities/experience/experience.d.ts.map +1 -1
  63. package/dist/types/entities/experience-instance/experience-instance.d.ts +17 -2
  64. package/dist/types/entities/experience-instance/experience-instance.d.ts.map +1 -1
  65. package/dist/types/entities/feature/feature.d.ts +26 -2
  66. package/dist/types/entities/feature/feature.d.ts.map +1 -1
  67. package/dist/types/entities/field/field.d.ts.map +1 -1
  68. package/dist/types/entities/framework/framework.d.ts +24 -3
  69. package/dist/types/entities/framework/framework.d.ts.map +1 -1
  70. package/dist/types/entities/plugin/plugin.d.ts +32 -1
  71. package/dist/types/entities/plugin/plugin.d.ts.map +1 -1
  72. package/dist/types/entities/predicate/predicate.d.ts +24 -3
  73. package/dist/types/entities/predicate/predicate.d.ts.map +1 -1
  74. package/dist/types/entities/session/session.d.ts +45 -0
  75. package/dist/types/entities/session/session.d.ts.map +1 -0
  76. package/dist/types/entities/step/step.d.ts +17 -2
  77. package/dist/types/entities/step/step.d.ts.map +1 -1
  78. package/dist/types/exports.d.ts +9 -3
  79. package/dist/types/exports.d.ts.map +1 -1
  80. package/dist/types/form/create-form.d.ts +55 -0
  81. package/dist/types/form/create-form.d.ts.map +1 -1
  82. package/dist/types/index.d.ts +3 -1
  83. package/dist/types/index.d.ts.map +1 -1
  84. package/dist/types/lifecycle/lifecycle.d.ts +47 -0
  85. package/dist/types/lifecycle/lifecycle.d.ts.map +1 -0
  86. package/dist/types/lifecycle/rules-logic.d.ts +24 -0
  87. package/dist/types/lifecycle/rules-logic.d.ts.map +1 -0
  88. package/dist/types/lifecycle/state-snapshot.d.ts +37 -0
  89. package/dist/types/lifecycle/state-snapshot.d.ts.map +1 -0
  90. package/dist/types/logic-engine.d.ts +1 -1
  91. package/dist/types/logic-engine.d.ts.map +1 -1
  92. package/dist/types/picked.d.ts +141 -187
  93. package/dist/types/picked.d.ts.map +1 -1
  94. package/dist/types/plugins/dom-observer/index.d.ts +3 -0
  95. package/dist/types/plugins/dom-observer/index.d.ts.map +1 -0
  96. package/dist/types/plugins/dom-observer/plugin.d.ts +50 -0
  97. package/dist/types/plugins/dom-observer/plugin.d.ts.map +1 -0
  98. package/dist/types/plugins/dynamic-experiences/index.d.ts +2 -1
  99. package/dist/types/plugins/dynamic-experiences/index.d.ts.map +1 -1
  100. package/dist/types/plugins/dynamic-experiences/plugin.d.ts +18 -1
  101. package/dist/types/plugins/dynamic-experiences/plugin.d.ts.map +1 -1
  102. package/dist/types/plugins/dynamic-experiences/types.d.ts +63 -1
  103. package/dist/types/plugins/dynamic-experiences/types.d.ts.map +1 -1
  104. package/dist/types/plugins/experience-browser-storage/plugin.d.ts.map +1 -1
  105. package/dist/types/plugins/web-components/plugin.d.ts.map +1 -1
  106. package/dist/types/server/index.d.ts +7 -0
  107. package/dist/types/server/index.d.ts.map +1 -1
  108. package/dist/types/utils/events.d.ts +1 -1
  109. package/dist/types/utils/events.d.ts.map +1 -1
  110. package/dist/types/utils/picked-config.d.ts +12 -4
  111. package/dist/types/utils/picked-config.d.ts.map +1 -1
  112. package/dist/types/utils/rules-logic.d.ts +4 -9
  113. package/dist/types/utils/rules-logic.d.ts.map +1 -1
  114. package/package.json +12 -2
  115. package/dist/api-BarClNGB.cjs +0 -1
  116. package/dist/docs/reference/index.md +0 -11
  117. package/dist/docs/tooling/index.md +0 -19
  118. package/dist/experience-instance-DC2gKwA6.cjs +0 -1
  119. package/dist/experience-instance-DJaNc5Ob.js +0 -486
  120. package/dist/rules-logic-BTIxfrJJ.js +0 -240
  121. package/dist/rules-logic-DNLOv90w.cjs +0 -1
  122. package/dist/types/utils/state-snaphot.d.ts +0 -26
  123. package/dist/types/utils/state-snaphot.d.ts.map +0 -1
  124. package/dist/types/utils/user-session.d.ts +0 -12
  125. package/dist/types/utils/user-session.d.ts.map +0 -1
  126. /package/dist/docs/{tooling/chrome-extension.md → chrome-extension.md} +0 -0
  127. /package/dist/docs/{tooling/gtm-template.md → gtm-template.md} +0 -0
  128. /package/dist/docs/{tooling/mcp.md → mcp.md} +0 -0
  129. /package/dist/docs/{tooling/vite-plugin.md → vite-plugin.md} +0 -0
  130. /package/dist/{id-Bv44ilEo.cjs → id-Cg7fVl1O.cjs} +0 -0
  131. /package/dist/{id-CRqy981z.js → id-n2yrHvNo.js} +0 -0
  132. /package/dist/{object-C82v79xc.js → object-DvUDOytC.js} +0 -0
  133. /package/dist/{object-CUJOjanb.cjs → object-ThO1DEyl.cjs} +0 -0
  134. /package/dist/{storage-B6lQtc4-.cjs → storage-BXSlLTRa.cjs} +0 -0
  135. /package/dist/{storage-6Xk0uAxB.js → storage-hOHCCXvP.js} +0 -0
@@ -0,0 +1,376 @@
1
+ ---
2
+ layout: docs
3
+ ---
4
+
5
+ # Authentication
6
+
7
+ The Picked API supports multiple authentication methods: email/password sessions, OAuth providers, magic links, and API keys. This guide covers each method and explains how to manage sessions and multi-factor authentication (MFA).
8
+
9
+ ## Email and password login
10
+
11
+ The `sessionCreate` mutation authenticates a user with email and password and returns a session.
12
+
13
+ ```graphql
14
+ mutation {
15
+ sessionCreate(input: {
16
+ email: "user@example.com"
17
+ password: "secure-password"
18
+ }) {
19
+ session {
20
+ accessToken
21
+ refreshToken
22
+ expiresAt
23
+ user {
24
+ id
25
+ email
26
+ }
27
+ aal
28
+ }
29
+ requiresMfa
30
+ mfaFactors {
31
+ id
32
+ factorType
33
+ status
34
+ }
35
+ }
36
+ }
37
+ ```
38
+
39
+ If the user has MFA enabled, the response sets `requiresMfa` to `true` and returns the enrolled `mfaFactors`. The caller must complete an MFA challenge before receiving a fully authenticated session. See the [MFA](#multi-factor-authentication-mfa) section below.
40
+
41
+ If MFA is not enabled, the response includes a `session` with an `accessToken`, a `refreshToken`, and an `expiresAt` timestamp.
42
+
43
+ ## Account creation
44
+
45
+ New accounts are created with the `accountCreate` mutation:
46
+
47
+ ```graphql
48
+ mutation {
49
+ accountCreate(input: {
50
+ email: "newuser@example.com"
51
+ password: "secure-password"
52
+ metadata: { firstName: "Jane", lastName: "Doe" }
53
+ }) {
54
+ user {
55
+ id
56
+ email
57
+ }
58
+ session {
59
+ accessToken
60
+ refreshToken
61
+ }
62
+ }
63
+ }
64
+ ```
65
+
66
+ The `metadata` field is optional and accepts arbitrary JSON for storing profile information. When email verification is required, the returned session is `null` until the email is verified.
67
+
68
+ ## Email verification
69
+
70
+ After account creation, the user receives a verification email containing a token. The `accountVerifyEmail` mutation completes verification:
71
+
72
+ ```graphql
73
+ mutation {
74
+ accountVerifyEmail(input: {
75
+ email: "newuser@example.com"
76
+ token: "verification-token-from-email"
77
+ }) {
78
+ session {
79
+ accessToken
80
+ refreshToken
81
+ }
82
+ }
83
+ }
84
+ ```
85
+
86
+ If the verification email needs to be resent:
87
+
88
+ ```graphql
89
+ mutation {
90
+ accountResendVerification(input: {
91
+ email: "newuser@example.com"
92
+ }) {
93
+ success
94
+ }
95
+ }
96
+ ```
97
+
98
+ ## OAuth flow (Google)
99
+
100
+ The API supports Google as an OAuth provider. The flow has two steps.
101
+
102
+ ### Step 1: Get the authorization URL
103
+
104
+ ```graphql
105
+ mutation {
106
+ oauthAuthorize(input: {
107
+ provider: GOOGLE
108
+ redirectUrl: "https://app.example.com/auth/callback"
109
+ scopes: ["openid", "email", "profile"]
110
+ }) {
111
+ url
112
+ }
113
+ }
114
+ ```
115
+
116
+ The caller redirects the user to the returned `url`. After the user authenticates with Google, they are redirected back to a URL on the same dashboard deployment with a `code` parameter, where the caller exchanges it for a session.
117
+
118
+ ### Step 2: Exchange the code for a session
119
+
120
+ ```graphql
121
+ mutation {
122
+ oauthCallback(input: {
123
+ provider: GOOGLE
124
+ code: "auth-code-from-google"
125
+ }) {
126
+ session {
127
+ accessToken
128
+ refreshToken
129
+ expiresAt
130
+ }
131
+ }
132
+ }
133
+ ```
134
+
135
+ ## Magic links
136
+
137
+ Magic links allow passwordless authentication via email.
138
+
139
+ ### Send a magic link
140
+
141
+ ```graphql
142
+ mutation {
143
+ magicLinkSend(input: {
144
+ email: "user@example.com"
145
+ redirectUrl: "https://app.example.com/auth/verify"
146
+ }) {
147
+ success
148
+ }
149
+ }
150
+ ```
151
+
152
+ The user receives an email containing a link with a token.
153
+
154
+ ### Verify the magic link
155
+
156
+ ```graphql
157
+ mutation {
158
+ magicLinkVerify(input: {
159
+ email: "user@example.com"
160
+ token: "token-from-magic-link"
161
+ }) {
162
+ session {
163
+ accessToken
164
+ refreshToken
165
+ }
166
+ }
167
+ }
168
+ ```
169
+
170
+ ## Password management
171
+
172
+ ### Reset a forgotten password
173
+
174
+ Send a password reset email:
175
+
176
+ ```graphql
177
+ mutation {
178
+ passwordResetSend(input: {
179
+ email: "user@example.com"
180
+ redirectUrl: "https://app.example.com/reset-password"
181
+ }) {
182
+ success
183
+ }
184
+ }
185
+ ```
186
+
187
+ Complete the reset with the token from the email:
188
+
189
+ ```graphql
190
+ mutation {
191
+ passwordReset(input: {
192
+ email: "user@example.com"
193
+ token: "reset-token-from-email"
194
+ newPassword: "new-secure-password"
195
+ }) {
196
+ session {
197
+ accessToken
198
+ refreshToken
199
+ }
200
+ }
201
+ }
202
+ ```
203
+
204
+ ### Update an existing password
205
+
206
+ This mutation requires authentication:
207
+
208
+ ```graphql
209
+ mutation {
210
+ passwordUpdate(input: {
211
+ currentPassword: "old-password"
212
+ newPassword: "new-secure-password"
213
+ }) {
214
+ success
215
+ }
216
+ }
217
+ ```
218
+
219
+ ## Multi-factor authentication (MFA)
220
+
221
+ MFA adds a second layer of security using phone-based verification. The API supports enrolling, challenging, verifying, and unenrolling MFA factors. All MFA mutations require authentication.
222
+
223
+ ### Enroll a factor
224
+
225
+ ```graphql
226
+ mutation {
227
+ mfaFactorEnroll(input: {
228
+ factorType: PHONE
229
+ phone: "+15551234567"
230
+ friendlyName: "My Phone"
231
+ }) {
232
+ factor {
233
+ id
234
+ factorType
235
+ status
236
+ }
237
+ }
238
+ }
239
+ ```
240
+
241
+ A newly enrolled factor has a status of `UNVERIFIED` until confirmed via a challenge.
242
+
243
+ ### Send a challenge
244
+
245
+ ```graphql
246
+ mutation {
247
+ mfaChallengeSend(input: {
248
+ factorId: "factor-id"
249
+ }) {
250
+ challengeId
251
+ }
252
+ }
253
+ ```
254
+
255
+ This sends a verification code to the enrolled phone number.
256
+
257
+ ### Verify the challenge
258
+
259
+ ```graphql
260
+ mutation {
261
+ mfaChallengeVerify(input: {
262
+ factorId: "factor-id"
263
+ challengeId: "challenge-id"
264
+ code: "123456"
265
+ }) {
266
+ session {
267
+ accessToken
268
+ refreshToken
269
+ aal
270
+ }
271
+ }
272
+ }
273
+ ```
274
+
275
+ On success, the session is upgraded to Authenticator Assurance Level `AAL2`. The factor status changes to `VERIFIED`.
276
+
277
+ ### Unenroll a factor
278
+
279
+ ```graphql
280
+ mutation {
281
+ mfaFactorUnenroll(input: {
282
+ factorId: "factor-id"
283
+ }) {
284
+ success
285
+ }
286
+ }
287
+ ```
288
+
289
+ ## Using a session
290
+
291
+ After obtaining a session, the caller includes the access token in the `Authorization` header of every authenticated request:
292
+
293
+ ```
294
+ Authorization: Bearer <accessToken>
295
+ ```
296
+
297
+ The `expiresAt` field on the session indicates when the access token expires. The caller should refresh the session before the token expires.
298
+
299
+ ### Authenticator assurance levels
300
+
301
+ The session includes an `aal` field indicating the authentication strength:
302
+
303
+ | Level | Meaning |
304
+ |-------|---------|
305
+ | `AAL1` | Single-factor authentication (email/password, OAuth, or magic link) |
306
+ | `AAL2` | Multi-factor authentication (MFA challenge completed) |
307
+
308
+ Some operations may require `AAL2`. If a caller with `AAL1` attempts such an operation, the API returns an authorization error.
309
+
310
+ ## Session refresh
311
+
312
+ Access tokens are short-lived. The `sessionRefresh` mutation exchanges a refresh token for a new session:
313
+
314
+ ```graphql
315
+ mutation {
316
+ sessionRefresh(input: {
317
+ refreshToken: "v1.refresh-token..."
318
+ }) {
319
+ session {
320
+ accessToken
321
+ refreshToken
322
+ expiresAt
323
+ }
324
+ }
325
+ }
326
+ ```
327
+
328
+ The response includes a new `accessToken` and a new `refreshToken`. The previous refresh token is invalidated. This mutation does not require the `Authorization` header.
329
+
330
+ ## API key authentication
331
+
332
+ API keys provide long-lived, scoped access to the API. They are typically used for server-to-server integrations and automated workflows.
333
+
334
+ To authenticate with an API key, include it in the `x-api-key` header:
335
+
336
+ ```
337
+ x-api-key: pk_live_abc123...
338
+ ```
339
+
340
+ API keys are scoped to an organization and optionally to a specific project. They carry a defined set of [authorization scopes](./authorization-and-scopes.md) that determine which operations the key can perform.
341
+
342
+ See [API Keys](/api-keys) for managing keys from the dashboard UI.
343
+
344
+ ### Creating an API key
345
+
346
+ The `apiKeyCreate` mutation creates a new API key. This mutation is only available to session-authenticated users (not other API keys):
347
+
348
+ ```graphql
349
+ mutation {
350
+ apiKeyCreate(input: {
351
+ organizationId: "org-id"
352
+ name: "CI/CD Pipeline Key"
353
+ projectId: "project-id"
354
+ scopes: [ORGANIZATION_READ, ORGANIZATION_PROJECTS_READ]
355
+ }) {
356
+ apiKey
357
+ }
358
+ }
359
+ ```
360
+
361
+ The full API key string is returned only once in the response. Store it securely; it cannot be retrieved again.
362
+
363
+ The requested scopes must be a subset of the creating user's scopes. See [Authorization and Scopes](./authorization-and-scopes.md) for more details.
364
+
365
+ ### Revoking an API key
366
+
367
+ ```graphql
368
+ mutation {
369
+ apiKeyRevoke(input: {
370
+ keyId: "key-id"
371
+ organizationId: "org-id"
372
+ })
373
+ }
374
+ ```
375
+
376
+ Revoked keys are immediately invalidated and cannot be used for further requests.
@@ -0,0 +1,262 @@
1
+ ---
2
+ layout: docs
3
+ ---
4
+
5
+ # Authorization and scopes
6
+
7
+ The Picked API uses a scope-based authorization model. Every authenticated caller (session user or API key) carries a set of scopes that determine which operations they can perform. This guide explains the scope system, roles, and how permissions are resolved.
8
+
9
+ ## Scope naming format
10
+
11
+ Scopes follow the `entity/action` convention:
12
+
13
+ ```
14
+ organization/read
15
+ organization_members/create
16
+ organization_projects/delete
17
+ ```
18
+
19
+ - **entity** — a lowercase name using underscores for multi-word entities (e.g., `organization_members`, `organization_api_keys`)
20
+ - **action** — one of `create`, `read`, `update`, or `delete`
21
+
22
+ In the GraphQL schema, scopes are represented as the `Scope` enum using uppercase with underscores (e.g., `ORGANIZATION_READ`, `ORGANIZATION_MEMBERS_CREATE`). Both formats refer to the same scope.
23
+
24
+ ## Full scope list
25
+
26
+ The following scopes are available:
27
+
28
+ ### Organization
29
+
30
+ | Scope | GraphQL Enum | Description |
31
+ |-------|-------------|-------------|
32
+ | `organization/create` | `ORGANIZATION_CREATE` | Create new organizations |
33
+ | `organization/read` | `ORGANIZATION_READ` | View organization details |
34
+ | `organization/update` | `ORGANIZATION_UPDATE` | Update organization name, slug, and settings |
35
+ | `organization/delete` | `ORGANIZATION_DELETE` | Delete an organization |
36
+
37
+ ### Organization members
38
+
39
+ | Scope | GraphQL Enum | Description |
40
+ |-------|-------------|-------------|
41
+ | `organization_members/create` | `ORGANIZATION_MEMBERS_CREATE` | Add members to an organization |
42
+ | `organization_members/read` | `ORGANIZATION_MEMBERS_READ` | View organization member list |
43
+ | `organization_members/update` | `ORGANIZATION_MEMBERS_UPDATE` | Update member roles |
44
+ | `organization_members/delete` | `ORGANIZATION_MEMBERS_DELETE` | Remove members from an organization |
45
+
46
+ ### Organization invitations
47
+
48
+ | Scope | GraphQL Enum | Description |
49
+ |-------|-------------|-------------|
50
+ | `organization_invitations/create` | `ORGANIZATION_INVITATIONS_CREATE` | Send invitations to join an organization |
51
+ | `organization_invitations/read` | `ORGANIZATION_INVITATIONS_READ` | View pending invitations |
52
+ | `organization_invitations/delete` | `ORGANIZATION_INVITATIONS_DELETE` | Revoke pending invitations |
53
+
54
+ ### Organization projects
55
+
56
+ | Scope | GraphQL Enum | Description |
57
+ |-------|-------------|-------------|
58
+ | `organization_projects/create` | `ORGANIZATION_PROJECTS_CREATE` | Create new projects |
59
+ | `organization_projects/read` | `ORGANIZATION_PROJECTS_READ` | View project details |
60
+ | `organization_projects/update` | `ORGANIZATION_PROJECTS_UPDATE` | Update project settings |
61
+ | `organization_projects/delete` | `ORGANIZATION_PROJECTS_DELETE` | Delete projects |
62
+
63
+ ### Organization API keys
64
+
65
+ | Scope | GraphQL Enum | Description |
66
+ |-------|-------------|-------------|
67
+ | `organization_api_keys/create` | `ORGANIZATION_API_KEYS_CREATE` | Create new API keys |
68
+ | `organization_api_keys/read` | `ORGANIZATION_API_KEYS_READ` | View API keys (prefix only, not the full key) |
69
+ | `organization_api_keys/delete` | `ORGANIZATION_API_KEYS_DELETE` | Revoke API keys |
70
+
71
+ ### Organization integrations
72
+
73
+ | Scope | GraphQL Enum | Description |
74
+ |-------|-------------|-------------|
75
+ | `organization_integrations/create` | `ORGANIZATION_INTEGRATIONS_CREATE` | Set up new integrations |
76
+ | `organization_integrations/read` | `ORGANIZATION_INTEGRATIONS_READ` | View integration configurations |
77
+ | `organization_integrations/update` | `ORGANIZATION_INTEGRATIONS_UPDATE` | Modify integration settings |
78
+ | `organization_integrations/delete` | `ORGANIZATION_INTEGRATIONS_DELETE` | Remove integrations |
79
+
80
+ ### Organization reports
81
+
82
+ | Scope | GraphQL Enum | Description |
83
+ |-------|-------------|-------------|
84
+ | `organization_reports/create` | `ORGANIZATION_REPORTS_CREATE` | Create new reports |
85
+ | `organization_reports/read` | `ORGANIZATION_REPORTS_READ` | View reports |
86
+ | `organization_reports/update` | `ORGANIZATION_REPORTS_UPDATE` | Update report settings |
87
+ | `organization_reports/delete` | `ORGANIZATION_REPORTS_DELETE` | Delete reports |
88
+
89
+ ## Roles
90
+
91
+ Organization members are assigned one of two roles:
92
+
93
+ ### Owner
94
+
95
+ Owners have full control within their organization. Their default scopes include:
96
+
97
+ - All `organization/*` scopes (`read`, `update`, `delete`)
98
+ - All `organization_members/*` scopes
99
+ - All `organization_invitations/*` scopes
100
+ - `organization_projects/read`, `organization_projects/update`, `organization_projects/delete`
101
+ - All `organization_api_keys/*` scopes
102
+ - All `organization_integrations/*` scopes
103
+ - `organization_reports/read`
104
+
105
+ ### Member
106
+
107
+ Members have broad access but cannot delete the organization or manage other members. Their default scopes include:
108
+
109
+ - `organization/read`, `organization/update`
110
+ - `organization_members/read`
111
+ - `organization_invitations/read`
112
+ - `organization_projects/read`, `organization_projects/update`, `organization_projects/delete`
113
+ - All `organization_api_keys/*` scopes
114
+ - All `organization_integrations/*` scopes
115
+ - `organization_reports/read`
116
+
117
+ The key differences between owner and member:
118
+
119
+ | Capability | Owner | Member |
120
+ |-----------|-------|--------|
121
+ | Delete the organization | Yes | No |
122
+ | Invite new members | Yes | No |
123
+ | Remove members | Yes | No |
124
+ | Update member roles | Yes | No |
125
+ | Revoke invitations | Yes | No |
126
+ | Manage projects and integrations | Yes | Yes |
127
+ | Manage API keys | Yes | Yes |
128
+ | View reports | Yes | Yes |
129
+
130
+ ## How scopes are resolved
131
+
132
+ When an authenticated request reaches the API, the authorization system resolves scopes as follows:
133
+
134
+ 1. **Session-authenticated users** — the API looks up the caller's role within the target organization and assigns the default scopes for that role.
135
+ 2. **API key requests** — the API uses the scopes that were assigned to the key at creation time. These scopes are always a subset of the creating user's scopes.
136
+
137
+ For operations that target a specific organization (most queries and mutations), the caller must have the required scope within that organization. For root-level operations like `organizationCreate`, the scope is resolved at the account level.
138
+
139
+ If the caller lacks the required scope, the API returns an authorization error with a `FORBIDDEN` code. See [Error Handling](./error-handling.md) for details.
140
+
141
+ ## Operations and required scopes
142
+
143
+ The following table maps common operations to the scopes they require:
144
+
145
+ ### Queries
146
+
147
+ | Query | Required Scope |
148
+ |-------|---------------|
149
+ | `organization` | `organization/read` |
150
+ | `organizations` | `organization/read` |
151
+ | `project` | `organization_projects/read` |
152
+ | `apiKeys` | `organization_api_keys/read` |
153
+ | `shopifyWebhooks` | `organization_integrations/read` |
154
+ | `shopifyMetafieldConfigs` | `organization_integrations/read` |
155
+ | `shopifyProduct` / `shopifyProducts` | `organization_integrations/read` |
156
+ | `report` | `organization_reports/read` |
157
+
158
+ ### Mutations
159
+
160
+ | Mutation | Required Scope |
161
+ |----------|---------------|
162
+ | `organizationCreate` | `organization/create` |
163
+ | `organizationUpdate` | `organization/update` |
164
+ | `organizationDelete` | `organization/delete` |
165
+ | `organizationMemberInvite` | `organization_invitations/create` |
166
+ | `organizationInvitationRevoke` | `organization_invitations/delete` |
167
+ | `organizationMemberRemove` | `organization_members/delete` |
168
+ | `organizationMemberRoleUpdate` | `organization_members/update` |
169
+ | `projectCreate` | `organization_projects/create` |
170
+ | `apiKeyCreate` | `organization_api_keys/create` |
171
+ | `apiKeyRevoke` | `organization_api_keys/delete` |
172
+ | `shopifyWebhookCreate` | `organization_integrations/create` |
173
+ | `shopifyWebhookUpdate` | `organization_integrations/update` |
174
+ | `shopifyWebhookDelete` | `organization_integrations/delete` |
175
+ | `shopifyMetafieldConfigCreate` | `organization_integrations/create` |
176
+ | `shopifyMetafieldConfigUpdate` | `organization_integrations/update` |
177
+ | `shopifyMetafieldConfigDelete` | `organization_integrations/delete` |
178
+ | `reportCreate` | `organization_reports/create` |
179
+ | `reportUpdate` | `organization_reports/update` |
180
+ | `reportDelete` | `organization_reports/delete` |
181
+
182
+ Some mutations do not require organization-level scopes because they operate on the caller's own data:
183
+
184
+ | Mutation | Requirement |
185
+ |----------|------------|
186
+ | `sessionCreate` / `sessionRefresh` | No authentication required |
187
+ | `accountCreate` / `accountVerifyEmail` | No authentication required |
188
+ | `oauthAuthorize` / `oauthCallback` | No authentication required |
189
+ | `magicLinkSend` / `magicLinkVerify` | No authentication required |
190
+ | `passwordResetSend` / `passwordReset` | No authentication required |
191
+ | `passwordUpdate` | Authenticated session (no specific scope) |
192
+ | `organizationInvitationAccept` / `organizationInvitationDecline` | Authenticated session (no specific scope) |
193
+ | `mfaFactorEnroll` / `mfaChallengeSend` / `mfaChallengeVerify` / `mfaFactorUnenroll` | Authenticated session (no specific scope) |
194
+
195
+ ## Querying scopes
196
+
197
+ ### Root-level scopes
198
+
199
+ The `viewerScopes` query returns the root-level scopes for the authenticated caller:
200
+
201
+ ```graphql
202
+ query {
203
+ viewerScopes
204
+ }
205
+ ```
206
+
207
+ Response:
208
+
209
+ ```json
210
+ {
211
+ "data": {
212
+ "viewerScopes": [
213
+ "ORGANIZATION_CREATE"
214
+ ]
215
+ }
216
+ }
217
+ ```
218
+
219
+ ### Organization-level scopes
220
+
221
+ Each `Organization` object exposes a `viewerScopes` field that returns the scopes the caller has within that specific organization:
222
+
223
+ ```graphql
224
+ query {
225
+ organization(input: { slug: "my-company" }) {
226
+ id
227
+ name
228
+ viewerScopes
229
+ }
230
+ }
231
+ ```
232
+
233
+ Response:
234
+
235
+ ```json
236
+ {
237
+ "data": {
238
+ "organization": {
239
+ "id": "org_abc123",
240
+ "name": "My Company",
241
+ "viewerScopes": [
242
+ "ORGANIZATION_READ",
243
+ "ORGANIZATION_UPDATE",
244
+ "ORGANIZATION_MEMBERS_READ",
245
+ "ORGANIZATION_PROJECTS_READ",
246
+ "ORGANIZATION_PROJECTS_UPDATE",
247
+ "ORGANIZATION_PROJECTS_DELETE",
248
+ "ORGANIZATION_API_KEYS_CREATE",
249
+ "ORGANIZATION_API_KEYS_READ",
250
+ "ORGANIZATION_API_KEYS_DELETE",
251
+ "ORGANIZATION_INTEGRATIONS_CREATE",
252
+ "ORGANIZATION_INTEGRATIONS_READ",
253
+ "ORGANIZATION_INTEGRATIONS_UPDATE",
254
+ "ORGANIZATION_INTEGRATIONS_DELETE",
255
+ "ORGANIZATION_REPORTS_READ"
256
+ ]
257
+ }
258
+ }
259
+ }
260
+ ```
261
+
262
+ This is useful for building UIs that adapt based on the caller's permissions — for example, hiding a "Delete Organization" button when the caller lacks `ORGANIZATION_DELETE`.