@picked/client 0.0.70-next.6 → 0.0.70

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 (73) hide show
  1. package/dist/{api-ahdSBQ3Y.js → api-CPy9G49C.js} +1 -1
  2. package/dist/api-Ce6xYI--.cjs +1 -0
  3. package/dist/cjs/index.js +1 -1
  4. package/dist/cjs/plugins/dynamic-experiences/index.js +1 -1
  5. package/dist/cjs/server/index.js +1 -1
  6. package/dist/docs/actions.mdx +38 -18
  7. package/dist/docs/api/authentication.md +75 -48
  8. package/dist/docs/api/authorization-and-scopes.md +63 -47
  9. package/dist/docs/api/error-handling.md +70 -54
  10. package/dist/docs/api/index.md +41 -22
  11. package/dist/docs/api/pagination.md +30 -20
  12. package/dist/docs/api-keys.mdx +11 -6
  13. package/dist/docs/chrome-extension.md +22 -5
  14. package/dist/docs/conditions.mdx +32 -19
  15. package/dist/docs/content/asset-library.mdx +65 -34
  16. package/dist/docs/content/content-entries.mdx +8 -2
  17. package/dist/docs/content/layouts.mdx +45 -29
  18. package/dist/docs/content/pages.mdx +29 -14
  19. package/dist/docs/content/sections.mdx +19 -8
  20. package/dist/docs/delivery-api/experience-content.md +10 -5
  21. package/dist/docs/delivery-api/index.md +19 -12
  22. package/dist/docs/delivery-api/preview-caching-crawling.md +27 -14
  23. package/dist/docs/delivery-api/project-config.md +6 -4
  24. package/dist/docs/experiences.mdx +26 -10
  25. package/dist/docs/features.mdx +66 -25
  26. package/dist/docs/fields.mdx +42 -27
  27. package/dist/docs/flow-builder/building-flows.md +38 -20
  28. package/dist/docs/flow-builder/condition-builder.md +32 -21
  29. package/dist/docs/flow-builder/keyboard-shortcuts.md +18 -18
  30. package/dist/docs/flow-builder/nodes.md +26 -13
  31. package/dist/docs/flow-builder/overview.mdx +28 -15
  32. package/dist/docs/gtm-template.md +35 -26
  33. package/dist/docs/guides/build-a-personalized-page.mdx +36 -18
  34. package/dist/docs/image-optimisation-service.md +33 -17
  35. package/dist/docs/index.md +28 -17
  36. package/dist/docs/mcp.md +46 -38
  37. package/dist/docs/pickedql/data-sources.md +65 -43
  38. package/dist/docs/pickedql/dimensions.md +27 -12
  39. package/dist/docs/pickedql/examples.md +38 -26
  40. package/dist/docs/pickedql/filtering.md +10 -6
  41. package/dist/docs/pickedql/index.md +32 -14
  42. package/dist/docs/pickedql/schema.md +85 -76
  43. package/dist/docs/pickedql/virtual-functions.md +62 -36
  44. package/dist/docs/projects.mdx +14 -7
  45. package/dist/docs/reports.mdx +5 -3
  46. package/dist/docs/sdk/actions.md +33 -17
  47. package/dist/docs/sdk/conditions.md +8 -5
  48. package/dist/docs/sdk/content-security-policy.md +5 -4
  49. package/dist/docs/sdk/experiences.md +29 -11
  50. package/dist/docs/sdk/feature-allocation.md +79 -47
  51. package/dist/docs/sdk/features.md +130 -76
  52. package/dist/docs/sdk/fields.md +66 -42
  53. package/dist/docs/sdk/index.md +22 -13
  54. package/dist/docs/sdk/plugins/create-experience.md +18 -15
  55. package/dist/docs/sdk/plugins/dynamic-experiences.md +42 -16
  56. package/dist/docs/sdk/predicates.md +53 -37
  57. package/dist/docs/sdk/ssr/index.md +25 -8
  58. package/dist/docs/sdk/ssr/rendering-and-hydration.md +37 -16
  59. package/dist/docs/sdk/ssr/serialize.md +38 -20
  60. package/dist/docs/sdk/ssr/slots-and-placements.md +28 -10
  61. package/dist/docs/sdk/steps.md +100 -55
  62. package/dist/docs/steps.mdx +20 -11
  63. package/dist/docs/team-management.md +17 -11
  64. package/dist/docs/vite-plugin.md +26 -19
  65. package/dist/docs/workspace-versioning.mdx +14 -8
  66. package/dist/docs/workspaces.mdx +55 -24
  67. package/dist/esm/index.js +1 -1
  68. package/dist/esm/plugins/dynamic-experiences/index.js +2 -2
  69. package/dist/esm/server/index.js +1 -1
  70. package/dist/{plugin-BxMR7fOV.js → plugin-ChPkTj__.js} +1 -1
  71. package/dist/{plugin-CASdGtTX.cjs → plugin-DHn11gn6.cjs} +1 -1
  72. package/package.json +1 -1
  73. package/dist/api-CoUkIxIr.cjs +0 -1
