@picked/client 0.0.70-next.6 → 0.0.71-next.1
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/dist/.tsbuildinfo +1 -0
- package/dist/api-CH2FNa6S.cjs +1 -0
- package/dist/api-CgqRvuKL.js +38 -0
- package/dist/cjs/form/index.js +1 -1
- package/dist/cjs/index.js +2 -2
- package/dist/cjs/plugins/dom-observer/index.js +1 -1
- package/dist/cjs/plugins/dynamic-experiences/index.js +1 -1
- package/dist/cjs/server/index.js +1 -1
- package/dist/docs/actions.mdx +38 -18
- package/dist/docs/api/authentication.md +75 -48
- package/dist/docs/api/authorization-and-scopes.md +63 -47
- package/dist/docs/api/error-handling.md +70 -54
- package/dist/docs/api/index.md +41 -22
- package/dist/docs/api/pagination.md +30 -20
- package/dist/docs/api-keys.mdx +11 -6
- package/dist/docs/chrome-extension.md +22 -5
- package/dist/docs/conditions.mdx +32 -19
- package/dist/docs/content/asset-library.mdx +65 -34
- package/dist/docs/content/content-entries.mdx +8 -2
- package/dist/docs/content/layouts.mdx +45 -29
- package/dist/docs/content/pages.mdx +29 -14
- package/dist/docs/content/sections.mdx +19 -8
- package/dist/docs/delivery-api/experience-content.md +10 -5
- package/dist/docs/delivery-api/index.md +19 -12
- package/dist/docs/delivery-api/preview-caching-crawling.md +27 -14
- package/dist/docs/delivery-api/project-config.md +6 -4
- package/dist/docs/experiences.mdx +26 -10
- package/dist/docs/features.mdx +66 -25
- package/dist/docs/fields.mdx +42 -27
- package/dist/docs/flow-builder/building-flows.md +38 -20
- package/dist/docs/flow-builder/condition-builder.md +32 -21
- package/dist/docs/flow-builder/keyboard-shortcuts.md +18 -18
- package/dist/docs/flow-builder/nodes.md +26 -13
- package/dist/docs/flow-builder/overview.mdx +28 -15
- package/dist/docs/gtm-template.md +35 -26
- package/dist/docs/guides/build-a-personalized-page.mdx +36 -18
- package/dist/docs/image-optimisation-service.md +33 -17
- package/dist/docs/index.md +28 -17
- package/dist/docs/mcp.md +46 -38
- package/dist/docs/pickedql/data-sources.md +65 -43
- package/dist/docs/pickedql/dimensions.md +27 -12
- package/dist/docs/pickedql/examples.md +38 -26
- package/dist/docs/pickedql/filtering.md +10 -6
- package/dist/docs/pickedql/index.md +32 -14
- package/dist/docs/pickedql/schema.md +85 -76
- package/dist/docs/pickedql/virtual-functions.md +62 -36
- package/dist/docs/projects.mdx +14 -7
- package/dist/docs/reports.mdx +5 -3
- package/dist/docs/sdk/actions.md +33 -17
- package/dist/docs/sdk/conditions.md +8 -5
- package/dist/docs/sdk/content-security-policy.md +5 -4
- package/dist/docs/sdk/experiences.md +29 -11
- package/dist/docs/sdk/feature-allocation.md +79 -47
- package/dist/docs/sdk/features.md +130 -76
- package/dist/docs/sdk/fields.md +66 -42
- package/dist/docs/sdk/index.md +22 -13
- package/dist/docs/sdk/plugins/create-experience.md +18 -15
- package/dist/docs/sdk/plugins/dynamic-experiences.md +42 -16
- package/dist/docs/sdk/predicates.md +53 -37
- package/dist/docs/sdk/ssr/index.md +25 -8
- package/dist/docs/sdk/ssr/rendering-and-hydration.md +37 -16
- package/dist/docs/sdk/ssr/serialize.md +38 -20
- package/dist/docs/sdk/ssr/slots-and-placements.md +28 -10
- package/dist/docs/sdk/steps.md +100 -55
- package/dist/docs/steps.mdx +20 -11
- package/dist/docs/team-management.md +17 -11
- package/dist/docs/vite-plugin.md +26 -19
- package/dist/docs/workspace-versioning.mdx +14 -8
- package/dist/docs/workspaces.mdx +55 -24
- package/dist/{dom-BwmTHeT7.js → dom-BqcToNL0.js} +1 -3
- package/dist/{dom-CDrHc_j9.cjs → dom-DlLH3FxV.cjs} +1 -1
- package/dist/esm/form/index.js +2 -4
- package/dist/esm/index.js +50 -45
- package/dist/esm/plugins/dom-observer/index.js +1 -1
- package/dist/esm/plugins/dynamic-experiences/index.js +2 -2
- package/dist/esm/preview/index.js +1 -1
- package/dist/esm/server/index.js +12 -10
- package/dist/{experience-instance-DOJShRk5.js → experience-instance-DGk11d8a.js} +3 -3
- package/dist/experience-instance-Dej3P1sE.cjs +1 -0
- package/dist/{plugin-BxMR7fOV.js → plugin-D2UqFikQ.js} +2 -2
- package/dist/plugin-DCW-tnjO.cjs +1 -0
- package/dist/{preview-channel-DW9J8t4B.js → preview-channel-CSr96XAo.js} +1 -1
- package/dist/{protocol-DSqOlW2r.js → protocol-C5KkkiZc.js} +1 -1
- package/dist/types/client.d.ts +7 -6
- package/dist/types/entities/action/action.d.ts +5 -4
- package/dist/types/entities/action/action.d.ts.map +1 -1
- package/dist/types/entities/component/component.d.ts +6 -5
- package/dist/types/entities/component/component.d.ts.map +1 -1
- package/dist/types/entities/condition/condition.d.ts +5 -5
- package/dist/types/entities/content/content.d.ts +4 -3
- package/dist/types/entities/experience/experience.d.ts +11 -10
- package/dist/types/entities/experience/experience.d.ts.map +1 -1
- package/dist/types/entities/experience-instance/experience-instance.d.ts +10 -9
- package/dist/types/entities/feature/feature.d.ts +3 -2
- package/dist/types/entities/feature/feature.d.ts.map +1 -1
- package/dist/types/entities/field/field.d.ts +7 -6
- package/dist/types/entities/framework/framework.d.ts +7 -6
- package/dist/types/entities/framework/framework.d.ts.map +1 -1
- package/dist/types/entities/history/history.d.ts +6 -6
- package/dist/types/entities/plugin/plugin.d.ts +3 -2
- package/dist/types/entities/plugin/plugin.d.ts.map +1 -1
- package/dist/types/entities/predicate/predicate.d.ts +4 -3
- package/dist/types/entities/predicate/predicate.d.ts.map +1 -1
- package/dist/types/entities/response/response-validation.d.ts +3 -2
- package/dist/types/entities/response/response.d.ts +12 -11
- package/dist/types/entities/session/session.d.ts +2 -1
- package/dist/types/entities/step/step.d.ts +6 -5
- package/dist/types/exports.d.ts +2 -1
- package/dist/types/exports.d.ts.map +1 -1
- package/dist/types/form/build-responses.d.ts +2 -1
- package/dist/types/form/create-field-evaluator.d.ts +4 -3
- package/dist/types/form/create-field-validation.d.ts +5 -4
- package/dist/types/form/create-form.d.ts +7 -6
- package/dist/types/form/create-submit-pipeline.d.ts +7 -6
- package/dist/types/form/get-step-fields.d.ts +3 -2
- package/dist/types/lifecycle/condition-selection.d.ts +14 -4
- package/dist/types/lifecycle/lifecycle.d.ts +4 -3
- package/dist/types/lifecycle/rules-logic.d.ts +4 -3
- package/dist/types/lifecycle/state-snapshot.d.ts +4 -3
- package/dist/types/picked.d.ts +64 -62
- package/dist/types/picked.d.ts.map +1 -1
- package/dist/types/ping.d.ts +1 -1
- package/dist/types/ping.d.ts.map +1 -1
- package/dist/types/plugins/create-experience/create-experience.d.ts +9 -8
- package/dist/types/plugins/create-experience/create-experience.d.ts.map +1 -1
- package/dist/types/plugins/create-experience/plugin.d.ts +3 -2
- package/dist/types/plugins/create-experience/plugin.d.ts.map +1 -1
- package/dist/types/plugins/create-experience/serialization.d.ts.map +1 -1
- package/dist/types/plugins/dom-observer/plugin.d.ts +1 -1
- package/dist/types/plugins/dynamic-experiences/plugin.d.ts +3 -3
- package/dist/types/plugins/dynamic-experiences/types.d.ts +8 -5
- package/dist/types/plugins/experience-browser-storage/plugin.d.ts +1 -1
- package/dist/types/plugins/experience-browser-storage/plugin.d.ts.map +1 -1
- package/dist/types/plugins/history-browser-storage/plugin.d.ts +1 -1
- package/dist/types/plugins/history-browser-storage/plugin.d.ts.map +1 -1
- package/dist/types/plugins/responses-browser-storage/plugin.d.ts +1 -1
- package/dist/types/plugins/responses-browser-storage/plugin.d.ts.map +1 -1
- package/dist/types/plugins/responses-indexeddb/plugin.d.ts +1 -1
- package/dist/types/plugins/single-page-app/plugin.d.ts +1 -1
- package/dist/types/plugins/single-page-app/plugin.d.ts.map +1 -1
- package/dist/types/plugins/web-components/plugin.d.ts +2 -1
- package/dist/types/plugins/window-object/plugin.d.ts +2 -2
- package/dist/types/preview-channel.d.ts +5 -1
- package/dist/types/preview-receiver/index.d.ts +18 -1
- package/dist/types/preview-receiver/protocol.d.ts +10 -1
- package/dist/types/server/index.d.ts +3 -3
- package/dist/types/server/serialize.d.ts +2 -2
- package/dist/types/utils/dom.d.ts +2 -1
- package/dist/types/utils/events.d.ts +6 -6
- package/dist/types/utils/events.d.ts.map +1 -1
- package/dist/types/utils/global-obj.d.ts +1 -1
- package/dist/types/utils/global-obj.d.ts.map +1 -1
- package/dist/types/utils/id.d.ts.map +1 -1
- package/dist/types/utils/logger.d.ts +2 -1
- package/dist/types/utils/logger.d.ts.map +1 -1
- package/dist/types/utils/node-tree/get-next-node-from-action.d.ts +2 -1
- package/dist/types/utils/node-tree/get-next-node-from-condition.d.ts +2 -1
- package/dist/types/utils/node-tree/get-next-node.d.ts +1 -1
- package/dist/types/utils/node-tree/get-next-node.d.ts.map +1 -1
- package/dist/types/utils/node-tree/get-origin-node.d.ts +1 -1
- package/dist/types/utils/node-tree/get-origin-node.d.ts.map +1 -1
- package/dist/types/utils/node-tree/min-max-steps-to-leaf.d.ts +1 -1
- package/dist/types/utils/node-tree/min-max-steps-to-leaf.d.ts.map +1 -1
- package/dist/types/utils/node-tree/min-max-steps-to-origin.d.ts +1 -1
- package/dist/types/utils/node-tree/min-max-steps-to-origin.d.ts.map +1 -1
- package/dist/types/utils/node-tree/next-reachable-step-nodes.d.ts +1 -1
- package/dist/types/utils/node-tree/next-reachable-step-nodes.d.ts.map +1 -1
- package/dist/types/utils/nodes.d.ts +1 -1
- package/dist/types/utils/picked-config.d.ts +2 -1
- package/dist/types/utils/rules-logic.d.ts +6 -2
- package/dist/types/utils/storage.d.ts +1 -1
- package/dist/types/utils/storage.d.ts.map +1 -1
- package/package.json +22 -23
- package/dist/api-CoUkIxIr.cjs +0 -1
- package/dist/api-ahdSBQ3Y.js +0 -40
- package/dist/experience-instance-DurC3pE-.cjs +0 -1
- package/dist/plugin-CASdGtTX.cjs +0 -1
|
@@ -2,13 +2,16 @@
|
|
|
2
2
|
layout: docs
|
|
3
3
|
---
|
|
4
4
|
|
|
5
|
-
#
|
|
5
|
+
# Errors
|
|
6
6
|
|
|
7
|
-
The Picked API returns
|
|
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
|
|
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
|
|
31
|
-
| `path` | The path to the field in the query that
|
|
32
|
-
| `extensions.code` |
|
|
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
|
-
|
|
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
|
|
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
|
|
43
|
-
| `EMAIL_NOT_CONFIRMED` | The account
|
|
44
|
-
| `USER_NOT_FOUND` |
|
|
45
|
-
| `USER_ALREADY_EXISTS` | An account with
|
|
46
|
-
| `WEAK_PASSWORD` | The
|
|
47
|
-
| `INVALID_TOKEN` | The token
|
|
48
|
-
| `TOKEN_EXPIRED` | The token
|
|
49
|
-
| `MFA_CHALLENGE_FAILED` | The MFA verification code is
|
|
50
|
-
| `MFA_FACTOR_NOT_FOUND` |
|
|
51
|
-
| `OAUTH_PROVIDER_ERROR` |
|
|
52
|
-
| `RATE_LIMITED` | Too many requests in a short period.
|
|
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
|
-
|
|
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` |
|
|
84
|
-
| `FORBIDDEN` | The caller is authenticated but does not
|
|
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:
|
|
92
|
+
### Example: the scope is not enough
|
|
87
93
|
|
|
88
|
-
A caller
|
|
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
|
|
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` |
|
|
116
|
-
| `ORGANIZATION_ALREADY_EXISTS` | An organization with
|
|
117
|
-
| `SLUG_ALREADY_EXISTS` |
|
|
118
|
-
| `INVALID_SLUG` | The slug
|
|
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` |
|
|
125
|
-
| `MEMBER_ALREADY_EXISTS` | The user is
|
|
126
|
-
| `CANNOT_REMOVE_PRIMARY_OWNER` |
|
|
127
|
-
| `CANNOT_CHANGE_OWN_ROLE` | A member cannot change
|
|
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` |
|
|
134
|
-
| `INVITATION_EXPIRED` | The invitation
|
|
135
|
-
| `INVITATION_ALREADY_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` |
|
|
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.
|
|
148
|
-
| `INTERNAL_ERROR` |
|
|
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
|
-
##
|
|
158
|
+
## What your code must do
|
|
151
159
|
|
|
152
|
-
###
|
|
160
|
+
### Look for an error first
|
|
153
161
|
|
|
154
|
-
|
|
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
|
-
//
|
|
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
|
|
188
|
+
// Wait, then try again.
|
|
181
189
|
break;
|
|
182
190
|
default:
|
|
183
|
-
//
|
|
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
|
|
198
|
+
### Refresh a token that expired
|
|
191
199
|
|
|
192
|
-
|
|
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
|
-
//
|
|
208
|
+
// Try the first request again with the new access token.
|
|
199
209
|
} else {
|
|
200
|
-
//
|
|
210
|
+
// Send the user to the sign-in page.
|
|
201
211
|
}
|
|
202
212
|
}
|
|
203
213
|
```
|
|
204
214
|
|
|
205
|
-
###
|
|
215
|
+
### Read data that is only part complete
|
|
206
216
|
|
|
207
|
-
GraphQL can return
|
|
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
|
-
###
|
|
223
|
+
### Try again after a transient failure
|
|
210
224
|
|
|
211
|
-
`INTERNAL_ERROR` and `RATE_LIMITED` are transient. For `RATE_LIMITED`,
|
|
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.
|
package/dist/docs/api/index.md
CHANGED
|
@@ -2,17 +2,21 @@
|
|
|
2
2
|
layout: docs
|
|
3
3
|
---
|
|
4
4
|
|
|
5
|
-
#
|
|
5
|
+
# Start here
|
|
6
6
|
|
|
7
|
-
This guide
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
68
|
+
This response means that the API is ready.
|
|
63
69
|
|
|
64
70
|
## Authenticate
|
|
65
71
|
|
|
66
|
-
Most operations
|
|
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`
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
##
|
|
185
|
+
## Errors
|
|
171
186
|
|
|
172
|
-
When something goes wrong, the API returns errors in the standard GraphQL
|
|
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
|
-
|
|
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
|
-
|
|
209
|
+
You can now authenticate and make a request. These guides go into more detail:
|
|
193
210
|
|
|
194
|
-
- [Authentication](./authentication.md) — email
|
|
195
|
-
|
|
196
|
-
- [
|
|
197
|
-
|
|
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.
|
|
@@ -4,11 +4,13 @@ layout: docs
|
|
|
4
4
|
|
|
5
5
|
# Pagination
|
|
6
6
|
|
|
7
|
-
The Picked API
|
|
7
|
+
The Picked API paginates every list field that can return a large result. It uses a
|
|
8
|
+
cursor, in the Relay-style pattern. This guide covers the connection pattern and
|
|
9
|
+
shows how to move through a result.
|
|
8
10
|
|
|
9
11
|
## Connection pattern
|
|
10
12
|
|
|
11
|
-
|
|
13
|
+
A paginated field returns a **connection** object with this shape:
|
|
12
14
|
|
|
13
15
|
```graphql
|
|
14
16
|
type ExampleConnection {
|
|
@@ -32,20 +34,21 @@ type PageInfo {
|
|
|
32
34
|
|
|
33
35
|
| Field | Description |
|
|
34
36
|
|-------|-------------|
|
|
35
|
-
| `edges` | An ordered list of edges
|
|
36
|
-
| `pageInfo.hasNextPage` | `true`
|
|
37
|
-
| `pageInfo.hasPreviousPage` | `true`
|
|
38
|
-
| `pageInfo.startCursor` |
|
|
39
|
-
| `pageInfo.endCursor` |
|
|
40
|
-
| `totalCount` |
|
|
37
|
+
| `edges` | An ordered list of edges. Each edge holds a `cursor` and a `node`, and the `node` holds the data. |
|
|
38
|
+
| `pageInfo.hasNextPage` | `true` when more results come after the last edge |
|
|
39
|
+
| `pageInfo.hasPreviousPage` | `true` when results come before the first edge |
|
|
40
|
+
| `pageInfo.startCursor` | The cursor of the first edge on the current page |
|
|
41
|
+
| `pageInfo.endCursor` | The cursor of the last edge on the current page |
|
|
42
|
+
| `totalCount` | The number of items on every page together |
|
|
41
43
|
|
|
42
44
|
## Forward pagination with `first` and `after`
|
|
43
45
|
|
|
44
|
-
To
|
|
46
|
+
To move forward through the results, use two arguments. The `first` argument gives
|
|
47
|
+
the page size. The `after` argument gives the cursor to start after.
|
|
45
48
|
|
|
46
49
|
### First page
|
|
47
50
|
|
|
48
|
-
|
|
51
|
+
Leave the `after` argument out to read the first page:
|
|
49
52
|
|
|
50
53
|
```graphql
|
|
51
54
|
query {
|
|
@@ -89,16 +92,17 @@ query {
|
|
|
89
92
|
}
|
|
90
93
|
```
|
|
91
94
|
|
|
92
|
-
##
|
|
95
|
+
## Check for more pages
|
|
93
96
|
|
|
94
|
-
|
|
97
|
+
Read the `hasNextPage` field before you ask for the next page:
|
|
95
98
|
|
|
96
|
-
- `hasNextPage: true` — more results are available
|
|
97
|
-
|
|
99
|
+
- `hasNextPage: true` — more results are available. Pass `endCursor` as the `after`
|
|
100
|
+
argument of the next request.
|
|
101
|
+
- `hasNextPage: false` — you read every result. Stop here.
|
|
98
102
|
|
|
99
103
|
## Full paginated query example
|
|
100
104
|
|
|
101
|
-
|
|
105
|
+
This example reads every member of an organization, one page at a time:
|
|
102
106
|
|
|
103
107
|
```graphql
|
|
104
108
|
# Page 1
|
|
@@ -154,7 +158,8 @@ Continue until `hasNextPage` returns `false`.
|
|
|
154
158
|
|
|
155
159
|
## Paginated fields
|
|
156
160
|
|
|
157
|
-
|
|
161
|
+
These fields in the schema return a connection type. They all take the `first` and
|
|
162
|
+
`after` arguments:
|
|
158
163
|
|
|
159
164
|
| Parent Type | Field | Connection Type |
|
|
160
165
|
|------------|-------|----------------|
|
|
@@ -170,7 +175,12 @@ All of these fields accept the same `first` and `after` arguments and return the
|
|
|
170
175
|
|
|
171
176
|
## Tips
|
|
172
177
|
|
|
173
|
-
- **
|
|
174
|
-
|
|
175
|
-
- **
|
|
176
|
-
|
|
178
|
+
- **The default page size** — the API applies a default limit when you leave `first`
|
|
179
|
+
out. Set `first` yourself to make sure that each page has the size you expect.
|
|
180
|
+
- **A cursor is opaque** — treat a cursor string as an opaque token. Do not parse it,
|
|
181
|
+
build it, or change it. Pass it back exactly as the API gave it.
|
|
182
|
+
- **The order is stable** — a connection returns its results in a stable order. The
|
|
183
|
+
same cursor always points at the same position in the result.
|
|
184
|
+
- **Use them with other fields** — you can use the pagination arguments together with
|
|
185
|
+
other query fields. One request that reads an organization and the first page of
|
|
186
|
+
its members is common, and it is efficient.
|
package/dist/docs/api-keys.mdx
CHANGED
|
@@ -6,7 +6,8 @@ import { Screenshot } from '../../components/Screenshot'
|
|
|
6
6
|
|
|
7
7
|
# API keys
|
|
8
8
|
|
|
9
|
-
API
|
|
9
|
+
An API key lets your code reach the Picked API. Use one for a server-side integration
|
|
10
|
+
or for a CI/CD workflow.
|
|
10
11
|
|
|
11
12
|
<Screenshot
|
|
12
13
|
route="/{orgSlug}/api-keys"
|
|
@@ -21,11 +22,12 @@ API keys let you access Picked's API programmatically. Use them for server-side
|
|
|
21
22
|
4. Enter a description.
|
|
22
23
|
5. Click **Create**.
|
|
23
24
|
|
|
24
|
-
Copy the key immediately
|
|
25
|
+
Copy the key immediately. The dashboard does not show it again.
|
|
25
26
|
|
|
26
|
-
##
|
|
27
|
+
## Use an API key
|
|
27
28
|
|
|
28
|
-
|
|
29
|
+
Put your API key in the `Authorization` header of each request to the
|
|
30
|
+
[Picked API](/api).
|
|
29
31
|
|
|
30
32
|
```
|
|
31
33
|
Authorization: Bearer your-api-key
|
|
@@ -33,8 +35,11 @@ Authorization: Bearer your-api-key
|
|
|
33
35
|
|
|
34
36
|
## Scopes
|
|
35
37
|
|
|
36
|
-
API
|
|
38
|
+
An API key carries scopes, and the scopes limit what the key can reach. Choose the
|
|
39
|
+
scopes that your integration needs when you create the key. A key can do only the
|
|
40
|
+
operations that its scopes permit.
|
|
37
41
|
|
|
38
42
|
## Revoke a key
|
|
39
43
|
|
|
40
|
-
|
|
44
|
+
You can revoke a key at any time from the API keys list. Click the revoke button next
|
|
45
|
+
to the key. A revoked key stops work immediately.
|
|
@@ -4,17 +4,21 @@ layout: docs
|
|
|
4
4
|
|
|
5
5
|
# Chrome Extension
|
|
6
6
|
|
|
7
|
-
The Picked Developer Tools
|
|
7
|
+
The extension for the Picked Developer Tools adds a Picked panel to Chrome DevTools.
|
|
8
|
+
Use the panel to check and debug your Picked integration on any page that runs the
|
|
9
|
+
SDK.
|
|
8
10
|
|
|
9
11
|

|
|
10
12
|
|
|
11
13
|
## Installation
|
|
12
14
|
|
|
13
|
-
Install
|
|
15
|
+
Install the extension from the
|
|
16
|
+
[Chrome Web Store](https://chromewebstore.google.com/detail/picked-developer-tools/nimmdamgnhlhdpngnalkpmnleebngidh).
|
|
14
17
|
|
|
15
18
|
## Setup
|
|
16
19
|
|
|
17
|
-
Add the `svelteDevtoolsPlugin` to your Picked client
|
|
20
|
+
Add the `svelteDevtoolsPlugin` to your Picked client. The extension then connects to
|
|
21
|
+
the SDK.
|
|
18
22
|
|
|
19
23
|
```typescript
|
|
20
24
|
import { createPicked } from '@picked/client'
|
|
@@ -30,7 +34,16 @@ const picked = createPicked({
|
|
|
30
34
|
|
|
31
35
|
## Custom Tools
|
|
32
36
|
|
|
33
|
-
You can extend the Developer Tools with your own
|
|
37
|
+
You can extend the Developer Tools with your own tools for one project. They appear
|
|
38
|
+
under a **Custom Tools** tab.
|
|
39
|
+
|
|
40
|
+
Each tool is a framework-adapted component. Write it with the same
|
|
41
|
+
`adaptReactComponent` or `adaptSvelteComponent` helper that you already use. You can
|
|
42
|
+
therefore build the tool in the framework of your application.
|
|
43
|
+
|
|
44
|
+
The tool receives the live Picked client, exactly as an experience component does. In
|
|
45
|
+
React, use `usePicked()`. In Svelte, use `getPicked()`. A tool can read client state
|
|
46
|
+
and call client methods.
|
|
34
47
|
|
|
35
48
|
```typescript
|
|
36
49
|
import { createPicked } from '@picked/client'
|
|
@@ -48,4 +61,8 @@ const picked = createPicked({
|
|
|
48
61
|
})
|
|
49
62
|
```
|
|
50
63
|
|
|
51
|
-
|
|
64
|
+
Svelte renders the Developer Tools shell, so you must also register the framework of
|
|
65
|
+
your tool on the client. A React application needs `reactFrameworkPlugin` for the
|
|
66
|
+
tool, together with the Svelte framework and `svelteDevtoolsPlugin` for the shell.
|
|
67
|
+
When the framework of a tool is not registered, the tab of that tool shows a warning
|
|
68
|
+
and no content.
|