@primitivedotdev/sdk 0.18.0 → 0.19.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +165 -65
- package/dist/api/generated/index.js +1 -1
- package/dist/api/generated/sdk.gen.js +49 -1
- package/dist/api/index.d.ts +2 -2
- package/dist/api/index.js +39 -7
- package/dist/{api-DrAZhxS-.js → api-C5VR_Opg.js} +81 -7
- package/dist/contract/index.d.ts +2 -2
- package/dist/contract/index.js +1 -1
- package/dist/{index-CbEivn3S.d.ts → index-CDlwyxdp.d.ts} +7 -7
- package/dist/{index-CHWqMBs6.d.ts → index-oRkCqj6u.d.ts} +195 -13
- package/dist/index.d.ts +2 -2
- package/dist/index.js +2 -2
- package/dist/oclif/api-command.js +46 -6
- package/dist/oclif/auth.js +168 -0
- package/dist/oclif/commands/emails-latest.js +18 -4
- package/dist/oclif/commands/login.js +233 -0
- package/dist/oclif/commands/logout.js +87 -0
- package/dist/oclif/commands/send.js +29 -7
- package/dist/oclif/commands/whoami.js +18 -4
- package/dist/oclif/fish-completion.js +1 -1
- package/dist/oclif/index.js +6 -0
- package/dist/openapi/openapi.generated.js +385 -2
- package/dist/openapi/operations.generated.js +178 -1
- package/dist/webhook/index.d.ts +1 -1
- package/dist/webhook/index.js +1 -1
- package/dist/{webhook-zkN4wUTs.js → webhook-rUjGV6Zu.js} +4 -4
- package/oclif.manifest.json +291 -38
- package/package.json +5 -2
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { Command, Errors, Flags } from "@oclif/core";
|
|
2
2
|
import { listDomains, sendEmail } from "../../api/generated/sdk.gen.js";
|
|
3
3
|
import { PrimitiveApiClient } from "../../api/index.js";
|
|
4
|
-
import { extractErrorCode, extractErrorPayload, formatErrorPayload, runWithTiming, TIME_FLAG_DESCRIPTION, writeErrorWithHints, } from "../api-command.js";
|
|
4
|
+
import { API_ERROR_CODES, extractErrorCode, extractErrorPayload, formatErrorPayload, removeStaleSavedCredentialOnUnauthorized, runWithTiming, TIME_FLAG_DESCRIPTION, writeErrorWithHints, } from "../api-command.js";
|
|
5
|
+
import { resolveCliAuth } from "../auth.js";
|
|
5
6
|
// `primitive send` is the agent-grade shortcut for the most common
|
|
6
7
|
// case: send a fresh outbound email. It wraps `sending:send-email`
|
|
7
8
|
// with two ergonomic defaults that the underlying operation can't
|
|
@@ -53,7 +54,7 @@ function deriveSubject(body) {
|
|
|
53
54
|
function isVerifiedDomain(domain) {
|
|
54
55
|
return domain.is_active === true;
|
|
55
56
|
}
|
|
56
|
-
async function pickDefaultFromAddress(apiClient) {
|
|
57
|
+
async function pickDefaultFromAddress(apiClient, authFailureContext) {
|
|
57
58
|
const result = await listDomains({
|
|
58
59
|
client: apiClient.client,
|
|
59
60
|
responseStyle: "fields",
|
|
@@ -65,8 +66,12 @@ async function pickDefaultFromAddress(apiClient) {
|
|
|
65
66
|
// Surface the auth hint via writeErrorWithHints and bail with
|
|
66
67
|
// a focused message instead of the verbose "underlying error"
|
|
67
68
|
// wrapping.
|
|
68
|
-
if (extractErrorCode(errorPayload) ===
|
|
69
|
+
if (extractErrorCode(errorPayload) === API_ERROR_CODES.unauthorized) {
|
|
69
70
|
writeErrorWithHints(errorPayload);
|
|
71
|
+
removeStaleSavedCredentialOnUnauthorized({
|
|
72
|
+
...authFailureContext,
|
|
73
|
+
payload: errorPayload,
|
|
74
|
+
});
|
|
70
75
|
// exit: 1 to match the run() unauthorized path (which uses
|
|
71
76
|
// `process.exitCode = 1`). oclif's CLIError defaults to 2,
|
|
72
77
|
// so without this override the same "unauthorized" condition
|
|
@@ -107,7 +112,7 @@ class SendCommand extends Command {
|
|
|
107
112
|
];
|
|
108
113
|
static flags = {
|
|
109
114
|
"api-key": Flags.string({
|
|
110
|
-
description: "Primitive API key (defaults to PRIMITIVE_API_KEY)",
|
|
115
|
+
description: "Primitive API key (defaults to PRIMITIVE_API_KEY or saved `primitive login` credentials)",
|
|
111
116
|
env: "PRIMITIVE_API_KEY",
|
|
112
117
|
}),
|
|
113
118
|
"base-url": Flags.string({
|
|
@@ -149,11 +154,23 @@ class SendCommand extends Command {
|
|
|
149
154
|
throw new Errors.CLIError("Either --body or --html (or both) is required.");
|
|
150
155
|
}
|
|
151
156
|
await runWithTiming(flags.time, async () => {
|
|
152
|
-
const
|
|
157
|
+
const baseUrlOverridden = flags["base-url"] !== undefined;
|
|
158
|
+
const auth = resolveCliAuth({
|
|
153
159
|
apiKey: flags["api-key"],
|
|
154
160
|
baseUrl: flags["base-url"],
|
|
161
|
+
configDir: this.config.configDir,
|
|
162
|
+
});
|
|
163
|
+
const apiClient = new PrimitiveApiClient({
|
|
164
|
+
apiKey: auth.apiKey,
|
|
165
|
+
baseUrl: auth.baseUrl,
|
|
155
166
|
});
|
|
156
|
-
const
|
|
167
|
+
const authFailureContext = {
|
|
168
|
+
auth,
|
|
169
|
+
baseUrlOverridden,
|
|
170
|
+
configDir: this.config.configDir,
|
|
171
|
+
};
|
|
172
|
+
const from = flags.from ??
|
|
173
|
+
(await pickDefaultFromAddress(apiClient, authFailureContext));
|
|
157
174
|
const subject = flags.subject ?? (flags.body ? deriveSubject(flags.body) : "Message");
|
|
158
175
|
const result = await sendEmail({
|
|
159
176
|
body: {
|
|
@@ -174,7 +191,12 @@ class SendCommand extends Command {
|
|
|
174
191
|
responseStyle: "fields",
|
|
175
192
|
});
|
|
176
193
|
if (result.error) {
|
|
177
|
-
|
|
194
|
+
const errorPayload = extractErrorPayload(result.error);
|
|
195
|
+
writeErrorWithHints(errorPayload);
|
|
196
|
+
removeStaleSavedCredentialOnUnauthorized({
|
|
197
|
+
...authFailureContext,
|
|
198
|
+
payload: errorPayload,
|
|
199
|
+
});
|
|
178
200
|
process.exitCode = 1;
|
|
179
201
|
return;
|
|
180
202
|
}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { Command, Errors, Flags } from "@oclif/core";
|
|
2
2
|
import { getAccount } from "../../api/generated/sdk.gen.js";
|
|
3
3
|
import { PrimitiveApiClient } from "../../api/index.js";
|
|
4
|
-
import { extractErrorPayload, runWithTiming, TIME_FLAG_DESCRIPTION, writeErrorWithHints, } from "../api-command.js";
|
|
4
|
+
import { extractErrorPayload, removeStaleSavedCredentialOnUnauthorized, runWithTiming, TIME_FLAG_DESCRIPTION, writeErrorWithHints, } from "../api-command.js";
|
|
5
|
+
import { resolveCliAuth } from "../auth.js";
|
|
5
6
|
// `primitive whoami` is the credentials smoke-test the AGX
|
|
6
7
|
// walkthrough kept asking for. Before this command, a user with a
|
|
7
8
|
// suspect API key had no fast way to verify "is my key live and
|
|
@@ -21,7 +22,7 @@ class WhoamiCommand extends Command {
|
|
|
21
22
|
];
|
|
22
23
|
static flags = {
|
|
23
24
|
"api-key": Flags.string({
|
|
24
|
-
description: "Primitive API key (defaults to PRIMITIVE_API_KEY)",
|
|
25
|
+
description: "Primitive API key (defaults to PRIMITIVE_API_KEY or saved `primitive login` credentials)",
|
|
25
26
|
env: "PRIMITIVE_API_KEY",
|
|
26
27
|
}),
|
|
27
28
|
"base-url": Flags.string({
|
|
@@ -35,16 +36,29 @@ class WhoamiCommand extends Command {
|
|
|
35
36
|
async run() {
|
|
36
37
|
const { flags } = await this.parse(WhoamiCommand);
|
|
37
38
|
await runWithTiming(flags.time, async () => {
|
|
38
|
-
const
|
|
39
|
+
const baseUrlOverridden = flags["base-url"] !== undefined;
|
|
40
|
+
const auth = resolveCliAuth({
|
|
39
41
|
apiKey: flags["api-key"],
|
|
40
42
|
baseUrl: flags["base-url"],
|
|
43
|
+
configDir: this.config.configDir,
|
|
44
|
+
});
|
|
45
|
+
const apiClient = new PrimitiveApiClient({
|
|
46
|
+
apiKey: auth.apiKey,
|
|
47
|
+
baseUrl: auth.baseUrl,
|
|
41
48
|
});
|
|
42
49
|
const result = await getAccount({
|
|
43
50
|
client: apiClient.client,
|
|
44
51
|
responseStyle: "fields",
|
|
45
52
|
});
|
|
46
53
|
if (result.error) {
|
|
47
|
-
|
|
54
|
+
const errorPayload = extractErrorPayload(result.error);
|
|
55
|
+
writeErrorWithHints(errorPayload);
|
|
56
|
+
removeStaleSavedCredentialOnUnauthorized({
|
|
57
|
+
auth,
|
|
58
|
+
baseUrlOverridden,
|
|
59
|
+
configDir: this.config.configDir,
|
|
60
|
+
payload: errorPayload,
|
|
61
|
+
});
|
|
48
62
|
process.exitCode = 1;
|
|
49
63
|
return;
|
|
50
64
|
}
|
|
@@ -73,7 +73,7 @@ export function renderFishCompletion(binName) {
|
|
|
73
73
|
]) {
|
|
74
74
|
lines.push(`complete -c ${binName} -n '${operationCondition(operation).replace(BIN_PLACEHOLDER, binName)}' -l '${fishEscape(parameter.name.replace(/_/g, "-"))}' -r -d '${fishEscape(parameter.description ?? parameter.name)}'`);
|
|
75
75
|
}
|
|
76
|
-
lines.push(`complete -c ${binName} -n '${operationCondition(operation).replace(BIN_PLACEHOLDER, binName)}' -l 'api-key' -r -d 'Primitive API key (defaults to PRIMITIVE_API_KEY)'`, `complete -c ${binName} -n '${operationCondition(operation).replace(BIN_PLACEHOLDER, binName)}' -l 'base-url' -r -d 'API base URL (defaults to PRIMITIVE_API_URL or production)'`);
|
|
76
|
+
lines.push(`complete -c ${binName} -n '${operationCondition(operation).replace(BIN_PLACEHOLDER, binName)}' -l 'api-key' -r -d 'Primitive API key (defaults to PRIMITIVE_API_KEY or saved primitive login credentials)'`, `complete -c ${binName} -n '${operationCondition(operation).replace(BIN_PLACEHOLDER, binName)}' -l 'base-url' -r -d 'API base URL (defaults to PRIMITIVE_API_URL or production)'`);
|
|
77
77
|
if (operation.hasJsonBody) {
|
|
78
78
|
lines.push(`complete -c ${binName} -n '${operationCondition(operation).replace(BIN_PLACEHOLDER, binName)}' -l 'body' -r -d 'JSON request body'`, `complete -c ${binName} -n '${operationCondition(operation).replace(BIN_PLACEHOLDER, binName)}' -l 'body-file' -r -d 'Path to a JSON file used as the request body'`);
|
|
79
79
|
}
|
package/dist/oclif/index.js
CHANGED
|
@@ -2,6 +2,8 @@ import { Args, Command, Errors } from "@oclif/core";
|
|
|
2
2
|
import { operationManifest, } from "../openapi/index.js";
|
|
3
3
|
import { createOperationCommand } from "./api-command.js";
|
|
4
4
|
import EmailsLatestCommand from "./commands/emails-latest.js";
|
|
5
|
+
import LoginCommand from "./commands/login.js";
|
|
6
|
+
import LogoutCommand from "./commands/logout.js";
|
|
5
7
|
import SendCommand from "./commands/send.js";
|
|
6
8
|
import WhoamiCommand from "./commands/whoami.js";
|
|
7
9
|
import { renderFishCompletion } from "./fish-completion.js";
|
|
@@ -116,6 +118,10 @@ export const COMMANDS = {
|
|
|
116
118
|
// operation stays available under sending:send-email for callers
|
|
117
119
|
// who want every flag.
|
|
118
120
|
send: SendCommand,
|
|
121
|
+
// `login` creates and stores an org-scoped CLI API key via browser approval.
|
|
122
|
+
login: LoginCommand,
|
|
123
|
+
// `logout` revokes the saved CLI API key and removes local credentials.
|
|
124
|
+
logout: LogoutCommand,
|
|
119
125
|
// `whoami` is the credentials smoke test. Prints the account the
|
|
120
126
|
// current API key authenticates as. AGX walkthroughs kept
|
|
121
127
|
// wanting this before risking a real call against a possibly-
|
|
@@ -31,6 +31,10 @@ export const openapiDocument = {
|
|
|
31
31
|
}
|
|
32
32
|
],
|
|
33
33
|
"tags": [
|
|
34
|
+
{
|
|
35
|
+
"name": "CLI",
|
|
36
|
+
"description": "Browser-assisted CLI authentication"
|
|
37
|
+
},
|
|
34
38
|
{
|
|
35
39
|
"name": "Account",
|
|
36
40
|
"description": "Manage your account settings, storage, and webhook secret"
|
|
@@ -61,6 +65,251 @@ export const openapiDocument = {
|
|
|
61
65
|
}
|
|
62
66
|
],
|
|
63
67
|
"paths": {
|
|
68
|
+
"/cli/login/start": {
|
|
69
|
+
"post": {
|
|
70
|
+
"operationId": "startCliLogin",
|
|
71
|
+
"summary": "Start CLI browser login",
|
|
72
|
+
"description": "Starts a browser-assisted CLI login session. The response includes a\ndevice code for polling and a user code that the user approves in the\nbrowser. This endpoint does not require an API key.\n",
|
|
73
|
+
"tags": [
|
|
74
|
+
"CLI"
|
|
75
|
+
],
|
|
76
|
+
"security": [],
|
|
77
|
+
"requestBody": {
|
|
78
|
+
"required": false,
|
|
79
|
+
"content": {
|
|
80
|
+
"application/json": {
|
|
81
|
+
"schema": {
|
|
82
|
+
"$ref": "#/components/schemas/StartCliLoginInput"
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
},
|
|
87
|
+
"responses": {
|
|
88
|
+
"201": {
|
|
89
|
+
"description": "CLI login session created",
|
|
90
|
+
"headers": {
|
|
91
|
+
"Cache-Control": {
|
|
92
|
+
"schema": {
|
|
93
|
+
"type": "string"
|
|
94
|
+
},
|
|
95
|
+
"description": "Always `no-store`"
|
|
96
|
+
}
|
|
97
|
+
},
|
|
98
|
+
"content": {
|
|
99
|
+
"application/json": {
|
|
100
|
+
"schema": {
|
|
101
|
+
"allOf": [
|
|
102
|
+
{
|
|
103
|
+
"$ref": "#/components/schemas/SuccessEnvelope"
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
"type": "object",
|
|
107
|
+
"properties": {
|
|
108
|
+
"data": {
|
|
109
|
+
"$ref": "#/components/schemas/CliLoginStartResult"
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
]
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
},
|
|
118
|
+
"400": {
|
|
119
|
+
"$ref": "#/components/responses/ValidationError"
|
|
120
|
+
},
|
|
121
|
+
"429": {
|
|
122
|
+
"$ref": "#/components/responses/RateLimited"
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
},
|
|
127
|
+
"/cli/login/poll": {
|
|
128
|
+
"post": {
|
|
129
|
+
"operationId": "pollCliLogin",
|
|
130
|
+
"summary": "Poll CLI browser login",
|
|
131
|
+
"description": "Polls a CLI login session until the browser approval either succeeds,\nis denied, expires, or is polled too quickly. The API key is generated\nonly after approval and is returned exactly once.\n",
|
|
132
|
+
"tags": [
|
|
133
|
+
"CLI"
|
|
134
|
+
],
|
|
135
|
+
"security": [],
|
|
136
|
+
"requestBody": {
|
|
137
|
+
"required": true,
|
|
138
|
+
"content": {
|
|
139
|
+
"application/json": {
|
|
140
|
+
"schema": {
|
|
141
|
+
"$ref": "#/components/schemas/PollCliLoginInput"
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
},
|
|
146
|
+
"responses": {
|
|
147
|
+
"200": {
|
|
148
|
+
"description": "CLI login approved and API key created",
|
|
149
|
+
"headers": {
|
|
150
|
+
"Cache-Control": {
|
|
151
|
+
"schema": {
|
|
152
|
+
"type": "string"
|
|
153
|
+
},
|
|
154
|
+
"description": "Always `no-store`"
|
|
155
|
+
}
|
|
156
|
+
},
|
|
157
|
+
"content": {
|
|
158
|
+
"application/json": {
|
|
159
|
+
"schema": {
|
|
160
|
+
"allOf": [
|
|
161
|
+
{
|
|
162
|
+
"$ref": "#/components/schemas/SuccessEnvelope"
|
|
163
|
+
},
|
|
164
|
+
{
|
|
165
|
+
"type": "object",
|
|
166
|
+
"properties": {
|
|
167
|
+
"data": {
|
|
168
|
+
"$ref": "#/components/schemas/CliLoginPollResult"
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
]
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
},
|
|
177
|
+
"400": {
|
|
178
|
+
"description": "Invalid request, pending authorization, slow polling, expired token, or invalid device code",
|
|
179
|
+
"headers": {
|
|
180
|
+
"Retry-After": {
|
|
181
|
+
"schema": {
|
|
182
|
+
"type": "integer"
|
|
183
|
+
},
|
|
184
|
+
"description": "Seconds to wait before polling again when the error code is `slow_down`"
|
|
185
|
+
}
|
|
186
|
+
},
|
|
187
|
+
"content": {
|
|
188
|
+
"application/json": {
|
|
189
|
+
"schema": {
|
|
190
|
+
"$ref": "#/components/schemas/ErrorResponse"
|
|
191
|
+
},
|
|
192
|
+
"examples": {
|
|
193
|
+
"authorization_pending": {
|
|
194
|
+
"summary": "Awaiting browser approval",
|
|
195
|
+
"value": {
|
|
196
|
+
"success": false,
|
|
197
|
+
"error": {
|
|
198
|
+
"code": "authorization_pending",
|
|
199
|
+
"message": "CLI login is still pending browser approval"
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
},
|
|
203
|
+
"expired_token": {
|
|
204
|
+
"summary": "Login session expired",
|
|
205
|
+
"value": {
|
|
206
|
+
"success": false,
|
|
207
|
+
"error": {
|
|
208
|
+
"code": "expired_token",
|
|
209
|
+
"message": "CLI login code expired; run primitive login again"
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
},
|
|
213
|
+
"invalid_device_code": {
|
|
214
|
+
"summary": "Unknown device code",
|
|
215
|
+
"value": {
|
|
216
|
+
"success": false,
|
|
217
|
+
"error": {
|
|
218
|
+
"code": "invalid_device_code",
|
|
219
|
+
"message": "Invalid CLI login device code"
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
},
|
|
223
|
+
"slow_down": {
|
|
224
|
+
"summary": "Polling too quickly",
|
|
225
|
+
"value": {
|
|
226
|
+
"success": false,
|
|
227
|
+
"error": {
|
|
228
|
+
"code": "slow_down",
|
|
229
|
+
"message": "Polling too quickly; slow down and retry later"
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
},
|
|
237
|
+
"403": {
|
|
238
|
+
"description": "CLI login was denied in the browser",
|
|
239
|
+
"content": {
|
|
240
|
+
"application/json": {
|
|
241
|
+
"schema": {
|
|
242
|
+
"$ref": "#/components/schemas/ErrorResponse"
|
|
243
|
+
},
|
|
244
|
+
"example": {
|
|
245
|
+
"success": false,
|
|
246
|
+
"error": {
|
|
247
|
+
"code": "access_denied",
|
|
248
|
+
"message": "CLI login was denied in the browser"
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
},
|
|
257
|
+
"/cli/logout": {
|
|
258
|
+
"post": {
|
|
259
|
+
"operationId": "cliLogout",
|
|
260
|
+
"summary": "Revoke the current CLI API key",
|
|
261
|
+
"description": "Revokes the API key used to authenticate the request. CLI clients use\nthis endpoint during `primitive logout` before removing local credentials.\n",
|
|
262
|
+
"tags": [
|
|
263
|
+
"CLI"
|
|
264
|
+
],
|
|
265
|
+
"requestBody": {
|
|
266
|
+
"required": false,
|
|
267
|
+
"content": {
|
|
268
|
+
"application/json": {
|
|
269
|
+
"schema": {
|
|
270
|
+
"$ref": "#/components/schemas/CliLogoutInput"
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
},
|
|
275
|
+
"responses": {
|
|
276
|
+
"200": {
|
|
277
|
+
"description": "CLI API key revoked",
|
|
278
|
+
"content": {
|
|
279
|
+
"application/json": {
|
|
280
|
+
"schema": {
|
|
281
|
+
"allOf": [
|
|
282
|
+
{
|
|
283
|
+
"$ref": "#/components/schemas/SuccessEnvelope"
|
|
284
|
+
},
|
|
285
|
+
{
|
|
286
|
+
"type": "object",
|
|
287
|
+
"properties": {
|
|
288
|
+
"data": {
|
|
289
|
+
"$ref": "#/components/schemas/CliLogoutResult"
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
]
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
},
|
|
298
|
+
"400": {
|
|
299
|
+
"$ref": "#/components/responses/ValidationError"
|
|
300
|
+
},
|
|
301
|
+
"401": {
|
|
302
|
+
"$ref": "#/components/responses/Unauthorized"
|
|
303
|
+
},
|
|
304
|
+
"403": {
|
|
305
|
+
"$ref": "#/components/responses/Forbidden"
|
|
306
|
+
},
|
|
307
|
+
"404": {
|
|
308
|
+
"$ref": "#/components/responses/NotFound"
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
},
|
|
64
313
|
"/account": {
|
|
65
314
|
"get": {
|
|
66
315
|
"operationId": "getAccount",
|
|
@@ -987,7 +1236,7 @@ export const openapiDocument = {
|
|
|
987
1236
|
"post": {
|
|
988
1237
|
"operationId": "discardEmailContent",
|
|
989
1238
|
"summary": "Discard email content",
|
|
990
|
-
"description": "Permanently deletes the email's raw bytes, parsed body (text + HTML),\nand attachments while preserving metadata (sender, recipient,\nsubject, timestamps, hashes, attachment manifest) for audit logs.\nIdempotent: a second call returns success with\n`already_discarded: true` and does no work.\n\n**Gated** on the customer's discard-content opt-in (managed in the\ndashboard at Settings > Webhooks). When the toggle is off, this\nendpoint returns `403` with code `discard_not_enabled` and a\nmessage pointing the human at the dashboard. There is intentionally\nno API to flip this toggle
|
|
1239
|
+
"description": "Permanently deletes the email's raw bytes, parsed body (text + HTML),\nand attachments while preserving metadata (sender, recipient,\nsubject, timestamps, hashes, attachment manifest) for audit logs.\nIdempotent: a second call returns success with\n`already_discarded: true` and does no work.\n\n**Gated** on the customer's discard-content opt-in (managed in the\ndashboard at Settings > Webhooks). When the toggle is off, this\nendpoint returns `403` with code `discard_not_enabled` and a\nmessage pointing the human at the dashboard. There is intentionally\nno API to flip this toggle. Opting in to a destructive,\nnon-reversible operation must be a deliberate human click in the\nUI.\n",
|
|
991
1240
|
"tags": [
|
|
992
1241
|
"Emails"
|
|
993
1242
|
],
|
|
@@ -2157,7 +2406,12 @@ export const openapiDocument = {
|
|
|
2157
2406
|
"outbound_response_malformed",
|
|
2158
2407
|
"outbound_relay_failed",
|
|
2159
2408
|
"discard_not_enabled",
|
|
2160
|
-
"inbound_not_repliable"
|
|
2409
|
+
"inbound_not_repliable",
|
|
2410
|
+
"authorization_pending",
|
|
2411
|
+
"slow_down",
|
|
2412
|
+
"access_denied",
|
|
2413
|
+
"expired_token",
|
|
2414
|
+
"invalid_device_code"
|
|
2161
2415
|
]
|
|
2162
2416
|
},
|
|
2163
2417
|
"message": {
|
|
@@ -2295,6 +2549,135 @@ export const openapiDocument = {
|
|
|
2295
2549
|
"subject"
|
|
2296
2550
|
]
|
|
2297
2551
|
},
|
|
2552
|
+
"StartCliLoginInput": {
|
|
2553
|
+
"type": "object",
|
|
2554
|
+
"additionalProperties": false,
|
|
2555
|
+
"properties": {
|
|
2556
|
+
"device_name": {
|
|
2557
|
+
"type": "string",
|
|
2558
|
+
"minLength": 1,
|
|
2559
|
+
"maxLength": 80,
|
|
2560
|
+
"description": "Human-readable device name shown during browser approval"
|
|
2561
|
+
},
|
|
2562
|
+
"metadata": {
|
|
2563
|
+
"type": "object",
|
|
2564
|
+
"additionalProperties": true,
|
|
2565
|
+
"description": "Optional client metadata stored with the login session; serialized JSON must be 2048 bytes or fewer"
|
|
2566
|
+
}
|
|
2567
|
+
}
|
|
2568
|
+
},
|
|
2569
|
+
"CliLoginStartResult": {
|
|
2570
|
+
"type": "object",
|
|
2571
|
+
"properties": {
|
|
2572
|
+
"device_code": {
|
|
2573
|
+
"type": "string",
|
|
2574
|
+
"description": "Opaque code used by the CLI to poll for approval"
|
|
2575
|
+
},
|
|
2576
|
+
"user_code": {
|
|
2577
|
+
"type": "string",
|
|
2578
|
+
"pattern": "^[BCDFGHJKLMNPQRSTVWXZ]{4}-[BCDFGHJKLMNPQRSTVWXZ]{4}$",
|
|
2579
|
+
"description": "Short code the user confirms in the browser"
|
|
2580
|
+
},
|
|
2581
|
+
"verification_uri": {
|
|
2582
|
+
"type": "string",
|
|
2583
|
+
"description": "Browser URL where the user approves the login"
|
|
2584
|
+
},
|
|
2585
|
+
"verification_uri_complete": {
|
|
2586
|
+
"type": "string",
|
|
2587
|
+
"description": "Browser URL with the user code prefilled"
|
|
2588
|
+
},
|
|
2589
|
+
"expires_in": {
|
|
2590
|
+
"type": "integer",
|
|
2591
|
+
"description": "Seconds until the login session expires"
|
|
2592
|
+
},
|
|
2593
|
+
"interval": {
|
|
2594
|
+
"type": "integer",
|
|
2595
|
+
"description": "Minimum seconds between poll requests"
|
|
2596
|
+
}
|
|
2597
|
+
},
|
|
2598
|
+
"required": [
|
|
2599
|
+
"device_code",
|
|
2600
|
+
"user_code",
|
|
2601
|
+
"verification_uri",
|
|
2602
|
+
"verification_uri_complete",
|
|
2603
|
+
"expires_in",
|
|
2604
|
+
"interval"
|
|
2605
|
+
]
|
|
2606
|
+
},
|
|
2607
|
+
"PollCliLoginInput": {
|
|
2608
|
+
"type": "object",
|
|
2609
|
+
"additionalProperties": false,
|
|
2610
|
+
"properties": {
|
|
2611
|
+
"device_code": {
|
|
2612
|
+
"type": "string",
|
|
2613
|
+
"minLength": 1
|
|
2614
|
+
}
|
|
2615
|
+
},
|
|
2616
|
+
"required": [
|
|
2617
|
+
"device_code"
|
|
2618
|
+
]
|
|
2619
|
+
},
|
|
2620
|
+
"CliLoginPollResult": {
|
|
2621
|
+
"type": "object",
|
|
2622
|
+
"properties": {
|
|
2623
|
+
"api_key": {
|
|
2624
|
+
"type": "string",
|
|
2625
|
+
"description": "Newly-created API key for CLI authentication"
|
|
2626
|
+
},
|
|
2627
|
+
"key_id": {
|
|
2628
|
+
"type": "string",
|
|
2629
|
+
"format": "uuid"
|
|
2630
|
+
},
|
|
2631
|
+
"key_prefix": {
|
|
2632
|
+
"type": "string"
|
|
2633
|
+
},
|
|
2634
|
+
"org_id": {
|
|
2635
|
+
"type": "string",
|
|
2636
|
+
"format": "uuid"
|
|
2637
|
+
},
|
|
2638
|
+
"org_name": {
|
|
2639
|
+
"type": [
|
|
2640
|
+
"string",
|
|
2641
|
+
"null"
|
|
2642
|
+
]
|
|
2643
|
+
}
|
|
2644
|
+
},
|
|
2645
|
+
"required": [
|
|
2646
|
+
"api_key",
|
|
2647
|
+
"key_id",
|
|
2648
|
+
"key_prefix",
|
|
2649
|
+
"org_id",
|
|
2650
|
+
"org_name"
|
|
2651
|
+
]
|
|
2652
|
+
},
|
|
2653
|
+
"CliLogoutInput": {
|
|
2654
|
+
"type": "object",
|
|
2655
|
+
"additionalProperties": false,
|
|
2656
|
+
"properties": {
|
|
2657
|
+
"key_id": {
|
|
2658
|
+
"type": "string",
|
|
2659
|
+
"format": "uuid",
|
|
2660
|
+
"description": "Optional key id guard; when provided it must match the authenticated API key"
|
|
2661
|
+
}
|
|
2662
|
+
}
|
|
2663
|
+
},
|
|
2664
|
+
"CliLogoutResult": {
|
|
2665
|
+
"type": "object",
|
|
2666
|
+
"properties": {
|
|
2667
|
+
"revoked": {
|
|
2668
|
+
"type": "boolean",
|
|
2669
|
+
"const": true
|
|
2670
|
+
},
|
|
2671
|
+
"key_id": {
|
|
2672
|
+
"type": "string",
|
|
2673
|
+
"format": "uuid"
|
|
2674
|
+
}
|
|
2675
|
+
},
|
|
2676
|
+
"required": [
|
|
2677
|
+
"revoked",
|
|
2678
|
+
"key_id"
|
|
2679
|
+
]
|
|
2680
|
+
},
|
|
2298
2681
|
"Account": {
|
|
2299
2682
|
"type": "object",
|
|
2300
2683
|
"properties": {
|