@@ -4,11 +4,14 @@ layout: docs
4
4
 
5
5
  # Authorization and scopes
6
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.
7
+ The Picked API controls access with scopes. Every authenticated caller carries a set
8
+ of scopes, and those scopes decide which operations the caller can do. A caller is
9
+ either a user with a session or an API key. This guide covers the scopes, the roles,
10
+ and how the API resolves a permission.
8
11
 
9
- ## Scope naming format
12
+ ## The format of a scope name
10
13
 
11
- Scopes follow the `entity/action` convention:
14
+ A scope has the form `entity/action`:
12
15
 
13
16
  ```
14
17
  organization/read
@@ -16,83 +19,87 @@ organization_members/create
16
19
  organization_projects/delete
17
20
  ```
18
21
 
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`
22
+ - **entity** — a lowercase name. It uses an underscore between words, as in
23
+ `organization_members` and `organization_api_keys`.
24
+ - **action** — one of `create`, `read`, `update`, or `delete`.
21
25
 
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.
26
+ The GraphQL schema holds the scopes in the `Scope` enum. There they are uppercase
27
+ with an underscore between words, as in `ORGANIZATION_READ` and
28
+ `ORGANIZATION_MEMBERS_CREATE`. The two forms name the same scope.
23
29
 
24
30
  ## Full scope list
25
31
 
26
- The following scopes are available:
32
+ These are the scopes:
27
33
 
28
34
  ### Organization
29
35
 
30
36
  | Scope | GraphQL Enum | Description |
31
37
  |-------|-------------|-------------|
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 |
38
+ | `organization/create` | `ORGANIZATION_CREATE` | Create an organization |
39
+ | `organization/read` | `ORGANIZATION_READ` | Read the detail of an organization |
40
+ | `organization/update` | `ORGANIZATION_UPDATE` | Update the name, the slug, and the settings of an organization |
35
41
  | `organization/delete` | `ORGANIZATION_DELETE` | Delete an organization |
36
42
 
37
43
  ### Organization members
38
44
 
39
45
  | Scope | GraphQL Enum | Description |
40
46
  |-------|-------------|-------------|
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 |
47
+ | `organization_members/create` | `ORGANIZATION_MEMBERS_CREATE` | Add a member to an organization |
48
+ | `organization_members/read` | `ORGANIZATION_MEMBERS_READ` | Read the member list of an organization |
49
+ | `organization_members/update` | `ORGANIZATION_MEMBERS_UPDATE` | Update the role of a member |
50
+ | `organization_members/delete` | `ORGANIZATION_MEMBERS_DELETE` | Remove a member from an organization |
45
51
 
46
52
  ### Organization invitations
47
53
 
48
54
  | Scope | GraphQL Enum | Description |
49
55
  |-------|-------------|-------------|
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 |
56
+ | `organization_invitations/create` | `ORGANIZATION_INVITATIONS_CREATE` | Send an invitation to join an organization |
57
+ | `organization_invitations/read` | `ORGANIZATION_INVITATIONS_READ` | Read the invitations that wait for an answer |
58
+ | `organization_invitations/delete` | `ORGANIZATION_INVITATIONS_DELETE` | Revoke an invitation that waits for an answer |
53
59
 
54
60
  ### Organization projects
55
61
 
56
62
  | Scope | GraphQL Enum | Description |
57
63
  |-------|-------------|-------------|
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 |
64
+ | `organization_projects/create` | `ORGANIZATION_PROJECTS_CREATE` | Create a project |
65
+ | `organization_projects/read` | `ORGANIZATION_PROJECTS_READ` | Read the detail of a project |
66
+ | `organization_projects/update` | `ORGANIZATION_PROJECTS_UPDATE` | Update the settings of a project |
67
+ | `organization_projects/delete` | `ORGANIZATION_PROJECTS_DELETE` | Delete a project |
62
68
 
63
69
  ### Organization API keys
64
70
 
65
71
  | Scope | GraphQL Enum | Description |
66
72
  |-------|-------------|-------------|
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 |
73
+ | `organization_api_keys/create` | `ORGANIZATION_API_KEYS_CREATE` | Create an API key |
74
+ | `organization_api_keys/read` | `ORGANIZATION_API_KEYS_READ` | Read the API keys. This gives the prefix only, and not the full key. |
75
+ | `organization_api_keys/delete` | `ORGANIZATION_API_KEYS_DELETE` | Revoke an API key |
70
76
 
71
77
  ### Organization integrations
72
78
 
73
79
  | Scope | GraphQL Enum | Description |
74
80
  |-------|-------------|-------------|
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 |
81
+ | `organization_integrations/create` | `ORGANIZATION_INTEGRATIONS_CREATE` | Set up an integration |
82
+ | `organization_integrations/read` | `ORGANIZATION_INTEGRATIONS_READ` | Read the options of an integration |
83
+ | `organization_integrations/update` | `ORGANIZATION_INTEGRATIONS_UPDATE` | Change the settings of an integration |
84
+ | `organization_integrations/delete` | `ORGANIZATION_INTEGRATIONS_DELETE` | Remove an integration |
79
85
 
80
86
  ### Organization reports
81
87
 
82
88
  | Scope | GraphQL Enum | Description |
83
89
  |-------|-------------|-------------|
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 |
90
+ | `organization_reports/create` | `ORGANIZATION_REPORTS_CREATE` | Create a report |
91
+ | `organization_reports/read` | `ORGANIZATION_REPORTS_READ` | Read a report |
92
+ | `organization_reports/update` | `ORGANIZATION_REPORTS_UPDATE` | Update the settings of a report |
93
+ | `organization_reports/delete` | `ORGANIZATION_REPORTS_DELETE` | Delete a report |
88
94
 
89
95
  ## Roles
90
96
 
91
- Organization members are assigned one of two roles:
97
+ Each member of an organization holds one of two roles:
92
98
 
93
99
  ### Owner
94
100
 
95
- Owners have full control within their organization. Their default scopes include:
101
+ An owner has full control inside the organization. These are the default scopes of an
102
+ owner:
96
103
 
97
104
  - All `organization/*` scopes (`read`, `update`, `delete`)
98
105
  - All `organization_members/*` scopes
@@ -104,7 +111,8 @@ Owners have full control within their organization. Their default scopes include
104
111
 
105
112
  ### Member
106
113
 
107
- Members have broad access but cannot delete the organization or manage other members. Their default scopes include:
114
+ A member can reach most things, but cannot delete the organization and cannot control
115
+ another member. These are the default scopes of a member:
108
116
 
109
117
  - `organization/read`, `organization/update`
110
118
  - `organization_members/read`
@@ -114,7 +122,7 @@ Members have broad access but cannot delete the organization or manage other mem
114
122
  - All `organization_integrations/*` scopes
115
123
  - `organization_reports/read`
116
124
 
117
- The key differences between owner and member:
125
+ These are the differences between an owner and a member:
118
126
 
119
127
  | Capability | Owner | Member |
120
128
  |-----------|-------|--------|
@@ -123,24 +131,30 @@ The key differences between owner and member:
123
131
  | Remove members | Yes | No |
124
132
  | Update member roles | Yes | No |
125
133
  | Revoke invitations | Yes | No |
126
- | Manage projects and integrations | Yes | Yes |
127
- | Manage API keys | Yes | Yes |
128
- | View reports | Yes | Yes |
134
+ | Control projects and integrations | Yes | Yes |
135
+ | Control API keys | Yes | Yes |
136
+ | Read reports | Yes | Yes |
129
137
 
130
138
  ## How scopes are resolved
131
139
 
132
- When an authenticated request reaches the API, the authorization system resolves scopes as follows:
140
+ When an authenticated request reaches the API, the API resolves the scopes like this:
133
141
 
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.
142
+ 1. **A user with a session** — the API finds the role of that caller in the target
143
+ organization. It then gives the caller the default scopes of that role.
144
+ 2. **A request with an API key** — the API uses the scopes that the key received when
145
+ somebody created it. Those scopes are always scopes of the user who created the
146
+ key.
136
147
 
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.
148
+ Most queries and mutations point at one organization. For those, the caller must hold
149
+ the scope inside that organization. For a root-level operation such as
150
+ `organizationCreate`, the API resolves the scope at the account level.
138
151
 
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.
152
+ When the caller does not hold the scope that an operation needs, the API returns an
153
+ authorization error with the code `FORBIDDEN`. See [Errors](./error-handling.md).
140
154
 
141
155
  ## Operations and required scopes
142
156
 
143
- The following table maps common operations to the scopes they require:
157
+ This table gives the scope that each common operation needs:
144
158
 
145
159
  ### Queries
146
160
 
@@ -192,7 +206,7 @@ Some mutations do not require organization-level scopes because they operate on
192
206
  | `organizationInvitationAccept` / `organizationInvitationDecline` | Authenticated session (no specific scope) |
193
207
  | `mfaFactorEnroll` / `mfaChallengeSend` / `mfaChallengeVerify` / `mfaFactorUnenroll` | Authenticated session (no specific scope) |
194
208
 
195
- ## Querying scopes
209
+ ## Read your scopes
196
210
 
197
211
  ### Root-level scopes
198
212
 
@@ -259,4 +273,6 @@ Response:
259
273
  }
260
274
  ```
261
275
 
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`.
276
+ Use this to build a UI that changes with the permissions of the caller. An example is
277
+ a **Delete Organization** button that the UI hides when the caller does not hold
278
+ `ORGANIZATION_DELETE`.
@@ -2,13 +2,16 @@
2
2
  layout: docs
3
3
  ---
4
4
 
5
- # Error handling
5
+ # Errors
6
6
 
7
- The Picked API returns errors using the standard GraphQL error format. This guide covers the error response shape, all error codes, and practical strategies for handling errors.
7
+ The Picked API returns an error in the standard GraphQL format. This guide covers the
8
+ shape of an error response, every error code, and what your code must do with each
9
+ one.
8
10
 
9
11
  ## Error response shape
10
12
 
11
- When an operation fails, the API returns an `errors` array in the response body. Each error object contains:
13
+ When an operation fails, the API returns an `errors` array in the response body. Each
14
+ error object holds these fields:
12
15
 
13
16
  ```json
14
17
  {
@@ -27,29 +30,31 @@ When an operation fails, the API returns an `errors` array in the response body.
27
30
 
28
31
  | Field | Description |
29
32
  |-------|-------------|
30
- | `message` | A human-readable error description. Suitable for logging but not for programmatic handling. |
31
- | `path` | The path to the field in the query that caused the error. |
32
- | `extensions.code` | A machine-readable error code. Use this for programmatic error handling. |
33
+ | `message` | A description of the error that a person can read. Put it in your log. Do not write code against it. |
34
+ | `path` | The path to the field in the query that made the error. |
35
+ | `extensions.code` | An error code that a machine can read. Write your code against this field. |
33
36
 
34
- Multiple errors may appear in a single response. The `data` field may be `null` if the entire operation failed, or it may contain partial data if only some fields failed.
37
+ One response can hold more than one error. The `data` field is `null` when the whole
38
+ operation failed. It holds part of the data when only some fields failed.
35
39
 
36
40
  ## Authentication error codes
37
41
 
38
- These errors relate to session creation, account management, OAuth, magic links, password operations, and MFA. They map to the `AuthErrorCode` enum in the schema.
42
+ These errors come from a session, an account, OAuth, a magic link, a password
43
+ operation, or MFA. They match the `AuthErrorCode` enum in the schema.
39
44
 
40
45
  | Code | Description |
41
46
  |------|-------------|
42
- | `INVALID_CREDENTIALS` | The email/password combination is incorrect. |
43
- | `EMAIL_NOT_CONFIRMED` | The account exists but the email address has not been verified. See [Authentication](/api/authentication#email-verification). |
44
- | `USER_NOT_FOUND` | No account exists with the provided email address. |
45
- | `USER_ALREADY_EXISTS` | An account with this email already exists. Returned during account creation. |
46
- | `WEAK_PASSWORD` | The provided password does not meet strength requirements. |
47
- | `INVALID_TOKEN` | The token (verification, reset, or magic link) is invalid or malformed. |
48
- | `TOKEN_EXPIRED` | The token has expired. The caller should request a new one. |
49
- | `MFA_CHALLENGE_FAILED` | The MFA verification code is incorrect. |
50
- | `MFA_FACTOR_NOT_FOUND` | The specified MFA factor does not exist or does not belong to the caller. |
51
- | `OAUTH_PROVIDER_ERROR` | An error occurred communicating with the OAuth provider (e.g., Google). |
52
- | `RATE_LIMITED` | Too many requests in a short period. The caller should wait and retry. |
47
+ | `INVALID_CREDENTIALS` | The email address and the password do not match. |
48
+ | `EMAIL_NOT_CONFIRMED` | The account is there, but nobody verified the email address. See [Authentication](/api/authentication#verify-an-email-address). |
49
+ | `USER_NOT_FOUND` | There is no account with that email address. |
50
+ | `USER_ALREADY_EXISTS` | An account with that email address is there already. The API returns this when you create an account. |
51
+ | `WEAK_PASSWORD` | The password is not strong enough. |
52
+ | `INVALID_TOKEN` | The token is invalid or malformed. This covers a verification token, a reset token, and a magic link token. |
53
+ | `TOKEN_EXPIRED` | The token expired. Ask for a new one. |
54
+ | `MFA_CHALLENGE_FAILED` | The MFA verification code is not correct. |
55
+ | `MFA_FACTOR_NOT_FOUND` | That MFA factor is not there, or it belongs to another caller. |
56
+ | `OAUTH_PROVIDER_ERROR` | The exchange with the OAuth provider, such as Google, failed. |
57
+ | `RATE_LIMITED` | Too many requests in a short period. Wait, then try again. |
53
58
 
54
59
  ### Example: invalid credentials
55
60
 
@@ -76,16 +81,18 @@ These errors relate to session creation, account management, OAuth, magic links,
76
81
 
77
82
  ## Authorization error codes
78
83
 
79
- Authorization errors occur when the caller lacks the required [scope](./authorization-and-scopes.md) for an operation.
84
+ An authorization error happens when the caller does not hold the
85
+ [scope](./authorization-and-scopes.md) that an operation needs.
80
86
 
81
87
  | Code | Description |
82
88
  |------|-------------|
83
- | `UNAUTHORIZED` | No valid authentication was provided. The request is missing the `Authorization` header or the `x-api-key` header, or the token/key is invalid. |
84
- | `FORBIDDEN` | The caller is authenticated but does not have the required scope for the operation. |
89
+ | `UNAUTHORIZED` | The request carries no valid authentication. It has no `Authorization` header and no `x-api-key` header, or the token is invalid. |
90
+ | `FORBIDDEN` | The caller is authenticated, but it does not hold the scope that the operation needs. |
85
91
 
86
- ### Example: insufficient scope
92
+ ### Example: the scope is not enough
87
93
 
88
- A caller with only `organization/read` attempting to delete an organization receives:
94
+ A caller that holds `organization/read` only tries to delete an organization. It
95
+ receives this:
89
96
 
90
97
  ```json
91
98
  {
@@ -106,52 +113,53 @@ A caller with only `organization/read` attempting to delete an organization rece
106
113
 
107
114
  ## Organization, member, and invitation error codes
108
115
 
109
- These errors relate to organization management, member operations, and invitation workflows. They map to the `ErrorCode` enum in the schema.
116
+ These errors come from an organization, a member operation, or an invitation. They
117
+ match the `ErrorCode` enum in the schema.
110
118
 
111
119
  ### Organization errors
112
120
 
113
121
  | Code | Description |
114
122
  |------|-------------|
115
- | `ORGANIZATION_NOT_FOUND` | The specified organization does not exist or the caller does not have access to it. |
116
- | `ORGANIZATION_ALREADY_EXISTS` | An organization with this name already exists. |
117
- | `SLUG_ALREADY_EXISTS` | The requested slug is already in use by another organization. |
118
- | `INVALID_SLUG` | The slug contains invalid characters. Slugs must be lowercase alphanumeric with hyphens. |
123
+ | `ORGANIZATION_NOT_FOUND` | That organization is not there, or the caller cannot reach it. |
124
+ | `ORGANIZATION_ALREADY_EXISTS` | An organization with that name is there already. |
125
+ | `SLUG_ALREADY_EXISTS` | Another organization uses that slug already. |
126
+ | `INVALID_SLUG` | The slug holds an invalid character. A slug takes lowercase letters, digits, and hyphens only. |
119
127
 
120
128
  ### Member errors
121
129
 
122
130
  | Code | Description |
123
131
  |------|-------------|
124
- | `MEMBER_NOT_FOUND` | The specified user is not a member of the organization. |
125
- | `MEMBER_ALREADY_EXISTS` | The user is already a member of the organization. |
126
- | `CANNOT_REMOVE_PRIMARY_OWNER` | The primary owner of an organization cannot be removed. |
127
- | `CANNOT_CHANGE_OWN_ROLE` | A member cannot change their own role. Another owner must perform this action. |
132
+ | `MEMBER_NOT_FOUND` | That user is not a member of the organization. |
133
+ | `MEMBER_ALREADY_EXISTS` | The user is a member of the organization already. |
134
+ | `CANNOT_REMOVE_PRIMARY_OWNER` | You cannot remove the primary owner of an organization. |
135
+ | `CANNOT_CHANGE_OWN_ROLE` | A member cannot change the role of that same member. Another owner must do it. |
128
136
 
129
137
  ### Invitation errors
130
138
 
131
139
  | Code | Description |
132
140
  |------|-------------|
133
- | `INVITATION_NOT_FOUND` | The specified invitation does not exist. |
134
- | `INVITATION_EXPIRED` | The invitation has expired and can no longer be accepted. |
135
- | `INVITATION_ALREADY_ACCEPTED` | The invitation has already been accepted. |
141
+ | `INVITATION_NOT_FOUND` | That invitation is not there. |
142
+ | `INVITATION_EXPIRED` | The invitation expired. Nobody can accept it now. |
143
+ | `INVITATION_ALREADY_ACCEPTED` | Somebody accepted the invitation already. |
136
144
 
137
145
  ### Project errors
138
146
 
139
147
  | Code | Description |
140
148
  |------|-------------|
141
- | `PROJECT_NOT_FOUND` | The specified project does not exist or the caller does not have access. |
149
+ | `PROJECT_NOT_FOUND` | That project is not there, or the caller cannot reach it. |
142
150
 
143
151
  ### General errors
144
152
 
145
153
  | Code | Description |
146
154
  |------|-------------|
147
- | `VALIDATION_ERROR` | The input failed validation. Check the `message` field for details about which field is invalid. |
148
- | `INTERNAL_ERROR` | An unexpected server error occurred. Retry the request or contact support if the issue persists. |
155
+ | `VALIDATION_ERROR` | The input failed a validation rule. Read the `message` field to learn which field is invalid. |
156
+ | `INTERNAL_ERROR` | The server met an unexpected error. Try the request again. Contact support when the error continues. |
149
157
 
150
- ## Handling errors in practice
158
+ ## What your code must do
151
159
 
152
- ### Check for errors first
160
+ ### Look for an error first
153
161
 
154
- Every response should be checked for the presence of an `errors` array before processing `data`:
162
+ Check every response for an `errors` array before you read `data`:
155
163
 
156
164
  ```javascript
157
165
  const response = await fetch('/graphql', {
@@ -171,41 +179,49 @@ if (result.errors) {
171
179
 
172
180
  switch (code) {
173
181
  case 'UNAUTHORIZED':
174
- // Redirect to login
182
+ // Send the user to the sign-in page.
175
183
  break;
176
184
  case 'FORBIDDEN':
177
- // Show a permissions error
185
+ // Show a permissions error.
178
186
  break;
179
187
  case 'RATE_LIMITED':
180
- // Wait and retry
188
+ // Wait, then try again.
181
189
  break;
182
190
  default:
183
- // Log or display the error message
191
+ // Put the message in the log, or show it.
184
192
  console.error(error.message);
185
193
  }
186
194
  }
187
195
  }
188
196
  ```
189
197
 
190
- ### Refresh expired tokens
198
+ ### Refresh a token that expired
191
199
 
192
- If a request returns `UNAUTHORIZED` and the caller has a refresh token, attempt a [session refresh](/api/authentication#session-refresh) before treating it as a login failure:
200
+ A request can return `UNAUTHORIZED` while the caller holds a refresh token. Try a
201
+ [session refresh](/api/authentication#refresh-a-session) first. Do not treat it as a
202
+ failure to sign in:
193
203
 
194
204
  ```javascript
195
205
  if (code === 'UNAUTHORIZED' && refreshToken) {
196
206
  const refreshResult = await sessionRefresh(refreshToken);
197
207
  if (refreshResult.session) {
198
- // Retry the original request with the new access token
208
+ // Try the first request again with the new access token.
199
209
  } else {
200
- // Redirect to login
210
+ // Send the user to the sign-in page.
201
211
  }
202
212
  }
203
213
  ```
204
214
 
205
- ### Handle partial data
215
+ ### Read data that is only part complete
206
216
 
207
- GraphQL can return partial data alongside errors. For example, a query that fetches an organization and its members might succeed for the organization but fail for the members if the caller lacks `organization_members/read`. In this case, the response contains both `data` (with the organization) and `errors` (for the members field). Always check both.
217
+ GraphQL can return part of the data together with an error. Take a query that reads
218
+ an organization and its members. It can succeed for the organization and fail for the
219
+ members, when the caller does not hold `organization_members/read`. The response then
220
+ holds `data` with the organization, and `errors` for the members field. Always read
221
+ both.
208
222
 
209
- ### Retry transient failures
223
+ ### Try again after a transient failure
210
224
 
211
- `INTERNAL_ERROR` and `RATE_LIMITED` are transient. For `RATE_LIMITED`, implement exponential backoff before retrying. For `INTERNAL_ERROR`, a single retry is usually sufficient.
225
+ `INTERNAL_ERROR` and `RATE_LIMITED` are both transient. For `RATE_LIMITED`, use an
226
+ exponential backoff before you try again. For `INTERNAL_ERROR`, one more try is
227
+ usually enough.
@@ -2,17 +2,21 @@
2
2
  layout: docs
3
3
  ---
4
4
 
5
- # Getting started
5
+ # Start here
6
6
 
7
- This guide walks you through your first interactions with the Picked GraphQL API, from sending a simple ping to making authenticated queries and mutations.
7
+ This guide takes you through your first calls to the Picked GraphQL API. It starts
8
+ with a ping and ends with an authenticated query and mutation.
8
9
 
9
10
  :::note
10
- This reference covers the authenticated GraphQL management API. To serve your published config and page content to your app, use the public, read-only [Delivery API](/delivery-api) instead.
11
+ This reference covers the GraphQL API that needs authentication. To serve your
12
+ published options and page content to your application, use the public
13
+ [Delivery API](/delivery-api), which is read-only.
11
14
  :::
12
15
 
13
16
  ## Playground
14
17
 
15
- Explore the API and run queries interactively in the [GraphQL Playground](https://api.picked.app/api/graphql).
18
+ Read the API and run a query in the
19
+ [GraphQL Playground](https://api.picked.app/api/graphql).
16
20
 
17
21
  ## API endpoint
18
22
 
@@ -22,7 +26,8 @@ All requests go to a single endpoint:
22
26
  POST https://api.picked.app/api/graphql
23
27
  ```
24
28
 
25
- Every request must use the `POST` method with a JSON body containing a `query` field and an optional `variables` field:
29
+ Every request must use the `POST` method. The JSON body must hold a `query` field,
30
+ and it can also hold a `variables` field:
26
31
 
27
32
  ```json
28
33
  {
@@ -41,7 +46,8 @@ curl -X POST https://api.picked.app/api/graphql \
41
46
 
42
47
  ## Ping the API
43
48
 
44
- Verify that the API is reachable with the `ping` query. This query does not require authentication.
49
+ Check that the API is reachable with the `ping` query. This query needs no
50
+ authentication.
45
51
 
46
52
  ```graphql
47
53
  query {
@@ -59,11 +65,12 @@ Response:
59
65
  }
60
66
  ```
61
67
 
62
- If you receive this response, the API is up and ready.
68
+ This response means that the API is ready.
63
69
 
64
70
  ## Authenticate
65
71
 
66
- Most operations require an access token. You obtain one by creating a session with your email and password.
72
+ Most operations need an access token. To get one, create a session with your email
73
+ address and your password.
67
74
 
68
75
  ```graphql
69
76
  mutation {
@@ -81,7 +88,8 @@ mutation {
81
88
  }
82
89
  ```
83
90
 
84
- A successful response returns a `Session` containing an `accessToken` and a `refreshToken`:
91
+ A successful response returns a `Session` with an `accessToken` and a
92
+ `refreshToken`:
85
93
 
86
94
  ```json
87
95
  {
@@ -98,9 +106,12 @@ A successful response returns a `Session` containing an `accessToken` and a `ref
98
106
  }
99
107
  ```
100
108
 
101
- If `requiresMfa` is `true`, you need to complete the MFA challenge before receiving a session. See the [Authentication](./authentication.md) guide for details.
109
+ When `requiresMfa` is `true`, you must complete the MFA challenge before you receive
110
+ a session. See the [Authentication](./authentication.md) guide.
102
111
 
103
- For server-to-server integrations, prefer [API key authentication](/api/authentication#api-key-authentication) over session tokens.
112
+ For a server-to-server integration, use
113
+ [API key authentication](/api/authentication#api-key-authentication) and not a
114
+ session token.
104
115
 
105
116
  ## Make an authenticated query
106
117
 
@@ -110,7 +121,8 @@ Pass the access token in the `Authorization` header as a Bearer token:
110
121
  Authorization: Bearer eyJhbGciOiJIUzI1NiIs...
111
122
  ```
112
123
 
113
- Now you can query resources that require authentication. For example, fetch your user profile:
124
+ You can now query a resource that needs authentication. This example reads your user
125
+ profile:
114
126
 
115
127
  ```graphql
116
128
  query {
@@ -132,7 +144,8 @@ query {
132
144
 
133
145
  ## Make a mutation
134
146
 
135
- Mutations follow the same pattern: send a `POST` request with the `Authorization` header. For example, create an organization:
147
+ A mutation follows the same pattern. Send a `POST` request with the `Authorization`
148
+ header. This example creates an organization:
136
149
 
137
150
  ```graphql
138
151
  mutation {
@@ -165,11 +178,14 @@ Response:
165
178
  }
166
179
  ```
167
180
 
168
- Every mutation accepts an `input` argument and returns a payload object. The payload contains the created or updated resource on success, or errors in the top-level `errors` array on failure.
181
+ Every mutation takes an `input` argument and returns a payload object. On success the
182
+ payload holds the resource that the mutation created or updated. On failure the
183
+ `errors` array at the top level holds the errors.
169
184
 
170
- ## Error handling basics
185
+ ## Errors
171
186
 
172
- When something goes wrong, the API returns errors in the standard GraphQL `errors` array:
187
+ When something goes wrong, the API returns the errors in the standard GraphQL
188
+ `errors` array:
173
189
 
174
190
  ```json
175
191
  {
@@ -185,13 +201,16 @@ When something goes wrong, the API returns errors in the standard GraphQL `error
185
201
  }
186
202
  ```
187
203
 
188
- Check the `extensions.code` field for a machine-readable error code you can use to handle specific failures programmatically. See the [Error Handling](./error-handling.md) guide for the full list of error codes.
204
+ Read the `extensions.code` field. It holds an error code that your code can act on.
205
+ See the [Errors](./error-handling.md) guide for the full list of codes.
189
206
 
190
207
  ## Next steps
191
208
 
192
- Now that you can authenticate and make requests, explore these guides to go deeper:
209
+ You can now authenticate and make a request. These guides go into more detail:
193
210
 
194
- - [Authentication](./authentication.md) — email/password, OAuth, magic links, MFA, API keys, and session management
195
- - [Authorization and Scopes](./authorization-and-scopes.md) — understand what each role can access and how scopes control permissions
196
- - [Pagination](./pagination.md) — navigate large result sets with cursor-based pagination
197
- - [Error Handling](./error-handling.md) — handle every error code the API can return
211
+ - [Authentication](./authentication.md) — an email address and a password, OAuth,
212
+ magic links, MFA, API keys, and how to control a session.
213
+ - [Authorization and Scopes](./authorization-and-scopes.md) — what each role can
214
+ reach, and how a scope controls a permission.
215
+ - [Pagination](./pagination.md) — how to move through a large result with a cursor.
216
+ - [Errors](./error-handling.md) — every error code that the API can return.