primcli 3.0.0-preview.12 → 3.0.0-preview.14
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 +2 -1
- package/dist/{api-command-CNCsVNk0.js → api-command-BTOIanV1.js} +2007 -125
- package/dist/{machine-session-DkXYR62a.js → machine-session-BTkDAKda.js} +4 -4
- package/dist/{notify-session-WKFsaFt3.js → notify-session-b_0jyPIk.js} +2 -2
- package/dist/oclif/auto-signal-worker.js +3 -3
- package/dist/oclif/auto-signals.js +1 -1
- package/dist/oclif/index.js +2248 -423
- package/dist/oclif/receiver-heal.js +2 -2
- package/dist/{repeats-Dj_o6c-X-wjmw2Jbx.js → repeats-Dj_o6c-X-B7sSj0oc.js} +128 -6
- package/dist/{scoped-chat-B1Fi6_qM.js → scoped-chat-Cqmn-k7q.js} +1 -1
- package/dist/{signal-command-BdJVZXGl.js → signal-command-ewPBzvjU.js} +4 -4
- package/man/primitive.1 +3 -2
- package/package.json +1 -1
package/dist/oclif/index.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import { $ as
|
|
2
|
-
import { $
|
|
3
|
-
import { S as readBundledConnectSkill, _ as NotificationOutcomeUnknownError, a as NotificationRetryError, b as installedConnectSkillFile, c as readConversationStatusContent, d as SESSION_UUID$1, g as NativeSessionNotLoadedError, h as NativeSessionError, i as notificationReceiptPage, l as createListenHandler, m as NativeSessionDisconnectedError, n as notificationSenders, o as isRoutineNotificationContent, p as defaultSessionSocket, r as openSessionNotifications, s as notificationPartReader, t as notificationScope, u as readableDelivery, v as connectSkillTarget, x as installedVersion, y as installConnectSkill } from "../notify-session-
|
|
1
|
+
import { $ as interactionNextActions, $t as ConnectedAgentKeyRequiredError, A as openBrowser, B as serverRelationship, Bt as runtimeSessionVariableList, C as approveThenRerunHint, Ct as sendIdentityFields, D as canOfferCopyHotkey, Dt as serializeErrorPayload, E as withApproval, Et as sentHistoryWindowStart, F as formatMailWakeLine, Ft as identitySuggestions, G as claimPendingMail, Gt as readCallerMailFor, H as wakeReadCommand, Ht as withFlagSuggestion, I as latestOwnOutbound, It as loadSkillLine, J as pendingMailPath, Jt as isEmailId, K as clearConsumedPendingMail, Kt as classificationFactLines, L as readThreadContext, Lt as currentMailSessionKey, M as bareAddress, Mt as connectNextSteps, N as describeWake, O as formatBrowserLoginInstructions, Ot as successfulSendOutcome, P as displayAddress, Pt as generatedAgentName, Q as interactionHumanLine, Qt as withAttachmentWait, R as sentInThread, Rt as detectAgentRuntime, S as STEP_UP_TOKEN_FLAG_DESCRIPTION, St as reportSendCommandResult, T as terminalApprovalIo, Tt as sendWaitClampNotice, U as wakeRecipientField, Ut as emptyEmailListHint, Vt as formatAlreadySentNotice, W as wakeRelationship, Wt as mailReach, X as recordPendingMail, Xt as ATTACHMENT_WAIT_FLAG_DESCRIPTION, Y as readPendingMail, Yt as noFactsMessage, Z as removePendingMail, Zt as ATTACHMENT_WAIT_MAX_MS, _ as surfaceUnauthorizedHint, _n as sanitizeForTerminal, _t as formatPriorRepliesCheckSkipped, a as createOperationCommand, an as resolveCliApiRequestConfig, at as MAX_SEND_WAIT_TIMEOUT_MS, b as BROWSER_FLAG_DESCRIPTION, bt as formatSendRecordFailureSummary, c as formatErrorPayload, cn as RESTRICTED_ROLE_NOTE, ct as assertValidIdempotencyKey, d as isGrantRefusal, dn as isBuiltInRoleKey, dt as checkPriorReplies, et as readEmailInteraction, f as networkErrorHint, fn as revokeUserLogin, ft as classifySendError, g as runWithTiming, gn as routeWarningLines, gt as formatDeletedEarlierSendNotice, h as refusalDetails, hn as switchCurrentOrg, ht as followUpCommandPrefix, i as TIME_FLAG_DESCRIPTION, in as refuseOrgSelectionForProfile, it as IDEMPOTENCY_KEY_FLAG_DESCRIPTION, j as explainPendingMailNotFound, jt as cliInvocation, k as listenForCopyHotkey, kt as thrownErrorExitCode, l as hasControlCharacter$1, ln as credentialsFromUserLogin, lt as buildFollowUpCommand, m as readTextFileFlag, mn as roleKeyText, mt as excludeSignalReplies, n as API_ERROR_CODES, nn as createCliApiClient, nt as replyInteractionWarning, o as extractErrorCode, on as selectedOrgFromEnv, ot as SEND_OUTCOME_HELP, p as parameterFlagName, pn as roleKeyLabel, pt as deriveSendIdempotencyKey, q as clearReadPendingMail, qt as classificationOf, r as OPERATION_PATH_FLAGS, rn as refreshStoredCliCredentials, s as extractErrorPayload, sn as CliOrgRefusedError, st as apiResultHttpStatus, t as API_BASE_URL_FLAG_DESCRIPTION, tn as createAuthenticatedCliApiClient, tt as replyExpectation, u as headerFlagName, un as fetchCliOrgs, ut as buildThrownSendFailureEnvelope, v as takesStepUp, vn as setKnownCommands, vt as formatPriorRepliesWarning, w as promptLine$2, wt as sendOutcomeExitCode, x as STEP_UP_HEADER$1, xt as priorRepliesThatWentOut, y as writeErrorWithHints, yn as operationManifest, yt as formatSendFailureSummary, z as serverMuted, zt as detectRuntimeSession } from "../api-command-BTOIanV1.js";
|
|
2
|
+
import { $n as setMemory, $t as getOwnGrant, A as filtersFromFlags, An as listWakeAuthorizations, At as deleteWakeAuthorization, B as awaitingRejectedError, Bn as reorderRoutes, C as transientFailure, Cn as listMailboxAccess, Ct as createRoute, D as cursorFromAcceptedRows, Dn as listRoutes, E as collectNewAcceptedEmails, En as listRoles, F as AWAITING_FLAG_DESCRIPTION, Ft as downloadSentAttachmentArchiveMember, G as isAwaitingRejectedError, Gn as runWakeSchedule, Gt as getFunction, H as formatAwaitingCell, Hn as resendAgentSignupVerification, Ht as getCreditBalance, I as AWAITING_VALUES, It as downloadSentAttachmentByFilename, Jn as searchMemories, Jt as getInboxStatus, K as queryUsesAwaiting, Kt as getFunctionRouting, L as REPLY_STATE_UNSUPPORTED_CODE, Lt as getAccessMode, M as quoteDslValue, Mn as listWakeSchedules, Mt as downloadEmailAttachmentArchiveMember, N as sinceFromFlags, Nn as pollCliLogin, Nt as downloadEmailAttachmentByFilename, O as encodeReceivedAtSearchCursor, On as listSentAttachmentArchiveMembers, Ot as deleteMemory, P as sleep$3, Pn as pullWebhookEvent, Q as isEmailReceivedEvent, Qn as setFunctionSecret, Qt as getOutboundStatus, R as ReplyStateUnsupportedError, Rn as redeemCreditCode, Rt as getAccount, S as retryingOperation, Sn as listKeys, St as createKey, T as SINCE_VALUE_HELP, Tn as listRepeatingSends, Tt as createWakeSchedule, U as formatRepliesCell, Un as revokeCredential, Ut as getDefaultNetworkAgent, V as ensureSearchReportsReplyState, Vn as replyToEmail, Vt as getConversation, W as hasReplyState, Wn as rotateKey, Wt as getEmail, Xn as sendEmail, Xt as getMemory, Yn as semanticSearch, Yt as getKey, Zn as setFunctionRoute, Zt as getOrgRoutingTopology, _ as backoffDelay, _n as listEmails, _t as checkSendability, a as REPEAT_IDLE_MAX_MINUTES, ar as testFunction, at as AutomatedFilterUnsupportedError, b as pauseForTransientFailure, bn as listFunctions, bt as createEndpoint, cr as updateFunction, d as parseRepeatTick, dr as updateRoute, dt as hasAutomatedVerdict, en as getRepeatingSend, er as simulateRoute, et as parseWebhookEvent, f as preparePresenceAliveEmail, fn as listDefaultNetworkAgents, fr as updateWakeAuthorization, ft as isAutomatedRejectedError, g as WaitDeadlineReached, gn as listEmailRuleResults, gr as whoami, gt as checkDefaultNetworkContactAdmission, hn as listEmailAttachmentArchiveMembers, hr as verifyAgentSignup, ht as cancelSentEmailByIdempotencyKey, i as REPEAT_EVERY_MAX_MINUTES, in as getWakeSchedule, ir as stopRepeatFromEmail, it as AUTOMATED_VALUES, j as lateMailReread, jn as listWakeDispatches, jt as deleteWakeSchedule, k as fetchEmailSearchPage, kn as listSentEmails, kt as deleteRoute, l as parseInteractionEnvelope, ln as listAgentContacts, lr as updateKey, m as repeatStopCommand, mn as listDomains, mr as verifyAgentClaim, mt as cancelSentEmail, nn as getSentEmail, nr as startAgentSignup, nt as AUTOMATED_FILTER_UNSUPPORTED_CODE, o as REPEAT_MAX_SENDS_MAX, on as leaveOrganization, or as unsetFunctionRoute, ot as assertAutomatedVerdict, p as prepareSignalEmail, pn as listDefaultNetworkMembers, pr as updateWakeSchedule, pt as addDefaultNetworkMember, qn as searchEmails, qt as getFunctionTestRunTrace, rn as getThread, rr as startCliLogin, rt as AUTOMATED_FLAG_DESCRIPTION, s as buildRepeatStopBody, sn as listActivityObjects, sr as updateDefaultNetworkMember, st as automatedRejectedError, tn as getSendFrom, tr as startAgentClaim, tt as parseFromHeader, u as parsePresenceEnvelope, un as listAgentNetworks, ur as updateRepeatingSend, v as describeApiFailure, vn as listEndpoints, vt as cliLogout, w as waitOutRetryAfter, wn as listOrganizationPeople, wt as createWakeAuthorization, x as retryTransient, xn as listGroups, xt as createFunction, yn as listFunctionLogs, yt as completeWebhookEvent, z as assertAwaitingFilterRows, zn as removeDefaultNetworkMember, zt as getAgentContactPolicy } from "../repeats-Dj_o6c-X-B7sSj0oc.js";
|
|
3
|
+
import { S as readBundledConnectSkill, _ as NotificationOutcomeUnknownError, a as NotificationRetryError, b as installedConnectSkillFile, c as readConversationStatusContent, d as SESSION_UUID$1, g as NativeSessionNotLoadedError, h as NativeSessionError, i as notificationReceiptPage, l as createListenHandler, m as NativeSessionDisconnectedError, n as notificationSenders, o as isRoutineNotificationContent, p as defaultSessionSocket, r as openSessionNotifications, s as notificationPartReader, t as notificationScope, u as readableDelivery, v as connectSkillTarget, x as installedVersion, y as installConnectSkill } from "../notify-session-b_0jyPIk.js";
|
|
4
4
|
import { A as AGENT_ADDRESS_ENV, C as saveCliCredentials, D as loadActiveChatState, E as deleteChatState, F as agentProfilesDirectory, H as requireDefaultLoginProfile, M as AgentConnectionSetupError, N as agentProfileDirectory, O as loadChatConversationByLocalId, P as agentProfileName, R as loadConnectedAgentProfile, S as resolveCliAuth, T as chatStatePath, W as PrimitiveApiClient, _ as deleteCliCredentials, a as normalizeCliEnvironmentName, b as loadCliCredentials, c as resolveConfigEnvironment, g as credentialsPath, h as credentialsLockPath, i as loadCliConfig, j as AGENT_PROFILE_ENV, k as saveActiveChatState, l as saveCliConfig, m as cliAccessTokenExpiresAt, n as deleteCliConfig, o as redactCliEnvironment, p as acquireCliCredentialsLock, r as emptyCliConfig, s as removeCliEnvironment, u as upsertCliEnvironment, v as deleteCliCredentialsLock, w as saveSignupCredentials, y as detectPrimitiveKeyEnvMisname } from "../cli-config-Ms_0vNjq.js";
|
|
5
5
|
import { C as resolveListenSubscription, S as normalizeListenOrigin, _ as acquireListenLock, a as mailLockBusy, c as mailTime, d as readMailJson, f as removeMailFile, g as ListenStateError, h as writeMailJson, i as mailId, l as privateMailDirectory, m as withMailLock, o as mailObject, u as privateMailPermissions, y as listenIdentity } from "../shared-mail-files-BAQSGxxz.js";
|
|
6
|
-
import { $ as saveConnectionName, A as ContactPolicyReadRetryError, B as AddressNotesApiError, C as RECEIVER_MODES, Ct as upsertManagedBlock, D as registrationSummary, Dt as refreshOwnerMemberAddressPeriodically, E as failedSessionRegistration, Et as addSessionReceiver, F as canonicalContactSelector, G as contactAgentAddress, H as runAddressNotesRequest, I as AgentDisconnectError, J as agentInvitationHash, K as runContactRequest, L as claimOverRevokedSetup, M as NetworkAdmissionPendingError, N as createNotificationContactPolicy, O as registrationSummaryLine, Ot as withOwnerMemberAddress, P as evaluateContactPolicy, Q as refusalAfterReplacement, R as clearRevokedAddresses, S as runAgentConnect, St as readManagedFile, T as verificationReplySubmitted, Tt as readHookLauncherWarning, U as ContactsApiError, V as addressNoteTarget, W as contactAddress, X as readAgentInvitation, Y as connectAgent, Z as receiverStatusCommand, _ as enrollmentResumes, _t as samePath, a as endSession, at as boundSessionProfiles, b as invitationProfileName, bt as inspectManagedBlock, c as pendingSessionDisconnects, ct as describeSessionHookChange, d as registerSession, dt as ephemeralCliReason, et as savedReceiver, f as retryPendingDisconnects, ft as findOnPath, g as enrollAgent, gt as repairClaudeHooks, h as staleSessionAgents, ht as readClaudeSettings, i as connectedProfilesForSession, it as recordHookMailCheck, j as NETWORK_ADMISSION_PENDING_RETRY_MS, k as waitForRegistrations, l as psProcessInfo, lt as describeSessionHookItem, m as runDetached, mt as inspectClaudeHooks, n as OMP_MAIN, nt as releaseRegistrationsForProfile, o as headlessClaudeRun, ot as claudeConfigDir, p as revokeStaleSessionAgents, pt as hasMachineSessionHooks, q as agentConnectionStatus, r as agentConnectionState, rt as installClaudeWakeHook, st as currentCliLocation, t as HOOK_END_GRACE_MS, tt as MACHINE_RUNTIMES, u as readClaudeHookInput, ut as ensureHookLauncher, v as connectWarnings, vt as writeClaudeSettings, w as externalReceivingState, wt as writeManagedFile, x as pollCheckCommand, xt as jsonIndentation, y as defaultAgentProfileName, yt as MachineFileError, z as disconnectAgent } from "../machine-session-
|
|
7
|
-
import { a as readBeforeDeadline, c as readPresenceProjection, i as readApiBeforeDeadline, n as isPlainChatReply, o as scopedChatSenderTrust, r as isScopedChatReply, s as presenceDisposition, t as isConnectedChatCredential } from "../scoped-chat-
|
|
6
|
+
import { $ as saveConnectionName, A as ContactPolicyReadRetryError, B as AddressNotesApiError, C as RECEIVER_MODES, Ct as upsertManagedBlock, D as registrationSummary, Dt as refreshOwnerMemberAddressPeriodically, E as failedSessionRegistration, Et as addSessionReceiver, F as canonicalContactSelector, G as contactAgentAddress, H as runAddressNotesRequest, I as AgentDisconnectError, J as agentInvitationHash, K as runContactRequest, L as claimOverRevokedSetup, M as NetworkAdmissionPendingError, N as createNotificationContactPolicy, O as registrationSummaryLine, Ot as withOwnerMemberAddress, P as evaluateContactPolicy, Q as refusalAfterReplacement, R as clearRevokedAddresses, S as runAgentConnect, St as readManagedFile, T as verificationReplySubmitted, Tt as readHookLauncherWarning, U as ContactsApiError, V as addressNoteTarget, W as contactAddress, X as readAgentInvitation, Y as connectAgent, Z as receiverStatusCommand, _ as enrollmentResumes, _t as samePath, a as endSession, at as boundSessionProfiles, b as invitationProfileName, bt as inspectManagedBlock, c as pendingSessionDisconnects, ct as describeSessionHookChange, d as registerSession, dt as ephemeralCliReason, et as savedReceiver, f as retryPendingDisconnects, ft as findOnPath, g as enrollAgent, gt as repairClaudeHooks, h as staleSessionAgents, ht as readClaudeSettings, i as connectedProfilesForSession, it as recordHookMailCheck, j as NETWORK_ADMISSION_PENDING_RETRY_MS, k as waitForRegistrations, l as psProcessInfo, lt as describeSessionHookItem, m as runDetached, mt as inspectClaudeHooks, n as OMP_MAIN, nt as releaseRegistrationsForProfile, o as headlessClaudeRun, ot as claudeConfigDir, p as revokeStaleSessionAgents, pt as hasMachineSessionHooks, q as agentConnectionStatus, r as agentConnectionState, rt as installClaudeWakeHook, st as currentCliLocation, t as HOOK_END_GRACE_MS, tt as MACHINE_RUNTIMES, u as readClaudeHookInput, ut as ensureHookLauncher, v as connectWarnings, vt as writeClaudeSettings, w as externalReceivingState, wt as writeManagedFile, x as pollCheckCommand, xt as jsonIndentation, y as defaultAgentProfileName, yt as MachineFileError, z as disconnectAgent } from "../machine-session-BTkDAKda.js";
|
|
7
|
+
import { a as readBeforeDeadline, c as readPresenceProjection, i as readApiBeforeDeadline, n as isPlainChatReply, o as scopedChatSenderTrust, r as isScopedChatReply, s as presenceDisposition, t as isConnectedChatCredential } from "../scoped-chat-Cqmn-k7q.js";
|
|
8
8
|
import { autoSignalEligible, dispatchAutoRead, dispatchAutoWorking, haltAutoWorking, isSentSignal, readWorkingLease, restoreAutoWorking } from "./auto-signals.js";
|
|
9
|
-
import { a as ensureSharedMailSubscription, c as isConnectedAgentKey, d as reserveSharedMailSubscription, f as followEmailConversation, g as EventReceiverError, h as EventConnection, i as sharedMailScope, l as createSharedMailWaiter, m as otherParticipants, n as reconcileChatSend, p as readConversationFollow, r as openSharedMailReceiver, s as AGENT_CONNECTION_REQUIRED_MESSAGE, t as sendSignal, u as openSharedMailStore } from "../signal-command-
|
|
9
|
+
import { a as ensureSharedMailSubscription, c as isConnectedAgentKey, d as reserveSharedMailSubscription, f as followEmailConversation, g as EventReceiverError, h as EventConnection, i as sharedMailScope, l as createSharedMailWaiter, m as otherParticipants, n as reconcileChatSend, p as readConversationFollow, r as openSharedMailReceiver, s as AGENT_CONNECTION_REQUIRED_MESSAGE, t as sendSignal, u as openSharedMailStore } from "../signal-command-ewPBzvjU.js";
|
|
10
10
|
import { a as thrownErrorBody, i as runStreamingJson, n as guardJsonOutputCommands, r as jsonOutputRequested, t as JSON_OUTPUT_FLAG } from "../json-output-ch756TXV.js";
|
|
11
11
|
import { a as backgroundListenStatus, c as runBackgroundListen, d as stopBackgroundListen, f as verifyBackgroundListenTarget, o as backgroundListenToken, r as backgroundListenConfiguration, u as startBackgroundListen } from "../listen-background-DyBYca28.js";
|
|
12
12
|
import { ORG_SELECTION_ENV } from "./global-org-flag.js";
|
|
@@ -184,6 +184,11 @@ const openapiDocument = {
|
|
|
184
184
|
"description": "Organization administration for people: your own connected apps and CLI logins, the organization's connected apps, its name and logo, and semantic search consent. API keys, agent connection keys and agents are refused. Billing and deleting the organization are done in the dashboard only.",
|
|
185
185
|
"x-openapi-spec": "https://api.primitive.dev/v1/openapi/tags/organization-settings"
|
|
186
186
|
},
|
|
187
|
+
{
|
|
188
|
+
"name": "Email notifications",
|
|
189
|
+
"description": "The notification emails Primitive sends an organization's members: the organization chooses, per kind, whether it is off, left to each member or required, and each member chooses where it is left to them. Some kinds are always sent. API keys, agent connection keys and agents are refused.",
|
|
190
|
+
"x-openapi-spec": "https://api.primitive.dev/v1/openapi/tags/email-notifications"
|
|
191
|
+
},
|
|
187
192
|
{
|
|
188
193
|
"name": "Roles and members",
|
|
189
194
|
"description": "Roles, member roles and removal, the mailbox access mode, break-glass, transfers and suspensions. Reads are open to people; every write is person only: a dashboard session, or the person's own OAuth or CLI token with a session step-up. API keys never reach these. Answers 404 feature_disabled while the feature is not enabled for the organization, with details.feature naming the feature; contact Primitive to turn it on.",
|
|
@@ -2502,7 +2507,12 @@ const openapiDocument = {
|
|
|
2502
2507
|
"name": "outbound_only",
|
|
2503
2508
|
"in": "query",
|
|
2504
2509
|
"schema": { "type": "boolean" },
|
|
2505
|
-
"description": "When true, include only outbound DNS records.
|
|
2510
|
+
"description": "When true, include only outbound DNS records. Cannot be combined with inbound_only=true. By default, include all records."
|
|
2511
|
+
}, {
|
|
2512
|
+
"name": "inbound_only",
|
|
2513
|
+
"in": "query",
|
|
2514
|
+
"schema": { "type": "boolean" },
|
|
2515
|
+
"description": "When true, include only inbound MX and ownership TXT records. Cannot be combined with outbound_only=true."
|
|
2506
2516
|
}],
|
|
2507
2517
|
"responses": {
|
|
2508
2518
|
"200": {
|
|
@@ -21769,7 +21779,7 @@ const openapiDocument = {
|
|
|
21769
21779
|
"/activity/objects": { "get": {
|
|
21770
21780
|
"operationId": "listActivityObjects",
|
|
21771
21781
|
"summary": "List what you created, or what you were asked",
|
|
21772
|
-
"description": "The objects the caller created, newest first, each with where it stands now, including ones since disabled, deleted or revoked where the object keeps a row: Functions, endpoints, Function triggers, pull subscriptions, recipient routes, filters, wake schedules, repeating sends and scheduled sends. An API key, an app or an agent sees what it created itself; a webhook given to an API key lists under that key. A person (a dashboard session, or their own Primitive CLI or app login) sees what they created through any credential of theirs, each item naming it, and also their API keys, personal agents, email rules, rule requests and objections to rules over their mail (objections only on the dashboard or the Primitive CLI). Each kind is listed only if the caller could list it through that kind's own route; a kind it could not is named in withheld, with no count. Ids, names and states only: never a webhook URL or mail content. Needs activity:read_self. With relation=asked it lists instead what other members asked of the person, open or answered: the rule requests made to them, and the objections to rules they wrote, under their current membership, each naming the member who asked or objected in created_through. A key, an app or an agent has none, and gets both kinds in withheld. With relation=concerning it lists instead what other members did that concerns the person's own mail: each time a rule started covering one of their mailboxes
|
|
21782
|
+
"description": "The objects the caller created, newest first, each with where it stands now, including ones since disabled, deleted or revoked where the object keeps a row: Functions, endpoints, Function triggers, pull subscriptions, recipient routes, filters, wake schedules, repeating sends and scheduled sends. An API key, an app or an agent sees what it created itself; a webhook given to an API key lists under that key. A person (a dashboard session, or their own Primitive CLI or app login) sees what they created through any credential of theirs, each item naming it, and also their API keys, personal agents, email rules, rule requests and objections to rules over their mail (objections only on the dashboard or the Primitive CLI). Each kind is listed only if the caller could list it through that kind's own route; a kind it could not is named in withheld, with no count. Ids, names and states only: never a webhook URL or mail content. Needs activity:read_self. With relation=asked it lists instead what other members asked of the person, open or answered: the rule requests made to them, and the objections to rules they wrote, under their current membership, each naming the member who asked or objected in created_through. A key, an app or an agent has none, and gets both kinds in withheld. With relation=concerning it lists instead what other members did that concerns the person's own mail: each time a rule started covering one of their mailboxes, saved a new version, or changed who receives its matches while covering it, named as the rule was then, naming its author in created_through, under their current membership; only on the dashboard or the Primitive CLI. A key, an app or an agent has none, and gets email_rule_coverage in withheld. A notice never reads as ended. Notices are written by a periodic job and can lag: until the notices record a rule starting to cover you, the first page lists that rule as pending (pending_complete says whether that check finished), and as_of says about when the notices last looked. For what covers you now, read GET /email-rules/covering-me.",
|
|
21773
21783
|
"tags": ["Activity"],
|
|
21774
21784
|
"security": [{ "BearerAuth": [] }],
|
|
21775
21785
|
"parameters": [
|
|
@@ -21780,7 +21790,7 @@ const openapiDocument = {
|
|
|
21780
21790
|
"description": "created (the default), asked or concerning.",
|
|
21781
21791
|
"schema": {
|
|
21782
21792
|
"default": "created",
|
|
21783
|
-
"description": "created (the default): what you created. asked: what other members asked of you, open or answered: rule requests made to you, and objections to rules you wrote; kind may then name only email_rule_request and email_rule_objection. concerning: what other members did that concerns your own mail:
|
|
21793
|
+
"description": "created (the default): what you created. asked: what other members asked of you, open or answered: rule requests made to you, and objections to rules you wrote; kind may then name only email_rule_request and email_rule_objection. concerning: what other members did that concerns your own mail: an active rule that started covering one of your mailboxes, saved a new version, or changed who receives its matches while covering it (email_rule_coverage, the only kind it takes). A rule in draft or observe is not told of; GET /v1/email-rules/covering-me lists it. asked and concerning are for a person's own view only.",
|
|
21784
21794
|
"type": "string",
|
|
21785
21795
|
"enum": [
|
|
21786
21796
|
"created",
|
|
@@ -21997,7 +22007,7 @@ const openapiDocument = {
|
|
|
21997
22007
|
"/email-rules/compile": { "post": {
|
|
21998
22008
|
"operationId": "compileEmailRule",
|
|
21999
22009
|
"summary": "Compile a rule description",
|
|
22000
|
-
"description": "Turns a plain-English description of which received emails to match into a rule tree over the column contract, with an explanation of what it matches, the assumptions made, and warnings for what the columns cannot express. Where the columns fall short it adds up to five yes/no questions, asked only after the conditions match; they read the subject, the new text and attachment file names, never attachment contents. Nothing is saved. For a rule with no questions, breadth counts, read only, how much of
|
|
22010
|
+
"description": "Turns a plain-English description of which received emails to match into a rule tree over the column contract, with an explanation of what it matches, the assumptions made, and warnings for what the columns cannot express. Where the columns fall short it adds up to five yes/no questions, asked only after the conditions match; they read the subject, the new text and attachment file names, never attachment contents. Nothing is saved. For a rule with no questions, breadth counts, read only, how much of the recent classified mail in evaluation_scope (by default your own mailboxes) its conditions match; for a grant rule with questions, conditions_breadth counts its conditions alone. include_sql returns the SQL it renders to. kind do_not_share compiles for recall. Needs mail:read, the email rules feature, and a member's own full-access credential (no API key, agent key or narrowed token).",
|
|
22001
22011
|
"tags": ["Email Rules"],
|
|
22002
22012
|
"security": [{ "BearerAuth": [] }],
|
|
22003
22013
|
"requestBody": {
|
|
@@ -22052,7 +22062,11 @@ const openapiDocument = {
|
|
|
22052
22062
|
}
|
|
22053
22063
|
},
|
|
22054
22064
|
"403": {
|
|
22055
|
-
"description": "feature_not_enabled: this organization lacks the email rules feature; permission refusals answer forbidden or grant_forbidden.",
|
|
22065
|
+
"description": "feature_not_enabled: this organization lacks the email rules feature; permission refusals answer forbidden or grant_forbidden. email_rule_scope_not_readable: scope_selection names something you cannot read.",
|
|
22066
|
+
"content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }
|
|
22067
|
+
},
|
|
22068
|
+
"409": {
|
|
22069
|
+
"description": "email_rule_author_unresolvable: with evaluation_scope selected, you cannot act as a rule's author right now (details.reason left, revoked, suspended, membership_ended or no_mail_read).",
|
|
22056
22070
|
"content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }
|
|
22057
22071
|
},
|
|
22058
22072
|
"422": {
|
|
@@ -22149,7 +22163,7 @@ const openapiDocument = {
|
|
|
22149
22163
|
}
|
|
22150
22164
|
},
|
|
22151
22165
|
"403": {
|
|
22152
|
-
"description": "feature_not_enabled: this organization lacks the email rules feature; permission refusals answer forbidden or grant_forbidden. grant_forbidden with details.reason mail_depth: your access to mail shows headers only, and a rule decides on what emails say. email_rule_scope_not_readable: scope_selection names something you cannot read.",
|
|
22166
|
+
"description": "feature_not_enabled: this organization lacks the email rules feature; permission refusals answer forbidden or grant_forbidden. grant_forbidden with details.reason mail_depth: your access to mail shows headers only, and a rule decides on what emails say. email_rule_scope_not_readable: scope_selection names something you cannot read. forbidden with details.reason covering_view_only: you read this rule only in the covering view, since it decided on your mail before and covers none of your mailboxes now (details.covering_view).",
|
|
22153
22167
|
"content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }
|
|
22154
22168
|
},
|
|
22155
22169
|
"404": {
|
|
@@ -22246,15 +22260,15 @@ const openapiDocument = {
|
|
|
22246
22260
|
}
|
|
22247
22261
|
},
|
|
22248
22262
|
"403": {
|
|
22249
|
-
"description": "feature_not_enabled: this organization lacks the email rules feature; permission refusals answer forbidden or grant_forbidden. email_rule_scope_not_readable: scope_selection names something you cannot read.",
|
|
22263
|
+
"description": "feature_not_enabled: this organization lacks the email rules feature; permission refusals answer forbidden or grant_forbidden. email_rule_scope_not_readable: scope_selection names something you cannot read. forbidden with details.reason covering_view_only: you read this rule only in the covering view, since it decided on your mail before and covers none of your mailboxes now (details.covering_view).",
|
|
22250
22264
|
"content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }
|
|
22251
22265
|
},
|
|
22252
22266
|
"404": {
|
|
22253
|
-
"description": "not_found: rule_id names no rule you can see (details.reason rule_not_found), or request_id no rule request you can see (request_not_found).",
|
|
22267
|
+
"description": "not_found: rule_id names no rule you can see or that covers your mailboxes (details.reason rule_not_found), or request_id no rule request you can see (request_not_found).",
|
|
22254
22268
|
"content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }
|
|
22255
22269
|
},
|
|
22256
22270
|
"409": {
|
|
22257
|
-
"description": "email_rule_author_unresolvable: the rule cannot run as its author (for an unsaved tree or a
|
|
22271
|
+
"description": "email_rule_author_unresolvable: the rule cannot run as its author (for an unsaved tree, a request or a rule covering you, you). email_rule_request_not_pending: request_id names a request already decided.",
|
|
22258
22272
|
"content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }
|
|
22259
22273
|
},
|
|
22260
22274
|
"422": {
|
|
@@ -22305,7 +22319,7 @@ const openapiDocument = {
|
|
|
22305
22319
|
"/email-rules/test-sample": { "post": {
|
|
22306
22320
|
"operationId": "testEmailRuleOnSample",
|
|
22307
22321
|
"summary": "Test a rule against a made-up email",
|
|
22308
|
-
"description": "Reports what a rule would decide for an email you describe (sender, recipients, subject, plain text, attachment types; optionally automated, from a mailing list, and a spam score), classified and decided as received mail would be. A rule reading a spam score you gave none for decides on none (unset_columns). Nothing is stored. Test an unsaved tree or a saved rule by rule_id. The answer includes what the classifier read. Needs mail:read, the email rules feature, and a member's own full-access credential (no API key, agent key or narrowed token).",
|
|
22322
|
+
"description": "Reports what a rule would decide for an email you describe (sender, recipients, subject, plain text, attachment types and file names; optionally automated, from a mailing list, and a spam score), classified and decided as received mail would be. A rule reading a spam score you gave none for decides on none (unset_columns). Nothing is stored. Test an unsaved tree or a saved rule by rule_id. The answer includes what the classifier read. Needs mail:read, the email rules feature, and a member's own full-access credential (no API key, agent key or narrowed token).",
|
|
22309
22323
|
"tags": ["Email Rules"],
|
|
22310
22324
|
"security": [{ "BearerAuth": [] }],
|
|
22311
22325
|
"requestBody": {
|
|
@@ -22351,15 +22365,15 @@ const openapiDocument = {
|
|
|
22351
22365
|
}
|
|
22352
22366
|
},
|
|
22353
22367
|
"403": {
|
|
22354
|
-
"description": "feature_not_enabled: this organization lacks the email rules feature; permission refusals answer forbidden or grant_forbidden. email_rule_scope_not_readable: scope_selection names something you cannot read.",
|
|
22368
|
+
"description": "feature_not_enabled: this organization lacks the email rules feature; permission refusals answer forbidden or grant_forbidden. email_rule_scope_not_readable: scope_selection names something you cannot read. forbidden with details.reason covering_view_only: you read this rule only in the covering view, since it decided on your mail before and covers none of your mailboxes now (details.covering_view).",
|
|
22355
22369
|
"content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }
|
|
22356
22370
|
},
|
|
22357
22371
|
"404": {
|
|
22358
|
-
"description": "not_found: rule_id names no rule you can see (details.reason rule_not_found).",
|
|
22372
|
+
"description": "not_found: rule_id names no rule you can see or that covers your mailboxes (details.reason rule_not_found).",
|
|
22359
22373
|
"content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }
|
|
22360
22374
|
},
|
|
22361
22375
|
"409": {
|
|
22362
|
-
"description": "email_rule_author_unresolvable: the rule cannot run as its author (for an unsaved tree, you), so it would decide nothing for real. details.reason is left, revoked, suspended, membership_ended, no_mail_read, no_own_mailbox, no_shareable_mailbox or no_selected_mailbox.",
|
|
22376
|
+
"description": "email_rule_author_unresolvable: the rule cannot run as its author (for an unsaved tree or a rule covering you, you), so it would decide nothing for real. details.reason is left, revoked, suspended, membership_ended, no_mail_read, no_own_mailbox, no_shareable_mailbox or no_selected_mailbox.",
|
|
22363
22377
|
"content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }
|
|
22364
22378
|
},
|
|
22365
22379
|
"429": {
|
|
@@ -22692,7 +22706,7 @@ const openapiDocument = {
|
|
|
22692
22706
|
}
|
|
22693
22707
|
},
|
|
22694
22708
|
"403": {
|
|
22695
|
-
"description": "feature_not_enabled: this organization lacks the email rules feature; permission refusals answer forbidden or grant_forbidden. forbidden with details.reason covering_view_only (your dashboard session or the Primitive CLI only): you may read this rule only because it covers your mailbox; details.covering_view is where (getEmailRuleCoveringMe).",
|
|
22709
|
+
"description": "feature_not_enabled: this organization lacks the email rules feature; permission refusals answer forbidden or grant_forbidden. forbidden with details.reason covering_view_only (your dashboard session or the Primitive CLI only): you may read this rule only because it covers your mailbox or decided on your mail; details.covering_view is where (getEmailRuleCoveringMe).",
|
|
22696
22710
|
"content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }
|
|
22697
22711
|
},
|
|
22698
22712
|
"404": {
|
|
@@ -22837,7 +22851,7 @@ const openapiDocument = {
|
|
|
22837
22851
|
"delete": {
|
|
22838
22852
|
"operationId": "deleteEmailRule",
|
|
22839
22853
|
"summary": "Delete a rule",
|
|
22840
|
-
"description": "Deletes a rule with its versions, its decisions and the access it granted. A share of it ends (end_reason rule_deleted), and the member who shared can still list what it handed over. The answer lists the shares it ended (ended_shares) and the unrevoked keys that still list it, which now read nothing through it (keys, other_keys; left out when they could not be read). The author or a reader of all the organization's mail may delete it; while it is shared, only the author or someone who manages members from the dashboard. Needs mail:read, the email rules feature, and a member's own full-access credential (no API key, agent key or narrowed token).",
|
|
22854
|
+
"description": "Deletes a rule with its versions, its decisions and the access it granted. A share of it ends (end_reason rule_deleted), and the member who shared can still list what it handed over. The answer lists the shares it ended (ended_shares), says whom with (share_notice), and lists the unrevoked keys that still list it, which now read nothing through it (keys, other_keys; left out when they could not be read). The author or a reader of all the organization's mail may delete it; while it is shared, only the author or someone who manages members from the dashboard. Needs mail:read, the email rules feature, and a member's own full-access credential (no API key, agent key or narrowed token).",
|
|
22841
22855
|
"tags": ["Email Rules"],
|
|
22842
22856
|
"security": [{ "BearerAuth": [] }],
|
|
22843
22857
|
"parameters": [{
|
|
@@ -23030,7 +23044,7 @@ const openapiDocument = {
|
|
|
23030
23044
|
}
|
|
23031
23045
|
},
|
|
23032
23046
|
"403": {
|
|
23033
|
-
"description": "feature_not_enabled: this organization lacks the email rules feature; permission refusals answer forbidden or grant_forbidden. grant_forbidden with details.reason mail_depth: your access to mail shows headers only, and what a rule decided is read from what emails say.",
|
|
23047
|
+
"description": "feature_not_enabled: this organization lacks the email rules feature; permission refusals answer forbidden or grant_forbidden. grant_forbidden with details.reason mail_depth: your access to mail shows headers only, and what a rule decided is read from what emails say. forbidden with details.reason covering_view_only (your dashboard session or the Primitive CLI only): you may read this rule only because it covers your mailbox or decided on your mail; details.covering_view is where (listEmailRuleDecisionsOnMyMail).",
|
|
23034
23048
|
"content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }
|
|
23035
23049
|
},
|
|
23036
23050
|
"404": {
|
|
@@ -23200,7 +23214,7 @@ const openapiDocument = {
|
|
|
23200
23214
|
"required": false,
|
|
23201
23215
|
"description": "Read only these version numbers, comma-separated, at most 100. Not with limit or cursor.",
|
|
23202
23216
|
"schema": {
|
|
23203
|
-
"description": "Read only these version numbers, comma-separated. Without it,
|
|
23217
|
+
"description": "Read only these version numbers, comma-separated. Without it, the versions listed, newest first, a page at a time.",
|
|
23204
23218
|
"type": "string",
|
|
23205
23219
|
"pattern": "^[1-9]\\d{0,8}(,[1-9]\\d{0,8}){0,99}$"
|
|
23206
23220
|
}
|
|
@@ -23260,7 +23274,7 @@ const openapiDocument = {
|
|
|
23260
23274
|
}
|
|
23261
23275
|
},
|
|
23262
23276
|
"403": {
|
|
23263
|
-
"description": "feature_not_enabled: this organization lacks the email rules feature; permission refusals answer forbidden or grant_forbidden.",
|
|
23277
|
+
"description": "feature_not_enabled: this organization lacks the email rules feature; permission refusals answer forbidden or grant_forbidden. forbidden with details.reason covering_view_only (your dashboard session or the Primitive CLI only): you may read this rule only because it covers your mailbox or decided on your mail; details.covering_view is where (listEmailRuleVersionsCoveringMe while it covers you).",
|
|
23264
23278
|
"content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }
|
|
23265
23279
|
},
|
|
23266
23280
|
"404": {
|
|
@@ -25154,264 +25168,69 @@ const openapiDocument = {
|
|
|
25154
25168
|
},
|
|
25155
25169
|
"x-primitive-permission": ["mail:read"]
|
|
25156
25170
|
} },
|
|
25157
|
-
"/email-
|
|
25158
|
-
"
|
|
25159
|
-
|
|
25160
|
-
|
|
25161
|
-
|
|
25162
|
-
|
|
25163
|
-
|
|
25164
|
-
|
|
25171
|
+
"/email-rules/covering-me/{id}/versions": { "get": {
|
|
25172
|
+
"operationId": "listEmailRuleVersionsCoveringMe",
|
|
25173
|
+
"summary": "Read versions of a rule over your mail",
|
|
25174
|
+
"description": "The versions of a rule that covers one of your own mailboxes now that applied to your mail: its current version and each earlier one that decided on mail at your own mailboxes, so a decision can be read in the words of the version that made it. Each has what the covering view shows of a version: its wording, tree, explanation and assumptions. A version that did neither is not listed. Without versions, newest first, a page at a time (meta.cursor); with versions, only those named that are listed, oldest first. Needs mail:read, the email rules feature, and a member's own full-access credential (no API key, agent key or narrowed token). Only on your own dashboard session or the Primitive CLI.",
|
|
25175
|
+
"tags": ["Email Rule Oversight"],
|
|
25176
|
+
"security": [{ "BearerAuth": [] }],
|
|
25177
|
+
"parameters": [
|
|
25178
|
+
{
|
|
25179
|
+
"name": "id",
|
|
25180
|
+
"in": "path",
|
|
25165
25181
|
"required": true,
|
|
25166
|
-
"
|
|
25182
|
+
"description": "The rule id.",
|
|
25183
|
+
"schema": {
|
|
25184
|
+
"type": "string",
|
|
25185
|
+
"format": "uuid"
|
|
25186
|
+
}
|
|
25167
25187
|
},
|
|
25168
|
-
|
|
25169
|
-
"
|
|
25170
|
-
|
|
25171
|
-
|
|
25172
|
-
|
|
25173
|
-
|
|
25174
|
-
|
|
25175
|
-
|
|
25176
|
-
"
|
|
25177
|
-
"ratelimit-limit": { "$ref": "#/components/headers/ratelimit-limit" },
|
|
25178
|
-
"ratelimit-remaining": { "$ref": "#/components/headers/ratelimit-remaining" },
|
|
25179
|
-
"ratelimit-reset": { "$ref": "#/components/headers/ratelimit-reset" },
|
|
25180
|
-
"ratelimit-policy": { "$ref": "#/components/headers/ratelimit-policy" }
|
|
25181
|
-
}
|
|
25182
|
-
},
|
|
25183
|
-
"400": {
|
|
25184
|
-
"description": "validation_error; details.reason own_rule: it is your own rule, which you can turn off yourself.",
|
|
25185
|
-
"content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }
|
|
25186
|
-
},
|
|
25187
|
-
"401": {
|
|
25188
|
-
"description": "Invalid or missing API key",
|
|
25189
|
-
"content": { "application/json": {
|
|
25190
|
-
"schema": { "$ref": "#/components/schemas/ErrorResponse" },
|
|
25191
|
-
"example": {
|
|
25192
|
-
"success": false,
|
|
25193
|
-
"error": {
|
|
25194
|
-
"code": "unauthorized",
|
|
25195
|
-
"message": "Invalid or missing API key"
|
|
25196
|
-
}
|
|
25197
|
-
}
|
|
25198
|
-
} },
|
|
25199
|
-
"headers": {
|
|
25200
|
-
"ratelimit-limit": { "$ref": "#/components/headers/ratelimit-limit-unauthenticated" },
|
|
25201
|
-
"ratelimit-remaining": { "$ref": "#/components/headers/ratelimit-remaining-unauthenticated" },
|
|
25202
|
-
"ratelimit-reset": { "$ref": "#/components/headers/ratelimit-reset-unauthenticated" },
|
|
25203
|
-
"ratelimit-policy": { "$ref": "#/components/headers/ratelimit-policy-unauthenticated" }
|
|
25204
|
-
}
|
|
25205
|
-
},
|
|
25206
|
-
"403": {
|
|
25207
|
-
"description": "feature_not_enabled: this organization lacks the email rules feature; permission refusals answer forbidden or grant_forbidden; details.reason first_party_required means this credential is not your dashboard session or the Primitive CLI.",
|
|
25208
|
-
"content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }
|
|
25209
|
-
},
|
|
25210
|
-
"404": {
|
|
25211
|
-
"description": "not_found: no rule with this id covers your mailboxes now.",
|
|
25212
|
-
"content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }
|
|
25213
|
-
},
|
|
25214
|
-
"409": {
|
|
25215
|
-
"description": "conflict: details.reason objection_pending, you already have a pending objection to this rule (details.objection_id); objection_limit, you have 50 pending objections.",
|
|
25216
|
-
"content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }
|
|
25217
|
-
},
|
|
25218
|
-
"429": {
|
|
25219
|
-
"description": "Rate limit exceeded. The `ratelimit-*` headers describe the limiter that rejected this request, with its own limit and window, not the API budget that successful responses report. A limiter that reports only a wait sends `Retry-After` alone.",
|
|
25220
|
-
"headers": {
|
|
25221
|
-
"Retry-After": {
|
|
25222
|
-
"schema": { "type": "integer" },
|
|
25223
|
-
"description": "Seconds to wait before retrying"
|
|
25224
|
-
},
|
|
25225
|
-
"ratelimit-limit": { "$ref": "#/components/headers/ratelimit-limit-429" },
|
|
25226
|
-
"ratelimit-remaining": { "$ref": "#/components/headers/ratelimit-remaining-429" },
|
|
25227
|
-
"ratelimit-reset": { "$ref": "#/components/headers/ratelimit-reset" },
|
|
25228
|
-
"ratelimit-policy": { "$ref": "#/components/headers/ratelimit-policy-429" }
|
|
25229
|
-
},
|
|
25230
|
-
"content": { "application/json": {
|
|
25231
|
-
"schema": { "$ref": "#/components/schemas/ErrorResponse" },
|
|
25232
|
-
"example": {
|
|
25233
|
-
"success": false,
|
|
25234
|
-
"error": {
|
|
25235
|
-
"code": "rate_limit_exceeded",
|
|
25236
|
-
"message": "Rate limit exceeded"
|
|
25237
|
-
}
|
|
25238
|
-
}
|
|
25239
|
-
} }
|
|
25240
|
-
},
|
|
25241
|
-
"503": {
|
|
25242
|
-
"description": "service_unavailable: checking which rules cover you, or the hourly limit, could not be done; nothing was written (Retry-After).",
|
|
25243
|
-
"content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }
|
|
25188
|
+
{
|
|
25189
|
+
"name": "limit",
|
|
25190
|
+
"in": "query",
|
|
25191
|
+
"required": false,
|
|
25192
|
+
"description": "Versions per page when versions is not given: 1 to 100, default 50.",
|
|
25193
|
+
"schema": {
|
|
25194
|
+
"type": "integer",
|
|
25195
|
+
"minimum": 1,
|
|
25196
|
+
"maximum": 100
|
|
25244
25197
|
}
|
|
25245
25198
|
},
|
|
25246
|
-
|
|
25247
|
-
|
|
25248
|
-
"
|
|
25249
|
-
"in": "header",
|
|
25199
|
+
{
|
|
25200
|
+
"name": "cursor",
|
|
25201
|
+
"in": "query",
|
|
25250
25202
|
"required": false,
|
|
25251
|
-
"description": "
|
|
25203
|
+
"description": "meta.cursor from the preceding page. Omit for the first page. Not with versions.",
|
|
25252
25204
|
"schema": {
|
|
25253
25205
|
"type": "string",
|
|
25254
|
-
"
|
|
25255
|
-
"maxLength": 255
|
|
25256
|
-
}
|
|
25257
|
-
}]
|
|
25258
|
-
},
|
|
25259
|
-
"get": {
|
|
25260
|
-
"operationId": "listEmailRuleObjections",
|
|
25261
|
-
"summary": "List objections",
|
|
25262
|
-
"description": "Objections you made, objections to rules you wrote, or every objection you can see, newest first. An objection is seen by the member who made it, the rule's author and the organization's owners and admins. It stays pending until the rule's author accepts it, turning the rule off, or declines it, or the member who made it withdraws it, which clears its message. It never changes after that. Needs mail:read, the email rules feature, and a member's own full-access credential (no API key, agent key or narrowed token). Only on your own dashboard session or the Primitive CLI.",
|
|
25263
|
-
"tags": ["Email Rule Oversight"],
|
|
25264
|
-
"security": [{ "BearerAuth": [] }],
|
|
25265
|
-
"parameters": [
|
|
25266
|
-
{
|
|
25267
|
-
"name": "box",
|
|
25268
|
-
"in": "query",
|
|
25269
|
-
"required": false,
|
|
25270
|
-
"description": "outgoing (the default): objections you made. incoming: objections to rules you wrote. all: every objection you can see.",
|
|
25271
|
-
"schema": {
|
|
25272
|
-
"default": "outgoing",
|
|
25273
|
-
"description": "outgoing: objections you made; incoming: objections to rules you wrote; all: every objection you can see (owners and admins see the organization's).",
|
|
25274
|
-
"type": "string",
|
|
25275
|
-
"enum": [
|
|
25276
|
-
"outgoing",
|
|
25277
|
-
"incoming",
|
|
25278
|
-
"all"
|
|
25279
|
-
]
|
|
25280
|
-
}
|
|
25281
|
-
},
|
|
25282
|
-
{
|
|
25283
|
-
"name": "status",
|
|
25284
|
-
"in": "query",
|
|
25285
|
-
"required": false,
|
|
25286
|
-
"description": "pending (the default), decided, or any.",
|
|
25287
|
-
"schema": {
|
|
25288
|
-
"default": "pending",
|
|
25289
|
-
"type": "string",
|
|
25290
|
-
"enum": [
|
|
25291
|
-
"pending",
|
|
25292
|
-
"decided",
|
|
25293
|
-
"any"
|
|
25294
|
-
]
|
|
25295
|
-
}
|
|
25296
|
-
},
|
|
25297
|
-
{
|
|
25298
|
-
"name": "limit",
|
|
25299
|
-
"in": "query",
|
|
25300
|
-
"required": false,
|
|
25301
|
-
"description": "Objections per page, from 1 to 100; defaults to 25.",
|
|
25302
|
-
"schema": {
|
|
25303
|
-
"default": 25,
|
|
25304
|
-
"type": "integer",
|
|
25305
|
-
"minimum": 1,
|
|
25306
|
-
"maximum": 100
|
|
25307
|
-
}
|
|
25308
|
-
},
|
|
25309
|
-
{
|
|
25310
|
-
"name": "cursor",
|
|
25311
|
-
"in": "query",
|
|
25312
|
-
"required": false,
|
|
25313
|
-
"description": "meta.cursor from the preceding page. Omit for the first page.",
|
|
25314
|
-
"schema": {
|
|
25315
|
-
"type": "string",
|
|
25316
|
-
"maxLength": 200
|
|
25317
|
-
}
|
|
25318
|
-
}
|
|
25319
|
-
],
|
|
25320
|
-
"responses": {
|
|
25321
|
-
"200": {
|
|
25322
|
-
"description": "List objections",
|
|
25323
|
-
"content": { "application/json": { "schema": { "allOf": [{ "$ref": "#/components/schemas/SuccessEnvelope" }, {
|
|
25324
|
-
"type": "object",
|
|
25325
|
-
"required": ["data", "meta"],
|
|
25326
|
-
"properties": {
|
|
25327
|
-
"data": { "$ref": "#/components/schemas/ListEmailRuleObjectionsResult" },
|
|
25328
|
-
"meta": { "$ref": "#/components/schemas/CursorPageMeta" }
|
|
25329
|
-
}
|
|
25330
|
-
}] } } },
|
|
25331
|
-
"headers": {
|
|
25332
|
-
"ratelimit-limit": { "$ref": "#/components/headers/ratelimit-limit" },
|
|
25333
|
-
"ratelimit-remaining": { "$ref": "#/components/headers/ratelimit-remaining" },
|
|
25334
|
-
"ratelimit-reset": { "$ref": "#/components/headers/ratelimit-reset" },
|
|
25335
|
-
"ratelimit-policy": { "$ref": "#/components/headers/ratelimit-policy" }
|
|
25336
|
-
}
|
|
25337
|
-
},
|
|
25338
|
-
"400": {
|
|
25339
|
-
"description": "validation_error: a cursor not returned in meta.cursor.",
|
|
25340
|
-
"content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }
|
|
25341
|
-
},
|
|
25342
|
-
"401": {
|
|
25343
|
-
"description": "Invalid or missing API key",
|
|
25344
|
-
"content": { "application/json": {
|
|
25345
|
-
"schema": { "$ref": "#/components/schemas/ErrorResponse" },
|
|
25346
|
-
"example": {
|
|
25347
|
-
"success": false,
|
|
25348
|
-
"error": {
|
|
25349
|
-
"code": "unauthorized",
|
|
25350
|
-
"message": "Invalid or missing API key"
|
|
25351
|
-
}
|
|
25352
|
-
}
|
|
25353
|
-
} },
|
|
25354
|
-
"headers": {
|
|
25355
|
-
"ratelimit-limit": { "$ref": "#/components/headers/ratelimit-limit-unauthenticated" },
|
|
25356
|
-
"ratelimit-remaining": { "$ref": "#/components/headers/ratelimit-remaining-unauthenticated" },
|
|
25357
|
-
"ratelimit-reset": { "$ref": "#/components/headers/ratelimit-reset-unauthenticated" },
|
|
25358
|
-
"ratelimit-policy": { "$ref": "#/components/headers/ratelimit-policy-unauthenticated" }
|
|
25359
|
-
}
|
|
25360
|
-
},
|
|
25361
|
-
"403": {
|
|
25362
|
-
"description": "feature_not_enabled: this organization lacks the email rules feature; permission refusals answer forbidden or grant_forbidden; details.reason first_party_required means this credential is not your dashboard session or the Primitive CLI.",
|
|
25363
|
-
"content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }
|
|
25364
|
-
},
|
|
25365
|
-
"429": {
|
|
25366
|
-
"description": "Rate limit exceeded. The `ratelimit-*` headers describe the limiter that rejected this request, with its own limit and window, not the API budget that successful responses report. A limiter that reports only a wait sends `Retry-After` alone.",
|
|
25367
|
-
"headers": {
|
|
25368
|
-
"Retry-After": {
|
|
25369
|
-
"schema": { "type": "integer" },
|
|
25370
|
-
"description": "Seconds to wait before retrying"
|
|
25371
|
-
},
|
|
25372
|
-
"ratelimit-limit": { "$ref": "#/components/headers/ratelimit-limit-429" },
|
|
25373
|
-
"ratelimit-remaining": { "$ref": "#/components/headers/ratelimit-remaining-429" },
|
|
25374
|
-
"ratelimit-reset": { "$ref": "#/components/headers/ratelimit-reset" },
|
|
25375
|
-
"ratelimit-policy": { "$ref": "#/components/headers/ratelimit-policy-429" }
|
|
25376
|
-
},
|
|
25377
|
-
"content": { "application/json": {
|
|
25378
|
-
"schema": { "$ref": "#/components/schemas/ErrorResponse" },
|
|
25379
|
-
"example": {
|
|
25380
|
-
"success": false,
|
|
25381
|
-
"error": {
|
|
25382
|
-
"code": "rate_limit_exceeded",
|
|
25383
|
-
"message": "Rate limit exceeded"
|
|
25384
|
-
}
|
|
25385
|
-
}
|
|
25386
|
-
} }
|
|
25206
|
+
"maxLength": 200
|
|
25387
25207
|
}
|
|
25388
25208
|
},
|
|
25389
|
-
|
|
25390
|
-
|
|
25391
|
-
|
|
25392
|
-
|
|
25393
|
-
|
|
25394
|
-
|
|
25395
|
-
|
|
25396
|
-
|
|
25397
|
-
|
|
25398
|
-
|
|
25399
|
-
"name": "id",
|
|
25400
|
-
"in": "path",
|
|
25401
|
-
"required": true,
|
|
25402
|
-
"description": "The objection id.",
|
|
25403
|
-
"schema": {
|
|
25404
|
-
"type": "string",
|
|
25405
|
-
"format": "uuid"
|
|
25209
|
+
{
|
|
25210
|
+
"name": "versions",
|
|
25211
|
+
"in": "query",
|
|
25212
|
+
"required": false,
|
|
25213
|
+
"description": "Read only these version numbers, comma-separated, at most 100. Not with limit or cursor.",
|
|
25214
|
+
"schema": {
|
|
25215
|
+
"description": "Read only these version numbers, comma-separated. Without it, the versions listed, newest first, a page at a time.",
|
|
25216
|
+
"type": "string",
|
|
25217
|
+
"pattern": "^[1-9]\\d{0,8}(,[1-9]\\d{0,8}){0,99}$"
|
|
25218
|
+
}
|
|
25406
25219
|
}
|
|
25407
|
-
|
|
25220
|
+
],
|
|
25408
25221
|
"responses": {
|
|
25409
25222
|
"200": {
|
|
25410
|
-
"description": "
|
|
25223
|
+
"description": "Read versions of a rule over your mail",
|
|
25411
25224
|
"content": { "application/json": { "schema": { "allOf": [{ "$ref": "#/components/schemas/SuccessEnvelope" }, {
|
|
25412
25225
|
"type": "object",
|
|
25413
25226
|
"required": ["data"],
|
|
25414
|
-
"properties": {
|
|
25227
|
+
"properties": {
|
|
25228
|
+
"data": { "$ref": "#/components/schemas/ListEmailRuleVersionsCoveringMeResult" },
|
|
25229
|
+
"meta": {
|
|
25230
|
+
"$ref": "#/components/schemas/CursorPageMeta",
|
|
25231
|
+
"description": "Absent when versions is given."
|
|
25232
|
+
}
|
|
25233
|
+
}
|
|
25415
25234
|
}] } } },
|
|
25416
25235
|
"headers": {
|
|
25417
25236
|
"ratelimit-limit": { "$ref": "#/components/headers/ratelimit-limit" },
|
|
@@ -25420,6 +25239,10 @@ const openapiDocument = {
|
|
|
25420
25239
|
"ratelimit-policy": { "$ref": "#/components/headers/ratelimit-policy" }
|
|
25421
25240
|
}
|
|
25422
25241
|
},
|
|
25242
|
+
"400": {
|
|
25243
|
+
"description": "validation_error: a cursor not returned in meta.cursor, or versions with limit or cursor.",
|
|
25244
|
+
"content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }
|
|
25245
|
+
},
|
|
25423
25246
|
"401": {
|
|
25424
25247
|
"description": "Invalid or missing API key",
|
|
25425
25248
|
"content": { "application/json": {
|
|
@@ -25440,11 +25263,331 @@ const openapiDocument = {
|
|
|
25440
25263
|
}
|
|
25441
25264
|
},
|
|
25442
25265
|
"403": {
|
|
25443
|
-
"description": "feature_not_enabled: this organization lacks the email rules feature; permission refusals answer forbidden or grant_forbidden; details.reason first_party_required means this credential is not your dashboard session or the Primitive CLI.",
|
|
25266
|
+
"description": "feature_not_enabled: this organization lacks the email rules feature; permission refusals answer forbidden or grant_forbidden; details.reason first_party_required means this credential is not your dashboard session or the Primitive CLI. forbidden with details.reason no_longer_covers: it decided on your mail before but covers none of your mailboxes now; details.covering_view (getEmailRuleCoveringMe) shows the version that last decided on your mail.",
|
|
25444
25267
|
"content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }
|
|
25445
25268
|
},
|
|
25446
25269
|
"404": {
|
|
25447
|
-
"description": "not_found: no
|
|
25270
|
+
"description": "not_found: no rule with this id covers your mailboxes or decided on your mail.",
|
|
25271
|
+
"content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }
|
|
25272
|
+
},
|
|
25273
|
+
"429": {
|
|
25274
|
+
"description": "Rate limit exceeded. The `ratelimit-*` headers describe the limiter that rejected this request, with its own limit and window, not the API budget that successful responses report. A limiter that reports only a wait sends `Retry-After` alone.",
|
|
25275
|
+
"headers": {
|
|
25276
|
+
"Retry-After": {
|
|
25277
|
+
"schema": { "type": "integer" },
|
|
25278
|
+
"description": "Seconds to wait before retrying"
|
|
25279
|
+
},
|
|
25280
|
+
"ratelimit-limit": { "$ref": "#/components/headers/ratelimit-limit-429" },
|
|
25281
|
+
"ratelimit-remaining": { "$ref": "#/components/headers/ratelimit-remaining-429" },
|
|
25282
|
+
"ratelimit-reset": { "$ref": "#/components/headers/ratelimit-reset" },
|
|
25283
|
+
"ratelimit-policy": { "$ref": "#/components/headers/ratelimit-policy-429" }
|
|
25284
|
+
},
|
|
25285
|
+
"content": { "application/json": {
|
|
25286
|
+
"schema": { "$ref": "#/components/schemas/ErrorResponse" },
|
|
25287
|
+
"example": {
|
|
25288
|
+
"success": false,
|
|
25289
|
+
"error": {
|
|
25290
|
+
"code": "rate_limit_exceeded",
|
|
25291
|
+
"message": "Rate limit exceeded"
|
|
25292
|
+
}
|
|
25293
|
+
}
|
|
25294
|
+
} }
|
|
25295
|
+
}
|
|
25296
|
+
},
|
|
25297
|
+
"x-primitive-permission": ["mail:read"]
|
|
25298
|
+
} },
|
|
25299
|
+
"/email-rule-objections": {
|
|
25300
|
+
"post": {
|
|
25301
|
+
"operationId": "createEmailRuleObjection",
|
|
25302
|
+
"summary": "Object to a rule over your mail",
|
|
25303
|
+
"description": "Asks the author of a rule that covers one of your own mailboxes now to turn it off. Only a rule that covers you now: one you see only because it once decided on your mail cannot be objected to. An objection is seen by the member who made it, the rule's author and the organization's owners and admins. It stays pending until the rule's author accepts it, turning the rule off, or declines it, or the member who made it withdraws it, which clears its message. It never changes after that. You may have one pending objection per rule and 50 in all, and object to the same rule again only 10 minutes after you last did, whatever became of it. Needs mail:read, the email rules feature, and a member's own full-access credential (no API key, agent key or narrowed token). Only on your own dashboard session or the Primitive CLI.",
|
|
25304
|
+
"tags": ["Email Rule Oversight"],
|
|
25305
|
+
"security": [{ "BearerAuth": [] }],
|
|
25306
|
+
"requestBody": {
|
|
25307
|
+
"required": true,
|
|
25308
|
+
"content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreateEmailRuleObjectionRequest" } } }
|
|
25309
|
+
},
|
|
25310
|
+
"responses": {
|
|
25311
|
+
"200": {
|
|
25312
|
+
"description": "Object to a rule over your mail",
|
|
25313
|
+
"content": { "application/json": { "schema": { "allOf": [{ "$ref": "#/components/schemas/SuccessEnvelope" }, {
|
|
25314
|
+
"type": "object",
|
|
25315
|
+
"required": ["data"],
|
|
25316
|
+
"properties": { "data": { "$ref": "#/components/schemas/CreateEmailRuleObjectionResult" } }
|
|
25317
|
+
}] } } },
|
|
25318
|
+
"headers": {
|
|
25319
|
+
"ratelimit-limit": { "$ref": "#/components/headers/ratelimit-limit" },
|
|
25320
|
+
"ratelimit-remaining": { "$ref": "#/components/headers/ratelimit-remaining" },
|
|
25321
|
+
"ratelimit-reset": { "$ref": "#/components/headers/ratelimit-reset" },
|
|
25322
|
+
"ratelimit-policy": { "$ref": "#/components/headers/ratelimit-policy" }
|
|
25323
|
+
}
|
|
25324
|
+
},
|
|
25325
|
+
"400": {
|
|
25326
|
+
"description": "validation_error; details.reason own_rule: it is your own rule, which you can turn off yourself.",
|
|
25327
|
+
"content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }
|
|
25328
|
+
},
|
|
25329
|
+
"401": {
|
|
25330
|
+
"description": "Invalid or missing API key",
|
|
25331
|
+
"content": { "application/json": {
|
|
25332
|
+
"schema": { "$ref": "#/components/schemas/ErrorResponse" },
|
|
25333
|
+
"example": {
|
|
25334
|
+
"success": false,
|
|
25335
|
+
"error": {
|
|
25336
|
+
"code": "unauthorized",
|
|
25337
|
+
"message": "Invalid or missing API key"
|
|
25338
|
+
}
|
|
25339
|
+
}
|
|
25340
|
+
} },
|
|
25341
|
+
"headers": {
|
|
25342
|
+
"ratelimit-limit": { "$ref": "#/components/headers/ratelimit-limit-unauthenticated" },
|
|
25343
|
+
"ratelimit-remaining": { "$ref": "#/components/headers/ratelimit-remaining-unauthenticated" },
|
|
25344
|
+
"ratelimit-reset": { "$ref": "#/components/headers/ratelimit-reset-unauthenticated" },
|
|
25345
|
+
"ratelimit-policy": { "$ref": "#/components/headers/ratelimit-policy-unauthenticated" }
|
|
25346
|
+
}
|
|
25347
|
+
},
|
|
25348
|
+
"403": {
|
|
25349
|
+
"description": "feature_not_enabled: this organization lacks the email rules feature; permission refusals answer forbidden or grant_forbidden; details.reason first_party_required means this credential is not your dashboard session or the Primitive CLI.",
|
|
25350
|
+
"content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }
|
|
25351
|
+
},
|
|
25352
|
+
"404": {
|
|
25353
|
+
"description": "not_found: no rule with this id covers your mailboxes now.",
|
|
25354
|
+
"content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }
|
|
25355
|
+
},
|
|
25356
|
+
"409": {
|
|
25357
|
+
"description": "conflict: details.reason objection_pending, you already have a pending objection to this rule (details.objection_id); objection_limit, you have 50 pending objections.",
|
|
25358
|
+
"content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }
|
|
25359
|
+
},
|
|
25360
|
+
"429": {
|
|
25361
|
+
"description": "Rate limit exceeded. The `ratelimit-*` headers describe the limiter that rejected this request, with its own limit and window, not the API budget that successful responses report. A limiter that reports only a wait sends `Retry-After` alone.",
|
|
25362
|
+
"headers": {
|
|
25363
|
+
"Retry-After": {
|
|
25364
|
+
"schema": { "type": "integer" },
|
|
25365
|
+
"description": "Seconds to wait before retrying"
|
|
25366
|
+
},
|
|
25367
|
+
"ratelimit-limit": { "$ref": "#/components/headers/ratelimit-limit-429" },
|
|
25368
|
+
"ratelimit-remaining": { "$ref": "#/components/headers/ratelimit-remaining-429" },
|
|
25369
|
+
"ratelimit-reset": { "$ref": "#/components/headers/ratelimit-reset" },
|
|
25370
|
+
"ratelimit-policy": { "$ref": "#/components/headers/ratelimit-policy-429" }
|
|
25371
|
+
},
|
|
25372
|
+
"content": { "application/json": {
|
|
25373
|
+
"schema": { "$ref": "#/components/schemas/ErrorResponse" },
|
|
25374
|
+
"example": {
|
|
25375
|
+
"success": false,
|
|
25376
|
+
"error": {
|
|
25377
|
+
"code": "rate_limit_exceeded",
|
|
25378
|
+
"message": "Rate limit exceeded"
|
|
25379
|
+
}
|
|
25380
|
+
}
|
|
25381
|
+
} }
|
|
25382
|
+
},
|
|
25383
|
+
"503": {
|
|
25384
|
+
"description": "service_unavailable: checking which rules cover you, or the hourly limit, could not be done; nothing was written (Retry-After).",
|
|
25385
|
+
"content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }
|
|
25386
|
+
}
|
|
25387
|
+
},
|
|
25388
|
+
"x-primitive-permission": ["mail:read"],
|
|
25389
|
+
"parameters": [{
|
|
25390
|
+
"name": "Idempotency-Key",
|
|
25391
|
+
"in": "header",
|
|
25392
|
+
"required": false,
|
|
25393
|
+
"description": "Optional, and not used to deduplicate: this operation stores no result by the key, so a retry with the same key runs the request again. Operations that need a credential refuse a key that is empty, longer than 255 characters or not printable ASCII with 400 `validation_error` and echo a valid one in the Idempotency-Key response header; operations that need none ignore it.",
|
|
25394
|
+
"schema": {
|
|
25395
|
+
"type": "string",
|
|
25396
|
+
"minLength": 1,
|
|
25397
|
+
"maxLength": 255
|
|
25398
|
+
}
|
|
25399
|
+
}]
|
|
25400
|
+
},
|
|
25401
|
+
"get": {
|
|
25402
|
+
"operationId": "listEmailRuleObjections",
|
|
25403
|
+
"summary": "List objections",
|
|
25404
|
+
"description": "Objections you made and objections to rules you wrote (by default), only one of the two, or every objection you can see, newest first. An objection is seen by the member who made it, the rule's author and the organization's owners and admins. It stays pending until the rule's author accepts it, turning the rule off, or declines it, or the member who made it withdraws it, which clears its message. It never changes after that. Needs mail:read, the email rules feature, and a member's own full-access credential (no API key, agent key or narrowed token). Only on your own dashboard session or the Primitive CLI.",
|
|
25405
|
+
"tags": ["Email Rule Oversight"],
|
|
25406
|
+
"security": [{ "BearerAuth": [] }],
|
|
25407
|
+
"parameters": [
|
|
25408
|
+
{
|
|
25409
|
+
"name": "box",
|
|
25410
|
+
"in": "query",
|
|
25411
|
+
"required": false,
|
|
25412
|
+
"description": "mine (the default): objections you made and objections to rules you wrote, each saying which (is_objector, is_author). outgoing: only those you made. incoming: only those to rules you wrote. all: every objection you can see.",
|
|
25413
|
+
"schema": {
|
|
25414
|
+
"default": "mine",
|
|
25415
|
+
"description": "mine: objections you made and objections to rules you wrote (is_objector or is_author says which); outgoing: only those you made; incoming: only those to rules you wrote; each in your current membership. all: every objection you can see (owners and admins see the organization's).",
|
|
25416
|
+
"type": "string",
|
|
25417
|
+
"enum": [
|
|
25418
|
+
"mine",
|
|
25419
|
+
"outgoing",
|
|
25420
|
+
"incoming",
|
|
25421
|
+
"all"
|
|
25422
|
+
]
|
|
25423
|
+
}
|
|
25424
|
+
},
|
|
25425
|
+
{
|
|
25426
|
+
"name": "status",
|
|
25427
|
+
"in": "query",
|
|
25428
|
+
"required": false,
|
|
25429
|
+
"description": "pending (the default), decided, or any.",
|
|
25430
|
+
"schema": {
|
|
25431
|
+
"default": "pending",
|
|
25432
|
+
"type": "string",
|
|
25433
|
+
"enum": [
|
|
25434
|
+
"pending",
|
|
25435
|
+
"decided",
|
|
25436
|
+
"any"
|
|
25437
|
+
]
|
|
25438
|
+
}
|
|
25439
|
+
},
|
|
25440
|
+
{
|
|
25441
|
+
"name": "limit",
|
|
25442
|
+
"in": "query",
|
|
25443
|
+
"required": false,
|
|
25444
|
+
"description": "Objections per page, from 1 to 100; defaults to 25.",
|
|
25445
|
+
"schema": {
|
|
25446
|
+
"default": 25,
|
|
25447
|
+
"type": "integer",
|
|
25448
|
+
"minimum": 1,
|
|
25449
|
+
"maximum": 100
|
|
25450
|
+
}
|
|
25451
|
+
},
|
|
25452
|
+
{
|
|
25453
|
+
"name": "cursor",
|
|
25454
|
+
"in": "query",
|
|
25455
|
+
"required": false,
|
|
25456
|
+
"description": "meta.cursor from the preceding page. Omit for the first page.",
|
|
25457
|
+
"schema": {
|
|
25458
|
+
"type": "string",
|
|
25459
|
+
"maxLength": 200
|
|
25460
|
+
}
|
|
25461
|
+
}
|
|
25462
|
+
],
|
|
25463
|
+
"responses": {
|
|
25464
|
+
"200": {
|
|
25465
|
+
"description": "List objections",
|
|
25466
|
+
"content": { "application/json": { "schema": { "allOf": [{ "$ref": "#/components/schemas/SuccessEnvelope" }, {
|
|
25467
|
+
"type": "object",
|
|
25468
|
+
"required": ["data", "meta"],
|
|
25469
|
+
"properties": {
|
|
25470
|
+
"data": { "$ref": "#/components/schemas/ListEmailRuleObjectionsResult" },
|
|
25471
|
+
"meta": { "$ref": "#/components/schemas/CursorPageMeta" }
|
|
25472
|
+
}
|
|
25473
|
+
}] } } },
|
|
25474
|
+
"headers": {
|
|
25475
|
+
"ratelimit-limit": { "$ref": "#/components/headers/ratelimit-limit" },
|
|
25476
|
+
"ratelimit-remaining": { "$ref": "#/components/headers/ratelimit-remaining" },
|
|
25477
|
+
"ratelimit-reset": { "$ref": "#/components/headers/ratelimit-reset" },
|
|
25478
|
+
"ratelimit-policy": { "$ref": "#/components/headers/ratelimit-policy" }
|
|
25479
|
+
}
|
|
25480
|
+
},
|
|
25481
|
+
"400": {
|
|
25482
|
+
"description": "validation_error: a cursor not returned in meta.cursor.",
|
|
25483
|
+
"content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }
|
|
25484
|
+
},
|
|
25485
|
+
"401": {
|
|
25486
|
+
"description": "Invalid or missing API key",
|
|
25487
|
+
"content": { "application/json": {
|
|
25488
|
+
"schema": { "$ref": "#/components/schemas/ErrorResponse" },
|
|
25489
|
+
"example": {
|
|
25490
|
+
"success": false,
|
|
25491
|
+
"error": {
|
|
25492
|
+
"code": "unauthorized",
|
|
25493
|
+
"message": "Invalid or missing API key"
|
|
25494
|
+
}
|
|
25495
|
+
}
|
|
25496
|
+
} },
|
|
25497
|
+
"headers": {
|
|
25498
|
+
"ratelimit-limit": { "$ref": "#/components/headers/ratelimit-limit-unauthenticated" },
|
|
25499
|
+
"ratelimit-remaining": { "$ref": "#/components/headers/ratelimit-remaining-unauthenticated" },
|
|
25500
|
+
"ratelimit-reset": { "$ref": "#/components/headers/ratelimit-reset-unauthenticated" },
|
|
25501
|
+
"ratelimit-policy": { "$ref": "#/components/headers/ratelimit-policy-unauthenticated" }
|
|
25502
|
+
}
|
|
25503
|
+
},
|
|
25504
|
+
"403": {
|
|
25505
|
+
"description": "feature_not_enabled: this organization lacks the email rules feature; permission refusals answer forbidden or grant_forbidden; details.reason first_party_required means this credential is not your dashboard session or the Primitive CLI.",
|
|
25506
|
+
"content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }
|
|
25507
|
+
},
|
|
25508
|
+
"429": {
|
|
25509
|
+
"description": "Rate limit exceeded. The `ratelimit-*` headers describe the limiter that rejected this request, with its own limit and window, not the API budget that successful responses report. A limiter that reports only a wait sends `Retry-After` alone.",
|
|
25510
|
+
"headers": {
|
|
25511
|
+
"Retry-After": {
|
|
25512
|
+
"schema": { "type": "integer" },
|
|
25513
|
+
"description": "Seconds to wait before retrying"
|
|
25514
|
+
},
|
|
25515
|
+
"ratelimit-limit": { "$ref": "#/components/headers/ratelimit-limit-429" },
|
|
25516
|
+
"ratelimit-remaining": { "$ref": "#/components/headers/ratelimit-remaining-429" },
|
|
25517
|
+
"ratelimit-reset": { "$ref": "#/components/headers/ratelimit-reset" },
|
|
25518
|
+
"ratelimit-policy": { "$ref": "#/components/headers/ratelimit-policy-429" }
|
|
25519
|
+
},
|
|
25520
|
+
"content": { "application/json": {
|
|
25521
|
+
"schema": { "$ref": "#/components/schemas/ErrorResponse" },
|
|
25522
|
+
"example": {
|
|
25523
|
+
"success": false,
|
|
25524
|
+
"error": {
|
|
25525
|
+
"code": "rate_limit_exceeded",
|
|
25526
|
+
"message": "Rate limit exceeded"
|
|
25527
|
+
}
|
|
25528
|
+
}
|
|
25529
|
+
} }
|
|
25530
|
+
}
|
|
25531
|
+
},
|
|
25532
|
+
"x-primitive-permission": ["mail:read"]
|
|
25533
|
+
}
|
|
25534
|
+
},
|
|
25535
|
+
"/email-rule-objections/{id}": { "get": {
|
|
25536
|
+
"operationId": "getEmailRuleObjection",
|
|
25537
|
+
"summary": "Get an objection",
|
|
25538
|
+
"description": "One objection you can see. An objection is seen by the member who made it, the rule's author and the organization's owners and admins. It stays pending until the rule's author accepts it, turning the rule off, or declines it, or the member who made it withdraws it, which clears its message. It never changes after that. Needs mail:read, the email rules feature, and a member's own full-access credential (no API key, agent key or narrowed token). Only on your own dashboard session or the Primitive CLI.",
|
|
25539
|
+
"tags": ["Email Rule Oversight"],
|
|
25540
|
+
"security": [{ "BearerAuth": [] }],
|
|
25541
|
+
"parameters": [{
|
|
25542
|
+
"name": "id",
|
|
25543
|
+
"in": "path",
|
|
25544
|
+
"required": true,
|
|
25545
|
+
"description": "The objection id.",
|
|
25546
|
+
"schema": {
|
|
25547
|
+
"type": "string",
|
|
25548
|
+
"format": "uuid"
|
|
25549
|
+
}
|
|
25550
|
+
}],
|
|
25551
|
+
"responses": {
|
|
25552
|
+
"200": {
|
|
25553
|
+
"description": "Get an objection",
|
|
25554
|
+
"content": { "application/json": { "schema": { "allOf": [{ "$ref": "#/components/schemas/SuccessEnvelope" }, {
|
|
25555
|
+
"type": "object",
|
|
25556
|
+
"required": ["data"],
|
|
25557
|
+
"properties": { "data": { "$ref": "#/components/schemas/GetEmailRuleObjectionResult" } }
|
|
25558
|
+
}] } } },
|
|
25559
|
+
"headers": {
|
|
25560
|
+
"ratelimit-limit": { "$ref": "#/components/headers/ratelimit-limit" },
|
|
25561
|
+
"ratelimit-remaining": { "$ref": "#/components/headers/ratelimit-remaining" },
|
|
25562
|
+
"ratelimit-reset": { "$ref": "#/components/headers/ratelimit-reset" },
|
|
25563
|
+
"ratelimit-policy": { "$ref": "#/components/headers/ratelimit-policy" }
|
|
25564
|
+
}
|
|
25565
|
+
},
|
|
25566
|
+
"401": {
|
|
25567
|
+
"description": "Invalid or missing API key",
|
|
25568
|
+
"content": { "application/json": {
|
|
25569
|
+
"schema": { "$ref": "#/components/schemas/ErrorResponse" },
|
|
25570
|
+
"example": {
|
|
25571
|
+
"success": false,
|
|
25572
|
+
"error": {
|
|
25573
|
+
"code": "unauthorized",
|
|
25574
|
+
"message": "Invalid or missing API key"
|
|
25575
|
+
}
|
|
25576
|
+
}
|
|
25577
|
+
} },
|
|
25578
|
+
"headers": {
|
|
25579
|
+
"ratelimit-limit": { "$ref": "#/components/headers/ratelimit-limit-unauthenticated" },
|
|
25580
|
+
"ratelimit-remaining": { "$ref": "#/components/headers/ratelimit-remaining-unauthenticated" },
|
|
25581
|
+
"ratelimit-reset": { "$ref": "#/components/headers/ratelimit-reset-unauthenticated" },
|
|
25582
|
+
"ratelimit-policy": { "$ref": "#/components/headers/ratelimit-policy-unauthenticated" }
|
|
25583
|
+
}
|
|
25584
|
+
},
|
|
25585
|
+
"403": {
|
|
25586
|
+
"description": "feature_not_enabled: this organization lacks the email rules feature; permission refusals answer forbidden or grant_forbidden; details.reason first_party_required means this credential is not your dashboard session or the Primitive CLI.",
|
|
25587
|
+
"content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }
|
|
25588
|
+
},
|
|
25589
|
+
"404": {
|
|
25590
|
+
"description": "not_found: no objection with this id that you can see.",
|
|
25448
25591
|
"content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }
|
|
25449
25592
|
},
|
|
25450
25593
|
"429": {
|
|
@@ -26106,6 +26249,218 @@ const openapiDocument = {
|
|
|
26106
26249
|
}
|
|
26107
26250
|
}]
|
|
26108
26251
|
} },
|
|
26252
|
+
"/mail-imports/google/messages": { "post": {
|
|
26253
|
+
"operationId": "importGoogleMailMessage",
|
|
26254
|
+
"summary": "Import one historical Google Workspace message",
|
|
26255
|
+
"description": "Requires the organization email_import entitlement, granted by staff. Only the configured current owner, using a full personal session or verified first-party CLI, may import into the configured primary mailbox. Primitive verifies the transient provider token and fetches RAW content itself. No raw uploads or client provenance are accepted. Sent, draft, spam and trash messages are skipped. Historical mail appears in normal history without live delivery effects. An indeterminate response requires retrying the same provider ID. User provider credentials are never stored or queued.",
|
|
26256
|
+
"tags": ["Emails"],
|
|
26257
|
+
"security": [{ "BearerAuth": [] }],
|
|
26258
|
+
"requestBody": {
|
|
26259
|
+
"required": true,
|
|
26260
|
+
"content": { "application/json": { "schema": { "$ref": "#/components/schemas/ImportGoogleMailMessageRequest" } } }
|
|
26261
|
+
},
|
|
26262
|
+
"responses": {
|
|
26263
|
+
"200": {
|
|
26264
|
+
"description": "Import one historical Google Workspace message",
|
|
26265
|
+
"content": { "application/json": { "schema": { "allOf": [{ "$ref": "#/components/schemas/SuccessEnvelope" }, {
|
|
26266
|
+
"type": "object",
|
|
26267
|
+
"required": ["data"],
|
|
26268
|
+
"properties": { "data": { "$ref": "#/components/schemas/ImportGoogleMailMessageResult" } }
|
|
26269
|
+
}] } } },
|
|
26270
|
+
"headers": {
|
|
26271
|
+
"ratelimit-limit": { "$ref": "#/components/headers/ratelimit-limit" },
|
|
26272
|
+
"ratelimit-remaining": { "$ref": "#/components/headers/ratelimit-remaining" },
|
|
26273
|
+
"ratelimit-reset": { "$ref": "#/components/headers/ratelimit-reset" },
|
|
26274
|
+
"ratelimit-policy": { "$ref": "#/components/headers/ratelimit-policy" }
|
|
26275
|
+
}
|
|
26276
|
+
},
|
|
26277
|
+
"400": {
|
|
26278
|
+
"description": "Invalid request parameters",
|
|
26279
|
+
"content": { "application/json": {
|
|
26280
|
+
"schema": { "$ref": "#/components/schemas/ErrorResponse" },
|
|
26281
|
+
"example": {
|
|
26282
|
+
"success": false,
|
|
26283
|
+
"error": {
|
|
26284
|
+
"code": "validation_error",
|
|
26285
|
+
"message": "Invalid domain format"
|
|
26286
|
+
}
|
|
26287
|
+
}
|
|
26288
|
+
} }
|
|
26289
|
+
},
|
|
26290
|
+
"401": {
|
|
26291
|
+
"description": "mail_import_provider_auth: reauthorize the provider.",
|
|
26292
|
+
"content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }
|
|
26293
|
+
},
|
|
26294
|
+
"403": {
|
|
26295
|
+
"description": "feature_disabled: the organization lacks email_import; or mail_import_forbidden: identity or current access differs.",
|
|
26296
|
+
"content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }
|
|
26297
|
+
},
|
|
26298
|
+
"409": {
|
|
26299
|
+
"description": "mail_import_cap_exceeded or mail_import_storage_limit: the fixed pilot budget is exhausted.",
|
|
26300
|
+
"content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }
|
|
26301
|
+
},
|
|
26302
|
+
"422": {
|
|
26303
|
+
"description": "Mail import input, content, storage or publication fence could not be accepted.",
|
|
26304
|
+
"content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }
|
|
26305
|
+
},
|
|
26306
|
+
"429": {
|
|
26307
|
+
"description": "Rate limit exceeded. The `ratelimit-*` headers describe the limiter that rejected this request, with its own limit and window, not the API budget that successful responses report. A limiter that reports only a wait sends `Retry-After` alone.",
|
|
26308
|
+
"headers": {
|
|
26309
|
+
"Retry-After": {
|
|
26310
|
+
"schema": { "type": "integer" },
|
|
26311
|
+
"description": "Seconds to wait before retrying"
|
|
26312
|
+
},
|
|
26313
|
+
"ratelimit-limit": { "$ref": "#/components/headers/ratelimit-limit-429" },
|
|
26314
|
+
"ratelimit-remaining": { "$ref": "#/components/headers/ratelimit-remaining-429" },
|
|
26315
|
+
"ratelimit-reset": { "$ref": "#/components/headers/ratelimit-reset" },
|
|
26316
|
+
"ratelimit-policy": { "$ref": "#/components/headers/ratelimit-policy-429" }
|
|
26317
|
+
},
|
|
26318
|
+
"content": { "application/json": {
|
|
26319
|
+
"schema": { "$ref": "#/components/schemas/ErrorResponse" },
|
|
26320
|
+
"example": {
|
|
26321
|
+
"success": false,
|
|
26322
|
+
"error": {
|
|
26323
|
+
"code": "rate_limit_exceeded",
|
|
26324
|
+
"message": "Rate limit exceeded"
|
|
26325
|
+
}
|
|
26326
|
+
}
|
|
26327
|
+
} }
|
|
26328
|
+
},
|
|
26329
|
+
"502": {
|
|
26330
|
+
"description": "Provider acquisition failed or exceeded its deadline.",
|
|
26331
|
+
"content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }
|
|
26332
|
+
},
|
|
26333
|
+
"503": {
|
|
26334
|
+
"description": "mail_import_disabled or mail_import_unavailable; retry the same ID after resolving the cause.",
|
|
26335
|
+
"content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }
|
|
26336
|
+
}
|
|
26337
|
+
},
|
|
26338
|
+
"parameters": [{
|
|
26339
|
+
"in": "header",
|
|
26340
|
+
"name": "X-Primitive-Google-Access-Token",
|
|
26341
|
+
"required": true,
|
|
26342
|
+
"description": "Transient provider user access token. Send only in this redacted header, never in a URL or JSON body. Uses the dedicated configured desktop client and readonly/identity scopes.",
|
|
26343
|
+
"schema": {
|
|
26344
|
+
"type": "string",
|
|
26345
|
+
"minLength": 1,
|
|
26346
|
+
"maxLength": 8192,
|
|
26347
|
+
"writeOnly": true
|
|
26348
|
+
}
|
|
26349
|
+
}, {
|
|
26350
|
+
"name": "Idempotency-Key",
|
|
26351
|
+
"in": "header",
|
|
26352
|
+
"required": false,
|
|
26353
|
+
"description": "Optional, and not used to deduplicate: this operation stores no result by the key, so a retry with the same key runs the request again. Operations that need a credential refuse a key that is empty, longer than 255 characters or not printable ASCII with 400 `validation_error` and echo a valid one in the Idempotency-Key response header; operations that need none ignore it.",
|
|
26354
|
+
"schema": {
|
|
26355
|
+
"type": "string",
|
|
26356
|
+
"minLength": 1,
|
|
26357
|
+
"maxLength": 255
|
|
26358
|
+
}
|
|
26359
|
+
}],
|
|
26360
|
+
"x-primitive-permission": ["mail:read"]
|
|
26361
|
+
} },
|
|
26362
|
+
"/mail-imports/google/reconcile": { "post": {
|
|
26363
|
+
"operationId": "reconcileGoogleMailImport",
|
|
26364
|
+
"summary": "Reconcile scoped historical import attempts",
|
|
26365
|
+
"description": "The same configured owner may recover expired attempts after the email_import entitlement is revoked. Recovery does not require that entitlement. Selects only server-owned manifests for the configured mailbox/account/policy. No provider token, object keys, prefixes or email IDs are accepted. Unsettled uploads remain unresolved and charged; this operation does not promise automatic cleanup after a process dies.",
|
|
26366
|
+
"tags": ["Emails"],
|
|
26367
|
+
"security": [{ "BearerAuth": [] }],
|
|
26368
|
+
"requestBody": {
|
|
26369
|
+
"required": true,
|
|
26370
|
+
"content": { "application/json": { "schema": { "$ref": "#/components/schemas/ReconcileGoogleMailImportRequest" } } }
|
|
26371
|
+
},
|
|
26372
|
+
"responses": {
|
|
26373
|
+
"200": {
|
|
26374
|
+
"description": "Reconcile scoped historical import attempts",
|
|
26375
|
+
"content": { "application/json": { "schema": { "allOf": [{ "$ref": "#/components/schemas/SuccessEnvelope" }, {
|
|
26376
|
+
"type": "object",
|
|
26377
|
+
"required": ["data"],
|
|
26378
|
+
"properties": { "data": { "$ref": "#/components/schemas/ReconcileGoogleMailImportResult" } }
|
|
26379
|
+
}] } } },
|
|
26380
|
+
"headers": {
|
|
26381
|
+
"ratelimit-limit": { "$ref": "#/components/headers/ratelimit-limit" },
|
|
26382
|
+
"ratelimit-remaining": { "$ref": "#/components/headers/ratelimit-remaining" },
|
|
26383
|
+
"ratelimit-reset": { "$ref": "#/components/headers/ratelimit-reset" },
|
|
26384
|
+
"ratelimit-policy": { "$ref": "#/components/headers/ratelimit-policy" }
|
|
26385
|
+
}
|
|
26386
|
+
},
|
|
26387
|
+
"400": {
|
|
26388
|
+
"description": "Invalid request parameters",
|
|
26389
|
+
"content": { "application/json": {
|
|
26390
|
+
"schema": { "$ref": "#/components/schemas/ErrorResponse" },
|
|
26391
|
+
"example": {
|
|
26392
|
+
"success": false,
|
|
26393
|
+
"error": {
|
|
26394
|
+
"code": "validation_error",
|
|
26395
|
+
"message": "Invalid domain format"
|
|
26396
|
+
}
|
|
26397
|
+
}
|
|
26398
|
+
} }
|
|
26399
|
+
},
|
|
26400
|
+
"401": {
|
|
26401
|
+
"description": "Invalid or missing API key",
|
|
26402
|
+
"content": { "application/json": {
|
|
26403
|
+
"schema": { "$ref": "#/components/schemas/ErrorResponse" },
|
|
26404
|
+
"example": {
|
|
26405
|
+
"success": false,
|
|
26406
|
+
"error": {
|
|
26407
|
+
"code": "unauthorized",
|
|
26408
|
+
"message": "Invalid or missing API key"
|
|
26409
|
+
}
|
|
26410
|
+
}
|
|
26411
|
+
} },
|
|
26412
|
+
"headers": {
|
|
26413
|
+
"ratelimit-limit": { "$ref": "#/components/headers/ratelimit-limit-unauthenticated" },
|
|
26414
|
+
"ratelimit-remaining": { "$ref": "#/components/headers/ratelimit-remaining-unauthenticated" },
|
|
26415
|
+
"ratelimit-reset": { "$ref": "#/components/headers/ratelimit-reset-unauthenticated" },
|
|
26416
|
+
"ratelimit-policy": { "$ref": "#/components/headers/ratelimit-policy-unauthenticated" }
|
|
26417
|
+
}
|
|
26418
|
+
},
|
|
26419
|
+
"403": {
|
|
26420
|
+
"description": "mail_import_forbidden: current owner access is required.",
|
|
26421
|
+
"content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }
|
|
26422
|
+
},
|
|
26423
|
+
"429": {
|
|
26424
|
+
"description": "Rate limit exceeded. The `ratelimit-*` headers describe the limiter that rejected this request, with its own limit and window, not the API budget that successful responses report. A limiter that reports only a wait sends `Retry-After` alone.",
|
|
26425
|
+
"headers": {
|
|
26426
|
+
"Retry-After": {
|
|
26427
|
+
"schema": { "type": "integer" },
|
|
26428
|
+
"description": "Seconds to wait before retrying"
|
|
26429
|
+
},
|
|
26430
|
+
"ratelimit-limit": { "$ref": "#/components/headers/ratelimit-limit-429" },
|
|
26431
|
+
"ratelimit-remaining": { "$ref": "#/components/headers/ratelimit-remaining-429" },
|
|
26432
|
+
"ratelimit-reset": { "$ref": "#/components/headers/ratelimit-reset" },
|
|
26433
|
+
"ratelimit-policy": { "$ref": "#/components/headers/ratelimit-policy-429" }
|
|
26434
|
+
},
|
|
26435
|
+
"content": { "application/json": {
|
|
26436
|
+
"schema": { "$ref": "#/components/schemas/ErrorResponse" },
|
|
26437
|
+
"example": {
|
|
26438
|
+
"success": false,
|
|
26439
|
+
"error": {
|
|
26440
|
+
"code": "rate_limit_exceeded",
|
|
26441
|
+
"message": "Rate limit exceeded"
|
|
26442
|
+
}
|
|
26443
|
+
}
|
|
26444
|
+
} }
|
|
26445
|
+
},
|
|
26446
|
+
"503": {
|
|
26447
|
+
"description": "mail_import_disabled: no valid policy is configured; or mail_import_unavailable.",
|
|
26448
|
+
"content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }
|
|
26449
|
+
}
|
|
26450
|
+
},
|
|
26451
|
+
"x-primitive-permission": ["mail:read"],
|
|
26452
|
+
"parameters": [{
|
|
26453
|
+
"name": "Idempotency-Key",
|
|
26454
|
+
"in": "header",
|
|
26455
|
+
"required": false,
|
|
26456
|
+
"description": "Optional, and not used to deduplicate: this operation stores no result by the key, so a retry with the same key runs the request again. Operations that need a credential refuse a key that is empty, longer than 255 characters or not printable ASCII with 400 `validation_error` and echo a valid one in the Idempotency-Key response header; operations that need none ignore it.",
|
|
26457
|
+
"schema": {
|
|
26458
|
+
"type": "string",
|
|
26459
|
+
"minLength": 1,
|
|
26460
|
+
"maxLength": 255
|
|
26461
|
+
}
|
|
26462
|
+
}]
|
|
26463
|
+
} },
|
|
26109
26464
|
"/emails/{id}/signal": { "post": {
|
|
26110
26465
|
"operationId": "signalEmail",
|
|
26111
26466
|
"summary": "Send a read, working or typing signal about an email",
|
|
@@ -35683,7 +36038,7 @@ const openapiDocument = {
|
|
|
35683
36038
|
"name": "Primitive-Step-Up",
|
|
35684
36039
|
"in": "header",
|
|
35685
36040
|
"required": false,
|
|
35686
|
-
"description": "A single-use step-up token of purpose `org_settings.write` (Change the organization name, logo
|
|
36041
|
+
"description": "A single-use step-up token of purpose `org_settings.write` (Change the organization name, logo, semantic search setting or email notification settings), valid 5 minutes. When this operation answers 403 `step_up_required`, the person confirms at the link in `details.step_up_url` and the request is retried with the token that page shows, in this header. Accepted only with the person's own OAuth token of the client it was confirmed for; refused with a token issued for MCP, with any API key or agent connection key, and with a dashboard session.",
|
|
35687
36042
|
"schema": {
|
|
35688
36043
|
"type": "string",
|
|
35689
36044
|
"pattern": "^pstep_[0-9a-f]{64}$"
|
|
@@ -35834,7 +36189,7 @@ const openapiDocument = {
|
|
|
35834
36189
|
"name": "Primitive-Step-Up",
|
|
35835
36190
|
"in": "header",
|
|
35836
36191
|
"required": false,
|
|
35837
|
-
"description": "A single-use step-up token of purpose `org_settings.write` (Change the organization name, logo
|
|
36192
|
+
"description": "A single-use step-up token of purpose `org_settings.write` (Change the organization name, logo, semantic search setting or email notification settings), valid 5 minutes. When this operation answers 403 `step_up_required`, the person confirms at the link in `details.step_up_url` and the request is retried with the token that page shows, in this header. Accepted only with the person's own OAuth token of the client it was confirmed for; refused with a token issued for MCP, with any API key or agent connection key, and with a dashboard session.",
|
|
35838
36193
|
"schema": {
|
|
35839
36194
|
"type": "string",
|
|
35840
36195
|
"pattern": "^pstep_[0-9a-f]{64}$"
|
|
@@ -35983,7 +36338,7 @@ const openapiDocument = {
|
|
|
35983
36338
|
"name": "Primitive-Step-Up",
|
|
35984
36339
|
"in": "header",
|
|
35985
36340
|
"required": false,
|
|
35986
|
-
"description": "A single-use step-up token of purpose `org_settings.write` (Change the organization name, logo
|
|
36341
|
+
"description": "A single-use step-up token of purpose `org_settings.write` (Change the organization name, logo, semantic search setting or email notification settings), valid 5 minutes. When this operation answers 403 `step_up_required`, the person confirms at the link in `details.step_up_url` and the request is retried with the token that page shows, in this header. Accepted only with the person's own OAuth token of the client it was confirmed for; refused with a token issued for MCP, with any API key or agent connection key, and with a dashboard session.",
|
|
35987
36342
|
"schema": {
|
|
35988
36343
|
"type": "string",
|
|
35989
36344
|
"pattern": "^pstep_[0-9a-f]{64}$"
|
|
@@ -36094,34 +36449,548 @@ const openapiDocument = {
|
|
|
36094
36449
|
}
|
|
36095
36450
|
}
|
|
36096
36451
|
} }
|
|
36097
|
-
},
|
|
36098
|
-
"502": {
|
|
36099
|
-
"description": "upstream_unavailable: the organization profile could not be reached, or (after a write) the change was saved but could not be read back. Retryable for a read; read the profile again before repeating a write.",
|
|
36100
|
-
"content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }
|
|
36101
|
-
},
|
|
36102
|
-
"503": {
|
|
36103
|
-
"description": "upstream_unavailable: the organization profile is temporarily unavailable. Retry-After says when to retry.",
|
|
36104
|
-
"headers": { "Retry-After": {
|
|
36105
|
-
"description": "Seconds to wait before retrying.",
|
|
36106
|
-
"schema": {
|
|
36107
|
-
"type": "integer",
|
|
36108
|
-
"minimum": 1
|
|
36109
|
-
}
|
|
36110
|
-
} },
|
|
36111
|
-
"content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }
|
|
36452
|
+
},
|
|
36453
|
+
"502": {
|
|
36454
|
+
"description": "upstream_unavailable: the organization profile could not be reached, or (after a write) the change was saved but could not be read back. Retryable for a read; read the profile again before repeating a write.",
|
|
36455
|
+
"content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }
|
|
36456
|
+
},
|
|
36457
|
+
"503": {
|
|
36458
|
+
"description": "upstream_unavailable: the organization profile is temporarily unavailable. Retry-After says when to retry.",
|
|
36459
|
+
"headers": { "Retry-After": {
|
|
36460
|
+
"description": "Seconds to wait before retrying.",
|
|
36461
|
+
"schema": {
|
|
36462
|
+
"type": "integer",
|
|
36463
|
+
"minimum": 1
|
|
36464
|
+
}
|
|
36465
|
+
} },
|
|
36466
|
+
"content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }
|
|
36467
|
+
}
|
|
36468
|
+
},
|
|
36469
|
+
"x-primitive-permission": ["org:manage"],
|
|
36470
|
+
"x-primitive-permission-alternatives": [["members:manage"]]
|
|
36471
|
+
}
|
|
36472
|
+
},
|
|
36473
|
+
"/search-settings": {
|
|
36474
|
+
"get": {
|
|
36475
|
+
"operationId": "getSearchSettings",
|
|
36476
|
+
"summary": "Get the semantic search setting",
|
|
36477
|
+
"tags": ["Organization settings"],
|
|
36478
|
+
"security": [{ "BearerAuth": [] }],
|
|
36479
|
+
"description": "Whether the organization consents to semantic search, and whether it is active: it also needs a paid plan, the feature to be available to the organization, and HIPAA mode off.\n\nPerson only: a dashboard session or the person's own OAuth token (the CLI or an app they connected). API keys, agent connection keys and agents are refused.",
|
|
36480
|
+
"x-primitive-person-only": { "access": "person" },
|
|
36481
|
+
"parameters": [],
|
|
36482
|
+
"responses": {
|
|
36483
|
+
"200": {
|
|
36484
|
+
"description": "Success",
|
|
36485
|
+
"content": { "application/json": { "schema": {
|
|
36486
|
+
"type": "object",
|
|
36487
|
+
"required": ["success", "data"],
|
|
36488
|
+
"properties": {
|
|
36489
|
+
"success": {
|
|
36490
|
+
"type": "boolean",
|
|
36491
|
+
"const": true
|
|
36492
|
+
},
|
|
36493
|
+
"data": { "$ref": "#/components/schemas/OrgSettings_getSearchSettings_Result" }
|
|
36494
|
+
}
|
|
36495
|
+
} } },
|
|
36496
|
+
"headers": {
|
|
36497
|
+
"ratelimit-limit": { "$ref": "#/components/headers/ratelimit-limit" },
|
|
36498
|
+
"ratelimit-remaining": { "$ref": "#/components/headers/ratelimit-remaining" },
|
|
36499
|
+
"ratelimit-reset": { "$ref": "#/components/headers/ratelimit-reset" },
|
|
36500
|
+
"ratelimit-policy": { "$ref": "#/components/headers/ratelimit-policy" }
|
|
36501
|
+
}
|
|
36502
|
+
},
|
|
36503
|
+
"400": {
|
|
36504
|
+
"description": "Invalid request parameters",
|
|
36505
|
+
"content": { "application/json": {
|
|
36506
|
+
"schema": { "$ref": "#/components/schemas/ErrorResponse" },
|
|
36507
|
+
"example": {
|
|
36508
|
+
"success": false,
|
|
36509
|
+
"error": {
|
|
36510
|
+
"code": "validation_error",
|
|
36511
|
+
"message": "Invalid domain format"
|
|
36512
|
+
}
|
|
36513
|
+
}
|
|
36514
|
+
} }
|
|
36515
|
+
},
|
|
36516
|
+
"401": {
|
|
36517
|
+
"description": "Invalid or missing API key",
|
|
36518
|
+
"content": { "application/json": {
|
|
36519
|
+
"schema": { "$ref": "#/components/schemas/ErrorResponse" },
|
|
36520
|
+
"example": {
|
|
36521
|
+
"success": false,
|
|
36522
|
+
"error": {
|
|
36523
|
+
"code": "unauthorized",
|
|
36524
|
+
"message": "Invalid or missing API key"
|
|
36525
|
+
}
|
|
36526
|
+
}
|
|
36527
|
+
} },
|
|
36528
|
+
"headers": {
|
|
36529
|
+
"ratelimit-limit": { "$ref": "#/components/headers/ratelimit-limit-unauthenticated" },
|
|
36530
|
+
"ratelimit-remaining": { "$ref": "#/components/headers/ratelimit-remaining-unauthenticated" },
|
|
36531
|
+
"ratelimit-reset": { "$ref": "#/components/headers/ratelimit-reset-unauthenticated" },
|
|
36532
|
+
"ratelimit-policy": { "$ref": "#/components/headers/ratelimit-policy-unauthenticated" }
|
|
36533
|
+
}
|
|
36534
|
+
},
|
|
36535
|
+
"403": {
|
|
36536
|
+
"description": "Authenticated caller lacks permission for the operation",
|
|
36537
|
+
"content": { "application/json": {
|
|
36538
|
+
"schema": { "$ref": "#/components/schemas/ErrorResponse" },
|
|
36539
|
+
"example": {
|
|
36540
|
+
"success": false,
|
|
36541
|
+
"error": {
|
|
36542
|
+
"code": "forbidden",
|
|
36543
|
+
"message": "Insufficient permissions"
|
|
36544
|
+
}
|
|
36545
|
+
}
|
|
36546
|
+
} }
|
|
36547
|
+
},
|
|
36548
|
+
"404": {
|
|
36549
|
+
"description": "Resource not found",
|
|
36550
|
+
"content": { "application/json": {
|
|
36551
|
+
"schema": { "$ref": "#/components/schemas/ErrorResponse" },
|
|
36552
|
+
"example": {
|
|
36553
|
+
"success": false,
|
|
36554
|
+
"error": {
|
|
36555
|
+
"code": "not_found",
|
|
36556
|
+
"message": "Resource not found"
|
|
36557
|
+
}
|
|
36558
|
+
}
|
|
36559
|
+
} }
|
|
36560
|
+
},
|
|
36561
|
+
"429": {
|
|
36562
|
+
"description": "Rate limit exceeded. The `ratelimit-*` headers describe the limiter that rejected this request, with its own limit and window, not the API budget that successful responses report. A limiter that reports only a wait sends `Retry-After` alone.",
|
|
36563
|
+
"headers": {
|
|
36564
|
+
"Retry-After": {
|
|
36565
|
+
"schema": { "type": "integer" },
|
|
36566
|
+
"description": "Seconds to wait before retrying"
|
|
36567
|
+
},
|
|
36568
|
+
"ratelimit-limit": { "$ref": "#/components/headers/ratelimit-limit-429" },
|
|
36569
|
+
"ratelimit-remaining": { "$ref": "#/components/headers/ratelimit-remaining-429" },
|
|
36570
|
+
"ratelimit-reset": { "$ref": "#/components/headers/ratelimit-reset" },
|
|
36571
|
+
"ratelimit-policy": { "$ref": "#/components/headers/ratelimit-policy-429" }
|
|
36572
|
+
},
|
|
36573
|
+
"content": { "application/json": {
|
|
36574
|
+
"schema": { "$ref": "#/components/schemas/ErrorResponse" },
|
|
36575
|
+
"example": {
|
|
36576
|
+
"success": false,
|
|
36577
|
+
"error": {
|
|
36578
|
+
"code": "rate_limit_exceeded",
|
|
36579
|
+
"message": "Rate limit exceeded"
|
|
36580
|
+
}
|
|
36581
|
+
}
|
|
36582
|
+
} }
|
|
36583
|
+
}
|
|
36584
|
+
},
|
|
36585
|
+
"x-primitive-permission": ["org:read"]
|
|
36586
|
+
},
|
|
36587
|
+
"put": {
|
|
36588
|
+
"operationId": "updateSearchSettings",
|
|
36589
|
+
"summary": "Change the semantic search setting",
|
|
36590
|
+
"tags": ["Organization settings"],
|
|
36591
|
+
"security": [{ "BearerAuth": [] }],
|
|
36592
|
+
"description": "Consent to, or withdraw consent from, semantic search. Consenting sends the organization's mail content to an embedding provider, so it is a person's decision. Turning it on is refused with 409 under HIPAA mode (hipaa_mode), without a paid plan (plan_required) or while it is not available to the organization (not_available). Turning it off is always allowed.\n\nPerson only: a dashboard session, or the person's own OAuth token with a step-up token for purpose `org_settings.write` in the Primitive-Step-Up header. Without one an OAuth token is answered 403 step_up_required, whose details carry the dashboard URL that issues it (or 403 grant_forbidden when the person's role could not do it anyway). The person's role must hold search_settings:manage. API keys and agent connection keys are refused. An agent acting for the person is refused unless it carries a step-up token the person issued in the dashboard.",
|
|
36593
|
+
"x-primitive-person-only": {
|
|
36594
|
+
"access": "step_up",
|
|
36595
|
+
"step_up_purpose": "org_settings.write"
|
|
36596
|
+
},
|
|
36597
|
+
"parameters": [{
|
|
36598
|
+
"name": "Primitive-Step-Up",
|
|
36599
|
+
"in": "header",
|
|
36600
|
+
"required": false,
|
|
36601
|
+
"description": "A single-use step-up token of purpose `org_settings.write` (Change the organization name, logo, semantic search setting or email notification settings), valid 5 minutes. When this operation answers 403 `step_up_required`, the person confirms at the link in `details.step_up_url` and the request is retried with the token that page shows, in this header. Accepted only with the person's own OAuth token of the client it was confirmed for; refused with a token issued for MCP, with any API key or agent connection key, and with a dashboard session.",
|
|
36602
|
+
"schema": {
|
|
36603
|
+
"type": "string",
|
|
36604
|
+
"pattern": "^pstep_[0-9a-f]{64}$"
|
|
36605
|
+
}
|
|
36606
|
+
}],
|
|
36607
|
+
"responses": {
|
|
36608
|
+
"200": {
|
|
36609
|
+
"description": "Success",
|
|
36610
|
+
"content": { "application/json": { "schema": {
|
|
36611
|
+
"type": "object",
|
|
36612
|
+
"required": ["success", "data"],
|
|
36613
|
+
"properties": {
|
|
36614
|
+
"success": {
|
|
36615
|
+
"type": "boolean",
|
|
36616
|
+
"const": true
|
|
36617
|
+
},
|
|
36618
|
+
"data": { "$ref": "#/components/schemas/OrgSettings_updateSearchSettings_Result" }
|
|
36619
|
+
}
|
|
36620
|
+
} } },
|
|
36621
|
+
"headers": {
|
|
36622
|
+
"ratelimit-limit": { "$ref": "#/components/headers/ratelimit-limit" },
|
|
36623
|
+
"ratelimit-remaining": { "$ref": "#/components/headers/ratelimit-remaining" },
|
|
36624
|
+
"ratelimit-reset": { "$ref": "#/components/headers/ratelimit-reset" },
|
|
36625
|
+
"ratelimit-policy": { "$ref": "#/components/headers/ratelimit-policy" }
|
|
36626
|
+
}
|
|
36627
|
+
},
|
|
36628
|
+
"400": {
|
|
36629
|
+
"description": "Invalid request parameters",
|
|
36630
|
+
"content": { "application/json": {
|
|
36631
|
+
"schema": { "$ref": "#/components/schemas/ErrorResponse" },
|
|
36632
|
+
"example": {
|
|
36633
|
+
"success": false,
|
|
36634
|
+
"error": {
|
|
36635
|
+
"code": "validation_error",
|
|
36636
|
+
"message": "Invalid domain format"
|
|
36637
|
+
}
|
|
36638
|
+
}
|
|
36639
|
+
} }
|
|
36640
|
+
},
|
|
36641
|
+
"401": {
|
|
36642
|
+
"description": "Invalid or missing API key",
|
|
36643
|
+
"content": { "application/json": {
|
|
36644
|
+
"schema": { "$ref": "#/components/schemas/ErrorResponse" },
|
|
36645
|
+
"example": {
|
|
36646
|
+
"success": false,
|
|
36647
|
+
"error": {
|
|
36648
|
+
"code": "unauthorized",
|
|
36649
|
+
"message": "Invalid or missing API key"
|
|
36650
|
+
}
|
|
36651
|
+
}
|
|
36652
|
+
} },
|
|
36653
|
+
"headers": {
|
|
36654
|
+
"ratelimit-limit": { "$ref": "#/components/headers/ratelimit-limit-unauthenticated" },
|
|
36655
|
+
"ratelimit-remaining": { "$ref": "#/components/headers/ratelimit-remaining-unauthenticated" },
|
|
36656
|
+
"ratelimit-reset": { "$ref": "#/components/headers/ratelimit-reset-unauthenticated" },
|
|
36657
|
+
"ratelimit-policy": { "$ref": "#/components/headers/ratelimit-policy-unauthenticated" }
|
|
36658
|
+
}
|
|
36659
|
+
},
|
|
36660
|
+
"403": {
|
|
36661
|
+
"description": "Authenticated caller lacks permission for the operation",
|
|
36662
|
+
"content": { "application/json": {
|
|
36663
|
+
"schema": { "$ref": "#/components/schemas/ErrorResponse" },
|
|
36664
|
+
"example": {
|
|
36665
|
+
"success": false,
|
|
36666
|
+
"error": {
|
|
36667
|
+
"code": "forbidden",
|
|
36668
|
+
"message": "Insufficient permissions"
|
|
36669
|
+
}
|
|
36670
|
+
}
|
|
36671
|
+
} }
|
|
36672
|
+
},
|
|
36673
|
+
"404": {
|
|
36674
|
+
"description": "Resource not found",
|
|
36675
|
+
"content": { "application/json": {
|
|
36676
|
+
"schema": { "$ref": "#/components/schemas/ErrorResponse" },
|
|
36677
|
+
"example": {
|
|
36678
|
+
"success": false,
|
|
36679
|
+
"error": {
|
|
36680
|
+
"code": "not_found",
|
|
36681
|
+
"message": "Resource not found"
|
|
36682
|
+
}
|
|
36683
|
+
}
|
|
36684
|
+
} }
|
|
36685
|
+
},
|
|
36686
|
+
"409": {
|
|
36687
|
+
"description": "The request conflicts with the current state of the resource",
|
|
36688
|
+
"content": { "application/json": {
|
|
36689
|
+
"schema": { "$ref": "#/components/schemas/ErrorResponse" },
|
|
36690
|
+
"example": {
|
|
36691
|
+
"success": false,
|
|
36692
|
+
"error": {
|
|
36693
|
+
"code": "conflict",
|
|
36694
|
+
"message": "The request conflicts with the current state of the resource"
|
|
36695
|
+
}
|
|
36696
|
+
}
|
|
36697
|
+
} }
|
|
36698
|
+
},
|
|
36699
|
+
"429": {
|
|
36700
|
+
"description": "Rate limit exceeded. The `ratelimit-*` headers describe the limiter that rejected this request, with its own limit and window, not the API budget that successful responses report. A limiter that reports only a wait sends `Retry-After` alone.",
|
|
36701
|
+
"headers": {
|
|
36702
|
+
"Retry-After": {
|
|
36703
|
+
"schema": { "type": "integer" },
|
|
36704
|
+
"description": "Seconds to wait before retrying"
|
|
36705
|
+
},
|
|
36706
|
+
"ratelimit-limit": { "$ref": "#/components/headers/ratelimit-limit-429" },
|
|
36707
|
+
"ratelimit-remaining": { "$ref": "#/components/headers/ratelimit-remaining-429" },
|
|
36708
|
+
"ratelimit-reset": { "$ref": "#/components/headers/ratelimit-reset" },
|
|
36709
|
+
"ratelimit-policy": { "$ref": "#/components/headers/ratelimit-policy-429" }
|
|
36710
|
+
},
|
|
36711
|
+
"content": { "application/json": {
|
|
36712
|
+
"schema": { "$ref": "#/components/schemas/ErrorResponse" },
|
|
36713
|
+
"example": {
|
|
36714
|
+
"success": false,
|
|
36715
|
+
"error": {
|
|
36716
|
+
"code": "rate_limit_exceeded",
|
|
36717
|
+
"message": "Rate limit exceeded"
|
|
36718
|
+
}
|
|
36719
|
+
}
|
|
36720
|
+
} }
|
|
36721
|
+
}
|
|
36722
|
+
},
|
|
36723
|
+
"requestBody": {
|
|
36724
|
+
"required": true,
|
|
36725
|
+
"content": { "application/json": { "schema": { "$ref": "#/components/schemas/OrgSettings_updateSearchSettings_Input" } } }
|
|
36726
|
+
},
|
|
36727
|
+
"x-primitive-permission": ["search_settings:manage"]
|
|
36728
|
+
}
|
|
36729
|
+
},
|
|
36730
|
+
"/email-notification-settings": {
|
|
36731
|
+
"get": {
|
|
36732
|
+
"operationId": "getEmailNotificationSettings",
|
|
36733
|
+
"summary": "Get the organization's email notification settings",
|
|
36734
|
+
"tags": ["Email notifications"],
|
|
36735
|
+
"security": [{ "BearerAuth": [] }],
|
|
36736
|
+
"description": "Every kind of notification email Primitive sends the organization's members, and who may turn it off. Each kind lists its label and description, its class, whether it needs an entitlement and whether the organization has it, the organization's mode (null for platform_required) and the modes it may choose, and whether the calling member gets it and may change that.\n\nPerson only: a dashboard session or the person's own OAuth token (the CLI or an app they connected). API keys, agent connection keys and agents are refused.",
|
|
36737
|
+
"x-primitive-person-only": { "access": "person" },
|
|
36738
|
+
"parameters": [],
|
|
36739
|
+
"responses": {
|
|
36740
|
+
"200": {
|
|
36741
|
+
"description": "Success",
|
|
36742
|
+
"content": { "application/json": { "schema": {
|
|
36743
|
+
"type": "object",
|
|
36744
|
+
"required": ["success", "data"],
|
|
36745
|
+
"properties": {
|
|
36746
|
+
"success": {
|
|
36747
|
+
"type": "boolean",
|
|
36748
|
+
"const": true
|
|
36749
|
+
},
|
|
36750
|
+
"data": { "$ref": "#/components/schemas/EmailNotifications_getEmailNotificationSettings_Result" }
|
|
36751
|
+
}
|
|
36752
|
+
} } },
|
|
36753
|
+
"headers": {
|
|
36754
|
+
"ratelimit-limit": { "$ref": "#/components/headers/ratelimit-limit" },
|
|
36755
|
+
"ratelimit-remaining": { "$ref": "#/components/headers/ratelimit-remaining" },
|
|
36756
|
+
"ratelimit-reset": { "$ref": "#/components/headers/ratelimit-reset" },
|
|
36757
|
+
"ratelimit-policy": { "$ref": "#/components/headers/ratelimit-policy" }
|
|
36758
|
+
}
|
|
36759
|
+
},
|
|
36760
|
+
"400": {
|
|
36761
|
+
"description": "Invalid request parameters",
|
|
36762
|
+
"content": { "application/json": {
|
|
36763
|
+
"schema": { "$ref": "#/components/schemas/ErrorResponse" },
|
|
36764
|
+
"example": {
|
|
36765
|
+
"success": false,
|
|
36766
|
+
"error": {
|
|
36767
|
+
"code": "validation_error",
|
|
36768
|
+
"message": "Invalid domain format"
|
|
36769
|
+
}
|
|
36770
|
+
}
|
|
36771
|
+
} }
|
|
36772
|
+
},
|
|
36773
|
+
"401": {
|
|
36774
|
+
"description": "Invalid or missing API key",
|
|
36775
|
+
"content": { "application/json": {
|
|
36776
|
+
"schema": { "$ref": "#/components/schemas/ErrorResponse" },
|
|
36777
|
+
"example": {
|
|
36778
|
+
"success": false,
|
|
36779
|
+
"error": {
|
|
36780
|
+
"code": "unauthorized",
|
|
36781
|
+
"message": "Invalid or missing API key"
|
|
36782
|
+
}
|
|
36783
|
+
}
|
|
36784
|
+
} },
|
|
36785
|
+
"headers": {
|
|
36786
|
+
"ratelimit-limit": { "$ref": "#/components/headers/ratelimit-limit-unauthenticated" },
|
|
36787
|
+
"ratelimit-remaining": { "$ref": "#/components/headers/ratelimit-remaining-unauthenticated" },
|
|
36788
|
+
"ratelimit-reset": { "$ref": "#/components/headers/ratelimit-reset-unauthenticated" },
|
|
36789
|
+
"ratelimit-policy": { "$ref": "#/components/headers/ratelimit-policy-unauthenticated" }
|
|
36790
|
+
}
|
|
36791
|
+
},
|
|
36792
|
+
"403": {
|
|
36793
|
+
"description": "Authenticated caller lacks permission for the operation",
|
|
36794
|
+
"content": { "application/json": {
|
|
36795
|
+
"schema": { "$ref": "#/components/schemas/ErrorResponse" },
|
|
36796
|
+
"example": {
|
|
36797
|
+
"success": false,
|
|
36798
|
+
"error": {
|
|
36799
|
+
"code": "forbidden",
|
|
36800
|
+
"message": "Insufficient permissions"
|
|
36801
|
+
}
|
|
36802
|
+
}
|
|
36803
|
+
} }
|
|
36804
|
+
},
|
|
36805
|
+
"404": {
|
|
36806
|
+
"description": "Resource not found",
|
|
36807
|
+
"content": { "application/json": {
|
|
36808
|
+
"schema": { "$ref": "#/components/schemas/ErrorResponse" },
|
|
36809
|
+
"example": {
|
|
36810
|
+
"success": false,
|
|
36811
|
+
"error": {
|
|
36812
|
+
"code": "not_found",
|
|
36813
|
+
"message": "Resource not found"
|
|
36814
|
+
}
|
|
36815
|
+
}
|
|
36816
|
+
} }
|
|
36817
|
+
},
|
|
36818
|
+
"429": {
|
|
36819
|
+
"description": "Rate limit exceeded. The `ratelimit-*` headers describe the limiter that rejected this request, with its own limit and window, not the API budget that successful responses report. A limiter that reports only a wait sends `Retry-After` alone.",
|
|
36820
|
+
"headers": {
|
|
36821
|
+
"Retry-After": {
|
|
36822
|
+
"schema": { "type": "integer" },
|
|
36823
|
+
"description": "Seconds to wait before retrying"
|
|
36824
|
+
},
|
|
36825
|
+
"ratelimit-limit": { "$ref": "#/components/headers/ratelimit-limit-429" },
|
|
36826
|
+
"ratelimit-remaining": { "$ref": "#/components/headers/ratelimit-remaining-429" },
|
|
36827
|
+
"ratelimit-reset": { "$ref": "#/components/headers/ratelimit-reset" },
|
|
36828
|
+
"ratelimit-policy": { "$ref": "#/components/headers/ratelimit-policy-429" }
|
|
36829
|
+
},
|
|
36830
|
+
"content": { "application/json": {
|
|
36831
|
+
"schema": { "$ref": "#/components/schemas/ErrorResponse" },
|
|
36832
|
+
"example": {
|
|
36833
|
+
"success": false,
|
|
36834
|
+
"error": {
|
|
36835
|
+
"code": "rate_limit_exceeded",
|
|
36836
|
+
"message": "Rate limit exceeded"
|
|
36837
|
+
}
|
|
36838
|
+
}
|
|
36839
|
+
} }
|
|
36840
|
+
}
|
|
36841
|
+
},
|
|
36842
|
+
"x-primitive-permission": ["org:read"]
|
|
36843
|
+
},
|
|
36844
|
+
"put": {
|
|
36845
|
+
"operationId": "updateEmailNotificationSettings",
|
|
36846
|
+
"summary": "Change the organization's email notification settings",
|
|
36847
|
+
"tags": ["Email notifications"],
|
|
36848
|
+
"security": [{ "BearerAuth": [] }],
|
|
36849
|
+
"description": "Set the organization's mode for one or more kinds: off (nobody gets it), optional_on (each member gets it unless they turn it off), optional_off (only members who turn it on get it) or required (every member gets it). Kinds left out keep their mode. An unknown kind is refused with 400 (validation_error, details.reason unknown_kind). A platform_required kind is refused with 409 (conflict, details.reason platform_required), and required for an optional kind with 409 (conflict, details.reason mode_not_allowed); nothing is changed. Answers the settings as they now stand. Each kind lists its label and description, its class, whether it needs an entitlement and whether the organization has it, the organization's mode (null for platform_required) and the modes it may choose, and whether the calling member gets it and may change that.\n\nPerson only: a dashboard session, or the person's own OAuth token with a step-up token for purpose `org_settings.write` in the Primitive-Step-Up header. Without one an OAuth token is answered 403 step_up_required, whose details carry the dashboard URL that issues it (or 403 grant_forbidden when the person's role could not do it anyway). The person's role must hold notification_policy:manage. API keys and agent connection keys are refused. An agent acting for the person is refused unless it carries a step-up token the person issued in the dashboard.",
|
|
36850
|
+
"x-primitive-person-only": {
|
|
36851
|
+
"access": "step_up",
|
|
36852
|
+
"step_up_purpose": "org_settings.write"
|
|
36853
|
+
},
|
|
36854
|
+
"parameters": [{
|
|
36855
|
+
"name": "Primitive-Step-Up",
|
|
36856
|
+
"in": "header",
|
|
36857
|
+
"required": false,
|
|
36858
|
+
"description": "A single-use step-up token of purpose `org_settings.write` (Change the organization name, logo, semantic search setting or email notification settings), valid 5 minutes. When this operation answers 403 `step_up_required`, the person confirms at the link in `details.step_up_url` and the request is retried with the token that page shows, in this header. Accepted only with the person's own OAuth token of the client it was confirmed for; refused with a token issued for MCP, with any API key or agent connection key, and with a dashboard session.",
|
|
36859
|
+
"schema": {
|
|
36860
|
+
"type": "string",
|
|
36861
|
+
"pattern": "^pstep_[0-9a-f]{64}$"
|
|
36862
|
+
}
|
|
36863
|
+
}],
|
|
36864
|
+
"responses": {
|
|
36865
|
+
"200": {
|
|
36866
|
+
"description": "Success",
|
|
36867
|
+
"content": { "application/json": { "schema": {
|
|
36868
|
+
"type": "object",
|
|
36869
|
+
"required": ["success", "data"],
|
|
36870
|
+
"properties": {
|
|
36871
|
+
"success": {
|
|
36872
|
+
"type": "boolean",
|
|
36873
|
+
"const": true
|
|
36874
|
+
},
|
|
36875
|
+
"data": { "$ref": "#/components/schemas/EmailNotifications_updateEmailNotificationSettings_Result" }
|
|
36876
|
+
}
|
|
36877
|
+
} } },
|
|
36878
|
+
"headers": {
|
|
36879
|
+
"ratelimit-limit": { "$ref": "#/components/headers/ratelimit-limit" },
|
|
36880
|
+
"ratelimit-remaining": { "$ref": "#/components/headers/ratelimit-remaining" },
|
|
36881
|
+
"ratelimit-reset": { "$ref": "#/components/headers/ratelimit-reset" },
|
|
36882
|
+
"ratelimit-policy": { "$ref": "#/components/headers/ratelimit-policy" }
|
|
36883
|
+
}
|
|
36884
|
+
},
|
|
36885
|
+
"400": {
|
|
36886
|
+
"description": "Invalid request parameters",
|
|
36887
|
+
"content": { "application/json": {
|
|
36888
|
+
"schema": { "$ref": "#/components/schemas/ErrorResponse" },
|
|
36889
|
+
"example": {
|
|
36890
|
+
"success": false,
|
|
36891
|
+
"error": {
|
|
36892
|
+
"code": "validation_error",
|
|
36893
|
+
"message": "Invalid domain format"
|
|
36894
|
+
}
|
|
36895
|
+
}
|
|
36896
|
+
} }
|
|
36897
|
+
},
|
|
36898
|
+
"401": {
|
|
36899
|
+
"description": "Invalid or missing API key",
|
|
36900
|
+
"content": { "application/json": {
|
|
36901
|
+
"schema": { "$ref": "#/components/schemas/ErrorResponse" },
|
|
36902
|
+
"example": {
|
|
36903
|
+
"success": false,
|
|
36904
|
+
"error": {
|
|
36905
|
+
"code": "unauthorized",
|
|
36906
|
+
"message": "Invalid or missing API key"
|
|
36907
|
+
}
|
|
36908
|
+
}
|
|
36909
|
+
} },
|
|
36910
|
+
"headers": {
|
|
36911
|
+
"ratelimit-limit": { "$ref": "#/components/headers/ratelimit-limit-unauthenticated" },
|
|
36912
|
+
"ratelimit-remaining": { "$ref": "#/components/headers/ratelimit-remaining-unauthenticated" },
|
|
36913
|
+
"ratelimit-reset": { "$ref": "#/components/headers/ratelimit-reset-unauthenticated" },
|
|
36914
|
+
"ratelimit-policy": { "$ref": "#/components/headers/ratelimit-policy-unauthenticated" }
|
|
36915
|
+
}
|
|
36916
|
+
},
|
|
36917
|
+
"403": {
|
|
36918
|
+
"description": "Authenticated caller lacks permission for the operation",
|
|
36919
|
+
"content": { "application/json": {
|
|
36920
|
+
"schema": { "$ref": "#/components/schemas/ErrorResponse" },
|
|
36921
|
+
"example": {
|
|
36922
|
+
"success": false,
|
|
36923
|
+
"error": {
|
|
36924
|
+
"code": "forbidden",
|
|
36925
|
+
"message": "Insufficient permissions"
|
|
36926
|
+
}
|
|
36927
|
+
}
|
|
36928
|
+
} }
|
|
36929
|
+
},
|
|
36930
|
+
"404": {
|
|
36931
|
+
"description": "Resource not found",
|
|
36932
|
+
"content": { "application/json": {
|
|
36933
|
+
"schema": { "$ref": "#/components/schemas/ErrorResponse" },
|
|
36934
|
+
"example": {
|
|
36935
|
+
"success": false,
|
|
36936
|
+
"error": {
|
|
36937
|
+
"code": "not_found",
|
|
36938
|
+
"message": "Resource not found"
|
|
36939
|
+
}
|
|
36940
|
+
}
|
|
36941
|
+
} }
|
|
36942
|
+
},
|
|
36943
|
+
"409": {
|
|
36944
|
+
"description": "The request conflicts with the current state of the resource",
|
|
36945
|
+
"content": { "application/json": {
|
|
36946
|
+
"schema": { "$ref": "#/components/schemas/ErrorResponse" },
|
|
36947
|
+
"example": {
|
|
36948
|
+
"success": false,
|
|
36949
|
+
"error": {
|
|
36950
|
+
"code": "conflict",
|
|
36951
|
+
"message": "The request conflicts with the current state of the resource"
|
|
36952
|
+
}
|
|
36953
|
+
}
|
|
36954
|
+
} }
|
|
36955
|
+
},
|
|
36956
|
+
"429": {
|
|
36957
|
+
"description": "Rate limit exceeded. The `ratelimit-*` headers describe the limiter that rejected this request, with its own limit and window, not the API budget that successful responses report. A limiter that reports only a wait sends `Retry-After` alone.",
|
|
36958
|
+
"headers": {
|
|
36959
|
+
"Retry-After": {
|
|
36960
|
+
"schema": { "type": "integer" },
|
|
36961
|
+
"description": "Seconds to wait before retrying"
|
|
36962
|
+
},
|
|
36963
|
+
"ratelimit-limit": { "$ref": "#/components/headers/ratelimit-limit-429" },
|
|
36964
|
+
"ratelimit-remaining": { "$ref": "#/components/headers/ratelimit-remaining-429" },
|
|
36965
|
+
"ratelimit-reset": { "$ref": "#/components/headers/ratelimit-reset" },
|
|
36966
|
+
"ratelimit-policy": { "$ref": "#/components/headers/ratelimit-policy-429" }
|
|
36967
|
+
},
|
|
36968
|
+
"content": { "application/json": {
|
|
36969
|
+
"schema": { "$ref": "#/components/schemas/ErrorResponse" },
|
|
36970
|
+
"example": {
|
|
36971
|
+
"success": false,
|
|
36972
|
+
"error": {
|
|
36973
|
+
"code": "rate_limit_exceeded",
|
|
36974
|
+
"message": "Rate limit exceeded"
|
|
36975
|
+
}
|
|
36976
|
+
}
|
|
36977
|
+
} }
|
|
36112
36978
|
}
|
|
36113
36979
|
},
|
|
36114
|
-
"
|
|
36115
|
-
|
|
36980
|
+
"requestBody": {
|
|
36981
|
+
"required": true,
|
|
36982
|
+
"content": { "application/json": { "schema": { "$ref": "#/components/schemas/EmailNotifications_updateEmailNotificationSettings_Input" } } }
|
|
36983
|
+
},
|
|
36984
|
+
"x-primitive-permission": ["notification_policy:manage"]
|
|
36116
36985
|
}
|
|
36117
36986
|
},
|
|
36118
|
-
"/
|
|
36987
|
+
"/email-notification-preferences": {
|
|
36119
36988
|
"get": {
|
|
36120
|
-
"operationId": "
|
|
36121
|
-
"summary": "Get
|
|
36122
|
-
"tags": ["
|
|
36989
|
+
"operationId": "getEmailNotificationPreferences",
|
|
36990
|
+
"summary": "Get my email notification preferences",
|
|
36991
|
+
"tags": ["Email notifications"],
|
|
36123
36992
|
"security": [{ "BearerAuth": [] }],
|
|
36124
|
-
"description": "
|
|
36993
|
+
"description": "The notification emails the calling member gets, and which of them they may turn on or off. Each kind lists its label and description, its class, whether it needs an entitlement and whether the organization has it, the organization's mode (null for platform_required) and the modes it may choose, and whether the calling member gets it and may change that.\n\nPerson only: a dashboard session or the person's own OAuth token (the CLI or an app they connected). The person's role must hold notifications:manage. API keys, agent connection keys and agents are refused.",
|
|
36125
36994
|
"x-primitive-person-only": { "access": "person" },
|
|
36126
36995
|
"parameters": [],
|
|
36127
36996
|
"responses": {
|
|
@@ -36135,7 +37004,7 @@ const openapiDocument = {
|
|
|
36135
37004
|
"type": "boolean",
|
|
36136
37005
|
"const": true
|
|
36137
37006
|
},
|
|
36138
|
-
"data": { "$ref": "#/components/schemas/
|
|
37007
|
+
"data": { "$ref": "#/components/schemas/EmailNotifications_getEmailNotificationPreferences_Result" }
|
|
36139
37008
|
}
|
|
36140
37009
|
} } },
|
|
36141
37010
|
"headers": {
|
|
@@ -36227,28 +37096,16 @@ const openapiDocument = {
|
|
|
36227
37096
|
} }
|
|
36228
37097
|
}
|
|
36229
37098
|
},
|
|
36230
|
-
"x-primitive-permission": ["
|
|
37099
|
+
"x-primitive-permission": ["notifications:manage"]
|
|
36231
37100
|
},
|
|
36232
37101
|
"put": {
|
|
36233
|
-
"operationId": "
|
|
36234
|
-
"summary": "Change
|
|
36235
|
-
"tags": ["
|
|
37102
|
+
"operationId": "updateEmailNotificationPreferences",
|
|
37103
|
+
"summary": "Change my email notification preferences",
|
|
37104
|
+
"tags": ["Email notifications"],
|
|
36236
37105
|
"security": [{ "BearerAuth": [] }],
|
|
36237
|
-
"description": "
|
|
36238
|
-
"x-primitive-person-only": {
|
|
36239
|
-
|
|
36240
|
-
"step_up_purpose": "org_settings.write"
|
|
36241
|
-
},
|
|
36242
|
-
"parameters": [{
|
|
36243
|
-
"name": "Primitive-Step-Up",
|
|
36244
|
-
"in": "header",
|
|
36245
|
-
"required": false,
|
|
36246
|
-
"description": "A single-use step-up token of purpose `org_settings.write` (Change the organization name, logo or semantic search setting), valid 5 minutes. When this operation answers 403 `step_up_required`, the person confirms at the link in `details.step_up_url` and the request is retried with the token that page shows, in this header. Accepted only with the person's own OAuth token of the client it was confirmed for; refused with a token issued for MCP, with any API key or agent connection key, and with a dashboard session.",
|
|
36247
|
-
"schema": {
|
|
36248
|
-
"type": "string",
|
|
36249
|
-
"pattern": "^pstep_[0-9a-f]{64}$"
|
|
36250
|
-
}
|
|
36251
|
-
}],
|
|
37106
|
+
"description": "Turn one or more kinds on or off for the calling member. Only a kind whose organization mode is optional_on or optional_off can be changed: a platform_required kind, one the organization requires, and one it turned off are refused with 409 (conflict, details.reason not_member_choice, details.org_mode saying which), and nothing is changed. An unknown kind is refused with 400. Answers the preferences as they now stand. Each kind lists its label and description, its class, whether it needs an entitlement and whether the organization has it, the organization's mode (null for platform_required) and the modes it may choose, and whether the calling member gets it and may change that.\n\nPerson only: a dashboard session or the person's own OAuth token (the CLI or an app they connected). The person's role must hold notifications:manage. API keys, agent connection keys and agents are refused.",
|
|
37107
|
+
"x-primitive-person-only": { "access": "person" },
|
|
37108
|
+
"parameters": [],
|
|
36252
37109
|
"responses": {
|
|
36253
37110
|
"200": {
|
|
36254
37111
|
"description": "Success",
|
|
@@ -36260,7 +37117,7 @@ const openapiDocument = {
|
|
|
36260
37117
|
"type": "boolean",
|
|
36261
37118
|
"const": true
|
|
36262
37119
|
},
|
|
36263
|
-
"data": { "$ref": "#/components/schemas/
|
|
37120
|
+
"data": { "$ref": "#/components/schemas/EmailNotifications_updateEmailNotificationPreferences_Result" }
|
|
36264
37121
|
}
|
|
36265
37122
|
} } },
|
|
36266
37123
|
"headers": {
|
|
@@ -36367,9 +37224,9 @@ const openapiDocument = {
|
|
|
36367
37224
|
},
|
|
36368
37225
|
"requestBody": {
|
|
36369
37226
|
"required": true,
|
|
36370
|
-
"content": { "application/json": { "schema": { "$ref": "#/components/schemas/
|
|
37227
|
+
"content": { "application/json": { "schema": { "$ref": "#/components/schemas/EmailNotifications_updateEmailNotificationPreferences_Input" } } }
|
|
36371
37228
|
},
|
|
36372
|
-
"x-primitive-permission": ["
|
|
37229
|
+
"x-primitive-permission": ["notifications:manage"]
|
|
36373
37230
|
}
|
|
36374
37231
|
},
|
|
36375
37232
|
"/roles": {
|
|
@@ -40304,6 +41161,7 @@ const openapiDocument = {
|
|
|
40304
41161
|
"agent_work_version_conflict",
|
|
40305
41162
|
"already_claimed",
|
|
40306
41163
|
"already_member",
|
|
41164
|
+
"always_sent",
|
|
40307
41165
|
"archive_busy",
|
|
40308
41166
|
"archive_invalid",
|
|
40309
41167
|
"archive_limit_exceeded",
|
|
@@ -40425,6 +41283,22 @@ const openapiDocument = {
|
|
|
40425
41283
|
"latest_message_mismatch",
|
|
40426
41284
|
"latest_message_thread_unresolved",
|
|
40427
41285
|
"load_test_recipient_not_allowed",
|
|
41286
|
+
"mail_import_cap_exceeded",
|
|
41287
|
+
"mail_import_content_mismatch",
|
|
41288
|
+
"mail_import_deadline",
|
|
41289
|
+
"mail_import_disabled",
|
|
41290
|
+
"mail_import_fenced",
|
|
41291
|
+
"mail_import_forbidden",
|
|
41292
|
+
"mail_import_invalid_input",
|
|
41293
|
+
"mail_import_invalid_provider_response",
|
|
41294
|
+
"mail_import_out_of_window",
|
|
41295
|
+
"mail_import_policy_mismatch",
|
|
41296
|
+
"mail_import_provider_auth",
|
|
41297
|
+
"mail_import_provider_unavailable",
|
|
41298
|
+
"mail_import_storage_failed",
|
|
41299
|
+
"mail_import_storage_limit",
|
|
41300
|
+
"mail_import_too_large",
|
|
41301
|
+
"mail_import_unavailable",
|
|
40428
41302
|
"mail_relay_unavailable",
|
|
40429
41303
|
"mail_routing_conflict",
|
|
40430
41304
|
"mail_topic_edit_conflict",
|
|
@@ -40480,6 +41354,7 @@ const openapiDocument = {
|
|
|
40480
41354
|
"removal_failed",
|
|
40481
41355
|
"removal_timeout",
|
|
40482
41356
|
"request_expired",
|
|
41357
|
+
"required_by_organization",
|
|
40483
41358
|
"resend_limit_exceeded",
|
|
40484
41359
|
"resend_too_soon",
|
|
40485
41360
|
"role_assigned",
|
|
@@ -43288,6 +44163,11 @@ const openapiDocument = {
|
|
|
43288
44163
|
}
|
|
43289
44164
|
}
|
|
43290
44165
|
},
|
|
44166
|
+
"attachments": {
|
|
44167
|
+
"type": "array",
|
|
44168
|
+
"items": { "$ref": "#/components/schemas/EmailAttachment" },
|
|
44169
|
+
"description": "Attachment metadata (file name, type, size), the same items in the same order as `parsed.attachments`. Kept for a credential that reads headers only, where `parsed` is null. Empty when the email has none, its content is withheld (read `status` and `rejection_reason`), or it is not parsed yet or its parse failed (read `parse_status`). No bytes; download a part with `/emails/{id}/attachments/{part_index}`."
|
|
44170
|
+
},
|
|
43291
44171
|
"imported": {
|
|
43292
44172
|
"type": ["object", "null"],
|
|
43293
44173
|
"description": "Historical acquisition; never sender or SMTP delivery proof. Null for live mail. Excluded from live tails and deltas.",
|
|
@@ -43346,6 +44226,7 @@ const openapiDocument = {
|
|
|
43346
44226
|
"from_verified",
|
|
43347
44227
|
"from_header_email",
|
|
43348
44228
|
"from_name",
|
|
44229
|
+
"attachments",
|
|
43349
44230
|
"imported",
|
|
43350
44231
|
"snippet"
|
|
43351
44232
|
]
|
|
@@ -44167,6 +45048,33 @@ const openapiDocument = {
|
|
|
44167
45048
|
}
|
|
44168
45049
|
}
|
|
44169
45050
|
},
|
|
45051
|
+
"unsubscribe": {
|
|
45052
|
+
"type": "object",
|
|
45053
|
+
"additionalProperties": false,
|
|
45054
|
+
"required": ["url"],
|
|
45055
|
+
"description": "One-click unsubscribe for the message. Primitive writes a `List-Unsubscribe` header naming `url` (and `mailto` when given) and, unless `one_click` is false, `List-Unsubscribe-Post: List-Unsubscribe=One-Click` (RFC 8058), and DKIM-signs both. A receiver that offers one-click unsubscribe sends an HTTPS POST to `url` with the form body `List-Unsubscribe=One-Click`; your endpoint records the unsubscribe. Every recipient of the message gets the same `url`, so send one message per recipient when the URL identifies who to unsubscribe. The `headers` map refuses both header names, so this field is the only way to set them, and the headers it adds count toward the 25 entries `headers` may hold. A field whose rendered `List-Unsubscribe` would pass 980 characters is refused. Not accepted on a repeating send.",
|
|
45056
|
+
"properties": {
|
|
45057
|
+
"url": {
|
|
45058
|
+
"type": "string",
|
|
45059
|
+
"minLength": 1,
|
|
45060
|
+
"maxLength": 700,
|
|
45061
|
+
"pattern": "^[Hh][Tt][Tt][Pp][Ss]://(?:[A-Za-z0-9\\-._~:/?#\\[\\]@!$&'()*+;=]|%[0-9A-Fa-f]{2})+$",
|
|
45062
|
+
"description": "Absolute https URL that unsubscribes the recipient, at most 700 characters of URL characters only: printable ASCII with no spaces, double quotes, angle brackets or commas (percent-encode a comma as %2C), `%` only as a percent-encoded byte, `[` and `]` only around an IPv6 address host, a port from 1 to 65535, no `=?` (which mail readers can decode as an encoded word), and no user name or password. The host must be public: not a single-label name, `localhost` or a name under `.localhost`, `.internal`, `.local`, `.home.arpa` or `.localdomain`, or a loopback, link-local, private, shared, reserved, multicast or unspecified IP address, including an IPv6 address that embeds one. The scheme is written in lower case."
|
|
45063
|
+
},
|
|
45064
|
+
"one_click": {
|
|
45065
|
+
"type": "boolean",
|
|
45066
|
+
"default": true,
|
|
45067
|
+
"description": "Whether to also write `List-Unsubscribe-Post: List-Unsubscribe=One-Click`, which tells the receiver it may unsubscribe with a single POST to `url`. Set false when `url` needs the recipient to open a page and confirm."
|
|
45068
|
+
},
|
|
45069
|
+
"mailto": {
|
|
45070
|
+
"type": "string",
|
|
45071
|
+
"minLength": 3,
|
|
45072
|
+
"maxLength": 254,
|
|
45073
|
+
"pattern": "^[A-Za-z0-9!$'*+=_~-]+(?:\\.[A-Za-z0-9!$'*+=_~-]+)*@[A-Za-z0-9](?:[A-Za-z0-9-]{0,61}[A-Za-z0-9])?(?:\\.[A-Za-z0-9](?:[A-Za-z0-9-]{0,61}[A-Za-z0-9])?)+$",
|
|
45074
|
+
"description": "An address to also offer as a `mailto:` unsubscribe, written after `url`. The address alone, without the `mailto:` prefix: at most 64 letters, digits and `.!$'*+=_~-` before the `@`, and a dotted domain after it. An `=` is written as `%3D` in the header."
|
|
45075
|
+
}
|
|
45076
|
+
}
|
|
45077
|
+
},
|
|
44170
45078
|
"headers": {
|
|
44171
45079
|
"type": "object",
|
|
44172
45080
|
"maxProperties": 25,
|
|
@@ -45212,6 +46120,33 @@ const openapiDocument = {
|
|
|
45212
46120
|
"type": "boolean",
|
|
45213
46121
|
"description": "Send an informational message the receiver need not answer or wake for. It is sent as an `ack` signal (status `received`) about the message in `in_reply_to`, whose note is `body_text` (at most 2000 characters, which may be omitted). Receivers see it as `fyi: true`. Plain text only: it cannot be combined with `body_html`, `attachments`, `payload_attachments` or `scheduled_at`, and on `/send-mail` it requires `in_reply_to`."
|
|
45214
46122
|
},
|
|
46123
|
+
"unsubscribe": {
|
|
46124
|
+
"type": "object",
|
|
46125
|
+
"additionalProperties": false,
|
|
46126
|
+
"required": ["url"],
|
|
46127
|
+
"description": "One-click unsubscribe for the message. Primitive writes a `List-Unsubscribe` header naming `url` (and `mailto` when given) and, unless `one_click` is false, `List-Unsubscribe-Post: List-Unsubscribe=One-Click` (RFC 8058), and DKIM-signs both. A receiver that offers one-click unsubscribe sends an HTTPS POST to `url` with the form body `List-Unsubscribe=One-Click`; your endpoint records the unsubscribe. Every recipient of the message gets the same `url`, so send one message per recipient when the URL identifies who to unsubscribe. The `headers` map refuses both header names, so this field is the only way to set them, and the headers it adds count toward the 25 entries `headers` may hold. A field whose rendered `List-Unsubscribe` would pass 980 characters is refused. Not accepted on a repeating send.",
|
|
46128
|
+
"properties": {
|
|
46129
|
+
"url": {
|
|
46130
|
+
"type": "string",
|
|
46131
|
+
"minLength": 1,
|
|
46132
|
+
"maxLength": 700,
|
|
46133
|
+
"pattern": "^[Hh][Tt][Tt][Pp][Ss]://(?:[A-Za-z0-9\\-._~:/?#\\[\\]@!$&'()*+;=]|%[0-9A-Fa-f]{2})+$",
|
|
46134
|
+
"description": "Absolute https URL that unsubscribes the recipient, at most 700 characters of URL characters only: printable ASCII with no spaces, double quotes, angle brackets or commas (percent-encode a comma as %2C), `%` only as a percent-encoded byte, `[` and `]` only around an IPv6 address host, a port from 1 to 65535, no `=?` (which mail readers can decode as an encoded word), and no user name or password. The host must be public: not a single-label name, `localhost` or a name under `.localhost`, `.internal`, `.local`, `.home.arpa` or `.localdomain`, or a loopback, link-local, private, shared, reserved, multicast or unspecified IP address, including an IPv6 address that embeds one. The scheme is written in lower case."
|
|
46135
|
+
},
|
|
46136
|
+
"one_click": {
|
|
46137
|
+
"type": "boolean",
|
|
46138
|
+
"default": true,
|
|
46139
|
+
"description": "Whether to also write `List-Unsubscribe-Post: List-Unsubscribe=One-Click`, which tells the receiver it may unsubscribe with a single POST to `url`. Set false when `url` needs the recipient to open a page and confirm."
|
|
46140
|
+
},
|
|
46141
|
+
"mailto": {
|
|
46142
|
+
"type": "string",
|
|
46143
|
+
"minLength": 3,
|
|
46144
|
+
"maxLength": 254,
|
|
46145
|
+
"pattern": "^[A-Za-z0-9!$'*+=_~-]+(?:\\.[A-Za-z0-9!$'*+=_~-]+)*@[A-Za-z0-9](?:[A-Za-z0-9-]{0,61}[A-Za-z0-9])?(?:\\.[A-Za-z0-9](?:[A-Za-z0-9-]{0,61}[A-Za-z0-9])?)+$",
|
|
46146
|
+
"description": "An address to also offer as a `mailto:` unsubscribe, written after `url`. The address alone, without the `mailto:` prefix: at most 64 letters, digits and `.!$'*+=_~-` before the `@`, and a dotted domain after it. An `=` is written as `%3D` in the header."
|
|
46147
|
+
}
|
|
46148
|
+
}
|
|
46149
|
+
},
|
|
45215
46150
|
"headers": {
|
|
45216
46151
|
"type": "object",
|
|
45217
46152
|
"maxProperties": 25,
|
|
@@ -50331,7 +51266,7 @@ const openapiDocument = {
|
|
|
50331
51266
|
},
|
|
50332
51267
|
"state": {
|
|
50333
51268
|
"type": "string",
|
|
50334
|
-
"description": "Where it stands now: enabled, disabled, deleted or replaced (endpoints and routes), enabled or disabled (filters, wake schedules), live, suspended or deleted (Functions), active, paused or ended (repeating sends), scheduled (scheduled sends), active, suspended, expired or revoked (API keys), pending, claimed, connected or revoked (agents), draft, observe or active (email rules), pending, accepted, declined or withdrawn (rule requests and objections), and for a coverage notice what it told: started (a rule
|
|
51269
|
+
"description": "Where it stands now: enabled, disabled, deleted or replaced (endpoints and routes), enabled or disabled (filters, wake schedules), live, suspended or deleted (Functions), active, paused or ended (repeating sends), scheduled (scheduled sends), active, suspended, expired or revoked (API keys), pending, claimed, connected or revoked (agents), draft, observe or active (email rules), pending, accepted, declined or withdrawn (rule requests and objections), and for a coverage notice what it told: started (a rule was made or turned active over your mailboxes, or an active rule came to reach you; once per rule, not per mailbox), new_version, receivers_changed (who receives an active rule's matches changed: its keys, webhooks or share), or baseline (it already covered you when notices began). pending: a rule started covering your mailbox and the notices have not recorded that start yet (a restart within an hour of the last start notice is recorded without a new one); its id is then the rule's, and created_at when the rule last changed."
|
|
50335
51270
|
},
|
|
50336
51271
|
"created_at": {
|
|
50337
51272
|
"type": "string",
|
|
@@ -50340,7 +51275,7 @@ const openapiDocument = {
|
|
|
50340
51275
|
"ended_at": {
|
|
50341
51276
|
"type": ["string", "null"],
|
|
50342
51277
|
"format": "date-time",
|
|
50343
|
-
"description": "When it was deleted, replaced or revoked, when an API key expired, when a repeating send stopped, or when a rule request or objection was decided (accepted, declined or withdrawn); null otherwise."
|
|
51278
|
+
"description": "When it was deleted, replaced or revoked, when an API key expired, when a repeating send stopped, or when a rule request or objection was decided (accepted, declined or withdrawn); null otherwise, as on every coverage notice."
|
|
50344
51279
|
},
|
|
50345
51280
|
"owned_now": {
|
|
50346
51281
|
"type": "string",
|
|
@@ -50368,6 +51303,74 @@ const openapiDocument = {
|
|
|
50368
51303
|
"id",
|
|
50369
51304
|
"name"
|
|
50370
51305
|
]
|
|
51306
|
+
},
|
|
51307
|
+
"rule_id": {
|
|
51308
|
+
"description": "For email_rule_coverage, always: the rule it is about, since names need not be unique (for pending, the same as id). GET /v1/email-rules/covering-me/{rule_id} shows the rule while it covers you, or as it last decided on your mail; otherwise it answers not_found.",
|
|
51309
|
+
"type": "string",
|
|
51310
|
+
"format": "uuid"
|
|
51311
|
+
},
|
|
51312
|
+
"receivers_change": {
|
|
51313
|
+
"description": "For a receivers_changed notice: who received the rule's matches before and after, in totals. They can be equal when only an owner or a webhook site changed; covering-me shows them by owner. A rotated key counts once here while the old one still works; covering-me counts both.",
|
|
51314
|
+
"type": "object",
|
|
51315
|
+
"properties": {
|
|
51316
|
+
"before": {
|
|
51317
|
+
"type": "object",
|
|
51318
|
+
"properties": {
|
|
51319
|
+
"keys": {
|
|
51320
|
+
"type": "integer",
|
|
51321
|
+
"minimum": 0
|
|
51322
|
+
},
|
|
51323
|
+
"reads_in_full": {
|
|
51324
|
+
"type": "integer",
|
|
51325
|
+
"minimum": 0,
|
|
51326
|
+
"description": "Of the keys, how many also read your own mail beyond the rule."
|
|
51327
|
+
},
|
|
51328
|
+
"webhooks": {
|
|
51329
|
+
"type": "integer",
|
|
51330
|
+
"minimum": 0
|
|
51331
|
+
},
|
|
51332
|
+
"shares": {
|
|
51333
|
+
"type": "integer",
|
|
51334
|
+
"minimum": 0
|
|
51335
|
+
}
|
|
51336
|
+
},
|
|
51337
|
+
"required": [
|
|
51338
|
+
"keys",
|
|
51339
|
+
"reads_in_full",
|
|
51340
|
+
"webhooks",
|
|
51341
|
+
"shares"
|
|
51342
|
+
]
|
|
51343
|
+
},
|
|
51344
|
+
"after": {
|
|
51345
|
+
"type": "object",
|
|
51346
|
+
"properties": {
|
|
51347
|
+
"keys": {
|
|
51348
|
+
"type": "integer",
|
|
51349
|
+
"minimum": 0
|
|
51350
|
+
},
|
|
51351
|
+
"reads_in_full": {
|
|
51352
|
+
"type": "integer",
|
|
51353
|
+
"minimum": 0,
|
|
51354
|
+
"description": "Of the keys, how many also read your own mail beyond the rule."
|
|
51355
|
+
},
|
|
51356
|
+
"webhooks": {
|
|
51357
|
+
"type": "integer",
|
|
51358
|
+
"minimum": 0
|
|
51359
|
+
},
|
|
51360
|
+
"shares": {
|
|
51361
|
+
"type": "integer",
|
|
51362
|
+
"minimum": 0
|
|
51363
|
+
}
|
|
51364
|
+
},
|
|
51365
|
+
"required": [
|
|
51366
|
+
"keys",
|
|
51367
|
+
"reads_in_full",
|
|
51368
|
+
"webhooks",
|
|
51369
|
+
"shares"
|
|
51370
|
+
]
|
|
51371
|
+
}
|
|
51372
|
+
},
|
|
51373
|
+
"required": ["before", "after"]
|
|
50371
51374
|
}
|
|
50372
51375
|
},
|
|
50373
51376
|
"required": [
|
|
@@ -50618,7 +51621,7 @@ const openapiDocument = {
|
|
|
50618
51621
|
"checked": {
|
|
50619
51622
|
"type": "integer",
|
|
50620
51623
|
"minimum": 0,
|
|
50621
|
-
"description": "Classified emails the conditions were decided for: of the newest 200 from the last 30 days in your own mailboxes
|
|
51624
|
+
"description": "Classified emails the conditions were decided for: of the newest 200 from the last 30 days in the scope counted (evaluation_scope as sent; by default your own mailboxes, for a grant rule those you may share), the ones you can still read."
|
|
50622
51625
|
},
|
|
50623
51626
|
"matched": {
|
|
50624
51627
|
"type": "integer",
|
|
@@ -50628,6 +51631,33 @@ const openapiDocument = {
|
|
|
50628
51631
|
},
|
|
50629
51632
|
"required": ["checked", "matched"]
|
|
50630
51633
|
},
|
|
51634
|
+
"EmailRuleScopeSelection": {
|
|
51635
|
+
"type": "object",
|
|
51636
|
+
"properties": {
|
|
51637
|
+
"addresses": {
|
|
51638
|
+
"maxItems": 100,
|
|
51639
|
+
"type": "array",
|
|
51640
|
+
"items": {
|
|
51641
|
+
"type": "string",
|
|
51642
|
+
"minLength": 3,
|
|
51643
|
+
"maxLength": 320,
|
|
51644
|
+
"pattern": "^[^\\s@]+@[^\\s@]+$"
|
|
51645
|
+
}
|
|
51646
|
+
},
|
|
51647
|
+
"domains": {
|
|
51648
|
+
"maxItems": 50,
|
|
51649
|
+
"type": "array",
|
|
51650
|
+
"items": {
|
|
51651
|
+
"type": "string",
|
|
51652
|
+
"minLength": 1,
|
|
51653
|
+
"maxLength": 253,
|
|
51654
|
+
"pattern": "^[^\\s@]+\\.[^\\s@]+$"
|
|
51655
|
+
}
|
|
51656
|
+
}
|
|
51657
|
+
},
|
|
51658
|
+
"additionalProperties": false,
|
|
51659
|
+
"description": "For a selected rule: inboxes, and domains (each address at them the author reads); each one you can read."
|
|
51660
|
+
},
|
|
50631
51661
|
"CompileEmailRuleRequest": {
|
|
50632
51662
|
"type": "object",
|
|
50633
51663
|
"properties": {
|
|
@@ -50645,7 +51675,17 @@ const openapiDocument = {
|
|
|
50645
51675
|
"include_sql": {
|
|
50646
51676
|
"description": "Also return the SQL the rule renders to. Off by default.",
|
|
50647
51677
|
"type": "boolean"
|
|
50648
|
-
}
|
|
51678
|
+
},
|
|
51679
|
+
"evaluation_scope": {
|
|
51680
|
+
"description": "The scope breadth is counted over, as a test of the tree runs it: send the one you will save the rule with. Nothing is saved. own_mailboxes (the default): mail to your own addresses and agents (for a rule that shares, those mail:share_own lets you share). author: all you can read, inboxes added later included. selected: what scope_selection names.",
|
|
51681
|
+
"type": "string",
|
|
51682
|
+
"enum": [
|
|
51683
|
+
"author",
|
|
51684
|
+
"own_mailboxes",
|
|
51685
|
+
"selected"
|
|
51686
|
+
]
|
|
51687
|
+
},
|
|
51688
|
+
"scope_selection": { "$ref": "#/components/schemas/EmailRuleScopeSelection" }
|
|
50649
51689
|
},
|
|
50650
51690
|
"required": ["english"],
|
|
50651
51691
|
"additionalProperties": false
|
|
@@ -50678,7 +51718,7 @@ const openapiDocument = {
|
|
|
50678
51718
|
"enum": ["grant", "do_not_share"]
|
|
50679
51719
|
},
|
|
50680
51720
|
"breadth": {
|
|
50681
|
-
"description": "How many
|
|
51721
|
+
"description": "How many recent emails its conditions hold for, warning or not, counted over evaluation_scope as sent (by default your own mailboxes, whatever scope you save it with). Null when not counted in full: a rule with questions, headers-only access, no test of it possible as you, a count that failed or ran out of time, or, with no scope sent, a rule that holds only for mail to To or Cc addresses outside your own mailboxes (a warning says so). Writes ignore it, so a compile answer (without include_sql) can be sent back unchanged.",
|
|
50682
51722
|
"anyOf": [{ "$ref": "#/components/schemas/EmailRuleBreadth" }, { "type": "null" }]
|
|
50683
51723
|
},
|
|
50684
51724
|
"conditions_breadth": {
|
|
@@ -50855,33 +51895,6 @@ const openapiDocument = {
|
|
|
50855
51895
|
],
|
|
50856
51896
|
"additionalProperties": false
|
|
50857
51897
|
},
|
|
50858
|
-
"EmailRuleScopeSelection": {
|
|
50859
|
-
"type": "object",
|
|
50860
|
-
"properties": {
|
|
50861
|
-
"addresses": {
|
|
50862
|
-
"maxItems": 100,
|
|
50863
|
-
"type": "array",
|
|
50864
|
-
"items": {
|
|
50865
|
-
"type": "string",
|
|
50866
|
-
"minLength": 3,
|
|
50867
|
-
"maxLength": 320,
|
|
50868
|
-
"pattern": "^[^\\s@]+@[^\\s@]+$"
|
|
50869
|
-
}
|
|
50870
|
-
},
|
|
50871
|
-
"domains": {
|
|
50872
|
-
"maxItems": 50,
|
|
50873
|
-
"type": "array",
|
|
50874
|
-
"items": {
|
|
50875
|
-
"type": "string",
|
|
50876
|
-
"minLength": 1,
|
|
50877
|
-
"maxLength": 253,
|
|
50878
|
-
"pattern": "^[^\\s@]+\\.[^\\s@]+$"
|
|
50879
|
-
}
|
|
50880
|
-
}
|
|
50881
|
-
},
|
|
50882
|
-
"additionalProperties": false,
|
|
50883
|
-
"description": "For a selected rule: inboxes, and domains (each address at them the author reads); each one you can read."
|
|
50884
|
-
},
|
|
50885
51898
|
"EmailRuleExpectationSampleInput": {
|
|
50886
51899
|
"type": "object",
|
|
50887
51900
|
"properties": {
|
|
@@ -50968,6 +51981,16 @@ const openapiDocument = {
|
|
|
50968
51981
|
"pattern": "^[^\\u0000]*$"
|
|
50969
51982
|
}
|
|
50970
51983
|
},
|
|
51984
|
+
"attachment_names": {
|
|
51985
|
+
"maxItems": 10,
|
|
51986
|
+
"type": "array",
|
|
51987
|
+
"items": {
|
|
51988
|
+
"type": "string",
|
|
51989
|
+
"minLength": 1,
|
|
51990
|
+
"maxLength": 255,
|
|
51991
|
+
"pattern": "^[^\\u0000-\\u001f\\u007f]*$"
|
|
51992
|
+
}
|
|
51993
|
+
},
|
|
50971
51994
|
"automated": { "type": "boolean" },
|
|
50972
51995
|
"from_mailing_list": { "type": "boolean" },
|
|
50973
51996
|
"spam_score": {
|
|
@@ -51306,6 +52329,11 @@ const openapiDocument = {
|
|
|
51306
52329
|
"minimum": 0,
|
|
51307
52330
|
"description": "Emails decided on their conditions: matched, near_miss and no_match, less any missing a fact the rule reads."
|
|
51308
52331
|
},
|
|
52332
|
+
"conditions_held": {
|
|
52333
|
+
"description": "Of decided, how many its conditions alone held for. Compile's breadth and conditions_breadth count this over recent mail in the scope sent to it, by default your own; here it is counted over the emails this test decided.",
|
|
52334
|
+
"type": "integer",
|
|
52335
|
+
"minimum": 0
|
|
52336
|
+
},
|
|
51309
52337
|
"conditions": {
|
|
51310
52338
|
"type": "array",
|
|
51311
52339
|
"items": {
|
|
@@ -51373,7 +52401,7 @@ const openapiDocument = {
|
|
|
51373
52401
|
"warnings": {
|
|
51374
52402
|
"type": "array",
|
|
51375
52403
|
"items": { "type": "string" },
|
|
51376
|
-
"description": "What to look at first, such as what stopped every email when nothing matched."
|
|
52404
|
+
"description": "What to look at first, such as what stopped every email when nothing matched. On a test of the recent window, a grant rule whose conditions held for more than 20% of at least 20 emails checked (at least 90% if it has no questions), or for every one of fewer, gets a warning saying so, before those about what it decided."
|
|
51377
52405
|
}
|
|
51378
52406
|
},
|
|
51379
52407
|
"required": [
|
|
@@ -51529,6 +52557,10 @@ const openapiDocument = {
|
|
|
51529
52557
|
"type": "array",
|
|
51530
52558
|
"items": { "type": "string" }
|
|
51531
52559
|
},
|
|
52560
|
+
"attachment_names": {
|
|
52561
|
+
"type": "array",
|
|
52562
|
+
"items": { "type": "string" }
|
|
52563
|
+
},
|
|
51532
52564
|
"automated": { "type": "boolean" },
|
|
51533
52565
|
"from_mailing_list": { "type": "boolean" }
|
|
51534
52566
|
},
|
|
@@ -51539,6 +52571,7 @@ const openapiDocument = {
|
|
|
51539
52571
|
"subject",
|
|
51540
52572
|
"text",
|
|
51541
52573
|
"attachment_types",
|
|
52574
|
+
"attachment_names",
|
|
51542
52575
|
"automated",
|
|
51543
52576
|
"from_mailing_list"
|
|
51544
52577
|
],
|
|
@@ -51614,7 +52647,7 @@ const openapiDocument = {
|
|
|
51614
52647
|
},
|
|
51615
52648
|
"scope_selection": { "$ref": "#/components/schemas/EmailRuleScopeSelection" },
|
|
51616
52649
|
"rule_id": {
|
|
51617
|
-
"description": "A saved rule you can see: its current version and words.",
|
|
52650
|
+
"description": "A saved rule you can see, or one that covers your mailboxes (as you, sent to one of them; dashboard or Primitive CLI only): its current version and words.",
|
|
51618
52651
|
"type": "string",
|
|
51619
52652
|
"format": "uuid"
|
|
51620
52653
|
},
|
|
@@ -51726,7 +52759,7 @@ const openapiDocument = {
|
|
|
51726
52759
|
},
|
|
51727
52760
|
"scope_selection": { "$ref": "#/components/schemas/EmailRuleScopeSelection" },
|
|
51728
52761
|
"rule_id": {
|
|
51729
|
-
"description": "A saved rule you can see. Its current version is tested.",
|
|
52762
|
+
"description": "A saved rule you can see, or one that covers your mailboxes (as you; dashboard or Primitive CLI only). Its current version is tested.",
|
|
51730
52763
|
"type": "string",
|
|
51731
52764
|
"format": "uuid"
|
|
51732
52765
|
},
|
|
@@ -51797,6 +52830,17 @@ const openapiDocument = {
|
|
|
51797
52830
|
"maxLength": 255
|
|
51798
52831
|
}
|
|
51799
52832
|
},
|
|
52833
|
+
"attachment_names": {
|
|
52834
|
+
"description": "File names of those attachments, which the classifier and the questions read. With attachment_types, the same number, paired in order.",
|
|
52835
|
+
"maxItems": 20,
|
|
52836
|
+
"type": "array",
|
|
52837
|
+
"items": {
|
|
52838
|
+
"type": "string",
|
|
52839
|
+
"minLength": 1,
|
|
52840
|
+
"maxLength": 255,
|
|
52841
|
+
"pattern": "^[^\\u0000-\\u001f\\u007f]*$"
|
|
52842
|
+
}
|
|
52843
|
+
},
|
|
51800
52844
|
"automated": {
|
|
51801
52845
|
"description": "Whether the email was sent automatically, as its headers would show. Defaults to false.",
|
|
51802
52846
|
"type": "boolean"
|
|
@@ -52019,6 +53063,15 @@ const openapiDocument = {
|
|
|
52019
53063
|
"type": "boolean",
|
|
52020
53064
|
"description": "Whether you are its author, in your current membership. Only its author edits it."
|
|
52021
53065
|
},
|
|
53066
|
+
"pending_objections": {
|
|
53067
|
+
"description": "To its author, on the dashboard or the Primitive CLI: objections to it waiting for their answer (GET /email-rule-objections). A write's answer leaves it out when it could not be read in time: absent is not 0.",
|
|
53068
|
+
"type": "integer",
|
|
53069
|
+
"minimum": 0
|
|
53070
|
+
},
|
|
53071
|
+
"objection_notice": {
|
|
53072
|
+
"description": "With pending_objections above 0: that members asked you to turn it off, and how to answer.",
|
|
53073
|
+
"type": "string"
|
|
53074
|
+
},
|
|
52022
53075
|
"created_at": {
|
|
52023
53076
|
"type": "string",
|
|
52024
53077
|
"format": "date-time"
|
|
@@ -52036,6 +53089,27 @@ const openapiDocument = {
|
|
|
52036
53089
|
"type": "array",
|
|
52037
53090
|
"items": { "$ref": "#/components/schemas/EmailRuleScopeWarning" }
|
|
52038
53091
|
},
|
|
53092
|
+
"expectations": {
|
|
53093
|
+
"description": "Its saved expectations, each list by email id, as stored: on every create, and on an update that set or removed them.",
|
|
53094
|
+
"type": "object",
|
|
53095
|
+
"properties": {
|
|
53096
|
+
"match": {
|
|
53097
|
+
"type": "array",
|
|
53098
|
+
"items": {
|
|
53099
|
+
"type": "string",
|
|
53100
|
+
"format": "uuid"
|
|
53101
|
+
}
|
|
53102
|
+
},
|
|
53103
|
+
"no_match": {
|
|
53104
|
+
"type": "array",
|
|
53105
|
+
"items": {
|
|
53106
|
+
"type": "string",
|
|
53107
|
+
"format": "uuid"
|
|
53108
|
+
}
|
|
53109
|
+
}
|
|
53110
|
+
},
|
|
53111
|
+
"required": ["match", "no_match"]
|
|
53112
|
+
},
|
|
52039
53113
|
"expectation_samples": {
|
|
52040
53114
|
"description": "Its saved made-up emails, when this write set or added some.",
|
|
52041
53115
|
"type": "array",
|
|
@@ -52137,6 +53211,10 @@ const openapiDocument = {
|
|
|
52137
53211
|
"notice": {
|
|
52138
53212
|
"description": "After a write that saved a version of an active rule keys or webhooks read without checking it first: what it changes, and how to have the next one checked.",
|
|
52139
53213
|
"type": "string"
|
|
53214
|
+
},
|
|
53215
|
+
"share_notice": {
|
|
53216
|
+
"description": "After a write that ended the rule's share: that it did, given even when affects is not, with whom it was with and the version they accepted when those can be read.",
|
|
53217
|
+
"type": "string"
|
|
52140
53218
|
}
|
|
52141
53219
|
},
|
|
52142
53220
|
"required": [
|
|
@@ -52359,7 +53437,7 @@ const openapiDocument = {
|
|
|
52359
53437
|
}
|
|
52360
53438
|
},
|
|
52361
53439
|
"warnings": {
|
|
52362
|
-
"description": "The compile response's warnings. Stored as sent, then any server note they lack on an address or domain the description names that the rule does not test (on a rule request, every compiler note on the rule).",
|
|
53440
|
+
"description": "The compile response's warnings. Stored as sent, then any server note they lack on an address or domain the description names that the rule does not test, or on a grant rule that tests only who an email was sent to (on a rule request, every compiler note on the rule).",
|
|
52363
53441
|
"maxItems": 10,
|
|
52364
53442
|
"type": "array",
|
|
52365
53443
|
"items": {
|
|
@@ -52450,6 +53528,15 @@ const openapiDocument = {
|
|
|
52450
53528
|
"type": "boolean",
|
|
52451
53529
|
"description": "Whether you are its author, in your current membership. Only its author edits it."
|
|
52452
53530
|
},
|
|
53531
|
+
"pending_objections": {
|
|
53532
|
+
"description": "To its author, on the dashboard or the Primitive CLI: objections to it waiting for their answer (GET /email-rule-objections). A write's answer leaves it out when it could not be read in time: absent is not 0.",
|
|
53533
|
+
"type": "integer",
|
|
53534
|
+
"minimum": 0
|
|
53535
|
+
},
|
|
53536
|
+
"objection_notice": {
|
|
53537
|
+
"description": "With pending_objections above 0: that members asked you to turn it off, and how to answer.",
|
|
53538
|
+
"type": "string"
|
|
53539
|
+
},
|
|
52453
53540
|
"created_at": {
|
|
52454
53541
|
"type": "string",
|
|
52455
53542
|
"format": "date-time"
|
|
@@ -52548,6 +53635,15 @@ const openapiDocument = {
|
|
|
52548
53635
|
"type": "boolean",
|
|
52549
53636
|
"description": "Whether you are its author, in your current membership. Only its author edits it."
|
|
52550
53637
|
},
|
|
53638
|
+
"pending_objections": {
|
|
53639
|
+
"description": "To its author, on the dashboard or the Primitive CLI: objections to it waiting for their answer (GET /email-rule-objections). A write's answer leaves it out when it could not be read in time: absent is not 0.",
|
|
53640
|
+
"type": "integer",
|
|
53641
|
+
"minimum": 0
|
|
53642
|
+
},
|
|
53643
|
+
"objection_notice": {
|
|
53644
|
+
"description": "With pending_objections above 0: that members asked you to turn it off, and how to answer.",
|
|
53645
|
+
"type": "string"
|
|
53646
|
+
},
|
|
52551
53647
|
"created_at": {
|
|
52552
53648
|
"type": "string",
|
|
52553
53649
|
"format": "date-time"
|
|
@@ -52645,7 +53741,7 @@ const openapiDocument = {
|
|
|
52645
53741
|
}
|
|
52646
53742
|
},
|
|
52647
53743
|
"warnings": {
|
|
52648
|
-
"description": "The compile response's warnings. Stored as sent, then any server note they lack on an address or domain the description names that the rule does not test (on a rule request, every compiler note on the rule).",
|
|
53744
|
+
"description": "The compile response's warnings. Stored as sent, then any server note they lack on an address or domain the description names that the rule does not test, or on a grant rule that tests only who an email was sent to (on a rule request, every compiler note on the rule).",
|
|
52649
53745
|
"maxItems": 10,
|
|
52650
53746
|
"type": "array",
|
|
52651
53747
|
"items": {
|
|
@@ -52779,6 +53875,10 @@ const openapiDocument = {
|
|
|
52779
53875
|
"other_keys": {
|
|
52780
53876
|
"description": "How many more keys, not revoked, list the rule that you may not read. They are not named.",
|
|
52781
53877
|
"type": "integer"
|
|
53878
|
+
},
|
|
53879
|
+
"share_notice": {
|
|
53880
|
+
"description": "When the delete ended a share: that it did, with whom it was with and the version they accepted when those can be read.",
|
|
53881
|
+
"type": "string"
|
|
52782
53882
|
}
|
|
52783
53883
|
},
|
|
52784
53884
|
"required": ["deleted"]
|
|
@@ -52953,6 +54053,16 @@ const openapiDocument = {
|
|
|
52953
54053
|
"count": {
|
|
52954
54054
|
"type": "integer",
|
|
52955
54055
|
"exclusiveMinimum": 0
|
|
54056
|
+
},
|
|
54057
|
+
"reads_in_full": {
|
|
54058
|
+
"description": "On a rule over your mail: how many of these keys also list one of your own mailboxes, or its domain, so they read your mail there beyond what this rule matches.",
|
|
54059
|
+
"type": "integer",
|
|
54060
|
+
"minimum": 0
|
|
54061
|
+
},
|
|
54062
|
+
"newest_at": {
|
|
54063
|
+
"description": "When the newest of these was made, to the minute: a key, a webhook or the share, or the rule (the share, for what reaches it only through the share) if that was later. Not when a key was given access to this rule, or anything turned back on; a rotated key shows as newly made, and one key on two rules can show the same minute under both.",
|
|
54064
|
+
"type": "string",
|
|
54065
|
+
"format": "date-time"
|
|
52956
54066
|
}
|
|
52957
54067
|
},
|
|
52958
54068
|
"required": ["owner", "count"]
|
|
@@ -53000,7 +54110,7 @@ const openapiDocument = {
|
|
|
53000
54110
|
"exclusiveMinimum": 0
|
|
53001
54111
|
},
|
|
53002
54112
|
"hosts": {
|
|
53003
|
-
"description": "Distinct sites they deliver to (a host's
|
|
54113
|
+
"description": "Distinct sites they deliver to (the domain a host is registered under, an app's own name on a known app platform, or a function URL service in its region), sorted, at most 10; \"unknown host\" (last) for any that cannot be named.",
|
|
53004
54114
|
"type": "array",
|
|
53005
54115
|
"items": { "type": "string" }
|
|
53006
54116
|
},
|
|
@@ -53008,6 +54118,11 @@ const openapiDocument = {
|
|
|
53008
54118
|
"description": "Hosts past those listed.",
|
|
53009
54119
|
"type": "integer",
|
|
53010
54120
|
"minimum": 0
|
|
54121
|
+
},
|
|
54122
|
+
"newest_at": {
|
|
54123
|
+
"description": "When the newest of these was made, to the minute: a key, a webhook or the share, or the rule (the share, for what reaches it only through the share) if that was later. Not when a key was given access to this rule, or anything turned back on; a rotated key shows as newly made, and one key on two rules can show the same minute under both.",
|
|
54124
|
+
"type": "string",
|
|
54125
|
+
"format": "date-time"
|
|
53011
54126
|
}
|
|
53012
54127
|
},
|
|
53013
54128
|
"required": ["owner", "count"]
|
|
@@ -53045,6 +54160,11 @@ const openapiDocument = {
|
|
|
53045
54160
|
"count": {
|
|
53046
54161
|
"type": "integer",
|
|
53047
54162
|
"exclusiveMinimum": 0
|
|
54163
|
+
},
|
|
54164
|
+
"newest_at": {
|
|
54165
|
+
"description": "When the newest of these was made, to the minute: a key, a webhook or the share, or the rule (the share, for what reaches it only through the share) if that was later. Not when a key was given access to this rule, or anything turned back on; a rotated key shows as newly made, and one key on two rules can show the same minute under both.",
|
|
54166
|
+
"type": "string",
|
|
54167
|
+
"format": "date-time"
|
|
53048
54168
|
}
|
|
53049
54169
|
},
|
|
53050
54170
|
"required": ["beneficiary", "count"]
|
|
@@ -53057,7 +54177,7 @@ const openapiDocument = {
|
|
|
53057
54177
|
"webhooks",
|
|
53058
54178
|
"shares"
|
|
53059
54179
|
],
|
|
53060
|
-
"description": "Who receives what the rule shares
|
|
54180
|
+
"description": "Who receives what the rule shares, by person and count. As receivers, empty unless it is an active grant rule."
|
|
53061
54181
|
},
|
|
53062
54182
|
"ListEmailRuleResultsResult": {
|
|
53063
54183
|
"type": "object",
|
|
@@ -53519,7 +54639,7 @@ const openapiDocument = {
|
|
|
53519
54639
|
}
|
|
53520
54640
|
},
|
|
53521
54641
|
"warnings": {
|
|
53522
|
-
"description": "The compile response's warnings. Stored as sent, then any server note they lack on an address or domain the description names that the rule does not test (on a rule request, every compiler note on the rule).",
|
|
54642
|
+
"description": "The compile response's warnings. Stored as sent, then any server note they lack on an address or domain the description names that the rule does not test, or on a grant rule that tests only who an email was sent to (on a rule request, every compiler note on the rule).",
|
|
53523
54643
|
"maxItems": 10,
|
|
53524
54644
|
"type": "array",
|
|
53525
54645
|
"items": {
|
|
@@ -53921,6 +55041,15 @@ const openapiDocument = {
|
|
|
53921
55041
|
"type": "boolean",
|
|
53922
55042
|
"description": "Whether you are its author, in your current membership. Only its author edits it."
|
|
53923
55043
|
},
|
|
55044
|
+
"pending_objections": {
|
|
55045
|
+
"description": "To its author, on the dashboard or the Primitive CLI: objections to it waiting for their answer (GET /email-rule-objections). A write's answer leaves it out when it could not be read in time: absent is not 0.",
|
|
55046
|
+
"type": "integer",
|
|
55047
|
+
"minimum": 0
|
|
55048
|
+
},
|
|
55049
|
+
"objection_notice": {
|
|
55050
|
+
"description": "With pending_objections above 0: that members asked you to turn it off, and how to answer.",
|
|
55051
|
+
"type": "string"
|
|
55052
|
+
},
|
|
53924
55053
|
"created_at": {
|
|
53925
55054
|
"type": "string",
|
|
53926
55055
|
"format": "date-time"
|
|
@@ -54141,7 +55270,7 @@ const openapiDocument = {
|
|
|
54141
55270
|
}
|
|
54142
55271
|
},
|
|
54143
55272
|
"warnings": {
|
|
54144
|
-
"description": "The compile response's warnings. Stored as sent, then any server note they lack on an address or domain the description names that the rule does not test (on a rule request, every compiler note on the rule).",
|
|
55273
|
+
"description": "The compile response's warnings. Stored as sent, then any server note they lack on an address or domain the description names that the rule does not test, or on a grant rule that tests only who an email was sent to (on a rule request, every compiler note on the rule).",
|
|
54145
55274
|
"maxItems": 10,
|
|
54146
55275
|
"type": "array",
|
|
54147
55276
|
"items": {
|
|
@@ -54186,7 +55315,7 @@ const openapiDocument = {
|
|
|
54186
55315
|
"rule": { "$ref": "#/components/schemas/EmailRuleWithVersion" },
|
|
54187
55316
|
"share": { "$ref": "#/components/schemas/EmailRuleShare" },
|
|
54188
55317
|
"notice": {
|
|
54189
|
-
"description": "What is now shared, in words.",
|
|
55318
|
+
"description": "What is now shared, in words. When you changed the rule and it can now match mail the request left out by who it was sent to (such as mail where an address is only copied), that comes first.",
|
|
54190
55319
|
"type": "string"
|
|
54191
55320
|
}
|
|
54192
55321
|
},
|
|
@@ -54551,7 +55680,11 @@ const openapiDocument = {
|
|
|
54551
55680
|
"exclusiveMinimum": 0
|
|
54552
55681
|
},
|
|
54553
55682
|
"receivers": {
|
|
54554
|
-
"description": "Null when it could not be read in time; try again.",
|
|
55683
|
+
"description": "Null when it could not be read in time, or for a rule shown in draft or observe that turned active or was deleted after the page was read; try again.",
|
|
55684
|
+
"anyOf": [{ "$ref": "#/components/schemas/EmailRuleReceivers" }, { "type": "null" }]
|
|
55685
|
+
},
|
|
55686
|
+
"receivers_once_active": {
|
|
55687
|
+
"description": "For a grant rule in draft or observe: who would receive what it matches once it is turned active. None of them receives anything yet. Null for any other rule, and as receivers is null.",
|
|
54555
55688
|
"anyOf": [{ "$ref": "#/components/schemas/EmailRuleReceivers" }, { "type": "null" }]
|
|
54556
55689
|
},
|
|
54557
55690
|
"created_at": {
|
|
@@ -54569,6 +55702,7 @@ const openapiDocument = {
|
|
|
54569
55702
|
"covers",
|
|
54570
55703
|
"current_version",
|
|
54571
55704
|
"receivers",
|
|
55705
|
+
"receivers_once_active",
|
|
54572
55706
|
"created_at"
|
|
54573
55707
|
]
|
|
54574
55708
|
},
|
|
@@ -54639,7 +55773,11 @@ const openapiDocument = {
|
|
|
54639
55773
|
"description": "Null when covers_now is false."
|
|
54640
55774
|
},
|
|
54641
55775
|
"receivers": {
|
|
54642
|
-
"description": "Null when covers_now is false (who receives it now is not about your mail),
|
|
55776
|
+
"description": "Null when covers_now is false (who receives it now is not about your mail), when it could not be read in time, or for a rule shown in draft or observe that turned active or was deleted after it was read; try again.",
|
|
55777
|
+
"anyOf": [{ "$ref": "#/components/schemas/EmailRuleReceivers" }, { "type": "null" }]
|
|
55778
|
+
},
|
|
55779
|
+
"receivers_once_active": {
|
|
55780
|
+
"description": "As on the list; also null when covers_now is false.",
|
|
54643
55781
|
"anyOf": [{ "$ref": "#/components/schemas/EmailRuleReceivers" }, { "type": "null" }]
|
|
54644
55782
|
},
|
|
54645
55783
|
"created_at": {
|
|
@@ -54669,6 +55807,7 @@ const openapiDocument = {
|
|
|
54669
55807
|
"covers",
|
|
54670
55808
|
"current_version",
|
|
54671
55809
|
"receivers",
|
|
55810
|
+
"receivers_once_active",
|
|
54672
55811
|
"created_at",
|
|
54673
55812
|
"covers_now",
|
|
54674
55813
|
"current"
|
|
@@ -54786,6 +55925,10 @@ const openapiDocument = {
|
|
|
54786
55925
|
"type": "array",
|
|
54787
55926
|
"items": { "$ref": "#/components/schemas/EmailRuleDecisionOnMyMail" }
|
|
54788
55927
|
},
|
|
55928
|
+
"ListEmailRuleVersionsCoveringMeResult": {
|
|
55929
|
+
"type": "array",
|
|
55930
|
+
"items": { "$ref": "#/components/schemas/EmailRuleCoveringMeVersion" }
|
|
55931
|
+
},
|
|
54789
55932
|
"EmailRuleObjection": {
|
|
54790
55933
|
"type": "object",
|
|
54791
55934
|
"properties": {
|
|
@@ -54826,8 +55969,14 @@ const openapiDocument = {
|
|
|
54826
55969
|
"description": "Who wrote the rule.",
|
|
54827
55970
|
"$ref": "#/components/schemas/EmailRuleObjectionPerson"
|
|
54828
55971
|
},
|
|
54829
|
-
"is_objector": {
|
|
54830
|
-
|
|
55972
|
+
"is_objector": {
|
|
55973
|
+
"type": "boolean",
|
|
55974
|
+
"description": "Whether you made it, in your current membership."
|
|
55975
|
+
},
|
|
55976
|
+
"is_author": {
|
|
55977
|
+
"type": "boolean",
|
|
55978
|
+
"description": "Whether it is to a rule you wrote, in your current membership."
|
|
55979
|
+
},
|
|
54831
55980
|
"message": {
|
|
54832
55981
|
"description": "Why the objector objects; null when none was given, or once withdrawn.",
|
|
54833
55982
|
"type": ["string", "null"]
|
|
@@ -55045,6 +56194,82 @@ const openapiDocument = {
|
|
|
55045
56194
|
"outbound_ready"
|
|
55046
56195
|
]
|
|
55047
56196
|
},
|
|
56197
|
+
"ImportGoogleMailMessageRequest": {
|
|
56198
|
+
"type": "object",
|
|
56199
|
+
"properties": { "message_id": {
|
|
56200
|
+
"type": "string",
|
|
56201
|
+
"pattern": "^[A-Za-z0-9_-]{1,128}$"
|
|
56202
|
+
} },
|
|
56203
|
+
"required": ["message_id"],
|
|
56204
|
+
"additionalProperties": false
|
|
56205
|
+
},
|
|
56206
|
+
"ImportGoogleMailMessageResult": { "anyOf": [
|
|
56207
|
+
{
|
|
56208
|
+
"type": "object",
|
|
56209
|
+
"properties": {
|
|
56210
|
+
"status": {
|
|
56211
|
+
"type": "string",
|
|
56212
|
+
"enum": ["imported", "already_present"]
|
|
56213
|
+
},
|
|
56214
|
+
"email_id": {
|
|
56215
|
+
"type": "string",
|
|
56216
|
+
"format": "uuid",
|
|
56217
|
+
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
|
|
56218
|
+
}
|
|
56219
|
+
},
|
|
56220
|
+
"required": ["status", "email_id"]
|
|
56221
|
+
},
|
|
56222
|
+
{
|
|
56223
|
+
"type": "object",
|
|
56224
|
+
"properties": { "status": {
|
|
56225
|
+
"type": "string",
|
|
56226
|
+
"enum": [
|
|
56227
|
+
"suppressed",
|
|
56228
|
+
"in_progress",
|
|
56229
|
+
"indeterminate"
|
|
56230
|
+
]
|
|
56231
|
+
} },
|
|
56232
|
+
"required": ["status"]
|
|
56233
|
+
},
|
|
56234
|
+
{
|
|
56235
|
+
"type": "object",
|
|
56236
|
+
"properties": {
|
|
56237
|
+
"status": {
|
|
56238
|
+
"type": "string",
|
|
56239
|
+
"const": "skipped"
|
|
56240
|
+
},
|
|
56241
|
+
"reason": {
|
|
56242
|
+
"type": "string",
|
|
56243
|
+
"enum": [
|
|
56244
|
+
"excluded_label",
|
|
56245
|
+
"out_of_window",
|
|
56246
|
+
"too_large",
|
|
56247
|
+
"provider_missing"
|
|
56248
|
+
]
|
|
56249
|
+
}
|
|
56250
|
+
},
|
|
56251
|
+
"required": ["status", "reason"]
|
|
56252
|
+
}
|
|
56253
|
+
] },
|
|
56254
|
+
"ReconcileGoogleMailImportRequest": {
|
|
56255
|
+
"type": "object",
|
|
56256
|
+
"properties": {},
|
|
56257
|
+
"additionalProperties": false
|
|
56258
|
+
},
|
|
56259
|
+
"ReconcileGoogleMailImportResult": {
|
|
56260
|
+
"type": "object",
|
|
56261
|
+
"properties": {
|
|
56262
|
+
"cleaned": {
|
|
56263
|
+
"type": "integer",
|
|
56264
|
+
"minimum": 0
|
|
56265
|
+
},
|
|
56266
|
+
"unresolved": {
|
|
56267
|
+
"type": "integer",
|
|
56268
|
+
"minimum": 0
|
|
56269
|
+
}
|
|
56270
|
+
},
|
|
56271
|
+
"required": ["cleaned", "unresolved"]
|
|
56272
|
+
},
|
|
55048
56273
|
"ThreadMute": {
|
|
55049
56274
|
"type": "object",
|
|
55050
56275
|
"required": [
|
|
@@ -59266,6 +60491,14 @@ const openapiDocument = {
|
|
|
59266
60491
|
"additionalProperties": {}
|
|
59267
60492
|
}
|
|
59268
60493
|
},
|
|
60494
|
+
"mail_limits_available": {
|
|
60495
|
+
"description": "Whether a key limited to some mail (addresses, domains or email rules) would be created now. While false, creating one is refused, so a client offers neither the listed-address presets nor a listed mail reach for a custom key.",
|
|
60496
|
+
"type": "boolean"
|
|
60497
|
+
},
|
|
60498
|
+
"org_reach_available": {
|
|
60499
|
+
"description": "Whether the credential policy lets a new key reach every mailbox. While false, creating one is refused, so a client offers no preset that reaches every mailbox, and a custom key lists its addresses or domains. With mail_limits_available false as well, no key can be created and every preset is unavailable.",
|
|
60500
|
+
"type": "boolean"
|
|
60501
|
+
},
|
|
59269
60502
|
"matrix": {
|
|
59270
60503
|
"type": "array",
|
|
59271
60504
|
"items": {
|
|
@@ -59651,6 +60884,27 @@ const openapiDocument = {
|
|
|
59651
60884
|
"additionalProperties": {}
|
|
59652
60885
|
}
|
|
59653
60886
|
},
|
|
60887
|
+
"rules_added": {
|
|
60888
|
+
"description": "Each rule (or share) the edit put on a key that reads mail, with granted_emails, how many emails it has already shared, which the key can now read while it is active: counted up to 10,000, with more: true past that; null when not counted.",
|
|
60889
|
+
"type": "array",
|
|
60890
|
+
"items": {
|
|
60891
|
+
"type": "object",
|
|
60892
|
+
"properties": {
|
|
60893
|
+
"id": { "type": "string" },
|
|
60894
|
+
"granted_emails": {
|
|
60895
|
+
"type": ["integer", "null"],
|
|
60896
|
+
"minimum": 0,
|
|
60897
|
+
"maximum": 9007199254740991
|
|
60898
|
+
},
|
|
60899
|
+
"more": {
|
|
60900
|
+
"type": "boolean",
|
|
60901
|
+
"const": true
|
|
60902
|
+
}
|
|
60903
|
+
},
|
|
60904
|
+
"required": ["id", "granted_emails"],
|
|
60905
|
+
"additionalProperties": {}
|
|
60906
|
+
}
|
|
60907
|
+
},
|
|
59654
60908
|
"grant_unguarded": {
|
|
59655
60909
|
"description": "Present, as true, when the grant was changed without expected_grant_ref, so the change was not checked against changes made since the key was read. Send grant.ref from GET /v1/keys/{id} as expected_grant_ref to have such a change refused instead (409 conflict, details.reason grant_changed).",
|
|
59656
60910
|
"type": "boolean",
|
|
@@ -60519,6 +61773,304 @@ const openapiDocument = {
|
|
|
60519
61773
|
"required": ["semantic_search_opt_in"],
|
|
60520
61774
|
"additionalProperties": false
|
|
60521
61775
|
},
|
|
61776
|
+
"EmailNotifications_getEmailNotificationSettings_Result": {
|
|
61777
|
+
"type": "object",
|
|
61778
|
+
"properties": {
|
|
61779
|
+
"entitled": { "type": "boolean" },
|
|
61780
|
+
"kinds": {
|
|
61781
|
+
"type": "array",
|
|
61782
|
+
"items": {
|
|
61783
|
+
"type": "object",
|
|
61784
|
+
"properties": {
|
|
61785
|
+
"kind": { "type": "string" },
|
|
61786
|
+
"label": { "type": "string" },
|
|
61787
|
+
"description": { "type": "string" },
|
|
61788
|
+
"class": {
|
|
61789
|
+
"type": "string",
|
|
61790
|
+
"enum": [
|
|
61791
|
+
"platform_required",
|
|
61792
|
+
"org_requirable",
|
|
61793
|
+
"optional"
|
|
61794
|
+
]
|
|
61795
|
+
},
|
|
61796
|
+
"entitlement_required": { "type": "boolean" },
|
|
61797
|
+
"entitled": { "type": "boolean" },
|
|
61798
|
+
"org_mode": {
|
|
61799
|
+
"type": ["string", "null"],
|
|
61800
|
+
"enum": [
|
|
61801
|
+
"off",
|
|
61802
|
+
"optional_on",
|
|
61803
|
+
"optional_off",
|
|
61804
|
+
"required",
|
|
61805
|
+
null
|
|
61806
|
+
]
|
|
61807
|
+
},
|
|
61808
|
+
"org_mode_is_default": { "type": "boolean" },
|
|
61809
|
+
"org_modes": {
|
|
61810
|
+
"type": "array",
|
|
61811
|
+
"items": {
|
|
61812
|
+
"type": "string",
|
|
61813
|
+
"enum": [
|
|
61814
|
+
"off",
|
|
61815
|
+
"optional_on",
|
|
61816
|
+
"optional_off",
|
|
61817
|
+
"required"
|
|
61818
|
+
]
|
|
61819
|
+
}
|
|
61820
|
+
},
|
|
61821
|
+
"member_enabled": { "type": "boolean" },
|
|
61822
|
+
"member_can_change": { "type": "boolean" }
|
|
61823
|
+
},
|
|
61824
|
+
"required": [
|
|
61825
|
+
"kind",
|
|
61826
|
+
"label",
|
|
61827
|
+
"description",
|
|
61828
|
+
"class",
|
|
61829
|
+
"entitlement_required",
|
|
61830
|
+
"entitled",
|
|
61831
|
+
"org_mode",
|
|
61832
|
+
"org_mode_is_default",
|
|
61833
|
+
"org_modes",
|
|
61834
|
+
"member_enabled",
|
|
61835
|
+
"member_can_change"
|
|
61836
|
+
]
|
|
61837
|
+
}
|
|
61838
|
+
}
|
|
61839
|
+
},
|
|
61840
|
+
"required": ["entitled", "kinds"]
|
|
61841
|
+
},
|
|
61842
|
+
"EmailNotifications_updateEmailNotificationSettings_Result": {
|
|
61843
|
+
"type": "object",
|
|
61844
|
+
"properties": {
|
|
61845
|
+
"entitled": { "type": "boolean" },
|
|
61846
|
+
"kinds": {
|
|
61847
|
+
"type": "array",
|
|
61848
|
+
"items": {
|
|
61849
|
+
"type": "object",
|
|
61850
|
+
"properties": {
|
|
61851
|
+
"kind": { "type": "string" },
|
|
61852
|
+
"label": { "type": "string" },
|
|
61853
|
+
"description": { "type": "string" },
|
|
61854
|
+
"class": {
|
|
61855
|
+
"type": "string",
|
|
61856
|
+
"enum": [
|
|
61857
|
+
"platform_required",
|
|
61858
|
+
"org_requirable",
|
|
61859
|
+
"optional"
|
|
61860
|
+
]
|
|
61861
|
+
},
|
|
61862
|
+
"entitlement_required": { "type": "boolean" },
|
|
61863
|
+
"entitled": { "type": "boolean" },
|
|
61864
|
+
"org_mode": {
|
|
61865
|
+
"type": ["string", "null"],
|
|
61866
|
+
"enum": [
|
|
61867
|
+
"off",
|
|
61868
|
+
"optional_on",
|
|
61869
|
+
"optional_off",
|
|
61870
|
+
"required",
|
|
61871
|
+
null
|
|
61872
|
+
]
|
|
61873
|
+
},
|
|
61874
|
+
"org_mode_is_default": { "type": "boolean" },
|
|
61875
|
+
"org_modes": {
|
|
61876
|
+
"type": "array",
|
|
61877
|
+
"items": {
|
|
61878
|
+
"type": "string",
|
|
61879
|
+
"enum": [
|
|
61880
|
+
"off",
|
|
61881
|
+
"optional_on",
|
|
61882
|
+
"optional_off",
|
|
61883
|
+
"required"
|
|
61884
|
+
]
|
|
61885
|
+
}
|
|
61886
|
+
},
|
|
61887
|
+
"member_enabled": { "type": "boolean" },
|
|
61888
|
+
"member_can_change": { "type": "boolean" }
|
|
61889
|
+
},
|
|
61890
|
+
"required": [
|
|
61891
|
+
"kind",
|
|
61892
|
+
"label",
|
|
61893
|
+
"description",
|
|
61894
|
+
"class",
|
|
61895
|
+
"entitlement_required",
|
|
61896
|
+
"entitled",
|
|
61897
|
+
"org_mode",
|
|
61898
|
+
"org_mode_is_default",
|
|
61899
|
+
"org_modes",
|
|
61900
|
+
"member_enabled",
|
|
61901
|
+
"member_can_change"
|
|
61902
|
+
]
|
|
61903
|
+
}
|
|
61904
|
+
}
|
|
61905
|
+
},
|
|
61906
|
+
"required": ["entitled", "kinds"]
|
|
61907
|
+
},
|
|
61908
|
+
"EmailNotifications_updateEmailNotificationSettings_Input": {
|
|
61909
|
+
"type": "object",
|
|
61910
|
+
"properties": { "modes": {
|
|
61911
|
+
"type": "object",
|
|
61912
|
+
"propertyNames": {
|
|
61913
|
+
"type": "string",
|
|
61914
|
+
"pattern": "^[a-z][a-z0-9_]{0,63}$"
|
|
61915
|
+
},
|
|
61916
|
+
"additionalProperties": {
|
|
61917
|
+
"type": "string",
|
|
61918
|
+
"enum": [
|
|
61919
|
+
"off",
|
|
61920
|
+
"optional_on",
|
|
61921
|
+
"optional_off",
|
|
61922
|
+
"required"
|
|
61923
|
+
]
|
|
61924
|
+
}
|
|
61925
|
+
} },
|
|
61926
|
+
"required": ["modes"],
|
|
61927
|
+
"additionalProperties": false
|
|
61928
|
+
},
|
|
61929
|
+
"EmailNotifications_getEmailNotificationPreferences_Result": {
|
|
61930
|
+
"type": "object",
|
|
61931
|
+
"properties": {
|
|
61932
|
+
"entitled": { "type": "boolean" },
|
|
61933
|
+
"kinds": {
|
|
61934
|
+
"type": "array",
|
|
61935
|
+
"items": {
|
|
61936
|
+
"type": "object",
|
|
61937
|
+
"properties": {
|
|
61938
|
+
"kind": { "type": "string" },
|
|
61939
|
+
"label": { "type": "string" },
|
|
61940
|
+
"description": { "type": "string" },
|
|
61941
|
+
"class": {
|
|
61942
|
+
"type": "string",
|
|
61943
|
+
"enum": [
|
|
61944
|
+
"platform_required",
|
|
61945
|
+
"org_requirable",
|
|
61946
|
+
"optional"
|
|
61947
|
+
]
|
|
61948
|
+
},
|
|
61949
|
+
"entitlement_required": { "type": "boolean" },
|
|
61950
|
+
"entitled": { "type": "boolean" },
|
|
61951
|
+
"org_mode": {
|
|
61952
|
+
"type": ["string", "null"],
|
|
61953
|
+
"enum": [
|
|
61954
|
+
"off",
|
|
61955
|
+
"optional_on",
|
|
61956
|
+
"optional_off",
|
|
61957
|
+
"required",
|
|
61958
|
+
null
|
|
61959
|
+
]
|
|
61960
|
+
},
|
|
61961
|
+
"org_mode_is_default": { "type": "boolean" },
|
|
61962
|
+
"org_modes": {
|
|
61963
|
+
"type": "array",
|
|
61964
|
+
"items": {
|
|
61965
|
+
"type": "string",
|
|
61966
|
+
"enum": [
|
|
61967
|
+
"off",
|
|
61968
|
+
"optional_on",
|
|
61969
|
+
"optional_off",
|
|
61970
|
+
"required"
|
|
61971
|
+
]
|
|
61972
|
+
}
|
|
61973
|
+
},
|
|
61974
|
+
"member_enabled": { "type": "boolean" },
|
|
61975
|
+
"member_can_change": { "type": "boolean" }
|
|
61976
|
+
},
|
|
61977
|
+
"required": [
|
|
61978
|
+
"kind",
|
|
61979
|
+
"label",
|
|
61980
|
+
"description",
|
|
61981
|
+
"class",
|
|
61982
|
+
"entitlement_required",
|
|
61983
|
+
"entitled",
|
|
61984
|
+
"org_mode",
|
|
61985
|
+
"org_mode_is_default",
|
|
61986
|
+
"org_modes",
|
|
61987
|
+
"member_enabled",
|
|
61988
|
+
"member_can_change"
|
|
61989
|
+
]
|
|
61990
|
+
}
|
|
61991
|
+
}
|
|
61992
|
+
},
|
|
61993
|
+
"required": ["entitled", "kinds"]
|
|
61994
|
+
},
|
|
61995
|
+
"EmailNotifications_updateEmailNotificationPreferences_Result": {
|
|
61996
|
+
"type": "object",
|
|
61997
|
+
"properties": {
|
|
61998
|
+
"entitled": { "type": "boolean" },
|
|
61999
|
+
"kinds": {
|
|
62000
|
+
"type": "array",
|
|
62001
|
+
"items": {
|
|
62002
|
+
"type": "object",
|
|
62003
|
+
"properties": {
|
|
62004
|
+
"kind": { "type": "string" },
|
|
62005
|
+
"label": { "type": "string" },
|
|
62006
|
+
"description": { "type": "string" },
|
|
62007
|
+
"class": {
|
|
62008
|
+
"type": "string",
|
|
62009
|
+
"enum": [
|
|
62010
|
+
"platform_required",
|
|
62011
|
+
"org_requirable",
|
|
62012
|
+
"optional"
|
|
62013
|
+
]
|
|
62014
|
+
},
|
|
62015
|
+
"entitlement_required": { "type": "boolean" },
|
|
62016
|
+
"entitled": { "type": "boolean" },
|
|
62017
|
+
"org_mode": {
|
|
62018
|
+
"type": ["string", "null"],
|
|
62019
|
+
"enum": [
|
|
62020
|
+
"off",
|
|
62021
|
+
"optional_on",
|
|
62022
|
+
"optional_off",
|
|
62023
|
+
"required",
|
|
62024
|
+
null
|
|
62025
|
+
]
|
|
62026
|
+
},
|
|
62027
|
+
"org_mode_is_default": { "type": "boolean" },
|
|
62028
|
+
"org_modes": {
|
|
62029
|
+
"type": "array",
|
|
62030
|
+
"items": {
|
|
62031
|
+
"type": "string",
|
|
62032
|
+
"enum": [
|
|
62033
|
+
"off",
|
|
62034
|
+
"optional_on",
|
|
62035
|
+
"optional_off",
|
|
62036
|
+
"required"
|
|
62037
|
+
]
|
|
62038
|
+
}
|
|
62039
|
+
},
|
|
62040
|
+
"member_enabled": { "type": "boolean" },
|
|
62041
|
+
"member_can_change": { "type": "boolean" }
|
|
62042
|
+
},
|
|
62043
|
+
"required": [
|
|
62044
|
+
"kind",
|
|
62045
|
+
"label",
|
|
62046
|
+
"description",
|
|
62047
|
+
"class",
|
|
62048
|
+
"entitlement_required",
|
|
62049
|
+
"entitled",
|
|
62050
|
+
"org_mode",
|
|
62051
|
+
"org_mode_is_default",
|
|
62052
|
+
"org_modes",
|
|
62053
|
+
"member_enabled",
|
|
62054
|
+
"member_can_change"
|
|
62055
|
+
]
|
|
62056
|
+
}
|
|
62057
|
+
}
|
|
62058
|
+
},
|
|
62059
|
+
"required": ["entitled", "kinds"]
|
|
62060
|
+
},
|
|
62061
|
+
"EmailNotifications_updateEmailNotificationPreferences_Input": {
|
|
62062
|
+
"type": "object",
|
|
62063
|
+
"properties": { "preferences": {
|
|
62064
|
+
"type": "object",
|
|
62065
|
+
"propertyNames": {
|
|
62066
|
+
"type": "string",
|
|
62067
|
+
"pattern": "^[a-z][a-z0-9_]{0,63}$"
|
|
62068
|
+
},
|
|
62069
|
+
"additionalProperties": { "type": "boolean" }
|
|
62070
|
+
} },
|
|
62071
|
+
"required": ["preferences"],
|
|
62072
|
+
"additionalProperties": false
|
|
62073
|
+
},
|
|
60522
62074
|
"ListRolesResult": {
|
|
60523
62075
|
"type": "object",
|
|
60524
62076
|
"properties": {
|
|
@@ -63416,8 +64968,21 @@ const KEY_WRITE_OPERATION_IDS = /* @__PURE__ */ new Set([
|
|
|
63416
64968
|
"credentials:update-key",
|
|
63417
64969
|
"credentials:revoke-credential"
|
|
63418
64970
|
]);
|
|
63419
|
-
|
|
63420
|
-
const
|
|
64971
|
+
const CREDENTIAL_HEADER_OPERATION_IDS = /* @__PURE__ */ new Set(["emails:import-google-mail-message"]);
|
|
64972
|
+
const NOT_YET_OFFERED_OPERATION_IDS = /* @__PURE__ */ new Set([
|
|
64973
|
+
"email-notifications:get-email-notification-preferences",
|
|
64974
|
+
"email-notifications:update-email-notification-preferences",
|
|
64975
|
+
"email-notifications:get-email-notification-settings",
|
|
64976
|
+
"email-notifications:update-email-notification-settings"
|
|
64977
|
+
]);
|
|
64978
|
+
/** Every operation id the CLI generates no command for. */
|
|
64979
|
+
const CLI_OMITTED_OPERATION_IDS = /* @__PURE__ */ new Set([
|
|
64980
|
+
...KEY_WRITE_OPERATION_IDS,
|
|
64981
|
+
...CREDENTIAL_HEADER_OPERATION_IDS,
|
|
64982
|
+
...NOT_YET_OFFERED_OPERATION_IDS
|
|
64983
|
+
]);
|
|
64984
|
+
/** The operations the CLI generates commands for: the manifest without CLI_OMITTED_OPERATION_IDS. */
|
|
64985
|
+
const cliOperationManifest = operationManifest.filter((operation) => !CLI_OMITTED_OPERATION_IDS.has(`${operation.tagCommand}:${operation.command}`));
|
|
63421
64986
|
// src/oclif/commands/activity.ts
|
|
63422
64987
|
const API_KEY_FLAG$3 = Flags.string({
|
|
63423
64988
|
description: "Primitive API key override (defaults to PRIMITIVE_API_KEY or saved OAuth login credentials)",
|
|
@@ -63429,16 +64994,79 @@ const API_BASE_URL_FLAG$4 = Flags.string({
|
|
|
63429
64994
|
hidden: true
|
|
63430
64995
|
});
|
|
63431
64996
|
const DEFAULT_LIMIT$3 = 50;
|
|
63432
|
-
|
|
64997
|
+
/** How long a read of the feed keeps retrying a transient failure. */
|
|
64998
|
+
const ACTIVITY_RETRY_MS = 15e3;
|
|
64999
|
+
/** The message printed when Primitive could not be reached at all. */
|
|
65000
|
+
const NETWORK_ERROR_MESSAGE = "Could not reach Primitive (network error). Try again in a moment.";
|
|
65001
|
+
/**
|
|
65002
|
+
* The error for a read that got no answer at all: always a plain object, so
|
|
65003
|
+
* it prints the same with and without --json. The transport's own code (a
|
|
65004
|
+
* host that does not resolve, say) is kept as `cause`, as a word, with no
|
|
65005
|
+
* local detail.
|
|
65006
|
+
*/
|
|
65007
|
+
function networkErrorPayload(error) {
|
|
65008
|
+
const cause = error instanceof Error ? error.cause : void 0;
|
|
65009
|
+
const code = cause && typeof cause === "object" ? cause.code : void 0;
|
|
65010
|
+
return {
|
|
65011
|
+
code: "network_error",
|
|
65012
|
+
message: NETWORK_ERROR_MESSAGE,
|
|
65013
|
+
...typeof code === "string" && /^[A-Z0-9_]+$/.test(code) ? { cause: code } : {}
|
|
65014
|
+
};
|
|
65015
|
+
}
|
|
65016
|
+
/**
|
|
65017
|
+
* One page read, retried as the CLI's waits retry a read: a rate limit, a
|
|
65018
|
+
* 502, 503 or 504, or a network error is tried again with backoff, up to
|
|
65019
|
+
* ACTIVITY_ATTEMPTS times within ACTIVITY_RETRY_MS. A read that throws is
|
|
65020
|
+
* taken as the network error it is. A wait (a Retry-After, or the backoff)
|
|
65021
|
+
* longer than the time left is not slept on: the failure is returned at once.
|
|
65022
|
+
*/
|
|
65023
|
+
async function readWithRetry(read) {
|
|
65024
|
+
const deadline = Date.now() + ACTIVITY_RETRY_MS;
|
|
65025
|
+
for (let attempt = 0;; attempt += 1) {
|
|
65026
|
+
let result;
|
|
65027
|
+
try {
|
|
65028
|
+
result = await read();
|
|
65029
|
+
} catch (error) {
|
|
65030
|
+
result = { error };
|
|
65031
|
+
}
|
|
65032
|
+
const failure = transientFailure(result);
|
|
65033
|
+
if (!failure || attempt + 1 >= 4) return result;
|
|
65034
|
+
const wait = failure.retryAfterMs ?? backoffDelay(attempt);
|
|
65035
|
+
if (wait > deadline - Date.now()) return result;
|
|
65036
|
+
if (!await pauseForTransientFailure({
|
|
65037
|
+
...failure,
|
|
65038
|
+
retryAfterMs: wait
|
|
65039
|
+
}, attempt, { deadline })) return result;
|
|
65040
|
+
}
|
|
65041
|
+
}
|
|
65042
|
+
async function call$1(read) {
|
|
63433
65043
|
try {
|
|
63434
|
-
const result = await
|
|
63435
|
-
if (result
|
|
65044
|
+
const result = await readWithRetry(read);
|
|
65045
|
+
if (transientFailure(result)?.reason === "network") return {
|
|
63436
65046
|
ok: false,
|
|
63437
|
-
payload: result.error
|
|
63438
|
-
code: "http_error",
|
|
63439
|
-
message: typeof result.error === "string" && result.error.trim() || `HTTP ${result.response?.status ?? ""}`.trim()
|
|
63440
|
-
} : extractErrorPayload(result.error)
|
|
65047
|
+
payload: networkErrorPayload(result.error)
|
|
63441
65048
|
};
|
|
65049
|
+
if (result.error !== void 0 || !result.response?.ok) {
|
|
65050
|
+
const status = result.response?.status;
|
|
65051
|
+
if (result.error instanceof Error) return {
|
|
65052
|
+
ok: false,
|
|
65053
|
+
response: result.response,
|
|
65054
|
+
payload: {
|
|
65055
|
+
code: "response_error",
|
|
65056
|
+
message: `The answer could not be read${status ? ` (HTTP ${status})` : ""}. Try again in a moment.`
|
|
65057
|
+
}
|
|
65058
|
+
};
|
|
65059
|
+
const extracted = result.error === void 0 || result.error === null || typeof result.error === "string" ? void 0 : extractErrorPayload(result.error);
|
|
65060
|
+
const coded = extracted !== null && typeof extracted === "object" && typeof extracted.code === "string";
|
|
65061
|
+
return {
|
|
65062
|
+
ok: false,
|
|
65063
|
+
response: result.response,
|
|
65064
|
+
payload: coded ? extracted : {
|
|
65065
|
+
code: "http_error",
|
|
65066
|
+
message: typeof result.error === "string" && result.error.trim() || `HTTP ${status ?? ""}`.trim()
|
|
65067
|
+
}
|
|
65068
|
+
};
|
|
65069
|
+
}
|
|
63442
65070
|
const data = result.data;
|
|
63443
65071
|
return {
|
|
63444
65072
|
ok: true,
|
|
@@ -63447,7 +65075,7 @@ async function call$1(request) {
|
|
|
63447
65075
|
} catch (error) {
|
|
63448
65076
|
return {
|
|
63449
65077
|
ok: false,
|
|
63450
|
-
payload:
|
|
65078
|
+
payload: networkErrorPayload(error)
|
|
63451
65079
|
};
|
|
63452
65080
|
}
|
|
63453
65081
|
}
|
|
@@ -63513,40 +65141,92 @@ function formatAskedRows(items, complete = false) {
|
|
|
63513
65141
|
const line = (cells) => cells.map((cell, i) => i === cells.length - 1 ? cell : cell.padEnd(widths[i] ?? 0)).join(" ");
|
|
63514
65142
|
return [line(header), ...rows.map(line)].join("\n");
|
|
63515
65143
|
}
|
|
65144
|
+
const TOTAL_KEYS = [
|
|
65145
|
+
"keys",
|
|
65146
|
+
"reads_in_full",
|
|
65147
|
+
"webhooks",
|
|
65148
|
+
"shares"
|
|
65149
|
+
];
|
|
65150
|
+
/** `value` as receiver totals: an object whose four counts are all non-negative integers. */
|
|
65151
|
+
function receiverTotals(value) {
|
|
65152
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return null;
|
|
65153
|
+
const totals = value;
|
|
65154
|
+
return TOTAL_KEYS.every((key) => {
|
|
65155
|
+
const count = totals[key];
|
|
65156
|
+
return typeof count === "number" && Number.isInteger(count) && count >= 0;
|
|
65157
|
+
}) ? totals : null;
|
|
65158
|
+
}
|
|
65159
|
+
/**
|
|
65160
|
+
* What a receivers_changed notice says changed, from its totals before and
|
|
65161
|
+
* after: "keys 3 to 2 (1 to 0 also reading your mail beyond the rule),
|
|
65162
|
+
* webhooks 2 to 1",
|
|
65163
|
+
* or, when the totals are equal (an owner or a webhook site changed), that
|
|
65164
|
+
* they are. Null for any other notice, and for one whose totals are missing
|
|
65165
|
+
* or not counts (an older server).
|
|
65166
|
+
*/
|
|
65167
|
+
function receiversChangeText(item) {
|
|
65168
|
+
if (item.state !== "receivers_changed") return null;
|
|
65169
|
+
const change = item.receivers_change;
|
|
65170
|
+
const before = receiverTotals(change?.before);
|
|
65171
|
+
const after = receiverTotals(change?.after);
|
|
65172
|
+
if (!before || !after) return null;
|
|
65173
|
+
const moved = (key) => before[key] !== after[key];
|
|
65174
|
+
const span = (key) => `${before[key]} to ${after[key]}`;
|
|
65175
|
+
const full = moved("reads_in_full") ? ` (${span("reads_in_full")} also reading your mail beyond the rule)` : "";
|
|
65176
|
+
const changed = [
|
|
65177
|
+
...moved("keys") ? [`keys ${span("keys")}${full}`] : full ? [`keys ${after.keys}${full}`] : [],
|
|
65178
|
+
...moved("webhooks") ? [`webhooks ${span("webhooks")}`] : [],
|
|
65179
|
+
...moved("shares") ? [`shares ${span("shares")}`] : []
|
|
65180
|
+
];
|
|
65181
|
+
return changed.length > 0 ? changed.join(", ") : "same totals, an owner or webhook site changed";
|
|
65182
|
+
}
|
|
63516
65183
|
/** Where to see what covers you now, whatever the notices have caught up with. */
|
|
63517
65184
|
const COVERING_NOW = `\`primitive email-rule-oversight list-email-rules-covering-me\` shows what covers you now.`;
|
|
63518
65185
|
/**
|
|
63519
|
-
* One line per coverage notice: what it told (started, new_version,
|
|
63520
|
-
* baseline for a rule that already covered you when
|
|
63521
|
-
* rule's name then, and its author; and, first,
|
|
63522
|
-
* whose notice is not written yet (pending,
|
|
63523
|
-
*
|
|
65186
|
+
* One line per coverage notice: what it told (started, new_version,
|
|
65187
|
+
* receivers_changed, or baseline for a rule that already covered you when
|
|
65188
|
+
* notices began), when, the rule's name then, and its author; and, first,
|
|
65189
|
+
* each rule that covers you now whose notice is not written yet (pending,
|
|
65190
|
+
* dated when the rule last changed), then the rule's id (rule_id; a pending
|
|
65191
|
+
* row's own id is the rule's), never the notice's own id, which no command
|
|
65192
|
+
* takes; and, when any row is a receivers_changed notice with its totals,
|
|
65193
|
+
* what changed in them (CHANGE).
|
|
63524
65194
|
* `complete` says the listing is the whole of it (from the first page to the
|
|
63525
65195
|
* last, nothing withheld). Even then an empty one is no promise that nothing
|
|
63526
|
-
* covers you: notices
|
|
65196
|
+
* covers you: notices can lag, so it
|
|
63527
65197
|
* says how far they go (`asOf`, when the server says) and where to look now.
|
|
63528
65198
|
*/
|
|
63529
|
-
function formatConcerningRows(items, complete = false, asOf) {
|
|
63530
|
-
|
|
65199
|
+
function formatConcerningRows(items, complete = false, asOf, pendingComplete) {
|
|
65200
|
+
const unfinished = pendingComplete === false ? "The check for rules that cover you with no notice written yet did not finish, so some may be missing: run this again in a moment." : null;
|
|
65201
|
+
if (items.length === 0) return complete ? `${asOf ? `Nothing recorded for you as of ${minute(asOf)} UTC.` : "Nothing recorded for you yet."} Notices can lag, so a rule that starts covering your mail may not appear here yet. ${COVERING_NOW}` : unfinished ? `${unfinished} ${COVERING_NOW}` : "Nothing to show.";
|
|
63531
65202
|
const rows = items.map((item) => [
|
|
63532
65203
|
item.state,
|
|
63533
65204
|
minute(item.created_at),
|
|
63534
65205
|
item.name ?? "(unnamed)",
|
|
63535
|
-
item.created_through.name ?? item.created_through.id ?? ""
|
|
65206
|
+
item.created_through.name ?? item.created_through.id ?? "",
|
|
65207
|
+
item.rule_id ?? (item.state === "pending" ? item.id : "")
|
|
63536
65208
|
]);
|
|
65209
|
+
const changes = items.map((item) => receiversChangeText(item));
|
|
65210
|
+
const withChange = changes.some((change) => change !== null);
|
|
65211
|
+
if (withChange) for (const [i, row] of rows.entries()) row.push(changes[i] ?? "");
|
|
63537
65212
|
const header = [
|
|
63538
65213
|
"NOTICE",
|
|
63539
65214
|
"WHEN (UTC)",
|
|
63540
65215
|
"RULE",
|
|
63541
|
-
"AUTHOR"
|
|
65216
|
+
"AUTHOR",
|
|
65217
|
+
"RULE ID",
|
|
65218
|
+
...withChange ? ["CHANGE"] : []
|
|
63542
65219
|
];
|
|
63543
65220
|
const widths = header.map((h, i) => Math.max(h.length, ...rows.map((row) => (row[i] ?? "").length)));
|
|
63544
|
-
const line = (cells) => cells.map((cell, i) => i === cells.length - 1 ? cell : cell.padEnd(widths[i] ?? 0)).join(" ");
|
|
65221
|
+
const line = (cells) => cells.map((cell, i) => i === cells.length - 1 ? cell : cell.padEnd(widths[i] ?? 0)).join(" ").trimEnd();
|
|
63545
65222
|
const pending = items.some((item) => item.state === "pending") ? ["", `pending: the rule covers you now and its notice is not written yet. ${COVERING_NOW}`] : [];
|
|
65223
|
+
const receivers = items.some((item) => item.state === "receivers_changed") ? ["", "receivers_changed: who receives the rule's matches changed, with the totals before and after where the server sends them. `primitive email-rule-oversight get-email-rule-covering-me <rule-id>` shows who receives them now, by owner, while the rule covers you."] : [];
|
|
63546
65224
|
return [
|
|
63547
65225
|
line(header),
|
|
63548
65226
|
...rows.map(line),
|
|
63549
|
-
...pending
|
|
65227
|
+
...pending,
|
|
65228
|
+
...receivers,
|
|
65229
|
+
...unfinished ? ["", pending.length > 0 ? unfinished : `${unfinished} ${COVERING_NOW}`] : []
|
|
63550
65230
|
].join("\n");
|
|
63551
65231
|
}
|
|
63552
65232
|
/** What was left out, and why: no counts, only the kinds and what would show them. */
|
|
@@ -63580,9 +65260,10 @@ async function runActivityList(command, flags, relation) {
|
|
|
63580
65260
|
let covers = "";
|
|
63581
65261
|
let withheld = [];
|
|
63582
65262
|
let asOf;
|
|
65263
|
+
let pendingComplete;
|
|
63583
65264
|
let cursor = flags.cursor;
|
|
63584
65265
|
for (let page = 0; page < (flags.all ? 50 : 1); page += 1) {
|
|
63585
|
-
const result = await call$1(listActivityObjects({
|
|
65266
|
+
const result = await call$1(() => listActivityObjects({
|
|
63586
65267
|
client: auth.apiClient.client,
|
|
63587
65268
|
query: {
|
|
63588
65269
|
...relation !== "created" ? { relation } : {},
|
|
@@ -63594,7 +65275,12 @@ async function runActivityList(command, flags, relation) {
|
|
|
63594
65275
|
}));
|
|
63595
65276
|
if (!result.ok) {
|
|
63596
65277
|
if (flags.json) process.stderr.write(`${JSON.stringify(result.payload)}\n`);
|
|
63597
|
-
else
|
|
65278
|
+
else {
|
|
65279
|
+
writeErrorWithHints(result.payload, result.response);
|
|
65280
|
+
const cause = result.payload?.cause;
|
|
65281
|
+
const hint = typeof cause === "string" ? networkErrorHint(cause) : void 0;
|
|
65282
|
+
if (hint) process.stderr.write(`${hint}\n`);
|
|
65283
|
+
}
|
|
63598
65284
|
surfaceUnauthorizedHint({
|
|
63599
65285
|
auth: auth.auth,
|
|
63600
65286
|
baseUrlOverridden: auth.baseUrlOverridden,
|
|
@@ -63606,7 +65292,11 @@ async function runActivityList(command, flags, relation) {
|
|
|
63606
65292
|
}
|
|
63607
65293
|
covers = result.data.covers;
|
|
63608
65294
|
withheld = result.data.withheld;
|
|
63609
|
-
if (page === 0)
|
|
65295
|
+
if (page === 0) {
|
|
65296
|
+
const first = result.data;
|
|
65297
|
+
asOf = first.as_of;
|
|
65298
|
+
pendingComplete = first.pending_complete;
|
|
65299
|
+
}
|
|
63610
65300
|
items.push(...result.data.items);
|
|
63611
65301
|
cursor = result.data.next_cursor ?? void 0;
|
|
63612
65302
|
if (!cursor) break;
|
|
@@ -63617,12 +65307,13 @@ async function runActivityList(command, flags, relation) {
|
|
|
63617
65307
|
items,
|
|
63618
65308
|
withheld,
|
|
63619
65309
|
next_cursor: cursor ?? null,
|
|
63620
|
-
...asOf !== void 0 ? { as_of: asOf } : {}
|
|
65310
|
+
...asOf !== void 0 ? { as_of: asOf } : {},
|
|
65311
|
+
...pendingComplete !== void 0 ? { pending_complete: pendingComplete } : {}
|
|
63621
65312
|
}, null, 2));
|
|
63622
65313
|
return;
|
|
63623
65314
|
}
|
|
63624
|
-
const complete = flags.state === "all" && !flags.cursor && !cursor && withheld.length === 0;
|
|
63625
|
-
command.log(relation === "asked" ? formatAskedRows(items, complete) : relation === "concerning" ? formatConcerningRows(items, complete, asOf) : formatActivityRows(covers, items));
|
|
65315
|
+
const complete = flags.state === "all" && !flags.cursor && !cursor && withheld.length === 0 && pendingComplete !== false;
|
|
65316
|
+
command.log(relation === "asked" ? formatAskedRows(items, complete) : relation === "concerning" ? formatConcerningRows(items, complete, asOf, pendingComplete) : formatActivityRows(covers, items));
|
|
63626
65317
|
const notShown = formatWithheld(withheld);
|
|
63627
65318
|
if (notShown) command.log(`\n${notShown}`);
|
|
63628
65319
|
if (cursor) command.log(`\nMore: ${resumeCommand(command.config.bin, flags, cursor, relation)}`);
|
|
@@ -63705,11 +65396,11 @@ var ActivityAskedCommand = class ActivityAskedCommand extends Command {
|
|
|
63705
65396
|
}
|
|
63706
65397
|
};
|
|
63707
65398
|
var ActivityConcerningCommand = class ActivityConcerningCommand extends Command {
|
|
63708
|
-
static description = `List what other members did that concerns your own mail, newest first: each time another member's rule started covering
|
|
65399
|
+
static description = `List what other members did that concerns your own mail, newest first: each time another member's rule started covering your mailboxes, that is, it was made or turned active over them or an active rule came to reach you, once per rule, not per mailbox (started), saved a new version while covering it (new_version), or changed who receives its matches: its keys, webhooks or share (receivers_changed, with the totals before and after in the table, and under receivers_change with --json, at most once every 15 minutes for a rule), named as the rule was then, with its author. A rule that already covered you when these notices began is listed once as baseline. A rule in draft or observe grants and blocks nothing and gets no notice while it stays there, though \`primitive email-rule-oversight list-email-rules-covering-me\` lists it; turning it active is what is listed, and a rule that covers you again within an hour of its last started or baseline notice gets no new one, though a version it saved meanwhile is listed as new_version, and a change in who receives its matches as receivers_changed. A rule that stops covering you and a rename are not listed.
|
|
63709
65400
|
|
|
63710
|
-
Notices
|
|
65401
|
+
Notices can lag. Until a start is written, a rule that now covers you is listed first as pending, dated when the rule last changed, and with --json its id is the rule's. When the check for such rules did not finish, the listing says so and --json carries pending_complete: false; run it again in a moment.
|
|
63711
65402
|
|
|
63712
|
-
Signed in with \`primitive login\`, you see the notices about your own mail under your current membership. A key, an app or an agent has none. The rules that cover you now, with their ids, are listed by \`primitive email-rule-oversight list-email-rules-covering-me\`; ask a rule's author to turn it off with \`primitive email-rule-oversight create-email-rule-objection --rule-id <rule-id>\`.
|
|
65403
|
+
Signed in with \`primitive login\`, you see the notices about your own mail under your current membership. A key, an app or an agent has none. The rules that cover you now, with their ids, are listed by \`primitive email-rule-oversight list-email-rules-covering-me\`; ask a rule's author to turn it off with \`primitive email-rule-oversight create-email-rule-objection --rule-id <rule-id>\`. The table's RULE ID, and rule_id with --json, is the rule's id, which \`primitive email-rule-oversight get-email-rule-covering-me <rule-id>\` and create-email-rule-objection --rule-id take; with --json each notice's own id is the notice's, not the rule's.`;
|
|
63713
65404
|
static summary = "List rules that started covering your mail or changed";
|
|
63714
65405
|
static examples = ["<%= config.bin %> activity concerning", "<%= config.bin %> activity concerning --all --json"];
|
|
63715
65406
|
static flags = {
|
|
@@ -68088,11 +69779,12 @@ async function sendFromAnswer(apiClient, authFailureContext, purpose) {
|
|
|
68088
69779
|
const missing = await missingSendPermission(apiClient);
|
|
68089
69780
|
if (missing) throw new MissingSendPermissionError(missing);
|
|
68090
69781
|
}
|
|
68091
|
-
const
|
|
69782
|
+
const nothingListed = addresses.length === 0 && domains.length === 0;
|
|
69783
|
+
const parts = [typeof data.reason === "string" && data.reason.trim() ? data.reason.trim().replace(/\.?$/, ".") : nothingListed ? "This credential lists no address or domain it can send from, so it cannot send." : "This credential has no single address to send from, so --from has no default."];
|
|
68092
69784
|
if (addresses.length > 0) parts.push(`Addresses you may send from: ${addresses.join(", ")}.`);
|
|
68093
69785
|
if (domains.length > 0) parts.push(`${addresses.length > 0 ? "Also any" : "Any"} address at: ${domains.join(", ")}.`);
|
|
68094
69786
|
parts.push(await claimHint(apiClient));
|
|
68095
|
-
parts.push("Pass the address to use with --from <address>.");
|
|
69787
|
+
if (!nothingListed) parts.push("Pass the address to use with --from <address>.");
|
|
68096
69788
|
throw new Errors.CLIError(parts.filter((part) => part).join(" "), { exit: 1 });
|
|
68097
69789
|
}
|
|
68098
69790
|
/** The address of resolveDefaultFrom. */
|
|
@@ -70879,7 +72571,7 @@ async function checkAskedOfYou(client, now = Date.now()) {
|
|
|
70879
72571
|
const waiting = asked.more ? [`at least ${requests + objections} rule requests and objections`] : [...requests > 0 ? [plural$1(requests, "rule request")] : [], ...objections > 0 ? [plural$1(objections, "objection")] : []];
|
|
70880
72572
|
const items = concerning?.items ?? [];
|
|
70881
72573
|
const pending = items.filter((item) => item.state === "pending").length;
|
|
70882
|
-
const notices = items.filter((item) => (item.state === "started" || item.state === "new_version") && Date.parse(item.created_at) >= since).length;
|
|
72574
|
+
const notices = items.filter((item) => (item.state === "started" || item.state === "new_version" || item.state === "receivers_changed") && Date.parse(item.created_at) >= since).length;
|
|
70883
72575
|
const parts = [
|
|
70884
72576
|
...waiting.length > 0 ? [`${waiting.join(" and ")} waiting for you`] : [],
|
|
70885
72577
|
...ASKED_KINDS.filter((kind) => hidden.has(kind)).map((kind) => `${kind === "email_rule_request" ? "rule requests" : "objections"} not shown to this credential`),
|
|
@@ -70891,7 +72583,7 @@ async function checkAskedOfYou(client, now = Date.now()) {
|
|
|
70891
72583
|
const asOf = concerning?.first.as_of;
|
|
70892
72584
|
return {
|
|
70893
72585
|
status: "ok",
|
|
70894
|
-
message: `nothing waiting for you; ${asOf ? `coverage notices as of ${minute(asOf)} UTC` : "coverage notices can
|
|
72586
|
+
message: `nothing waiting for you; ${asOf ? `coverage notices as of ${minute(asOf)} UTC` : "coverage notices can lag"}`
|
|
70895
72587
|
};
|
|
70896
72588
|
}
|
|
70897
72589
|
const commands = [...waiting.length > 0 ? ["`primitive activity asked --state live`"] : [], ...pending > 0 || notices > 0 || !concerning || coverageUnfinished ? ["`primitive activity concerning`"] : []];
|
|
@@ -70929,7 +72621,7 @@ function checkReceiverStart(configDir, profileName) {
|
|
|
70929
72621
|
}
|
|
70930
72622
|
}
|
|
70931
72623
|
var DoctorCommand = class DoctorCommand extends Command {
|
|
70932
|
-
static description = `Run a one-shot environment health check: Node version, proxy env, CLI auth resolution, /account reachability, verified-domain status, and, signed in as yourself, the rule requests and objections waiting for you and the rules that recently started covering
|
|
72624
|
+
static description = `Run a one-shot environment health check: Node version, proxy env, CLI auth resolution, /account reachability, verified-domain status, and, signed in as yourself, the rule requests and objections waiting for you and the rules over your mail that recently started covering it or changed. With PRIMITIVE_AGENT_PROFILE selected, reports saved identity offline and points to whoami and exact listener status instead of account-only checks. Prints actionable hints for each warning or failure.`;
|
|
70933
72625
|
static summary = "Check the local environment and live API for common problems";
|
|
70934
72626
|
static examples = ["<%= config.bin %> doctor", "<%= config.bin %> doctor --api-key prim_..."];
|
|
70935
72627
|
static flags = {
|
|
@@ -71022,9 +72714,10 @@ var DoctorCommand = class DoctorCommand extends Command {
|
|
|
71022
72714
|
}
|
|
71023
72715
|
};
|
|
71024
72716
|
// src/oclif/commands/domains-zone-file.ts
|
|
71025
|
-
function zoneFileUrl(baseUrl, domainId, outboundOnly) {
|
|
72717
|
+
function zoneFileUrl(baseUrl, domainId, outboundOnly, inboundOnly = false) {
|
|
71026
72718
|
const url = new URL(`${baseUrl.replace(/\/$/, "")}/domains/${encodeURIComponent(domainId)}/zone-file`);
|
|
71027
72719
|
if (outboundOnly) url.searchParams.set("outbound_only", "true");
|
|
72720
|
+
if (inboundOnly) url.searchParams.set("inbound_only", "true");
|
|
71028
72721
|
return url.toString();
|
|
71029
72722
|
}
|
|
71030
72723
|
function contentDispositionFilename(value) {
|
|
@@ -71064,7 +72757,8 @@ var DomainsZoneFileCommand = class DomainsZoneFileCommand extends Command {
|
|
|
71064
72757
|
"<%= config.bin %> domains zone-file --id <domain-id>",
|
|
71065
72758
|
"<%= config.bin %> domains zone-file --id <domain-id> --output example.com.zone",
|
|
71066
72759
|
"<%= config.bin %> domains zone-file --domain example.com --output example.com.zone",
|
|
71067
|
-
"<%= config.bin %> domains zone-file --id <domain-id> --outbound-only"
|
|
72760
|
+
"<%= config.bin %> domains zone-file --id <domain-id> --outbound-only",
|
|
72761
|
+
"<%= config.bin %> domains zone-file --id <domain-id> --inbound-only"
|
|
71068
72762
|
];
|
|
71069
72763
|
static flags = {
|
|
71070
72764
|
"api-key": Flags.string({
|
|
@@ -71082,7 +72776,14 @@ var DomainsZoneFileCommand = class DomainsZoneFileCommand extends Command {
|
|
|
71082
72776
|
char: "o",
|
|
71083
72777
|
description: "Write the zone file to this path instead of stdout. Defaults to stdout."
|
|
71084
72778
|
}),
|
|
71085
|
-
"outbound-only": Flags.boolean({
|
|
72779
|
+
"outbound-only": Flags.boolean({
|
|
72780
|
+
description: "Include only the outbound DNS records. Without it or --inbound-only, the file has every record. Not with --inbound-only.",
|
|
72781
|
+
exclusive: ["inbound-only"]
|
|
72782
|
+
}),
|
|
72783
|
+
"inbound-only": Flags.boolean({
|
|
72784
|
+
description: "Include only the inbound records: MX and the ownership TXT record. Not with --outbound-only.",
|
|
72785
|
+
exclusive: ["outbound-only"]
|
|
72786
|
+
}),
|
|
71086
72787
|
time: Flags.boolean({ description: TIME_FLAG_DESCRIPTION })
|
|
71087
72788
|
};
|
|
71088
72789
|
async run() {
|
|
@@ -71119,7 +72820,7 @@ var DomainsZoneFileCommand = class DomainsZoneFileCommand extends Command {
|
|
|
71119
72820
|
if (!domainId) throw new Errors.CLIError("Could not resolve a domain id.", { exit: 1 });
|
|
71120
72821
|
let response;
|
|
71121
72822
|
try {
|
|
71122
|
-
response = await fetch(zoneFileUrl(requestConfig.resolvedApiBaseUrl, domainId, flags["outbound-only"]), { headers: {
|
|
72823
|
+
response = await fetch(zoneFileUrl(requestConfig.resolvedApiBaseUrl, domainId, flags["outbound-only"], flags["inbound-only"]), { headers: {
|
|
71123
72824
|
...requestConfig.headers ?? {},
|
|
71124
72825
|
...auth.apiKey ? { authorization: `Bearer ${auth.apiKey}` } : {}
|
|
71125
72826
|
} });
|
|
@@ -74020,8 +75721,8 @@ const PRIMITIVE_TEAM_AUTHOR = {
|
|
|
74020
75721
|
name: "Primitive Team",
|
|
74021
75722
|
url: "https://primitive.dev"
|
|
74022
75723
|
};
|
|
74023
|
-
const SDK_VERSION_RANGE = "^3.0.0-preview.
|
|
74024
|
-
const CLI_VERSION_RANGE = "^3.0.0-preview.
|
|
75724
|
+
const SDK_VERSION_RANGE = "^3.0.0-preview.14";
|
|
75725
|
+
const CLI_VERSION_RANGE = "^3.0.0-preview.14";
|
|
74025
75726
|
const ESBUILD_VERSION_RANGE = "^0.27.0";
|
|
74026
75727
|
function renderHandler() {
|
|
74027
75728
|
return `// env.PRIMITIVE_API_KEY, env.PRIMITIVE_WEBHOOK_SECRET, and
|
|
@@ -77409,6 +79110,39 @@ function keyNotes(key) {
|
|
|
77409
79110
|
return typeof description === "string" ? [description] : [];
|
|
77410
79111
|
});
|
|
77411
79112
|
}
|
|
79113
|
+
/** Whether a note says the key reads a whole mailbox beside its email rules. */
|
|
79114
|
+
function isWholeMailboxNote(note) {
|
|
79115
|
+
return note !== null && typeof note === "object" && note.reason === "whole_mailbox";
|
|
79116
|
+
}
|
|
79117
|
+
/**
|
|
79118
|
+
* The addresses and domains a key reads in full beside its email rules: the
|
|
79119
|
+
* whole_mailbox notes of its address_warnings, read loosely.
|
|
79120
|
+
*/
|
|
79121
|
+
function wholeMailboxAddresses(notes) {
|
|
79122
|
+
if (!Array.isArray(notes)) return [];
|
|
79123
|
+
return notes.flatMap((note) => {
|
|
79124
|
+
if (!isWholeMailboxNote(note)) return [];
|
|
79125
|
+
const address = note.address;
|
|
79126
|
+
return typeof address === "string" && address ? [address] : [];
|
|
79127
|
+
});
|
|
79128
|
+
}
|
|
79129
|
+
/**
|
|
79130
|
+
* What a key create or update says when the key reads whole mailboxes beside
|
|
79131
|
+
* its email rules: on stderr as a Warning line, or under --json in the
|
|
79132
|
+
* document's whole_mailbox_warning (the JSON guard keeps stderr empty, and the
|
|
79133
|
+
* answer's warnings are the server's objects). Null when it does not. The
|
|
79134
|
+
* exit code is not changed: the key is made or changed as asked.
|
|
79135
|
+
*/
|
|
79136
|
+
function wholeMailboxWarning(keyId, notes) {
|
|
79137
|
+
if (!Array.isArray(notes) || !notes.some(isWholeMailboxNote)) return null;
|
|
79138
|
+
const addresses = wholeMailboxAddresses(notes);
|
|
79139
|
+
return `This key reads ${addresses.length > 0 ? `every email at ${addresses.join(", ")}` : "every email at an address or domain it lists"}, not only the emails its rules grant. An address or domain listed beside a rule is read in full. To limit a key to the rules' emails, create one with the same permissions and only --rule for its mail (no --address or --domain), then revoke this one with \`primitive keys revoke ${keyId}\`.`;
|
|
79140
|
+
}
|
|
79141
|
+
/** The notes other than whole_mailbox, which wholeMailboxWarning says instead. */
|
|
79142
|
+
function otherNotes(notes) {
|
|
79143
|
+
if (!Array.isArray(notes)) return [];
|
|
79144
|
+
return notes.filter((note) => note !== null && typeof note === "object" && !isWholeMailboxNote(note));
|
|
79145
|
+
}
|
|
77412
79146
|
function formatKeyRows(rows, options = {}) {
|
|
77413
79147
|
if (rows.length === 0) return options.includeRevoked ? "No API keys, revoked or not." : "No API keys. Revoked keys are left out; pass --include-revoked to list them too.";
|
|
77414
79148
|
return rows.map((row) => {
|
|
@@ -77416,9 +79150,16 @@ function formatKeyRows(rows, options = {}) {
|
|
|
77416
79150
|
const revoked = row.status === "revoked" ? ` [revoked${row.revoked_at ? ` ${row.revoked_at}` : ""}]` : "";
|
|
77417
79151
|
const count = keyNotes(row).length;
|
|
77418
79152
|
const notes = count > 0 ? ` [${count} ${count === 1 ? "note" : "notes"}: primitive keys get ${row.id}]` : "";
|
|
77419
|
-
|
|
79153
|
+
const inFull = wholeMailboxAddresses(row.address_warnings);
|
|
79154
|
+
const whole = inFull.length > 0 ? ` [reads all mail at ${inFull.join(", ")}, not only its rules' emails]` : "";
|
|
79155
|
+
return `${row.id} ${row.key_prefix} ${row.name ?? "(unnamed)"} ${describePermissions(row.grant)} mail: ${describeMailScope(row.grant)} expires ${row.expires_at ?? "never"} last used ${row.last_used_at ?? "never"}${suspended}${revoked}${whole}${notes}`;
|
|
77420
79156
|
}).join("\n");
|
|
77421
79157
|
}
|
|
79158
|
+
/** The detail line for the mailboxes a key reads in full beside its rules. */
|
|
79159
|
+
function wholeMailboxLines(key) {
|
|
79160
|
+
const inFull = wholeMailboxAddresses(key.address_warnings);
|
|
79161
|
+
return inFull.length > 0 ? [`In full: ${inFull.join(", ")} (listed beside its rules, so every email there is read, not only what the rules grant)`] : [];
|
|
79162
|
+
}
|
|
77422
79163
|
function formatKeyDetail(key) {
|
|
77423
79164
|
const lines = [
|
|
77424
79165
|
`Name: ${key.name ?? "(unnamed)"}`,
|
|
@@ -77426,6 +79167,7 @@ function formatKeyDetail(key) {
|
|
|
77426
79167
|
`Prefix: ${key.key_prefix}`,
|
|
77427
79168
|
`Kind: ${key.kind}`,
|
|
77428
79169
|
`Mail: ${describeMailScope(key.grant)}`,
|
|
79170
|
+
...wholeMailboxLines(key),
|
|
77429
79171
|
`Created: ${key.created_at ?? "unknown"}`,
|
|
77430
79172
|
`Expires: ${key.expires_at ?? "never"}`,
|
|
77431
79173
|
`Last used: ${key.last_used_at ?? "never"}`,
|
|
@@ -77525,13 +79267,16 @@ function buildCreateGrant(choice) {
|
|
|
77525
79267
|
* The mail scope fields a change can restate. `ended_rules` (rules the key
|
|
77526
79268
|
* lists that read nothing any more: a deleted rule, or a share that ended) is
|
|
77527
79269
|
* read only: a change never sends it back, so the key is saved without them.
|
|
79270
|
+
* `rule_versions` (each listed rule's current version, its state and when
|
|
79271
|
+
* that version was saved) is read only too, and is never sent back.
|
|
77528
79272
|
*/
|
|
77529
79273
|
const RESTATABLE_SCOPE_FIELDS = /* @__PURE__ */ new Set([
|
|
77530
79274
|
"addresses",
|
|
77531
79275
|
"domains",
|
|
77532
79276
|
"rules",
|
|
77533
79277
|
"depth",
|
|
77534
|
-
"ended_rules"
|
|
79278
|
+
"ended_rules",
|
|
79279
|
+
"rule_versions"
|
|
77535
79280
|
]);
|
|
77536
79281
|
/** Whether a change touches the key's email rules at all. */
|
|
77537
79282
|
function changesRules(change) {
|
|
@@ -77628,7 +79373,7 @@ function buildUpdatedGrant(current, change) {
|
|
|
77628
79373
|
}
|
|
77629
79374
|
if (!scope || typeof scope !== "object" || Array.isArray(scope)) return {
|
|
77630
79375
|
ok: false,
|
|
77631
|
-
message: "This key's mail scope could not be read. Change
|
|
79376
|
+
message: "This key's mail scope could not be read. Change the key in the dashboard, or create a key with what you need and revoke this one."
|
|
77632
79377
|
};
|
|
77633
79378
|
const record = scope;
|
|
77634
79379
|
const addresses = stringList(record.addresses ?? []);
|
|
@@ -77641,9 +79386,21 @@ function buildUpdatedGrant(current, change) {
|
|
|
77641
79386
|
message: `${readdedEnded.id} reads nothing any more (${readdedEnded.reason}), so it cannot be added back to the key.`
|
|
77642
79387
|
};
|
|
77643
79388
|
const rules = heldRules ? nextRules(heldRules, change) : null;
|
|
77644
|
-
|
|
79389
|
+
const unknownFields = Object.keys(record).filter((field) => !RESTATABLE_SCOPE_FIELDS.has(field));
|
|
79390
|
+
if (unknownFields.length > 0) {
|
|
79391
|
+
const one = unknownFields.length === 1;
|
|
79392
|
+
return {
|
|
79393
|
+
ok: false,
|
|
79394
|
+
message: `This key's mail scope has ${one ? "a setting" : "settings"} the CLI cannot restate (${unknownFields.join(", ")}), and a change here could drop ${one ? "it" : "them"}. Change the key in the dashboard, or create a key with what you need and revoke this one.`
|
|
79395
|
+
};
|
|
79396
|
+
}
|
|
79397
|
+
if (!addresses || !domains || !rules) return {
|
|
77645
79398
|
ok: false,
|
|
77646
|
-
message:
|
|
79399
|
+
message: `This key's mail scope has ${[
|
|
79400
|
+
...addresses ? [] : ["addresses"],
|
|
79401
|
+
...domains ? [] : ["domains"],
|
|
79402
|
+
...rules ? [] : ["rules"]
|
|
79403
|
+
].join(" and ")} the CLI could not read. Change the key in the dashboard, or create a key with what you need and revoke this one.`
|
|
77647
79404
|
};
|
|
77648
79405
|
if (addresses.length === 0 && domains.length === 0 && rules.length === 0) {
|
|
77649
79406
|
if ((heldRules?.length ?? 0) === 0 && ended.length > 0) return {
|
|
@@ -77786,7 +79543,7 @@ var KeysCreateCommand = class KeysCreateCommand extends Command {
|
|
|
77786
79543
|
|
|
77787
79544
|
The secret is printed once, on its own line on stdout (in the JSON with --json). Store it right away: it is not shown again and the CLI does not save it.
|
|
77788
79545
|
|
|
77789
|
-
A key limited to email rules (--rule, repeatable) reaches only the mail those rules match. Give it mail:read with --permission, or use --preset own_addresses_read_only.
|
|
79546
|
+
A key limited to email rules (--rule, repeatable) reaches only the mail those rules match. Give it mail:read with --permission, or use --preset own_addresses_read_only. An --address or --domain given beside --rule is read in full, every email there and not only what the rules match: the key is still made, and the command warns on stderr, or in whole_mailbox_warning with --json.
|
|
77790
79547
|
|
|
77791
79548
|
${keyPermissionsHelp()}`;
|
|
77792
79549
|
static summary = "Create an API key";
|
|
@@ -77866,9 +79623,13 @@ var KeysCreateCommand = class KeysCreateCommand extends Command {
|
|
|
77866
79623
|
const key = result.data;
|
|
77867
79624
|
if (!flags.json) {
|
|
77868
79625
|
writeWarnings(key.warnings);
|
|
77869
|
-
writeWarnings(key.address_warnings);
|
|
79626
|
+
writeWarnings(otherNotes(key.address_warnings));
|
|
77870
79627
|
}
|
|
77871
|
-
|
|
79628
|
+
const whole = wholeMailboxWarning(key.id, key.address_warnings);
|
|
79629
|
+
printSecret(this, Boolean(flags.json), whole ? {
|
|
79630
|
+
...key,
|
|
79631
|
+
whole_mailbox_warning: whole
|
|
79632
|
+
} : key, key.key, [
|
|
77872
79633
|
`Created API key ${key.name ?? "(unnamed)"} (${key.id}), prefix ${key.key_prefix}.`,
|
|
77873
79634
|
`Permissions: ${describePermissions({
|
|
77874
79635
|
template: key.grant.template,
|
|
@@ -77882,6 +79643,7 @@ var KeysCreateCommand = class KeysCreateCommand extends Command {
|
|
|
77882
79643
|
})}`,
|
|
77883
79644
|
`Expires: ${key.expires_at ?? "never"}`
|
|
77884
79645
|
].join("\n"));
|
|
79646
|
+
if (whole && !flags.json) process.stderr.write(`Warning: ${whole}\n`);
|
|
77885
79647
|
}
|
|
77886
79648
|
};
|
|
77887
79649
|
var KeysRotateCommand = class KeysRotateCommand extends Command {
|
|
@@ -77944,11 +79706,11 @@ var KeysRotateCommand = class KeysRotateCommand extends Command {
|
|
|
77944
79706
|
var KeysUpdateCommand = class KeysUpdateCommand extends Command {
|
|
77945
79707
|
static description = `Change an API key's permissions, email rules, name or expiry. It keeps its id and secret.
|
|
77946
79708
|
|
|
77947
|
-
--add and --remove take permissions (repeatable, or comma-separated); \`primitive keys create --help-full\` lists the permissions a key can hold. --add-rule and --remove-rule add email rules to the key or take them off, and keep its other rules. --rule replaces every rule the key has with the ones given, so pass all the rules it should keep; it cannot be combined with --add-rule or --remove-rule. --expires-at gives it a new expiry: a later one extends it, within your organization's credential policy, and is how to keep a key working past its expiry (rotating keeps the expiry). --no-expiry removes it where the policy allows. An expired key cannot be extended. Everything else about the key stays as it is, and the output lists any rules a change took off.
|
|
79709
|
+
--add and --remove take permissions (repeatable, or comma-separated); \`primitive keys create --help-full\` lists the permissions a key can hold. --add-rule and --remove-rule add email rules to the key or take them off, and keep its other rules. --rule replaces every rule the key has with the ones given, so pass all the rules it should keep; it cannot be combined with --add-rule or --remove-rule. --expires-at gives it a new expiry: a later one extends it, within your organization's credential policy, and is how to keep a key working past its expiry (rotating keeps the expiry). --no-expiry removes it where the policy allows. An expired key cannot be extended. Everything else about the key stays as it is, and the output lists any rules a change took off. When the key's grant is written and it lists an address or domain beside its rules, which it reads in full, the command warns on stderr, or in whole_mailbox_warning with --json.
|
|
77948
79710
|
|
|
77949
79711
|
The change runs directly; if your organization requires approval, the command prints the approval link and takes --step-up-token, as for a new key. A key never holds more than you do.
|
|
77950
79712
|
|
|
77951
|
-
With --json it prints the key as it is now, in the shape keys get prints, with changed, warnings and address_warnings alongside, notice when its permissions or mail changed, rules_removed when a change took rules off,
|
|
79713
|
+
With --json it prints the key as it is now, in the shape keys get prints, with changed, warnings and address_warnings alongside, notice when its permissions or mail changed, rules_added when a change put rules on it (each with granted_emails, how many emails the rule has already shared), rules_removed when a change took rules off, rules_not_on_key for rules asked to be taken off that it did not have, and whole_mailbox_warning when the grant written reads a whole mailbox beside its rules. A permission or rule change that leaves the grant as it is, with no --name, --expires-at or --no-expiry, sends nothing and prints the key with changed: []. If the key cannot be read back after the change, key_error says why and only the change's fields are printed.`;
|
|
77952
79714
|
static summary = "Change an API key";
|
|
77953
79715
|
static examples = [
|
|
77954
79716
|
"<%= config.bin %> keys update 0f0e0d0c-0b0a-4908-8706-050403020100 --add mail:send,mail:reply",
|
|
@@ -78082,6 +79844,7 @@ var KeysUpdateCommand = class KeysUpdateCommand extends Command {
|
|
|
78082
79844
|
path: { id: args.id }
|
|
78083
79845
|
})) : null;
|
|
78084
79846
|
const standingNotes = after?.ok ? after.data.address_warnings : void 0;
|
|
79847
|
+
const whole = grantChanged ? wholeMailboxWarning(args.id, result.data.address_warnings) : null;
|
|
78085
79848
|
if (flags.json) {
|
|
78086
79849
|
this.log(JSON.stringify({
|
|
78087
79850
|
...after?.ok ? after.data : {},
|
|
@@ -78090,12 +79853,13 @@ var KeysUpdateCommand = class KeysUpdateCommand extends Command {
|
|
|
78090
79853
|
...grantChanged ? { notice: HELD_REQUEST_NOTICE } : {},
|
|
78091
79854
|
...grantChanged && rulesRemoved.length > 0 ? { rules_removed: rulesRemoved } : {},
|
|
78092
79855
|
...rulesNotOnKey.length > 0 ? { rules_not_on_key: rulesNotOnKey } : {},
|
|
78093
|
-
...after && !after.ok ? { key_error: after.payload } : {}
|
|
79856
|
+
...after && !after.ok ? { key_error: after.payload } : {},
|
|
79857
|
+
...whole ? { whole_mailbox_warning: whole } : {}
|
|
78094
79858
|
}, null, 2));
|
|
78095
79859
|
return;
|
|
78096
79860
|
}
|
|
78097
79861
|
writeWarnings(result.data.warnings);
|
|
78098
|
-
writeWarnings(result.data.address_warnings);
|
|
79862
|
+
writeWarnings(whole ? otherNotes(result.data.address_warnings) : result.data.address_warnings);
|
|
78099
79863
|
const changed = result.data.changed;
|
|
78100
79864
|
this.log(changed.length > 0 ? `Updated key ${args.id}: ${changed.join(", ")}.` : `Key ${args.id} already matched; nothing changed.`);
|
|
78101
79865
|
if (after?.ok) {
|
|
@@ -78105,6 +79869,7 @@ var KeysUpdateCommand = class KeysUpdateCommand extends Command {
|
|
|
78105
79869
|
if (grantChanged && rulesRemoved.length > 0) this.log(`Rules taken off: ${rulesRemoved.join(", ")}`);
|
|
78106
79870
|
if (changed.includes("expires_at")) this.log(`Expires: ${expiry.expiresAt ?? "never"}`);
|
|
78107
79871
|
if (grantChanged) process.stderr.write(`${HELD_REQUEST_NOTICE}\n`);
|
|
79872
|
+
if (whole) process.stderr.write(`Warning: ${whole}\n`);
|
|
78108
79873
|
}
|
|
78109
79874
|
};
|
|
78110
79875
|
/**
|
|
@@ -82558,9 +84323,51 @@ function compareVersions(a, b) {
|
|
|
82558
84323
|
}
|
|
82559
84324
|
return 0;
|
|
82560
84325
|
}
|
|
82561
|
-
|
|
84326
|
+
function parseVersion(value) {
|
|
84327
|
+
const match = /^v?(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z.-]+))?(?:\+[0-9A-Za-z.-]+)?$/.exec(value.trim());
|
|
84328
|
+
if (!match) return null;
|
|
84329
|
+
return {
|
|
84330
|
+
core: match.slice(1, 4).map(Number),
|
|
84331
|
+
prerelease: match[4] ? match[4].split(".") : []
|
|
84332
|
+
};
|
|
84333
|
+
}
|
|
84334
|
+
/** Whether a version is a prerelease, such as 3.0.0-preview.12. */
|
|
84335
|
+
function isPrerelease(version) {
|
|
84336
|
+
return (parseVersion(version)?.prerelease.length ?? 0) > 0;
|
|
84337
|
+
}
|
|
84338
|
+
/**
|
|
84339
|
+
* Compare two versions in semver order, prereleases included: 3.0.0-preview.9
|
|
84340
|
+
* is older than 3.0.0-preview.12, which is older than 3.0.0. Null when either
|
|
84341
|
+
* is not a version.
|
|
84342
|
+
*/
|
|
84343
|
+
function compareReleaseVersions(a, b) {
|
|
84344
|
+
const left = parseVersion(a);
|
|
84345
|
+
const right = parseVersion(b);
|
|
84346
|
+
if (!left || !right) return null;
|
|
84347
|
+
for (let index = 0; index < 3; index++) {
|
|
84348
|
+
const difference = (left.core[index] ?? 0) - (right.core[index] ?? 0);
|
|
84349
|
+
if (difference) return Math.sign(difference);
|
|
84350
|
+
}
|
|
84351
|
+
if (left.prerelease.length === 0 || right.prerelease.length === 0) return Math.sign(right.prerelease.length - left.prerelease.length);
|
|
84352
|
+
const length = Math.max(left.prerelease.length, right.prerelease.length);
|
|
84353
|
+
for (let index = 0; index < length; index++) {
|
|
84354
|
+
const l = left.prerelease[index];
|
|
84355
|
+
const r = right.prerelease[index];
|
|
84356
|
+
if (l === void 0) return -1;
|
|
84357
|
+
if (r === void 0) return 1;
|
|
84358
|
+
const ln = /^\d+$/.test(l) ? Number(l) : null;
|
|
84359
|
+
const rn = /^\d+$/.test(r) ? Number(r) : null;
|
|
84360
|
+
if (ln !== null && rn !== null) {
|
|
84361
|
+
if (ln !== rn) return Math.sign(ln - rn);
|
|
84362
|
+
} else if (ln !== null) return -1;
|
|
84363
|
+
else if (rn !== null) return 1;
|
|
84364
|
+
else if (l !== r) return l < r ? -1 : 1;
|
|
84365
|
+
}
|
|
84366
|
+
return 0;
|
|
84367
|
+
}
|
|
84368
|
+
async function npmLatestVersion(fetchImpl, tag = "latest") {
|
|
82562
84369
|
try {
|
|
82563
|
-
const response = await fetchImpl(
|
|
84370
|
+
const response = await fetchImpl(`https://registry.npmjs.org/primitive/${tag}`, {
|
|
82564
84371
|
headers: { accept: "application/json" },
|
|
82565
84372
|
redirect: "follow",
|
|
82566
84373
|
signal: AbortSignal.timeout(3e3)
|
|
@@ -83237,11 +85044,23 @@ async function runMachineDoctor(options) {
|
|
|
83237
85044
|
"cli.version": { inspect: async () => {
|
|
83238
85045
|
const version = options.cliVersion;
|
|
83239
85046
|
if (options.minCliVersion) {
|
|
83240
|
-
const compared = compareVersions(version, options.minCliVersion);
|
|
85047
|
+
const compared = isPrerelease(version) ? compareReleaseVersions(version, options.minCliVersion) : compareVersions(version, options.minCliVersion);
|
|
83241
85048
|
if (compared === null) return check("cli.version", "warn", `Cannot compare ${version} with the required ${options.minCliVersion}.`);
|
|
83242
85049
|
if (compared < 0) return check("cli.version", "fail", `${version} is older than the required ${options.minCliVersion}.`, { action: "update_cli" });
|
|
83243
85050
|
}
|
|
83244
|
-
const
|
|
85051
|
+
const readLatest = options.latestVersion ?? (() => npmLatestVersion(fetchImpl));
|
|
85052
|
+
if (isPrerelease(version)) {
|
|
85053
|
+
const [latest, preview] = await Promise.all([readLatest(), (options.previewVersion ?? (() => npmLatestVersion(fetchImpl, "preview")))()]);
|
|
85054
|
+
const released = latest ? compareReleaseVersions(version, latest) : null;
|
|
85055
|
+
if (released !== null && released < 0) return check("cli.version", "warn", `${version} is a preview older than the latest release ${latest}. Install it with \`npm install -g primitive@latest\`.`, { action: "update_cli" });
|
|
85056
|
+
const previewBehind = preview ? compareReleaseVersions(version, preview) : null;
|
|
85057
|
+
if (previewBehind !== null && previewBehind < 0) return check("cli.version", "warn", `${version} is older than the newest preview ${preview}. Install it with \`npm install -g primitive@preview\`.`, { action: "update_cli" });
|
|
85058
|
+
if (previewBehind === 0) return check("cli.version", "ok", `${version} is the newest preview.`);
|
|
85059
|
+
if (previewBehind !== null) return check("cli.version", "ok", `${version} is not older than the newest preview ${preview}.`);
|
|
85060
|
+
const met = options.minCliVersion ? ` meets the required ${options.minCliVersion};` : ";";
|
|
85061
|
+
return check("cli.version", released !== null || options.minCliVersion ? "ok" : "skip", preview ? `${version}; the newest preview could not be compared with it (the tag is not a version).` : released !== null ? `${version} is not older than the latest release ${latest}; the newest preview could not be checked.` : `${version}${met} neither the latest release nor the newest preview could be checked.`);
|
|
85062
|
+
}
|
|
85063
|
+
const latest = await readLatest();
|
|
83245
85064
|
if (!latest) return options.minCliVersion ? check("cli.version", "ok", `${version} meets the required ${options.minCliVersion}; the latest release could not be checked.`) : check("cli.version", "skip", `${version}; the latest release could not be checked.`);
|
|
83246
85065
|
const behind = compareVersions(version, latest);
|
|
83247
85066
|
if (behind !== null && behind < 0) return check("cli.version", "warn", `${version} is older than the latest release ${latest}.`, { action: "update_cli" });
|
|
@@ -85525,7 +87344,10 @@ var ReplyCommand = class ReplyCommand extends Command {
|
|
|
85525
87344
|
description: "Reply to everyone on the email: the sender (or Reply-To) as To, every other To and Cc address as Cc, minus your own address. Bcc is never included.",
|
|
85526
87345
|
exclusive: ["fyi"]
|
|
85527
87346
|
}),
|
|
85528
|
-
body: Flags.string({
|
|
87347
|
+
body: Flags.string({
|
|
87348
|
+
aliases: ["text"],
|
|
87349
|
+
description: "Plain-text reply body (--text is the same flag). Either --body or --html (or both) is required, except with --fyi."
|
|
87350
|
+
}),
|
|
85529
87351
|
"body-file": Flags.string({ description: "Read the plain-text reply body from a UTF-8 file; this does not attach the file. Use --attachment for attachments. Mutually exclusive with --body and --body-stdin." }),
|
|
85530
87352
|
"body-stdin": Flags.boolean({ description: "Read the plain-text reply body from stdin. Mutually exclusive with --body and --body-file. Stdin can only be consumed once." }),
|
|
85531
87353
|
html: Flags.string({ description: "HTML reply body. Either --body or --html (or both) is required." }),
|
|
@@ -86862,7 +88684,10 @@ var SendCommand = class SendCommand extends Command {
|
|
|
86862
88684
|
}),
|
|
86863
88685
|
from: Flags.string({ description: "Sender address. Without it: a connected agent's own address, your member address on your own login, or a key's one granted address; otherwise the command asks for --from, except that a credential without mail:send is refused by that permission." }),
|
|
86864
88686
|
subject: Flags.string({ description: "Subject line. Defaults to the first line of --body / --html when omitted." }),
|
|
86865
|
-
body: Flags.string({
|
|
88687
|
+
body: Flags.string({
|
|
88688
|
+
aliases: ["text"],
|
|
88689
|
+
description: "Plain-text message body (--text is the same flag). Either --body or --html (or both) is required."
|
|
88690
|
+
}),
|
|
86866
88691
|
"body-file": Flags.string({ description: "Read the plain-text message body from a UTF-8 file. This does not attach the file; use --attachment for file attachments. Mutually exclusive with --body and --body-stdin." }),
|
|
86867
88692
|
"body-stdin": Flags.boolean({ description: "Read the plain-text message body from stdin. Mutually exclusive with --body and --body-file. Stdin can only be consumed once." }),
|
|
86868
88693
|
html: Flags.string({ description: "HTML message body. Either --body or --html (or both) is required." }),
|