primcli 1.70.0 → 1.71.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{api-command-C58Oh38I.js → api-command-B8C3jr7A.js} +205 -6
- package/dist/{machine-session-CKmn5bvB.js → machine-session-BrTXqxK9.js} +4 -4
- package/dist/{notify-session-CVZtEboh.js → notify-session-JbFGcjiM.js} +2 -2
- package/dist/oclif/auto-signal-worker.js +2 -2
- package/dist/oclif/auto-signals.js +1 -1
- package/dist/oclif/index.js +3979 -73
- package/dist/oclif/listen-supervisor.js +1 -2
- package/dist/oclif/receiver-heal.js +1 -1
- package/dist/{repeats-BL7ZC-_6-BZ-MphTV.js → repeats-BL7ZC-_6-CKMKBlx7.js} +1 -1
- package/dist/{scoped-chat-Dsark9hm.js → scoped-chat-DTFkP360.js} +1 -1
- package/dist/{signal-command-Dy_oub7_.js → signal-command-B2TZ8n9c.js} +4 -4
- package/package.json +11 -8
package/dist/oclif/index.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import { a as REPEAT_IDLE_MAX_MINUTES, b as parseWebhookEvent, d as parseRepeatTick, f as preparePresenceAliveEmail, i as REPEAT_EVERY_MAX_MINUTES, l as parseInteractionEnvelope$1, m as repeatStopCommand, o as REPEAT_MAX_SENDS_MAX, p as prepareSignalEmail, s as buildRepeatStopBody, u as parsePresenceEnvelope, v as isEmailReceivedEvent, x as parseFromHeader } from "../repeats-BL7ZC-_6-
|
|
1
|
+
import { a as REPEAT_IDLE_MAX_MINUTES, b as parseWebhookEvent, d as parseRepeatTick, f as preparePresenceAliveEmail, i as REPEAT_EVERY_MAX_MINUTES, l as parseInteractionEnvelope$1, m as repeatStopCommand, o as REPEAT_MAX_SENDS_MAX, p as prepareSignalEmail, s as buildRepeatStopBody, u as parsePresenceEnvelope, v as isEmailReceivedEvent, x as parseFromHeader } from "../repeats-BL7ZC-_6-CKMKBlx7.js";
|
|
2
2
|
import { A as saveActiveChatState, C as resolveCliAuth, D as deleteChatState, E as chatStatePath, F as agentProfilesDirectory, H as requireDefaultLoginProfile, M as AgentConnectionSetupError, N as agentProfileDirectory, O as loadActiveChatState, P as agentProfileName, R as loadConnectedAgentProfile, T as saveSignupCredentials, W as PrimitiveApiClient, _ as deleteCliCredentials, a as normalizeCliEnvironmentName, b as hasStoredCliLogin, c as resolveConfigEnvironment, g as credentialsPath, h as credentialsLockPath, i as loadCliConfig, j as AGENT_PROFILE_ENV, k as loadChatConversationByLocalId, 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 saveCliCredentials, x as loadCliCredentials, y as detectPrimitiveKeyEnvMisname } from "../cli-config-DMszFbGn.js";
|
|
3
|
-
import { $ as apiResultHttpStatus, $n as
|
|
3
|
+
import { $ as apiResultHttpStatus, $n as listWakeAuthorizations, $t as addDefaultNetworkMember, A as readThreadContext, An as getFunctionTestRunTrace, Ar as updateRoute, At as formatAlreadySentNotice, B as pendingMailPath, Bt as expectedAutomatedVerdicts, C as queryUsesAwaiting, Cr as startCliLogin, Ct as cliInvocation, D as displayAddress, Dn as getEmail, Dr as updateDefaultNetworkMember, Dt as identitySuggestions, E as describeWake, En as getDefaultNetworkAgent, Er as unsetFunctionRoute, Et as generatedAgentName, F as wakeReadCommand, Fn as getSentEmail, Ft as AUTOMATED_VALUES, G as interactionNextActions, Gn as listEmails, Gt as ATTACHMENT_WAIT_MAX_MS, H as recordPendingMail, Hn as listDefaultNetworkMembers, Ht as isAutomatedRejectedError, I as wakeRecipientField, In as getThread, It as AutomatedFilterUnsupportedError, J as replyInteractionWarning, Jn as listFunctions, K as readEmailInteraction, Kn as listEndpoints, Kt as withAttachmentWait, L as wakeRelationship, Ln as getWakeSchedule, Lt as assertAutomatedVerdict, M as serverMuted, Mn as getMemory, Mr as updateWakeSchedule, Mt as withFlagSuggestion, N as serverRelationship, Nn as getOrgRoutingTopology, Nr as verifyAgentClaim, Nt as AUTOMATED_FILTER_UNSUPPORTED_CODE, O as formatMailWakeLine, On as getFunction, Or as updateFunction, Ot as loadSkillLine, Pn as getRepeatingSend, Pr as verifyAgentSignup, Pt as AUTOMATED_FLAG_DESCRIPTION, Q as SEND_OUTCOME_HELP, Qn as listSentEmails, Qt as resolveCliApiRequestConfig, R as clearConsumedPendingMail, Rn as listAgentContacts, Rt as automatedRejectedError, S as isAwaitingRejectedError, Sn as getAgentContactPolicy, Sr as startAgentSignup, T as bareAddress, Tn as getCreditBalance, Tr as testFunction, U as removePendingMail, Un as listDomains, Ut as queryUsesAutomated, V as readPendingMail, Vn as listDefaultNetworkAgents, Vt as hasAutomatedVerdict, W as interactionHumanLine, Wn as listEmailAttachmentArchiveMembers, Wt as ATTACHMENT_WAIT_FLAG_DESCRIPTION, X as IDEMPOTENCY_KEY_FLAG_DESCRIPTION, Xn as listRoutes, Xt as createCliApiClient, Yn as listRepeatingSends, Yt as createAuthenticatedCliApiClient, Z as MAX_SEND_WAIT_TIMEOUT_MS, Zn as listSentAttachmentArchiveMembers, Zt as refreshStoredCliCredentials, _ as awaitingRejectedError, _n as downloadEmailAttachmentByFilename, _r as setFunctionRoute, _t as sendWaitClampNotice, a as extractErrorCode, an as createEndpoint, at as deriveSendIdempotencyKey, b as formatRepliesCell, bn as downloadSentAttachmentByFilename, br as simulateRoute, bt as successfulSendOutcome, c as readTextFileFlag, cn as createWakeAuthorization, cr as removeDefaultNetworkMember, ct as formatDeletedEarlierSendNotice, d as writeErrorWithHints, dr as resendAgentSignupVerification, dt as formatSendFailureSummary, en as cancelSentEmail, er as listWakeDispatches, et as assertValidIdempotencyKey, f as AWAITING_FLAG_DESCRIPTION, fn as deleteMemory, fr as runWakeSchedule, ft as formatSendRecordFailureSummary, g as assertAwaitingFilterRows, gn as downloadEmailAttachmentArchiveMember, gr as sendEmail, gt as sendOutcomeExitCode, h as ReplyStateUnsupportedError, hn as deleteWakeSchedule, hr as semanticSearch, ht as sendIdentityFields, i as createOperationCommand, in as completeWebhookEvent, it as classifySendError, j as sentInThread, jn as getInboxStatus, jr as updateWakeAuthorization, jt as writeIdempotentReplayBannerIfReplay, k as latestOwnOutbound, kn as getFunctionRouting, kr as updateRepeatingSend, kt as currentMailSessionKey, l as runWithTiming, ln as createWakeSchedule, lr as reorderRoutes, lt as formatPriorRepliesCheckSkipped, m as REPLY_STATE_UNSUPPORTED_CODE, mn as deleteWakeAuthorization, mr as searchMemories, mt as reportSendCommandResult, n as API_ERROR_CODES, nn as checkDefaultNetworkContactAdmission, nr as pollCliLogin, nt as buildThrownSendFailureEnvelope, o as extractErrorPayload, on as createFunction, ot as excludeSignalReplies, p as AWAITING_VALUES, pn as deleteRoute, pr as searchEmails, pt as priorRepliesThatWentOut, q as replyExpectation, qn as listFunctionLogs, qt as ConnectedAgentKeyRequiredError, r as TIME_FLAG_DESCRIPTION, rn as cliLogout, rr as pullWebhookEvent, rt as checkPriorReplies, s as formatErrorPayload, sn as createRoute, sr as redeemCreditCode, st as followUpCommandPrefix, t as API_BASE_URL_FLAG_DESCRIPTION, tn as cancelSentEmailByIdempotencyKey, tr as listWakeSchedules, tt as buildFollowUpCommand, u as surfaceUnauthorizedHint, ur as replyToEmail, ut as formatPriorRepliesWarning, v as ensureSearchReportsReplyState, vr as setFunctionSecret, vt as sentHistoryWindowStart, w as explainPendingMailNotFound, wn as getConversation, wr as stopRepeatFromEmail, wt as connectNextSteps, x as hasReplyState, xn as getAccount, xr as startAgentClaim, xt as thrownErrorExitCode, y as formatAwaitingCell, yn as downloadSentAttachmentArchiveMember, yr as setMemory, yt as serializeErrorPayload, z as clearReadPendingMail, zn as listAgentNetworks, zt as ensureSearchReportsAutomated } from "../api-command-B8C3jr7A.js";
|
|
4
4
|
import { S as resolveListenSubscription, _ 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, x as normalizeListenOrigin, y as listenIdentity } from "../shared-mail-files-MwlVE90c.js";
|
|
5
|
-
import { _ as detectAgentRuntime, a as NotificationRetryError, b as installedVersion, c as readConversationStatusContent, d as defaultSessionSocket, f as NativeSessionDisconnectedError, g as connectSkillTarget, h as NotificationOutcomeUnknownError, i as notificationReceiptPage, l as SESSION_UUID$1, m as NativeSessionNotLoadedError, n as notificationSenders, o as isRoutineNotificationContent, p as NativeSessionError, r as openSessionNotifications, s as notificationPartReader, t as notificationScope, v as installConnectSkill, x as readBundledConnectSkill, y as installedConnectSkillFile } from "../notify-session-
|
|
6
|
-
import { $ as installClaudeWakeHook, A as evaluateContactPolicy, B as contactAddress, C as RECEIVER_MODES, Ct as withOwnerMemberAddress, D as NETWORK_ADMISSION_PENDING_RETRY_MS, E as ContactPolicyReadRetryError, F as disconnectAgent, G as connectAgent, H as runContactRequest, I as AddressNotesApiError, J as refusalAfterReplacement, K as readAgentInvitation, L as addressNoteTarget, M as AgentDisconnectError, N as claimOverRevokedSetup, O as NetworkAdmissionPendingError, P as clearRevokedAddresses, Q as releaseRegistrationsForProfile, R as runAddressNotesRequest, S as runAgentConnect, St as refreshOwnerMemberAddressPeriodically, T as verificationReplySubmitted, U as agentConnectionStatus, V as contactAgentAddress, W as agentInvitationHash, X as savedReceiver, Y as saveConnectionName, Z as MACHINE_RUNTIMES, _ as enrollmentResumes, _t as jsonIndentation, a as endSession, at as describeSessionHookItem, b as invitationProfileName, bt as writeManagedFile, c as pendingSessionDisconnects, ct as findOnPath, d as registerSession, dt as readClaudeSettings, et as recordHookMailCheck, f as retryPendingDisconnects, ft as repairClaudeHooks, g as enrollAgent, gt as inspectManagedBlock, h as staleSessionAgents, ht as MachineFileError, i as connectedProfilesForSession, it as describeSessionHookChange, j as canonicalContactSelector, k as createNotificationContactPolicy, l as psProcessInfo, lt as hasMachineSessionHooks, m as runDetached, mt as writeClaudeSettings, n as OMP_MAIN, nt as claudeConfigDir, o as headlessClaudeRun, ot as ensureHookLauncher, p as revokeStaleSessionAgents, pt as samePath, q as receiverStatusCommand, r as agentConnectionState, rt as currentCliLocation, st as ephemeralCliReason, t as HOOK_END_GRACE_MS, tt as boundSessionProfiles, u as readClaudeHookInput, ut as inspectClaudeHooks, v as connectWarnings, vt as readManagedFile, w as externalReceivingState, x as pollCheckCommand, xt as readHookLauncherWarning, y as defaultAgentProfileName, yt as upsertManagedBlock, z as ContactsApiError } from "../machine-session-
|
|
7
|
-
import { a as scopedChatSenderTrust, i as readBeforeDeadline, n as isPlainChatReply, o as presenceDisposition, r as isScopedChatReply, s as readPresenceProjection, t as isConnectedChatCredential } from "../scoped-chat-
|
|
5
|
+
import { _ as detectAgentRuntime, a as NotificationRetryError, b as installedVersion, c as readConversationStatusContent, d as defaultSessionSocket, f as NativeSessionDisconnectedError, g as connectSkillTarget, h as NotificationOutcomeUnknownError, i as notificationReceiptPage, l as SESSION_UUID$1, m as NativeSessionNotLoadedError, n as notificationSenders, o as isRoutineNotificationContent, p as NativeSessionError, r as openSessionNotifications, s as notificationPartReader, t as notificationScope, v as installConnectSkill, x as readBundledConnectSkill, y as installedConnectSkillFile } from "../notify-session-JbFGcjiM.js";
|
|
6
|
+
import { $ as installClaudeWakeHook, A as evaluateContactPolicy, B as contactAddress, C as RECEIVER_MODES, Ct as withOwnerMemberAddress, D as NETWORK_ADMISSION_PENDING_RETRY_MS, E as ContactPolicyReadRetryError, F as disconnectAgent, G as connectAgent, H as runContactRequest, I as AddressNotesApiError, J as refusalAfterReplacement, K as readAgentInvitation, L as addressNoteTarget, M as AgentDisconnectError, N as claimOverRevokedSetup, O as NetworkAdmissionPendingError, P as clearRevokedAddresses, Q as releaseRegistrationsForProfile, R as runAddressNotesRequest, S as runAgentConnect, St as refreshOwnerMemberAddressPeriodically, T as verificationReplySubmitted, U as agentConnectionStatus, V as contactAgentAddress, W as agentInvitationHash, X as savedReceiver, Y as saveConnectionName, Z as MACHINE_RUNTIMES, _ as enrollmentResumes, _t as jsonIndentation, a as endSession, at as describeSessionHookItem, b as invitationProfileName, bt as writeManagedFile, c as pendingSessionDisconnects, ct as findOnPath, d as registerSession, dt as readClaudeSettings, et as recordHookMailCheck, f as retryPendingDisconnects, ft as repairClaudeHooks, g as enrollAgent, gt as inspectManagedBlock, h as staleSessionAgents, ht as MachineFileError, i as connectedProfilesForSession, it as describeSessionHookChange, j as canonicalContactSelector, k as createNotificationContactPolicy, l as psProcessInfo, lt as hasMachineSessionHooks, m as runDetached, mt as writeClaudeSettings, n as OMP_MAIN, nt as claudeConfigDir, o as headlessClaudeRun, ot as ensureHookLauncher, p as revokeStaleSessionAgents, pt as samePath, q as receiverStatusCommand, r as agentConnectionState, rt as currentCliLocation, st as ephemeralCliReason, t as HOOK_END_GRACE_MS, tt as boundSessionProfiles, u as readClaudeHookInput, ut as inspectClaudeHooks, v as connectWarnings, vt as readManagedFile, w as externalReceivingState, x as pollCheckCommand, xt as readHookLauncherWarning, y as defaultAgentProfileName, yt as upsertManagedBlock, z as ContactsApiError } from "../machine-session-BrTXqxK9.js";
|
|
7
|
+
import { a as scopedChatSenderTrust, i as readBeforeDeadline, n as isPlainChatReply, o as presenceDisposition, r as isScopedChatReply, s as readPresenceProjection, t as isConnectedChatCredential } from "../scoped-chat-DTFkP360.js";
|
|
8
8
|
import { c as startBackgroundListen, i as backgroundListenToken, l as stopBackgroundListen, o as runBackgroundListen, r as backgroundListenStatus, u as verifyBackgroundListenTarget } from "../listen-background--0peztKE.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-B2TZ8n9c.js";
|
|
10
10
|
import { autoSignalEligible, dispatchAutoRead, dispatchAutoWorking, haltAutoWorking, isSentSignal, readWorkingLease, restoreAutoWorking } from "./auto-signals.js";
|
|
11
11
|
import { t as guardJsonOutputCommands } from "../json-output-CEskSQJi.js";
|
|
12
12
|
import { Args, Command, Errors, Flags, ux } from "@oclif/core";
|
|
@@ -104,6 +104,10 @@ const openapiDocument = {
|
|
|
104
104
|
"name": "Threads",
|
|
105
105
|
"description": "Conversation threads spanning received and sent emails"
|
|
106
106
|
},
|
|
107
|
+
{
|
|
108
|
+
"name": "Changes",
|
|
109
|
+
"description": "One replayable feed of what changed, to keep local snapshots current"
|
|
110
|
+
},
|
|
107
111
|
{
|
|
108
112
|
"name": "Endpoints",
|
|
109
113
|
"description": "Manage webhook endpoints that receive email events"
|
|
@@ -1120,6 +1124,16 @@ const openapiDocument = {
|
|
|
1120
1124
|
},
|
|
1121
1125
|
"description": "Only return emails whose `automated` has this value.\n`awaiting=you&automated=false` lists mail from people that is\nwaiting on your reply. Combines with every other filter and with\nboth `cursor` and `since`. An inbound email is `automated` when any of these holds, decided once\nwhen it arrives: `null_envelope_sender` (an empty MAIL FROM, a bounce);\n`no_identifiable_sender` (no address in the envelope or From);\n`own_address` (a sender address is exactly one of the addresses the\nmail was delivered to; sharing a domain with the recipient is not\nenough, so a colleague or another agent in the organization is not\nautomated);\n`mailer_daemon` (mailer-daemon@ or postmaster@ on any domain);\n`auto_submitted` (Auto-Submitted with any keyword but `no`, an\nempty one included);\n`precedence` (Precedence bulk, list, junk or auto_reply);\n`list_unsubscribe` (List-Unsubscribe present); `list_id` (List-Id\npresent); `auto_response_suppress` (X-Auto-Response-Suppress asks\nfor no automatic reply: All, OOF or AutoReply);\n`failed_recipients` (X-Failed-Recipients present, a bounce);\n`report` (a delivery, feedback or disposition report). Mail\nPrimitive's own automatic responders decline to answer is always\n`automated`. The header rules only see headers captured at ingest, so\nolder mail may lack them. This is loop and noise protection, not\nsender authentication.\n"
|
|
1122
1126
|
},
|
|
1127
|
+
{
|
|
1128
|
+
"name": "from_verified",
|
|
1129
|
+
"in": "query",
|
|
1130
|
+
"required": false,
|
|
1131
|
+
"schema": {
|
|
1132
|
+
"type": "string",
|
|
1133
|
+
"enum": ["true", "false"]
|
|
1134
|
+
},
|
|
1135
|
+
"description": "Only return emails whose `from_verified` (the From domain was authenticated on arrival; see the field) has this value. Combines with every other filter and with both `cursor` and `since`."
|
|
1136
|
+
},
|
|
1123
1137
|
{
|
|
1124
1138
|
"name": "recipient",
|
|
1125
1139
|
"in": "query",
|
|
@@ -1140,6 +1154,17 @@ const openapiDocument = {
|
|
|
1140
1154
|
"enum": ["true", "false"]
|
|
1141
1155
|
},
|
|
1142
1156
|
"description": "With `true`, leave out emails whose `fyi` is true. Combines with every other filter and with both `cursor` and `since`."
|
|
1157
|
+
},
|
|
1158
|
+
{
|
|
1159
|
+
"name": "include",
|
|
1160
|
+
"in": "query",
|
|
1161
|
+
"required": false,
|
|
1162
|
+
"schema": {
|
|
1163
|
+
"type": "string",
|
|
1164
|
+
"maxLength": 200,
|
|
1165
|
+
"example": "body_text,headers"
|
|
1166
|
+
},
|
|
1167
|
+
"description": "Extra parts on every row, comma-separated: `body_text`, `body_html`, `headers`, `attachments`. Reads a page of messages in one request instead of one detail read per row. Parts are withheld where the detail read withholds them. Each body field is cut to 64 KiB per row and a response carries at most 2048 KiB of body text, in page order; a cut or omitted body is named in `truncated_fields`. Unknown values are a `validation_error`."
|
|
1143
1168
|
}
|
|
1144
1169
|
],
|
|
1145
1170
|
"responses": {
|
|
@@ -1170,7 +1195,7 @@ const openapiDocument = {
|
|
|
1170
1195
|
"type": "string",
|
|
1171
1196
|
"maxLength": 500
|
|
1172
1197
|
},
|
|
1173
|
-
"description": "Full-text search DSL query. Supports `awaiting:you` and `awaiting:them` to filter on reply state (see the `awaiting` field; delivered mail only, never `rejected`). Supports `automated:true` and `automated:false` to filter on the `automated` field. Terms are separated by spaces and every term must match. A term is a word, a `\"quoted phrase\"`, or a `field:value` filter (`from:`, `to:`, `subject:`, `body:`, `has:attachment`, `before:`, `after:`, `domain:`, `status:`, `awaiting:`, `automated:`). An upper-case `OR` between two text terms (words, phrases, `subject:` and `body:` terms) accepts either, and binds tighter than the implicit AND: `acme invoice OR receipt` is `acme` and either `invoice` or `receipt`. `OR` cannot join the other filters, and there is no `NOT` and no parentheses. A query that uses an unsupported operator, an unknown field, an unterminated quote, or only stop words is a `validation_error` that says what to change; it is never silently reinterpreted. Alternatives chain (`invoice OR receipt OR \"past due\"`), and each one counts toward the limit of 32 terms. These are rejected with a 400 `validation_error`: `OR` next to a filter (`from:a@example.com OR from:b@example.com`; run one search per value instead); an `OR` with no term on one side (a leading, trailing or doubled `OR`); an upper-case `NOT` (a search cannot exclude a term); and an alternative with no searchable word once stop words are removed (`the OR invoice`). Only upper case is an operator: to search for the word `OR` or `NOT` itself, write it in lower case or in double quotes (`\"OR\"`). With `prefix=true`, a trailing `OR` or `NOT` is read as a word still being typed. To search for text that contains a colon, such as a URL, put it in double quotes."
|
|
1198
|
+
"description": "Full-text search DSL query. Supports `awaiting:you` and `awaiting:them` to filter on reply state (see the `awaiting` field; delivered mail only, never `rejected`). Supports `automated:true` and `automated:false` to filter on the `automated` field. Terms are separated by spaces and every term must match. A term is a word, a `\"quoted phrase\"`, or a `field:value` filter (`from:`, `to:`, `subject:`, `body:`, `has:attachment`, `before:`, `after:`, `domain:`, `status:`, `awaiting:`, `automated:`, `header:`, `has-header:`). An upper-case `OR` between two text terms (words, phrases, `subject:` and `body:` terms) accepts either, and binds tighter than the implicit AND: `acme invoice OR receipt` is `acme` and either `invoice` or `receipt`. `OR` cannot join the other filters, and there is no `NOT` and no parentheses. A query that uses an unsupported operator, an unknown field, an unterminated quote, or only stop words is a `validation_error` that says what to change; it is never silently reinterpreted. Alternatives chain (`invoice OR receipt OR \"past due\"`), and each one counts toward the limit of 32 terms. These are rejected with a 400 `validation_error`: `OR` next to a filter (`from:a@example.com OR from:b@example.com`; run one search per value instead); an `OR` with no term on one side (a leading, trailing or doubled `OR`); an upper-case `NOT` (a search cannot exclude a term); and an alternative with no searchable word once stop words are removed (`the OR invoice`). Only upper case is an operator: to search for the word `OR` or `NOT` itself, write it in lower case or in double quotes (`\"OR\"`). With `prefix=true`, a trailing `OR` or `NOT` is read as a word still being typed. To search for text that contains a colon, such as a URL, put it in double quotes. Supports `from_verified:true` and `from_verified:false` to filter on the `from_verified` field. `header:Name=Value` matches mail whose header `Name` has exactly `Value` (name and value case-insensitive, the whole value, quote the term for a value with spaces: `header:\"List-Id=Team news <news.example.com>\"`); `has-header:Name` matches mail that carries header `Name` with any value. Neither can be an `OR` alternative. Header filters read a stored copy of each message's headers: for received mail every header except trace and signature headers (`Received`, `DKIM-Signature`, `ARC-*`, `Authentication-Results` and similar) and credential-like names (a word such as `auth`, `token`, `key`, `secret`, `cookie`, `session`, `bearer`, `jwt` or `hmac`, including joined forms such as `AuthToken` or `SessionId`), at most 100 headers and 16 KiB per message, each value cut to 1024 bytes; for sent mail the `headers` the request set. A filter on a header that is never stored is a `validation_error`. Mail stored before header search was available has no stored headers and is not matched."
|
|
1174
1199
|
},
|
|
1175
1200
|
{
|
|
1176
1201
|
"name": "from",
|
|
@@ -1291,6 +1316,16 @@ const openapiDocument = {
|
|
|
1291
1316
|
},
|
|
1292
1317
|
"description": "Only return emails whose `automated` has this value. Also\navailable in `q` as `automated:true` or `automated:false`. An inbound email is `automated` when any of these holds, decided once\nwhen it arrives: `null_envelope_sender` (an empty MAIL FROM, a bounce);\n`no_identifiable_sender` (no address in the envelope or From);\n`own_address` (a sender address is exactly one of the addresses the\nmail was delivered to; sharing a domain with the recipient is not\nenough, so a colleague or another agent in the organization is not\nautomated);\n`mailer_daemon` (mailer-daemon@ or postmaster@ on any domain);\n`auto_submitted` (Auto-Submitted with any keyword but `no`, an\nempty one included);\n`precedence` (Precedence bulk, list, junk or auto_reply);\n`list_unsubscribe` (List-Unsubscribe present); `list_id` (List-Id\npresent); `auto_response_suppress` (X-Auto-Response-Suppress asks\nfor no automatic reply: All, OOF or AutoReply);\n`failed_recipients` (X-Failed-Recipients present, a bounce);\n`report` (a delivery, feedback or disposition report). Mail\nPrimitive's own automatic responders decline to answer is always\n`automated`. The header rules only see headers captured at ingest, so\nolder mail may lack them. This is loop and noise protection, not\nsender authentication.\n"
|
|
1293
1318
|
},
|
|
1319
|
+
{
|
|
1320
|
+
"name": "from_verified",
|
|
1321
|
+
"in": "query",
|
|
1322
|
+
"required": false,
|
|
1323
|
+
"schema": {
|
|
1324
|
+
"type": "string",
|
|
1325
|
+
"enum": ["true", "false"]
|
|
1326
|
+
},
|
|
1327
|
+
"description": "Only return emails whose `from_verified` (the From domain was authenticated on arrival; see the field) has this value. Also available in `q` as `from_verified:true` or `from_verified:false`."
|
|
1328
|
+
},
|
|
1294
1329
|
{
|
|
1295
1330
|
"name": "sort",
|
|
1296
1331
|
"in": "query",
|
|
@@ -1365,6 +1400,32 @@ const openapiDocument = {
|
|
|
1365
1400
|
"default": "true"
|
|
1366
1401
|
},
|
|
1367
1402
|
"description": "When `false`, the total match count is not computed and `meta.total`\nis null (`meta.total_capped` is false). Use `meta.cursor` to tell\nwhether another page exists. Skipping the count makes a broad query,\nsuch as a one-letter prefix, cheaper to serve. Defaults to `true`.\n"
|
|
1403
|
+
},
|
|
1404
|
+
{
|
|
1405
|
+
"name": "direction",
|
|
1406
|
+
"in": "query",
|
|
1407
|
+
"required": false,
|
|
1408
|
+
"schema": {
|
|
1409
|
+
"type": "string",
|
|
1410
|
+
"enum": [
|
|
1411
|
+
"inbound",
|
|
1412
|
+
"outbound",
|
|
1413
|
+
"any"
|
|
1414
|
+
],
|
|
1415
|
+
"default": "inbound"
|
|
1416
|
+
},
|
|
1417
|
+
"description": "Which mail to search: `inbound` (mail received by the organization, the default), `outbound` (mail it sent) or `any` (both, merged in date order). Each result carries `direction`. When sent mail is included, results are in date order only (`sort=relevance` is a `validation_error`), filters that describe received mail only (`status`, `spam_score_lt`, `spam_score_gte`, `awaiting`, `automated`, `reply_to_sent_email_id`, `from_verified`, and `status:`, `awaiting:`, `automated:`, `from_verified:` in `q`) are a `validation_error`, as is `include`, and `facets` are not computed. `meta.total` is computed on the first page only and is null on later pages. A `meta.cursor` continues the direction it was issued for: using it with another `direction` is a `validation_error`. Text search over sent mail (`q`, `subject` or `body`) answers 503 `sent_mail_search_unavailable` in an environment where sent-mail search is not enabled. An agent connection searches only the mail its own address sent. On a sent result, `received_at` is when the message was accepted for sending, `recipient` is its first To address, and fields that describe receiving (`spam_score`, `raw_size_bytes`, `webhook_status`) are null, as is `from_verified` (sent mail has no From-authentication verdict). A sent result carries its sending status (`queued`, `delivered`, `bounced` and so on) in `delivery_status`; its `status` stays in the received-mail vocabulary as the nearest reading of it: `pending` (queued, scheduled, deferred), `accepted` (handed off, outcome not confirmed), `completed` (delivered) or `rejected` (not delivered: failed, denied, bounced or canceled)."
|
|
1418
|
+
},
|
|
1419
|
+
{
|
|
1420
|
+
"name": "include",
|
|
1421
|
+
"in": "query",
|
|
1422
|
+
"required": false,
|
|
1423
|
+
"schema": {
|
|
1424
|
+
"type": "string",
|
|
1425
|
+
"maxLength": 200,
|
|
1426
|
+
"example": "body_text,headers"
|
|
1427
|
+
},
|
|
1428
|
+
"description": "Extra parts on every row, comma-separated: `body_text`, `body_html`, `headers`, `attachments`. Reads a page of messages in one request instead of one detail read per row. Parts are withheld where the detail read withholds them. Each body field is cut to 64 KiB per row and a response carries at most 2048 KiB of body text, in page order; a cut or omitted body is named in `truncated_fields`. Unknown values are a `validation_error`."
|
|
1368
1429
|
}
|
|
1369
1430
|
],
|
|
1370
1431
|
"responses": {
|
|
@@ -1386,6 +1447,10 @@ const openapiDocument = {
|
|
|
1386
1447
|
"400": { "$ref": "#/components/responses/ValidationError" },
|
|
1387
1448
|
"401": { "$ref": "#/components/responses/Unauthorized" },
|
|
1388
1449
|
"404": { "$ref": "#/components/responses/NotFound" },
|
|
1450
|
+
"503": {
|
|
1451
|
+
"description": "Sent-mail text search is not enabled in this environment (`sent_mail_search_unavailable`). Returned only for text terms (`q`, `subject` or `body`) with `direction=outbound` or `direction=any`; retry with `direction=inbound`, or without text terms.",
|
|
1452
|
+
"content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }
|
|
1453
|
+
},
|
|
1389
1454
|
"504": {
|
|
1390
1455
|
"description": "Search query timed out",
|
|
1391
1456
|
"content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }
|
|
@@ -1551,6 +1616,145 @@ const openapiDocument = {
|
|
|
1551
1616
|
}
|
|
1552
1617
|
}
|
|
1553
1618
|
},
|
|
1619
|
+
"/emails/{id}/attachments": {
|
|
1620
|
+
"parameters": [{ "$ref": "#/components/parameters/ResourceId" }],
|
|
1621
|
+
"get": {
|
|
1622
|
+
"parameters": [{ "$ref": "#/components/parameters/AttachmentFilename" }],
|
|
1623
|
+
"operationId": "downloadEmailAttachmentByFilename",
|
|
1624
|
+
"summary": "Download one received email attachment by filename",
|
|
1625
|
+
"description": "Returns the same bytes as downloadEmailAttachmentPart, selecting the attachment by its exact filename. The path form `/emails/{id}/attachments/{filename}` also works for any name that is not digits only, which that form reads as a part_index. When several attachments share the filename the response is 409 attachment_name_ambiguous with every matching part_index, and nothing is returned. Authenticate with an organization API key, an authorized organization OAuth token, or an address-bound agent connection key. A connected agent reads only mail received at its own address. Function credentials are not supported. Discarded content is unavailable.",
|
|
1626
|
+
"tags": ["Emails"],
|
|
1627
|
+
"security": [{ "BearerAuth": [] }],
|
|
1628
|
+
"responses": {
|
|
1629
|
+
"200": { "$ref": "#/components/responses/AttachmentPart" },
|
|
1630
|
+
"400": { "$ref": "#/components/responses/ValidationError" },
|
|
1631
|
+
"401": { "$ref": "#/components/responses/Unauthorized" },
|
|
1632
|
+
"403": { "$ref": "#/components/responses/Forbidden" },
|
|
1633
|
+
"404": {
|
|
1634
|
+
"description": "Missing or inaccessible message, or no attachment with this filename",
|
|
1635
|
+
"content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }
|
|
1636
|
+
},
|
|
1637
|
+
"409": { "$ref": "#/components/responses/AttachmentNameAmbiguous" },
|
|
1638
|
+
"410": {
|
|
1639
|
+
"description": "content_discarded; the original content is no longer available",
|
|
1640
|
+
"content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }
|
|
1641
|
+
},
|
|
1642
|
+
"413": {
|
|
1643
|
+
"description": "attachment_limit_exceeded; the attachment exceeds download limits",
|
|
1644
|
+
"content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }
|
|
1645
|
+
},
|
|
1646
|
+
"502": {
|
|
1647
|
+
"description": "attachment_integrity_failed; stored content failed integrity validation",
|
|
1648
|
+
"content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }
|
|
1649
|
+
},
|
|
1650
|
+
"503": {
|
|
1651
|
+
"description": "attachment_not_ready or attachment_storage_unavailable; retry after the indicated delay",
|
|
1652
|
+
"headers": { "Retry-After": {
|
|
1653
|
+
"description": "Delay in seconds before retrying this read",
|
|
1654
|
+
"schema": {
|
|
1655
|
+
"type": "integer",
|
|
1656
|
+
"minimum": 0
|
|
1657
|
+
}
|
|
1658
|
+
} },
|
|
1659
|
+
"content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }
|
|
1660
|
+
}
|
|
1661
|
+
}
|
|
1662
|
+
}
|
|
1663
|
+
},
|
|
1664
|
+
"/emails/{id}/attachments/{attachment}/members": {
|
|
1665
|
+
"parameters": [{ "$ref": "#/components/parameters/ResourceId" }, { "$ref": "#/components/parameters/AttachmentSelector" }],
|
|
1666
|
+
"get": {
|
|
1667
|
+
"operationId": "listEmailAttachmentArchiveMembers",
|
|
1668
|
+
"summary": "List the files inside a received zip or tar attachment",
|
|
1669
|
+
"description": "Lists the regular files inside a zip, tar or tar.gz attachment with each one's path, decoded size and SHA-256. The format is detected from the content. Unsafe paths refuse the whole archive, and resource limits bound the work (see the 413 and 422 responses). Authenticate with an organization API key, an authorized organization OAuth token, or an address-bound agent connection key. A connected agent reads only mail received at its own address. Function credentials are not supported. Discarded content is unavailable.",
|
|
1670
|
+
"tags": ["Emails"],
|
|
1671
|
+
"security": [{ "BearerAuth": [] }],
|
|
1672
|
+
"responses": {
|
|
1673
|
+
"200": {
|
|
1674
|
+
"description": "Files inside the archive",
|
|
1675
|
+
"content": { "application/json": { "schema": { "allOf": [{ "$ref": "#/components/schemas/SuccessEnvelope" }, {
|
|
1676
|
+
"type": "object",
|
|
1677
|
+
"properties": { "data": { "$ref": "#/components/schemas/ArchiveMembers" } }
|
|
1678
|
+
}] } } }
|
|
1679
|
+
},
|
|
1680
|
+
"400": { "$ref": "#/components/responses/ValidationError" },
|
|
1681
|
+
"401": { "$ref": "#/components/responses/Unauthorized" },
|
|
1682
|
+
"403": { "$ref": "#/components/responses/Forbidden" },
|
|
1683
|
+
"404": {
|
|
1684
|
+
"description": "Missing or inaccessible message, or no such attachment",
|
|
1685
|
+
"content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }
|
|
1686
|
+
},
|
|
1687
|
+
"409": { "$ref": "#/components/responses/AttachmentNameAmbiguous" },
|
|
1688
|
+
"410": {
|
|
1689
|
+
"description": "content_discarded; the original content is no longer available",
|
|
1690
|
+
"content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }
|
|
1691
|
+
},
|
|
1692
|
+
"413": { "$ref": "#/components/responses/ArchiveLimitExceeded" },
|
|
1693
|
+
"422": { "$ref": "#/components/responses/ArchiveUnprocessable" },
|
|
1694
|
+
"502": {
|
|
1695
|
+
"description": "attachment_integrity_failed; stored content failed integrity validation",
|
|
1696
|
+
"content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }
|
|
1697
|
+
},
|
|
1698
|
+
"503": {
|
|
1699
|
+
"description": "attachment_not_ready, attachment_storage_unavailable or archive_busy; retry after the indicated delay. archive_busy means this server is already reading as many archives as it allows at once; nothing is wrong with the archive.",
|
|
1700
|
+
"headers": { "Retry-After": {
|
|
1701
|
+
"description": "Delay in seconds before retrying this read",
|
|
1702
|
+
"schema": {
|
|
1703
|
+
"type": "integer",
|
|
1704
|
+
"minimum": 0
|
|
1705
|
+
}
|
|
1706
|
+
} },
|
|
1707
|
+
"content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }
|
|
1708
|
+
}
|
|
1709
|
+
}
|
|
1710
|
+
}
|
|
1711
|
+
},
|
|
1712
|
+
"/emails/{id}/attachments/{attachment}/members/{path}": {
|
|
1713
|
+
"parameters": [
|
|
1714
|
+
{ "$ref": "#/components/parameters/ResourceId" },
|
|
1715
|
+
{ "$ref": "#/components/parameters/AttachmentSelector" },
|
|
1716
|
+
{ "$ref": "#/components/parameters/ArchiveMemberPath" }
|
|
1717
|
+
],
|
|
1718
|
+
"get": {
|
|
1719
|
+
"operationId": "downloadEmailAttachmentArchiveMember",
|
|
1720
|
+
"summary": "Download one file from inside a received zip or tar attachment",
|
|
1721
|
+
"description": "Returns the decoded bytes of one regular file inside a zip, tar or tar.gz attachment, by its listed path. The archive's structure, every member path and every limit are checked as for the listing, but only the requested file is decoded and verified (its zip CRC-32, and X-Content-SHA256 of the bytes returned); list the archive to have every file decoded. Authenticate with an organization API key, an authorized organization OAuth token, or an address-bound agent connection key. A connected agent reads only mail received at its own address. Function credentials are not supported. Discarded content is unavailable.",
|
|
1722
|
+
"tags": ["Emails"],
|
|
1723
|
+
"security": [{ "BearerAuth": [] }],
|
|
1724
|
+
"responses": {
|
|
1725
|
+
"200": { "$ref": "#/components/responses/AttachmentPart" },
|
|
1726
|
+
"400": { "$ref": "#/components/responses/ValidationError" },
|
|
1727
|
+
"401": { "$ref": "#/components/responses/Unauthorized" },
|
|
1728
|
+
"403": { "$ref": "#/components/responses/Forbidden" },
|
|
1729
|
+
"404": {
|
|
1730
|
+
"description": "Missing or inaccessible message, no such attachment, or no regular file at this path",
|
|
1731
|
+
"content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }
|
|
1732
|
+
},
|
|
1733
|
+
"409": { "$ref": "#/components/responses/AttachmentNameAmbiguous" },
|
|
1734
|
+
"410": {
|
|
1735
|
+
"description": "content_discarded; the original content is no longer available",
|
|
1736
|
+
"content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }
|
|
1737
|
+
},
|
|
1738
|
+
"413": { "$ref": "#/components/responses/ArchiveLimitExceeded" },
|
|
1739
|
+
"422": { "$ref": "#/components/responses/ArchiveUnprocessable" },
|
|
1740
|
+
"502": {
|
|
1741
|
+
"description": "attachment_integrity_failed; stored content failed integrity validation",
|
|
1742
|
+
"content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }
|
|
1743
|
+
},
|
|
1744
|
+
"503": {
|
|
1745
|
+
"description": "attachment_not_ready, attachment_storage_unavailable or archive_busy; retry after the indicated delay. archive_busy means this server is already reading as many archives as it allows at once; nothing is wrong with the archive.",
|
|
1746
|
+
"headers": { "Retry-After": {
|
|
1747
|
+
"description": "Delay in seconds before retrying this read",
|
|
1748
|
+
"schema": {
|
|
1749
|
+
"type": "integer",
|
|
1750
|
+
"minimum": 0
|
|
1751
|
+
}
|
|
1752
|
+
} },
|
|
1753
|
+
"content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }
|
|
1754
|
+
}
|
|
1755
|
+
}
|
|
1756
|
+
}
|
|
1757
|
+
},
|
|
1554
1758
|
"/sent-emails/{id}/attachments/{part_index}": {
|
|
1555
1759
|
"parameters": [{ "$ref": "#/components/parameters/ResourceId" }, { "$ref": "#/components/parameters/AttachmentPartIndex" }],
|
|
1556
1760
|
"get": {
|
|
@@ -1598,6 +1802,145 @@ const openapiDocument = {
|
|
|
1598
1802
|
}
|
|
1599
1803
|
}
|
|
1600
1804
|
},
|
|
1805
|
+
"/sent-emails/{id}/attachments": {
|
|
1806
|
+
"parameters": [{ "$ref": "#/components/parameters/ResourceId" }],
|
|
1807
|
+
"get": {
|
|
1808
|
+
"parameters": [{ "$ref": "#/components/parameters/AttachmentFilename" }],
|
|
1809
|
+
"operationId": "downloadSentAttachmentByFilename",
|
|
1810
|
+
"summary": "Download one sent email attachment by filename",
|
|
1811
|
+
"description": "Returns the same bytes as downloadSentAttachmentPart, selecting the attachment by its exact filename. The path form `/sent-emails/{id}/attachments/{filename}` also works for any name that is not digits only, which that form reads as a part_index. When several attachments share the filename the response is 409 attachment_name_ambiguous with every matching part_index, and nothing is returned. Authenticate with an organization API key, an authorized organization OAuth token, or an address-bound agent connection key. A connected agent reads only mail sent from its own address. Function credentials are not supported. Discarded content is unavailable.",
|
|
1812
|
+
"tags": ["Sending"],
|
|
1813
|
+
"security": [{ "BearerAuth": [] }],
|
|
1814
|
+
"responses": {
|
|
1815
|
+
"200": { "$ref": "#/components/responses/AttachmentPart" },
|
|
1816
|
+
"400": { "$ref": "#/components/responses/ValidationError" },
|
|
1817
|
+
"401": { "$ref": "#/components/responses/Unauthorized" },
|
|
1818
|
+
"403": { "$ref": "#/components/responses/Forbidden" },
|
|
1819
|
+
"404": {
|
|
1820
|
+
"description": "Missing or inaccessible message, or no attachment with this filename",
|
|
1821
|
+
"content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }
|
|
1822
|
+
},
|
|
1823
|
+
"409": { "$ref": "#/components/responses/AttachmentNameAmbiguous" },
|
|
1824
|
+
"410": {
|
|
1825
|
+
"description": "content_discarded; the original content is no longer available",
|
|
1826
|
+
"content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }
|
|
1827
|
+
},
|
|
1828
|
+
"413": {
|
|
1829
|
+
"description": "attachment_limit_exceeded; the attachment exceeds download limits",
|
|
1830
|
+
"content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }
|
|
1831
|
+
},
|
|
1832
|
+
"502": {
|
|
1833
|
+
"description": "attachment_integrity_failed; stored content failed integrity validation",
|
|
1834
|
+
"content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }
|
|
1835
|
+
},
|
|
1836
|
+
"503": {
|
|
1837
|
+
"description": "attachment_not_ready or attachment_storage_unavailable; retry after the indicated delay",
|
|
1838
|
+
"headers": { "Retry-After": {
|
|
1839
|
+
"description": "Delay in seconds before retrying this read",
|
|
1840
|
+
"schema": {
|
|
1841
|
+
"type": "integer",
|
|
1842
|
+
"minimum": 0
|
|
1843
|
+
}
|
|
1844
|
+
} },
|
|
1845
|
+
"content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }
|
|
1846
|
+
}
|
|
1847
|
+
}
|
|
1848
|
+
}
|
|
1849
|
+
},
|
|
1850
|
+
"/sent-emails/{id}/attachments/{attachment}/members": {
|
|
1851
|
+
"parameters": [{ "$ref": "#/components/parameters/ResourceId" }, { "$ref": "#/components/parameters/AttachmentSelector" }],
|
|
1852
|
+
"get": {
|
|
1853
|
+
"operationId": "listSentAttachmentArchiveMembers",
|
|
1854
|
+
"summary": "List the files inside a sent zip or tar attachment",
|
|
1855
|
+
"description": "Lists the regular files inside a zip, tar or tar.gz attachment with each one's path, decoded size and SHA-256. The format is detected from the content. Unsafe paths refuse the whole archive, and resource limits bound the work (see the 413 and 422 responses). Authenticate with an organization API key, an authorized organization OAuth token, or an address-bound agent connection key. A connected agent reads only mail sent from its own address. Function credentials are not supported. Discarded content is unavailable.",
|
|
1856
|
+
"tags": ["Sending"],
|
|
1857
|
+
"security": [{ "BearerAuth": [] }],
|
|
1858
|
+
"responses": {
|
|
1859
|
+
"200": {
|
|
1860
|
+
"description": "Files inside the archive",
|
|
1861
|
+
"content": { "application/json": { "schema": { "allOf": [{ "$ref": "#/components/schemas/SuccessEnvelope" }, {
|
|
1862
|
+
"type": "object",
|
|
1863
|
+
"properties": { "data": { "$ref": "#/components/schemas/ArchiveMembers" } }
|
|
1864
|
+
}] } } }
|
|
1865
|
+
},
|
|
1866
|
+
"400": { "$ref": "#/components/responses/ValidationError" },
|
|
1867
|
+
"401": { "$ref": "#/components/responses/Unauthorized" },
|
|
1868
|
+
"403": { "$ref": "#/components/responses/Forbidden" },
|
|
1869
|
+
"404": {
|
|
1870
|
+
"description": "Missing or inaccessible message, or no such attachment",
|
|
1871
|
+
"content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }
|
|
1872
|
+
},
|
|
1873
|
+
"409": { "$ref": "#/components/responses/AttachmentNameAmbiguous" },
|
|
1874
|
+
"410": {
|
|
1875
|
+
"description": "content_discarded; the original content is no longer available",
|
|
1876
|
+
"content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }
|
|
1877
|
+
},
|
|
1878
|
+
"413": { "$ref": "#/components/responses/ArchiveLimitExceeded" },
|
|
1879
|
+
"422": { "$ref": "#/components/responses/ArchiveUnprocessable" },
|
|
1880
|
+
"502": {
|
|
1881
|
+
"description": "attachment_integrity_failed; stored content failed integrity validation",
|
|
1882
|
+
"content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }
|
|
1883
|
+
},
|
|
1884
|
+
"503": {
|
|
1885
|
+
"description": "attachment_not_ready, attachment_storage_unavailable or archive_busy; retry after the indicated delay. archive_busy means this server is already reading as many archives as it allows at once; nothing is wrong with the archive.",
|
|
1886
|
+
"headers": { "Retry-After": {
|
|
1887
|
+
"description": "Delay in seconds before retrying this read",
|
|
1888
|
+
"schema": {
|
|
1889
|
+
"type": "integer",
|
|
1890
|
+
"minimum": 0
|
|
1891
|
+
}
|
|
1892
|
+
} },
|
|
1893
|
+
"content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }
|
|
1894
|
+
}
|
|
1895
|
+
}
|
|
1896
|
+
}
|
|
1897
|
+
},
|
|
1898
|
+
"/sent-emails/{id}/attachments/{attachment}/members/{path}": {
|
|
1899
|
+
"parameters": [
|
|
1900
|
+
{ "$ref": "#/components/parameters/ResourceId" },
|
|
1901
|
+
{ "$ref": "#/components/parameters/AttachmentSelector" },
|
|
1902
|
+
{ "$ref": "#/components/parameters/ArchiveMemberPath" }
|
|
1903
|
+
],
|
|
1904
|
+
"get": {
|
|
1905
|
+
"operationId": "downloadSentAttachmentArchiveMember",
|
|
1906
|
+
"summary": "Download one file from inside a sent zip or tar attachment",
|
|
1907
|
+
"description": "Returns the decoded bytes of one regular file inside a zip, tar or tar.gz attachment, by its listed path. The archive's structure, every member path and every limit are checked as for the listing, but only the requested file is decoded and verified (its zip CRC-32, and X-Content-SHA256 of the bytes returned); list the archive to have every file decoded. Authenticate with an organization API key, an authorized organization OAuth token, or an address-bound agent connection key. A connected agent reads only mail sent from its own address. Function credentials are not supported. Discarded content is unavailable.",
|
|
1908
|
+
"tags": ["Sending"],
|
|
1909
|
+
"security": [{ "BearerAuth": [] }],
|
|
1910
|
+
"responses": {
|
|
1911
|
+
"200": { "$ref": "#/components/responses/AttachmentPart" },
|
|
1912
|
+
"400": { "$ref": "#/components/responses/ValidationError" },
|
|
1913
|
+
"401": { "$ref": "#/components/responses/Unauthorized" },
|
|
1914
|
+
"403": { "$ref": "#/components/responses/Forbidden" },
|
|
1915
|
+
"404": {
|
|
1916
|
+
"description": "Missing or inaccessible message, no such attachment, or no regular file at this path",
|
|
1917
|
+
"content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }
|
|
1918
|
+
},
|
|
1919
|
+
"409": { "$ref": "#/components/responses/AttachmentNameAmbiguous" },
|
|
1920
|
+
"410": {
|
|
1921
|
+
"description": "content_discarded; the original content is no longer available",
|
|
1922
|
+
"content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }
|
|
1923
|
+
},
|
|
1924
|
+
"413": { "$ref": "#/components/responses/ArchiveLimitExceeded" },
|
|
1925
|
+
"422": { "$ref": "#/components/responses/ArchiveUnprocessable" },
|
|
1926
|
+
"502": {
|
|
1927
|
+
"description": "attachment_integrity_failed; stored content failed integrity validation",
|
|
1928
|
+
"content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }
|
|
1929
|
+
},
|
|
1930
|
+
"503": {
|
|
1931
|
+
"description": "attachment_not_ready, attachment_storage_unavailable or archive_busy; retry after the indicated delay. archive_busy means this server is already reading as many archives as it allows at once; nothing is wrong with the archive.",
|
|
1932
|
+
"headers": { "Retry-After": {
|
|
1933
|
+
"description": "Delay in seconds before retrying this read",
|
|
1934
|
+
"schema": {
|
|
1935
|
+
"type": "integer",
|
|
1936
|
+
"minimum": 0
|
|
1937
|
+
}
|
|
1938
|
+
} },
|
|
1939
|
+
"content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }
|
|
1940
|
+
}
|
|
1941
|
+
}
|
|
1942
|
+
}
|
|
1943
|
+
},
|
|
1601
1944
|
"/emails/{id}/repeat-stop": {
|
|
1602
1945
|
"parameters": [{ "$ref": "#/components/parameters/ResourceId" }],
|
|
1603
1946
|
"post": {
|
|
@@ -1662,12 +2005,16 @@ const openapiDocument = {
|
|
|
1662
2005
|
"401": { "$ref": "#/components/responses/Unauthorized" },
|
|
1663
2006
|
"403": { "$ref": "#/components/responses/Forbidden" },
|
|
1664
2007
|
"404": { "$ref": "#/components/responses/NotFound" },
|
|
2008
|
+
"409": {
|
|
2009
|
+
"description": "latest_message_mismatch; if_latest_message_id is no longer the newest message in the thread. Nothing was sent. error.details carries latest_message_id (null when the thread has no message visible to this credential), latest_message_at and thread_id.",
|
|
2010
|
+
"content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }
|
|
2011
|
+
},
|
|
1665
2012
|
"410": {
|
|
1666
2013
|
"description": "The prior send was deleted. Its idempotency key or automatic reply suppression remains reserved, so this request will not send another message. Do not retry with a new key to bypass this refusal.",
|
|
1667
2014
|
"content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }
|
|
1668
2015
|
},
|
|
1669
2016
|
"422": {
|
|
1670
|
-
"description": "Inbound is not repliable: the row exists but lacks a\n`message_id` (no thread anchor) or a `recipient` (cannot\nderive the From address).\n",
|
|
2017
|
+
"description": "Inbound is not repliable: the row exists but lacks a\n`message_id` (no thread anchor) or a `recipient` (cannot\nderive the From address). Or latest_message_thread_unresolved:\nif_latest_message_id was given but in_reply_to resolves to no single\nthread this credential can check; nothing was sent, and the same\nrequest will not succeed on retry.\n",
|
|
1671
2018
|
"content": { "application/json": {
|
|
1672
2019
|
"schema": { "$ref": "#/components/schemas/ErrorResponse" },
|
|
1673
2020
|
"example": {
|
|
@@ -2586,10 +2933,18 @@ const openapiDocument = {
|
|
|
2586
2933
|
"400": { "$ref": "#/components/responses/ValidationError" },
|
|
2587
2934
|
"401": { "$ref": "#/components/responses/Unauthorized" },
|
|
2588
2935
|
"403": { "$ref": "#/components/responses/Forbidden" },
|
|
2936
|
+
"409": {
|
|
2937
|
+
"description": "latest_message_mismatch; if_latest_message_id is no longer the newest message in the thread. Nothing was sent. error.details carries latest_message_id (null when the thread has no message visible to this credential), latest_message_at and thread_id.",
|
|
2938
|
+
"content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }
|
|
2939
|
+
},
|
|
2589
2940
|
"410": {
|
|
2590
2941
|
"description": "The prior send was deleted. Its idempotency key or automatic reply suppression remains reserved, so this request will not send another message. Do not retry with a new key to bypass this refusal.",
|
|
2591
2942
|
"content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }
|
|
2592
2943
|
},
|
|
2944
|
+
"422": {
|
|
2945
|
+
"description": "latest_message_thread_unresolved; if_latest_message_id was given but in_reply_to resolves to no single thread this credential can check. Nothing was sent, and the same request will not succeed on retry. error.details.thread_id is null.",
|
|
2946
|
+
"content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }
|
|
2947
|
+
},
|
|
2593
2948
|
"429": { "$ref": "#/components/responses/RateLimited" },
|
|
2594
2949
|
"500": { "$ref": "#/components/responses/InternalError" },
|
|
2595
2950
|
"502": { "$ref": "#/components/responses/BadGateway" },
|
|
@@ -2795,6 +3150,28 @@ const openapiDocument = {
|
|
|
2795
3150
|
"maxLength": 320
|
|
2796
3151
|
},
|
|
2797
3152
|
"description": "Exact case-insensitive sender mailbox filter. Returns\nonly sends whose from address equals this value; it is\nnot a substring match, so a partial mailbox matches\nnothing. Use `q` to search sender text loosely.\nCombines with every other filter, and unlike `q` it is\nserved by every deployment.\n"
|
|
3153
|
+
},
|
|
3154
|
+
{
|
|
3155
|
+
"name": "header",
|
|
3156
|
+
"in": "query",
|
|
3157
|
+
"required": false,
|
|
3158
|
+
"schema": {
|
|
3159
|
+
"type": "string",
|
|
3160
|
+
"minLength": 1,
|
|
3161
|
+
"maxLength": 1200
|
|
3162
|
+
},
|
|
3163
|
+
"description": "Return only sends that set this header: `Name=Value` matches that exact value, `Name` alone any value. Name and value compare case-insensitively; the value is trimmed and matched whole, not as a substring, and only the first `=` separates the two. Matches only the `headers` the send request set. History pages only: with `since` or `wait` it is a `validation_error`, as is a name that is never stored (trace, signature or credential-like). Sends made before header search was available are not matched."
|
|
3164
|
+
},
|
|
3165
|
+
{
|
|
3166
|
+
"name": "include",
|
|
3167
|
+
"in": "query",
|
|
3168
|
+
"required": false,
|
|
3169
|
+
"schema": {
|
|
3170
|
+
"type": "string",
|
|
3171
|
+
"maxLength": 200,
|
|
3172
|
+
"example": "body_text,headers"
|
|
3173
|
+
},
|
|
3174
|
+
"description": "Extra parts on every row, comma-separated: `body_text`, `body_html`, `headers`, `attachments`. Reads a page of messages in one request instead of one detail read per row. Parts are withheld where the detail read withholds them. Each body field is cut to 64 KiB per row and a response carries at most 2048 KiB of body text, in page order; a cut or omitted body is named in `truncated_fields`. Unknown values are a `validation_error`."
|
|
2798
3175
|
}
|
|
2799
3176
|
],
|
|
2800
3177
|
"responses": {
|
|
@@ -4782,6 +5159,222 @@ const openapiDocument = {
|
|
|
4782
5159
|
}
|
|
4783
5160
|
}
|
|
4784
5161
|
} },
|
|
5162
|
+
"/changes": { "get": {
|
|
5163
|
+
"operationId": "listChanges",
|
|
5164
|
+
"summary": "Long-poll what changed",
|
|
5165
|
+
"description": "One feed of small invalidations (ids and kinds) for everything an app keeps a snapshot of: inbound mail, sent mail, the member's conversation read state, the agent directory and presence, and the credential's own access. Non-consumptive and replayable: any number of devices can read the same cursor independently.\n\nStart with `since=start` (or no `since`): it returns an empty page whose `next_cursor` is a baseline. Take it BEFORE the initial list reads; every change that commits after it is delivered, including ones those reads already reflect. Then call with `since=<next_cursor>&wait=20` in a loop. Changes come oldest first in commit order, at least once: a write that commits late is never skipped, because positions are assigned at commit.\n\nEvery change is scoped to what this credential's full reads would show: a connected agent sees only its own mail, its own sends and sends it provably received; mail held for sender authentication is announced only when released; read state reaches only the member it belongs to; agent changes reach members and agents allowed to view the network; organization API keys receive no agent changes. Function credentials cannot call this.\n\nChanges are kept 7 days. A cursor answers 410 `cursor_expired` only when changes after it were pruned (an idle cursor stays valid); rebuild the snapshots from `since=start`.",
|
|
5166
|
+
"tags": ["Changes"],
|
|
5167
|
+
"security": [{ "BearerAuth": [] }],
|
|
5168
|
+
"parameters": [
|
|
5169
|
+
{
|
|
5170
|
+
"name": "since",
|
|
5171
|
+
"in": "query",
|
|
5172
|
+
"required": false,
|
|
5173
|
+
"schema": {
|
|
5174
|
+
"type": "string",
|
|
5175
|
+
"minLength": 1,
|
|
5176
|
+
"maxLength": 512
|
|
5177
|
+
},
|
|
5178
|
+
"description": "`start` (the default) for a baseline, otherwise the previous `next_cursor`, unchanged."
|
|
5179
|
+
},
|
|
5180
|
+
{
|
|
5181
|
+
"name": "wait",
|
|
5182
|
+
"in": "query",
|
|
5183
|
+
"required": false,
|
|
5184
|
+
"schema": {
|
|
5185
|
+
"type": "integer",
|
|
5186
|
+
"minimum": 0,
|
|
5187
|
+
"maximum": 25,
|
|
5188
|
+
"default": 0
|
|
5189
|
+
},
|
|
5190
|
+
"description": "Seconds to hold a caught-up request open. It returns as soon as a change this credential can see commits, else an empty page at the deadline. Ignored for a baseline request."
|
|
5191
|
+
},
|
|
5192
|
+
{
|
|
5193
|
+
"name": "limit",
|
|
5194
|
+
"in": "query",
|
|
5195
|
+
"required": false,
|
|
5196
|
+
"schema": {
|
|
5197
|
+
"type": "integer",
|
|
5198
|
+
"minimum": 1,
|
|
5199
|
+
"maximum": 100,
|
|
5200
|
+
"default": 100
|
|
5201
|
+
},
|
|
5202
|
+
"description": "Maximum changes in one page."
|
|
5203
|
+
},
|
|
5204
|
+
{
|
|
5205
|
+
"name": "expand",
|
|
5206
|
+
"in": "query",
|
|
5207
|
+
"required": false,
|
|
5208
|
+
"schema": {
|
|
5209
|
+
"type": "string",
|
|
5210
|
+
"enum": ["message"]
|
|
5211
|
+
},
|
|
5212
|
+
"description": "`message` adds `message` to every change: the current list row of the message a message change names, so a reader needs no follow-up read. Ignored for a baseline request."
|
|
5213
|
+
},
|
|
5214
|
+
{
|
|
5215
|
+
"name": "address",
|
|
5216
|
+
"in": "query",
|
|
5217
|
+
"required": false,
|
|
5218
|
+
"schema": {
|
|
5219
|
+
"type": "string",
|
|
5220
|
+
"minLength": 1
|
|
5221
|
+
},
|
|
5222
|
+
"description": "Up to 20 email addresses, comma-separated (or the parameter repeated). Keeps only the message changes whose message is to or from one of them, matched case-insensitively. Inbound mail matches on the address it was delivered to, the envelope sender and the From header address; sent mail on the From address and the To and Cc addresses, and Bcc only for a credential that can read the sender side. Deletions, thread, agent and access changes are never filtered, because a deleted message's addresses can no longer be read: ignore a deletion for an id you do not hold. The cursor still moves past every change the filter drops, so a filtered page can be empty with a new cursor."
|
|
5223
|
+
}
|
|
5224
|
+
],
|
|
5225
|
+
"responses": {
|
|
5226
|
+
"200": {
|
|
5227
|
+
"description": "A page of changes",
|
|
5228
|
+
"headers": { "Cache-Control": { "schema": {
|
|
5229
|
+
"type": "string",
|
|
5230
|
+
"const": "no-store"
|
|
5231
|
+
} } },
|
|
5232
|
+
"content": { "application/json": { "schema": { "allOf": [{ "$ref": "#/components/schemas/SuccessEnvelope" }, {
|
|
5233
|
+
"type": "object",
|
|
5234
|
+
"properties": { "data": { "$ref": "#/components/schemas/ChangePage" } }
|
|
5235
|
+
}] } } }
|
|
5236
|
+
},
|
|
5237
|
+
"400": {
|
|
5238
|
+
"description": "`validation_error` for a bad parameter, or `invalid_cursor` for a cursor that is malformed or belongs to another organization. Rebuild from `since=start`.",
|
|
5239
|
+
"content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }
|
|
5240
|
+
},
|
|
5241
|
+
"401": {
|
|
5242
|
+
"description": "Invalid or missing API key",
|
|
5243
|
+
"content": { "application/json": {
|
|
5244
|
+
"schema": { "$ref": "#/components/schemas/ErrorResponse" },
|
|
5245
|
+
"example": {
|
|
5246
|
+
"success": false,
|
|
5247
|
+
"error": {
|
|
5248
|
+
"code": "unauthorized",
|
|
5249
|
+
"message": "Invalid or missing API key"
|
|
5250
|
+
}
|
|
5251
|
+
}
|
|
5252
|
+
} },
|
|
5253
|
+
"headers": {}
|
|
5254
|
+
},
|
|
5255
|
+
"410": {
|
|
5256
|
+
"description": "`cursor_expired`: changes after this cursor were pruned. `error.details.retention_seconds` is the window. Rebuild the snapshots from `since=start`.",
|
|
5257
|
+
"content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }
|
|
5258
|
+
},
|
|
5259
|
+
"429": {
|
|
5260
|
+
"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.",
|
|
5261
|
+
"headers": { "Retry-After": {
|
|
5262
|
+
"schema": { "type": "integer" },
|
|
5263
|
+
"description": "Seconds to wait before retrying"
|
|
5264
|
+
} },
|
|
5265
|
+
"content": { "application/json": {
|
|
5266
|
+
"schema": { "$ref": "#/components/schemas/ErrorResponse" },
|
|
5267
|
+
"example": {
|
|
5268
|
+
"success": false,
|
|
5269
|
+
"error": {
|
|
5270
|
+
"code": "rate_limit_exceeded",
|
|
5271
|
+
"message": "Rate limit exceeded"
|
|
5272
|
+
}
|
|
5273
|
+
}
|
|
5274
|
+
} }
|
|
5275
|
+
}
|
|
5276
|
+
}
|
|
5277
|
+
} },
|
|
5278
|
+
"/sent-emails/cancel": { "post": {
|
|
5279
|
+
"operationId": "cancelSentEmailByIdempotencyKey",
|
|
5280
|
+
"summary": "Cancel a scheduled send by idempotency key",
|
|
5281
|
+
"description": "Cancels the still-scheduled send created with this `Idempotency-Key` (matched exactly), as `POST /sent-emails/{id}/cancel` does, and releases the key. 404 `not_found`: no send this credential can act on was created with the key. 409 `not_scheduled`: the send holding the key is no longer scheduled; `error.details` names its `sent_email_id` and `status` (`canceled` if already canceled). Pass `created_before` on a retry so it cannot reach a newer send that reused the released key. A connected agent can cancel any scheduled send from its own address, whoever created it, and no other.",
|
|
5282
|
+
"tags": ["Sending"],
|
|
5283
|
+
"security": [{ "BearerAuth": [] }],
|
|
5284
|
+
"requestBody": {
|
|
5285
|
+
"required": true,
|
|
5286
|
+
"content": { "application/json": { "schema": {
|
|
5287
|
+
"type": "object",
|
|
5288
|
+
"additionalProperties": false,
|
|
5289
|
+
"required": ["idempotency_key"],
|
|
5290
|
+
"properties": {
|
|
5291
|
+
"idempotency_key": {
|
|
5292
|
+
"type": "string",
|
|
5293
|
+
"minLength": 1,
|
|
5294
|
+
"maxLength": 255,
|
|
5295
|
+
"pattern": "^[\\x21-\\x7e]+$",
|
|
5296
|
+
"description": "The `Idempotency-Key` the send was created with."
|
|
5297
|
+
},
|
|
5298
|
+
"created_before": {
|
|
5299
|
+
"type": "string",
|
|
5300
|
+
"format": "date-time",
|
|
5301
|
+
"description": "Only consider sends created at or before this instant, such as when you first asked to cancel."
|
|
5302
|
+
}
|
|
5303
|
+
}
|
|
5304
|
+
} } }
|
|
5305
|
+
},
|
|
5306
|
+
"responses": {
|
|
5307
|
+
"200": {
|
|
5308
|
+
"description": "Canceled sent-email detail",
|
|
5309
|
+
"content": { "application/json": { "schema": { "allOf": [{ "$ref": "#/components/schemas/SuccessEnvelope" }, {
|
|
5310
|
+
"type": "object",
|
|
5311
|
+
"properties": { "data": { "$ref": "#/components/schemas/SentEmailDetail" } }
|
|
5312
|
+
}] } } },
|
|
5313
|
+
"headers": {}
|
|
5314
|
+
},
|
|
5315
|
+
"400": {
|
|
5316
|
+
"description": "Invalid request parameters",
|
|
5317
|
+
"content": { "application/json": {
|
|
5318
|
+
"schema": { "$ref": "#/components/schemas/ErrorResponse" },
|
|
5319
|
+
"example": {
|
|
5320
|
+
"success": false,
|
|
5321
|
+
"error": {
|
|
5322
|
+
"code": "validation_error",
|
|
5323
|
+
"message": "Invalid domain format"
|
|
5324
|
+
}
|
|
5325
|
+
}
|
|
5326
|
+
} }
|
|
5327
|
+
},
|
|
5328
|
+
"401": {
|
|
5329
|
+
"description": "Invalid or missing API key",
|
|
5330
|
+
"content": { "application/json": {
|
|
5331
|
+
"schema": { "$ref": "#/components/schemas/ErrorResponse" },
|
|
5332
|
+
"example": {
|
|
5333
|
+
"success": false,
|
|
5334
|
+
"error": {
|
|
5335
|
+
"code": "unauthorized",
|
|
5336
|
+
"message": "Invalid or missing API key"
|
|
5337
|
+
}
|
|
5338
|
+
}
|
|
5339
|
+
} },
|
|
5340
|
+
"headers": {}
|
|
5341
|
+
},
|
|
5342
|
+
"404": {
|
|
5343
|
+
"description": "Resource not found",
|
|
5344
|
+
"content": { "application/json": {
|
|
5345
|
+
"schema": { "$ref": "#/components/schemas/ErrorResponse" },
|
|
5346
|
+
"example": {
|
|
5347
|
+
"success": false,
|
|
5348
|
+
"error": {
|
|
5349
|
+
"code": "not_found",
|
|
5350
|
+
"message": "Resource not found"
|
|
5351
|
+
}
|
|
5352
|
+
}
|
|
5353
|
+
} }
|
|
5354
|
+
},
|
|
5355
|
+
"409": {
|
|
5356
|
+
"description": "`not_scheduled`: `error.details` carries the `sent_email_id` and current `status` of the send holding the key.",
|
|
5357
|
+
"content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }
|
|
5358
|
+
},
|
|
5359
|
+
"429": {
|
|
5360
|
+
"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.",
|
|
5361
|
+
"headers": { "Retry-After": {
|
|
5362
|
+
"schema": { "type": "integer" },
|
|
5363
|
+
"description": "Seconds to wait before retrying"
|
|
5364
|
+
} },
|
|
5365
|
+
"content": { "application/json": {
|
|
5366
|
+
"schema": { "$ref": "#/components/schemas/ErrorResponse" },
|
|
5367
|
+
"example": {
|
|
5368
|
+
"success": false,
|
|
5369
|
+
"error": {
|
|
5370
|
+
"code": "rate_limit_exceeded",
|
|
5371
|
+
"message": "Rate limit exceeded"
|
|
5372
|
+
}
|
|
5373
|
+
}
|
|
5374
|
+
} }
|
|
5375
|
+
}
|
|
5376
|
+
}
|
|
5377
|
+
} },
|
|
4785
5378
|
"/sent-emails/{id}/cancel": {
|
|
4786
5379
|
"parameters": [{ "$ref": "#/components/parameters/ResourceId" }],
|
|
4787
5380
|
"post": {
|
|
@@ -8171,6 +8764,39 @@ const openapiDocument = {
|
|
|
8171
8764
|
}
|
|
8172
8765
|
},
|
|
8173
8766
|
"parameters": {
|
|
8767
|
+
"AttachmentFilename": {
|
|
8768
|
+
"name": "filename",
|
|
8769
|
+
"in": "query",
|
|
8770
|
+
"required": true,
|
|
8771
|
+
"description": "The attachment's exact filename from its metadata. Any name, digits only included. No other query parameter is accepted.",
|
|
8772
|
+
"schema": {
|
|
8773
|
+
"type": "string",
|
|
8774
|
+
"minLength": 1,
|
|
8775
|
+
"maxLength": 1024
|
|
8776
|
+
}
|
|
8777
|
+
},
|
|
8778
|
+
"AttachmentSelector": {
|
|
8779
|
+
"name": "attachment",
|
|
8780
|
+
"in": "path",
|
|
8781
|
+
"required": true,
|
|
8782
|
+
"description": "The attachment, by its exact part_index when the value is digits only, otherwise by its exact filename (case-sensitive, compared after Unicode NFC normalization). A digits-only value always selects that part_index, even when another attachment has that name; read such a name with ?filename=.",
|
|
8783
|
+
"schema": {
|
|
8784
|
+
"type": "string",
|
|
8785
|
+
"minLength": 1,
|
|
8786
|
+
"maxLength": 1024
|
|
8787
|
+
}
|
|
8788
|
+
},
|
|
8789
|
+
"ArchiveMemberPath": {
|
|
8790
|
+
"name": "path",
|
|
8791
|
+
"in": "path",
|
|
8792
|
+
"required": true,
|
|
8793
|
+
"description": "The member path exactly as listed, percent-encoded as one path segment (slashes as %2F).",
|
|
8794
|
+
"schema": {
|
|
8795
|
+
"type": "string",
|
|
8796
|
+
"minLength": 1,
|
|
8797
|
+
"maxLength": 1024
|
|
8798
|
+
}
|
|
8799
|
+
},
|
|
8174
8800
|
"AgentNetworkAddress": {
|
|
8175
8801
|
"name": "address",
|
|
8176
8802
|
"in": "path",
|
|
@@ -8312,6 +8938,18 @@ const openapiDocument = {
|
|
|
8312
8938
|
}
|
|
8313
8939
|
},
|
|
8314
8940
|
"responses": {
|
|
8941
|
+
"AttachmentNameAmbiguous": {
|
|
8942
|
+
"description": "attachment_name_ambiguous; more than one attachment has this filename, or a digits-only path value matches no part_index but is the name of an attachment. error.details.part_indexes lists every match, so fetch by part_index or by ?filename=. Or attachment_changed; attachment metadata changed during the read.",
|
|
8943
|
+
"content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }
|
|
8944
|
+
},
|
|
8945
|
+
"ArchiveLimitExceeded": {
|
|
8946
|
+
"description": "archive_limit_exceeded, or attachment_limit_exceeded for the attachment itself. Limits: at most 1000 entries of any kind (files, directories and links all count); each file at most 31457280 bytes decoded; all files together (for tar.gz, the whole decoded tar stream) at most 33554432 bytes; a compression ratio above 100:1 is refused once decoded output passes 1048576 bytes; member paths at most 1024 UTF-8 bytes.",
|
|
8947
|
+
"content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }
|
|
8948
|
+
},
|
|
8949
|
+
"ArchiveUnprocessable": {
|
|
8950
|
+
"description": "attachment_not_archive, archive_unsafe_path, archive_invalid or archive_unsupported. An archive with an absolute, drive-letter, backslash, control-character, Unicode format or separator character (bidirectional controls included), empty, \".\" or \"..\" path segment is refused whole (archive_unsafe_path) rather than listed without the entry. Any symbolic or hard link is refused the same way, wherever it points. Paths that differ only by letter case or Unicode normalization count as duplicates. Duplicate paths, overlapping zip data, a zip local header whose name or method disagrees with the central directory, and truncated or corrupt structures are archive_invalid; encryption, zip64, multi-volume archives, compression other than stored or deflate, sparse tar files, and device or other special files are archive_unsupported.",
|
|
8951
|
+
"content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }
|
|
8952
|
+
},
|
|
8315
8953
|
"AttachmentPart": {
|
|
8316
8954
|
"description": "Original attachment bytes; never a JSON envelope or a base64 string",
|
|
8317
8955
|
"content": { "application/octet-stream": { "schema": {
|
|
@@ -8559,6 +9197,109 @@ const openapiDocument = {
|
|
|
8559
9197
|
}
|
|
8560
9198
|
},
|
|
8561
9199
|
"schemas": {
|
|
9200
|
+
"IncludedMessageHeaders": {
|
|
9201
|
+
"type": "object",
|
|
9202
|
+
"description": "Stored addressing and threading headers. A member is null when absent or withheld.",
|
|
9203
|
+
"properties": {
|
|
9204
|
+
"from": { "type": ["string", "null"] },
|
|
9205
|
+
"to": {
|
|
9206
|
+
"type": ["array", "null"],
|
|
9207
|
+
"items": { "type": "string" }
|
|
9208
|
+
},
|
|
9209
|
+
"cc": {
|
|
9210
|
+
"type": ["array", "null"],
|
|
9211
|
+
"items": { "type": "string" }
|
|
9212
|
+
},
|
|
9213
|
+
"bcc": {
|
|
9214
|
+
"type": ["array", "null"],
|
|
9215
|
+
"items": { "type": "string" }
|
|
9216
|
+
},
|
|
9217
|
+
"reply_to": {
|
|
9218
|
+
"type": ["array", "null"],
|
|
9219
|
+
"items": { "type": "string" }
|
|
9220
|
+
},
|
|
9221
|
+
"in_reply_to": {
|
|
9222
|
+
"type": ["array", "null"],
|
|
9223
|
+
"items": { "type": "string" }
|
|
9224
|
+
},
|
|
9225
|
+
"references": {
|
|
9226
|
+
"type": ["array", "null"],
|
|
9227
|
+
"items": { "type": "string" }
|
|
9228
|
+
},
|
|
9229
|
+
"automation": {
|
|
9230
|
+
"type": ["object", "null"],
|
|
9231
|
+
"additionalProperties": { "type": "string" },
|
|
9232
|
+
"description": "Received mail only."
|
|
9233
|
+
}
|
|
9234
|
+
},
|
|
9235
|
+
"required": [
|
|
9236
|
+
"from",
|
|
9237
|
+
"to",
|
|
9238
|
+
"cc",
|
|
9239
|
+
"bcc",
|
|
9240
|
+
"reply_to",
|
|
9241
|
+
"in_reply_to",
|
|
9242
|
+
"references",
|
|
9243
|
+
"automation"
|
|
9244
|
+
]
|
|
9245
|
+
},
|
|
9246
|
+
"ArchiveMember": {
|
|
9247
|
+
"type": "object",
|
|
9248
|
+
"required": [
|
|
9249
|
+
"path",
|
|
9250
|
+
"size_bytes",
|
|
9251
|
+
"sha256"
|
|
9252
|
+
],
|
|
9253
|
+
"properties": {
|
|
9254
|
+
"path": {
|
|
9255
|
+
"type": "string",
|
|
9256
|
+
"description": "Member path inside the archive, relative, with \"/\" separators and no leading \"./\"."
|
|
9257
|
+
},
|
|
9258
|
+
"size_bytes": {
|
|
9259
|
+
"type": "integer",
|
|
9260
|
+
"minimum": 0,
|
|
9261
|
+
"description": "Decoded size in bytes."
|
|
9262
|
+
},
|
|
9263
|
+
"sha256": {
|
|
9264
|
+
"type": "string",
|
|
9265
|
+
"pattern": "^[0-9a-f]{64}$",
|
|
9266
|
+
"description": "SHA-256 of the decoded bytes."
|
|
9267
|
+
}
|
|
9268
|
+
}
|
|
9269
|
+
},
|
|
9270
|
+
"ArchiveMembers": {
|
|
9271
|
+
"type": "object",
|
|
9272
|
+
"required": [
|
|
9273
|
+
"part_index",
|
|
9274
|
+
"filename",
|
|
9275
|
+
"format",
|
|
9276
|
+
"members"
|
|
9277
|
+
],
|
|
9278
|
+
"properties": {
|
|
9279
|
+
"part_index": {
|
|
9280
|
+
"type": "integer",
|
|
9281
|
+
"minimum": 0,
|
|
9282
|
+
"description": "The attachment the listing is of."
|
|
9283
|
+
},
|
|
9284
|
+
"filename": {
|
|
9285
|
+
"type": ["string", "null"],
|
|
9286
|
+
"description": "That attachment's filename."
|
|
9287
|
+
},
|
|
9288
|
+
"format": {
|
|
9289
|
+
"type": "string",
|
|
9290
|
+
"enum": [
|
|
9291
|
+
"zip",
|
|
9292
|
+
"tar",
|
|
9293
|
+
"tar.gz"
|
|
9294
|
+
]
|
|
9295
|
+
},
|
|
9296
|
+
"members": {
|
|
9297
|
+
"type": "array",
|
|
9298
|
+
"description": "Regular files in archive order.",
|
|
9299
|
+
"items": { "$ref": "#/components/schemas/ArchiveMember" }
|
|
9300
|
+
}
|
|
9301
|
+
}
|
|
9302
|
+
},
|
|
8562
9303
|
"ContactPolicyRuleInput": {
|
|
8563
9304
|
"type": "object",
|
|
8564
9305
|
"additionalProperties": false,
|
|
@@ -9919,7 +10660,20 @@ const openapiDocument = {
|
|
|
9919
10660
|
"agent_connection_scope_forbidden",
|
|
9920
10661
|
"address_note_conflict",
|
|
9921
10662
|
"address_not_controlled",
|
|
9922
|
-
"contact_conflict"
|
|
10663
|
+
"contact_conflict",
|
|
10664
|
+
"sent_mail_search_unavailable",
|
|
10665
|
+
"latest_message_mismatch",
|
|
10666
|
+
"latest_message_thread_unresolved",
|
|
10667
|
+
"thread_busy",
|
|
10668
|
+
"attachment_name_ambiguous",
|
|
10669
|
+
"archive_limit_exceeded",
|
|
10670
|
+
"archive_unsafe_path",
|
|
10671
|
+
"archive_invalid",
|
|
10672
|
+
"archive_unsupported",
|
|
10673
|
+
"attachment_not_archive",
|
|
10674
|
+
"archive_busy",
|
|
10675
|
+
"invalid_cursor",
|
|
10676
|
+
"cursor_expired"
|
|
9923
10677
|
]
|
|
9924
10678
|
},
|
|
9925
10679
|
"message": { "type": "string" },
|
|
@@ -11592,6 +12346,142 @@ const openapiDocument = {
|
|
|
11592
12346
|
"scopes"
|
|
11593
12347
|
]
|
|
11594
12348
|
},
|
|
12349
|
+
"Change": {
|
|
12350
|
+
"type": "object",
|
|
12351
|
+
"additionalProperties": false,
|
|
12352
|
+
"description": "One invalidation: what changed, by id, never its content. Every field is present; fields that do not apply to the kind are null. Apply idempotently: a change can repeat.",
|
|
12353
|
+
"required": [
|
|
12354
|
+
"kind",
|
|
12355
|
+
"email_id",
|
|
12356
|
+
"sent_email_id",
|
|
12357
|
+
"thread_id",
|
|
12358
|
+
"status",
|
|
12359
|
+
"agent_address",
|
|
12360
|
+
"reason",
|
|
12361
|
+
"changed_at"
|
|
12362
|
+
],
|
|
12363
|
+
"properties": {
|
|
12364
|
+
"kind": {
|
|
12365
|
+
"type": "string",
|
|
12366
|
+
"enum": [
|
|
12367
|
+
"email.visible",
|
|
12368
|
+
"email.updated",
|
|
12369
|
+
"email.deleted",
|
|
12370
|
+
"sent_email.created",
|
|
12371
|
+
"sent_email.updated",
|
|
12372
|
+
"sent_email.deleted",
|
|
12373
|
+
"thread.read_state_changed",
|
|
12374
|
+
"thread.mute_changed",
|
|
12375
|
+
"agent.changed",
|
|
12376
|
+
"access.changed"
|
|
12377
|
+
],
|
|
12378
|
+
"description": "`email.visible`: an inbound email became visible to this credential (arrived, or its sender-authentication hold was released). `email.updated`: something a list row or conversation shows changed. `email.deleted`: gone, or withdrawn from view. `sent_email.created` / `sent_email.updated`: a send appeared or changed; `status` is its current status, and several changes between two reads collapse into one entry. Created is a hint (first committed after the cursor): treat both as upsert, including `sent_email.updated` for an id you have not seen. `sent_email.deleted`: the send was deleted. `thread.read_state_changed`: the signed-in member's read cursor for `thread_id` moved, on any device. `thread.mute_changed`: `thread_id` was muted or unmuted for an address this credential reads, which changes the `muted` flag and `exclude_muted` results of its mail. `agent.changed`: an agent in the directory changed (connected, disconnected, renamed, hidden, listed, presence extended, receiver live). `access.changed`: this credential's own access changed; rebuild every snapshot from `since=start`."
|
|
12379
|
+
},
|
|
12380
|
+
"email_id": {
|
|
12381
|
+
"type": ["string", "null"],
|
|
12382
|
+
"format": "uuid",
|
|
12383
|
+
"description": "email.* kinds."
|
|
12384
|
+
},
|
|
12385
|
+
"sent_email_id": {
|
|
12386
|
+
"type": ["string", "null"],
|
|
12387
|
+
"format": "uuid",
|
|
12388
|
+
"description": "sent_email.* kinds."
|
|
12389
|
+
},
|
|
12390
|
+
"thread_id": {
|
|
12391
|
+
"type": ["string", "null"],
|
|
12392
|
+
"format": "uuid",
|
|
12393
|
+
"description": "The conversation, when known. Null for a connected agent reading a send, or its deletion, that it received but did not send, matching what GET /sent-emails withholds. Inbound mail is announced when it becomes visible, which can be before its conversation is resolved, so `email.visible` can carry null; resolving or changing the conversation is always announced afterwards as `email.updated` with the `thread_id`."
|
|
12394
|
+
},
|
|
12395
|
+
"status": {
|
|
12396
|
+
"type": ["string", "null"],
|
|
12397
|
+
"description": "sent_email.created and sent_email.updated: the send's current status."
|
|
12398
|
+
},
|
|
12399
|
+
"agent_address": {
|
|
12400
|
+
"type": ["string", "null"],
|
|
12401
|
+
"description": "agent.changed: the agent's address."
|
|
12402
|
+
},
|
|
12403
|
+
"reason": {
|
|
12404
|
+
"type": ["string", "null"],
|
|
12405
|
+
"enum": [
|
|
12406
|
+
"membership",
|
|
12407
|
+
"agent_connection",
|
|
12408
|
+
null
|
|
12409
|
+
],
|
|
12410
|
+
"description": "access.changed: `membership` (the member's role or membership) or `agent_connection` (this agent connection's status, owner or generation)."
|
|
12411
|
+
},
|
|
12412
|
+
"changed_at": {
|
|
12413
|
+
"type": "string",
|
|
12414
|
+
"format": "date-time",
|
|
12415
|
+
"description": "The commit position in microsecond UTC form. Orders the feed; not a business timestamp."
|
|
12416
|
+
},
|
|
12417
|
+
"message": {
|
|
12418
|
+
"oneOf": [{ "$ref": "#/components/schemas/ChangeMessage" }, { "type": "null" }],
|
|
12419
|
+
"description": "Present only when the request sets `expand=message`. For `email.visible`, `email.updated`, `sent_email.created`, `sent_email.updated`: the message as it reads at the time of this response (not at the time of the change), exactly as the list endpoints return it, marked by `direction`. Null for every other kind, and when the message is no longer readable by this credential (deleted, or withdrawn from view after the change)."
|
|
12420
|
+
}
|
|
12421
|
+
}
|
|
12422
|
+
},
|
|
12423
|
+
"ChangePage": {
|
|
12424
|
+
"type": "object",
|
|
12425
|
+
"additionalProperties": false,
|
|
12426
|
+
"required": [
|
|
12427
|
+
"changes",
|
|
12428
|
+
"next_cursor",
|
|
12429
|
+
"has_more",
|
|
12430
|
+
"baseline"
|
|
12431
|
+
],
|
|
12432
|
+
"properties": {
|
|
12433
|
+
"changes": {
|
|
12434
|
+
"type": "array",
|
|
12435
|
+
"items": { "$ref": "#/components/schemas/Change" },
|
|
12436
|
+
"description": "Oldest first, in commit order."
|
|
12437
|
+
},
|
|
12438
|
+
"next_cursor": {
|
|
12439
|
+
"type": "string",
|
|
12440
|
+
"description": "Always present. Send it unchanged as `since` next time, and store it only after applying the page. It is exactly the `since` you sent when nothing at all happened after it; it moves past changes this credential cannot see, so the page can be empty with a new cursor."
|
|
12441
|
+
},
|
|
12442
|
+
"has_more": {
|
|
12443
|
+
"type": "boolean",
|
|
12444
|
+
"description": "More changes are already pending: call again at once, without waiting."
|
|
12445
|
+
},
|
|
12446
|
+
"baseline": {
|
|
12447
|
+
"type": "boolean",
|
|
12448
|
+
"description": "True only for a `since=start` request, whose page is always empty."
|
|
12449
|
+
}
|
|
12450
|
+
}
|
|
12451
|
+
},
|
|
12452
|
+
"ChangeMessage": {
|
|
12453
|
+
"description": "A message inlined by `expand=message`. `direction` `inbound` is a GET /emails row; `outbound` is a GET /sent-emails row.",
|
|
12454
|
+
"oneOf": [{ "$ref": "#/components/schemas/ChangeInboundMessage" }, { "$ref": "#/components/schemas/ChangeOutboundMessage" }],
|
|
12455
|
+
"discriminator": {
|
|
12456
|
+
"propertyName": "direction",
|
|
12457
|
+
"mapping": {
|
|
12458
|
+
"inbound": "#/components/schemas/ChangeInboundMessage",
|
|
12459
|
+
"outbound": "#/components/schemas/ChangeOutboundMessage"
|
|
12460
|
+
}
|
|
12461
|
+
}
|
|
12462
|
+
},
|
|
12463
|
+
"ChangeInboundMessage": {
|
|
12464
|
+
"description": "An inbound message inlined by `expand=message`, as GET /emails returns it.",
|
|
12465
|
+
"allOf": [{ "$ref": "#/components/schemas/EmailSummary" }, {
|
|
12466
|
+
"type": "object",
|
|
12467
|
+
"required": ["direction"],
|
|
12468
|
+
"properties": { "direction": {
|
|
12469
|
+
"type": "string",
|
|
12470
|
+
"enum": ["inbound"]
|
|
12471
|
+
} }
|
|
12472
|
+
}]
|
|
12473
|
+
},
|
|
12474
|
+
"ChangeOutboundMessage": {
|
|
12475
|
+
"description": "A sent message inlined by `expand=message`, as GET /sent-emails returns it.",
|
|
12476
|
+
"allOf": [{ "$ref": "#/components/schemas/SentEmailSummary" }, {
|
|
12477
|
+
"type": "object",
|
|
12478
|
+
"required": ["direction"],
|
|
12479
|
+
"properties": { "direction": {
|
|
12480
|
+
"type": "string",
|
|
12481
|
+
"enum": ["outbound"]
|
|
12482
|
+
} }
|
|
12483
|
+
}]
|
|
12484
|
+
},
|
|
11595
12485
|
"EmailSummary": {
|
|
11596
12486
|
"type": "object",
|
|
11597
12487
|
"properties": {
|
|
@@ -11669,6 +12559,14 @@ const openapiDocument = {
|
|
|
11669
12559
|
"items": { "type": "string" },
|
|
11670
12560
|
"description": "Why `automated` is true, in rule order; empty when it is false.\nCurrent values: `null_envelope_sender`, `no_identifiable_sender`,\n`own_address`, `mailer_daemon`, `auto_submitted`, `precedence`,\n`list_unsubscribe`, `list_id`, `auto_response_suppress`,\n`failed_recipients`, `report`. Treat an unfamiliar value as a\nreason added after your client was built.\n"
|
|
11671
12561
|
},
|
|
12562
|
+
"from_verified": {
|
|
12563
|
+
"type": ["boolean", "null"],
|
|
12564
|
+
"description": "True when the From header address, `from_header_email`, passed DMARC for its domain on arrival (aligned SPF or DKIM); decided once, at receipt. It is about the From header, never the envelope: `sender`, the `from` parameter and `from:` term, and the change feed's `address` match the envelope sender, which can differ. It proves the domain, not the mailbox. False with no From header address, when authentication failed or was not attempted (a delivery between Primitive mailboxes counts only when it passed DMARC too), and for mail stored before the verdict was recorded. Null on sent rows in search results. Not a spam or safety judgement."
|
|
12565
|
+
},
|
|
12566
|
+
"from_header_email": {
|
|
12567
|
+
"type": ["string", "null"],
|
|
12568
|
+
"description": "The From header address, lowercased, that `from_verified` is about; null when the message had none. Unlike `sender` it is never the envelope address."
|
|
12569
|
+
},
|
|
11672
12570
|
"repeat": {
|
|
11673
12571
|
"type": ["object", "null"],
|
|
11674
12572
|
"description": "Set when Primitive sent this message as part of a repeating send. Resolved from Primitive's own records, never from the message content. Null on every other message and on servers that predate repeating sends.",
|
|
@@ -11735,6 +12633,38 @@ const openapiDocument = {
|
|
|
11735
12633
|
"from_name": {
|
|
11736
12634
|
"type": ["string", "null"],
|
|
11737
12635
|
"description": "Display name from the `From:` header, decoded for display:\nRFC 2047 encoded words are decoded in their declared charset\nand folded lines are joined (e.g. `Dana Rúiz` for\n`=?UTF-8?Q?Dana_R=C3=BAiz?= <dana@example.com>`). Null when\nthe header carries no display name, and may be absent; treat\na missing value the same as null.\n\nFor display only: the sender chooses this name freely, so\nidentify the sender by `from_email`, never by this name.\n"
|
|
12636
|
+
},
|
|
12637
|
+
"body_text": {
|
|
12638
|
+
"type": ["string", "null"],
|
|
12639
|
+
"description": "With `include=body_text`: the plain-text body, cut as `truncated_fields` says."
|
|
12640
|
+
},
|
|
12641
|
+
"body_html": {
|
|
12642
|
+
"type": ["string", "null"],
|
|
12643
|
+
"description": "With `include=body_html`: the HTML body, cut as `truncated_fields` says."
|
|
12644
|
+
},
|
|
12645
|
+
"headers": {
|
|
12646
|
+
"anyOf": [{ "$ref": "#/components/schemas/IncludedMessageHeaders" }, { "type": "null" }],
|
|
12647
|
+
"description": "With `include=headers`: stored addressing and threading headers."
|
|
12648
|
+
},
|
|
12649
|
+
"attachments": {
|
|
12650
|
+
"type": "array",
|
|
12651
|
+
"items": {
|
|
12652
|
+
"type": "object",
|
|
12653
|
+
"additionalProperties": true
|
|
12654
|
+
},
|
|
12655
|
+
"description": "With `include=attachments`: attachment metadata as the detail read reports it. Never the bytes."
|
|
12656
|
+
},
|
|
12657
|
+
"truncated": {
|
|
12658
|
+
"type": "boolean",
|
|
12659
|
+
"description": "With `include`: true when a body on this row was cut or left out."
|
|
12660
|
+
},
|
|
12661
|
+
"truncated_fields": {
|
|
12662
|
+
"type": "array",
|
|
12663
|
+
"items": {
|
|
12664
|
+
"type": "string",
|
|
12665
|
+
"enum": ["body_text", "body_html"]
|
|
12666
|
+
},
|
|
12667
|
+
"description": "With `include`: the body fields on this row that were cut or left out."
|
|
11738
12668
|
}
|
|
11739
12669
|
},
|
|
11740
12670
|
"required": [
|
|
@@ -11793,8 +12723,9 @@ const openapiDocument = {
|
|
|
11793
12723
|
"direction": {
|
|
11794
12724
|
"type": "string",
|
|
11795
12725
|
"enum": ["inbound", "outbound"],
|
|
11796
|
-
"description": "Which side of the conversation the result is on: `inbound` for mail received by the organization, `outbound` for mail it sent.
|
|
11797
|
-
}
|
|
12726
|
+
"description": "Which side of the conversation the result is on: `inbound` for mail received by the organization, `outbound` for mail it sent. Only `direction=outbound` or `direction=any` returns `outbound` results. Treat an unfamiliar value as one added after your client was built. An `outbound` result also carries `delivery_status`."
|
|
12727
|
+
},
|
|
12728
|
+
"delivery_status": { "$ref": "#/components/schemas/SentEmailStatus" }
|
|
11798
12729
|
},
|
|
11799
12730
|
"required": [
|
|
11800
12731
|
"attachment_count",
|
|
@@ -11830,6 +12761,14 @@ const openapiDocument = {
|
|
|
11830
12761
|
"received_at_asc"
|
|
11831
12762
|
],
|
|
11832
12763
|
"description": "Sort mode used for the result page."
|
|
12764
|
+
},
|
|
12765
|
+
"total_unavailable": {
|
|
12766
|
+
"type": "boolean",
|
|
12767
|
+
"description": "Present and `true` when a count was requested but did not fit the search time budget. The page in `data` and `meta.cursor` are still returned (the page may itself be partial, see `partial`); only `meta.total` is missing (null). Retry with narrower filters, or use `count=false` and `meta.cursor`."
|
|
12768
|
+
},
|
|
12769
|
+
"partial": {
|
|
12770
|
+
"type": "boolean",
|
|
12771
|
+
"description": "Present and `true` when the page stopped at the search time bound before it filled. This happens when the filters reject most of what the text matches, for example a one-letter `prefix` search with a `from` that matches little or nothing. `data` then holds fewer than `limit` results, possibly none, and `meta.cursor` continues the search after the last email examined. Every result returned is correct and in order; keep following `meta.cursor` until it is null to read the rest. A page shorter than `limit` is therefore not the end of the results: only a null `meta.cursor` is."
|
|
11833
12772
|
}
|
|
11834
12773
|
},
|
|
11835
12774
|
"required": [
|
|
@@ -12035,6 +12974,14 @@ const openapiDocument = {
|
|
|
12035
12974
|
"items": { "type": "string" },
|
|
12036
12975
|
"description": "Why `automated` is true, in rule order; empty when it is false.\nCurrent values: `null_envelope_sender`, `no_identifiable_sender`,\n`own_address`, `mailer_daemon`, `auto_submitted`, `precedence`,\n`list_unsubscribe`, `list_id`, `auto_response_suppress`,\n`failed_recipients`, `report`. Treat an unfamiliar value as a\nreason added after your client was built.\n"
|
|
12037
12976
|
},
|
|
12977
|
+
"from_verified": {
|
|
12978
|
+
"type": "boolean",
|
|
12979
|
+
"description": "Whether the From header domain was authenticated when the message arrived. See EmailSummary.from_verified."
|
|
12980
|
+
},
|
|
12981
|
+
"from_header_email": {
|
|
12982
|
+
"type": ["string", "null"],
|
|
12983
|
+
"description": "The From header address, lowercased, that `from_verified` is about; null when the message had none. Unlike `sender` it is never the envelope address."
|
|
12984
|
+
},
|
|
12038
12985
|
"repeat": {
|
|
12039
12986
|
"type": ["object", "null"],
|
|
12040
12987
|
"description": "Set when Primitive sent this message as part of a repeating send. Resolved from Primitive's own records, never from the message content. Null on every other message and on servers that predate repeating sends.",
|
|
@@ -12493,6 +13440,11 @@ const openapiDocument = {
|
|
|
12493
13440
|
"cursor": {
|
|
12494
13441
|
"type": "string",
|
|
12495
13442
|
"description": "Present only on a `since` read: the position to send as\n`since` on the next read. An opaque string: send it back\nverbatim and do not parse it or build one.\n"
|
|
13443
|
+
},
|
|
13444
|
+
"latest_message_id": {
|
|
13445
|
+
"type": ["string", "null"],
|
|
13446
|
+
"format": "uuid",
|
|
13447
|
+
"description": "The id to pass as `if_latest_message_id` when replying in this\nconversation: the message that joined the thread last among\nthose this credential can read, by the rule a conditional send\nchecks. It can differ from the last message in `messages`, which\nis ordered by timestamp, when mail joined the thread after a\nnewer-timestamped message. Present on a full read and a window\nread of a threaded conversation; null when no message in the\nthread is visible; absent on a `since` delta.\n"
|
|
12496
13448
|
}
|
|
12497
13449
|
},
|
|
12498
13450
|
"required": [
|
|
@@ -12747,6 +13699,32 @@ const openapiDocument = {
|
|
|
12747
13699
|
"type": "string",
|
|
12748
13700
|
"format": "date-time",
|
|
12749
13701
|
"description": "Optional future execution time (ISO 8601). When set, the\nsend is recorded with status `scheduled` and executed at\nthe requested time instead of immediately. Must be in the\nfuture and at most 30 days out. Incompatible with `wait`\n(a scheduled send resolves after this request completes)\nand with `attachments` / `payload_attachments` (not yet\nsupported on scheduled sends). Reschedule via PATCH\n/sent-emails/{id}; cancel via /sent-emails/{id}/cancel.\nAccepts `Z` or a numeric UTC offset\n(`2026-10-09T17:00:00Z`, `2026-10-09T13:00:00-04:00`); an\noffset is converted to UTC, so the stored and echoed value\nis UTC. A time with no zone, or a bare date, is rejected.\n"
|
|
13702
|
+
},
|
|
13703
|
+
"headers": {
|
|
13704
|
+
"type": "object",
|
|
13705
|
+
"maxProperties": 25,
|
|
13706
|
+
"propertyNames": {
|
|
13707
|
+
"type": "string",
|
|
13708
|
+
"minLength": 1,
|
|
13709
|
+
"maxLength": 64,
|
|
13710
|
+
"pattern": "^[A-Za-z0-9]+(-[A-Za-z0-9]+)*$"
|
|
13711
|
+
},
|
|
13712
|
+
"additionalProperties": {
|
|
13713
|
+
"type": "string",
|
|
13714
|
+
"minLength": 1,
|
|
13715
|
+
"maxLength": 200,
|
|
13716
|
+
"pattern": "^[\\x20-\\x7E]+$"
|
|
13717
|
+
},
|
|
13718
|
+
"description": "Extra headers, as `{ \"Name\": \"value\" }`, at most 25. A name is one of `Auto-Submitted`, `Precedence`, `X-Auto-Response-Suppress`, `X-Primitive-Interaction`, `X-Primitive-Interaction-Ref`, or a custom `X-` name of letters, digits and hyphens that is not credential-like (a word such as `auth`, `token`, `key`, `secret`, `session` or `bearer`, joined forms included) and not in a reserved family: X-Primitive*, X-Original-*, X-Originating-*, X-Forwarded-*, X-Received*, X-Authenticated*, X-Envelope-*, X-Delivered-To, X-Apparently-To, X-Failed-Recipients, X-Loop, X-Priority, X-MSMail-*, X-MS-*, X-Microsoft-*, X-Forefront-*, X-Exchange-*, X-Google-*, X-GM-*, X-Apple-*, X-Yahoo-*, X-YMail-*, X-AOL-*, X-Spam*, X-Virus-*, X-Proofpoint-*, X-Barracuda-*, X-Rspamd-*, X-Mimecast-*, X-IronPort-*, X-Sophos-*, X-Report-Abuse*, X-CSA-*. Names compare case-insensitively (`_` and `.` count as `-` for families); a repeated or any other name is a `validation_error`. Values: 1 to 200 printable ASCII characters. Custom headers are sent as spelled, are not DKIM-signed, and can be searched with `header` on `GET /sent-emails`.",
|
|
13719
|
+
"example": {
|
|
13720
|
+
"X-Campaign-Id": "spring-sale",
|
|
13721
|
+
"Auto-Submitted": "auto-generated"
|
|
13722
|
+
}
|
|
13723
|
+
},
|
|
13724
|
+
"if_latest_message_id": {
|
|
13725
|
+
"type": "string",
|
|
13726
|
+
"format": "uuid",
|
|
13727
|
+
"description": "Send only if this message id is still the newest message in the thread this send joins, as this credential can see it. Newest means the message that joined the thread last, in the order messages were attached to it, not by timestamp: a message accepted earlier but attached later is newer. Messages stored before join order was recorded count as older than every message that has one, and among themselves sort by timestamp (received_at for mail received, created_at for mail sent), ties going to the larger id. Pass latest_message_id from GET /emails/{id}/conversation: it is read by the same rule, whereas the last message listed there is ordered by timestamp and can differ when mail joined the thread late. An organization credential sees every message in the thread; a mailbox-scoped agent connection key sees only mail received at its address and mail sent from it. Mail awaiting sender authentication never counts. Otherwise nothing is sent and the response is 409 latest_message_mismatch with error.details.latest_message_id, latest_message_at and thread_id. If in_reply_to resolves to no single thread this credential can check, the response is 422 latest_message_thread_unresolved; that does not change on retry. If the thread stays locked by another writer for longer than the send waits, the response is 503 thread_busy with Retry-After; nothing was sent and the same request can be retried. Checked inside the send transaction under a lock on the thread, so two sends conditioned on the same id cannot both go out. The value is part of the request a replay is matched on: a replay of an already accepted request with the same idempotency key, or the identical request within the automatic deduplication window, returns the original result without checking again, and a request that differs only in if_latest_message_id is not a replay. On /send-mail it requires in_reply_to; it cannot be combined with scheduled_at or repeat, because it is checked when the request is accepted."
|
|
12750
13728
|
}
|
|
12751
13729
|
},
|
|
12752
13730
|
"required": [
|
|
@@ -12970,6 +13948,63 @@ const openapiDocument = {
|
|
|
12970
13948
|
"user_id",
|
|
12971
13949
|
"name"
|
|
12972
13950
|
]
|
|
13951
|
+
},
|
|
13952
|
+
"body_text": {
|
|
13953
|
+
"type": ["string", "null"],
|
|
13954
|
+
"description": "Plain-text body sent on the wire. Null when the\nsend carried only an HTML body, or when bodies have\nbeen discarded post-send (`content_discarded_at`\nset).\n"
|
|
13955
|
+
},
|
|
13956
|
+
"body_html": {
|
|
13957
|
+
"type": ["string", "null"],
|
|
13958
|
+
"description": "HTML body sent on the wire. Null when the send\ncarried only a plain-text body, or when bodies\nhave been discarded post-send.\n"
|
|
13959
|
+
},
|
|
13960
|
+
"headers": {
|
|
13961
|
+
"anyOf": [{ "$ref": "#/components/schemas/IncludedMessageHeaders" }, { "type": "null" }],
|
|
13962
|
+
"description": "With `include=headers`: stored addressing and threading headers."
|
|
13963
|
+
},
|
|
13964
|
+
"attachments": {
|
|
13965
|
+
"type": "array",
|
|
13966
|
+
"description": "Metadata for submitted inline attachments; download availability is reported separately. Metadata may exist when the archive is unavailable, and legacy sends may have no metadata. Offloaded payload files are not included in the inline archive.",
|
|
13967
|
+
"items": {
|
|
13968
|
+
"type": "object",
|
|
13969
|
+
"description": "Metadata for one submitted inline attachment.",
|
|
13970
|
+
"additionalProperties": true,
|
|
13971
|
+
"properties": {
|
|
13972
|
+
"filename": { "type": ["string", "null"] },
|
|
13973
|
+
"content_type": { "type": "string" },
|
|
13974
|
+
"size_bytes": {
|
|
13975
|
+
"type": "integer",
|
|
13976
|
+
"minimum": 0,
|
|
13977
|
+
"maximum": 9007199254740991
|
|
13978
|
+
},
|
|
13979
|
+
"sha256": { "type": "string" },
|
|
13980
|
+
"part_index": {
|
|
13981
|
+
"type": "integer",
|
|
13982
|
+
"minimum": 0,
|
|
13983
|
+
"maximum": 9007199254740991
|
|
13984
|
+
},
|
|
13985
|
+
"tar_path": { "type": "string" }
|
|
13986
|
+
},
|
|
13987
|
+
"required": [
|
|
13988
|
+
"filename",
|
|
13989
|
+
"content_type",
|
|
13990
|
+
"size_bytes",
|
|
13991
|
+
"sha256",
|
|
13992
|
+
"part_index",
|
|
13993
|
+
"tar_path"
|
|
13994
|
+
]
|
|
13995
|
+
}
|
|
13996
|
+
},
|
|
13997
|
+
"truncated": {
|
|
13998
|
+
"type": "boolean",
|
|
13999
|
+
"description": "With `include`: true when a body on this row was cut or left out."
|
|
14000
|
+
},
|
|
14001
|
+
"truncated_fields": {
|
|
14002
|
+
"type": "array",
|
|
14003
|
+
"items": {
|
|
14004
|
+
"type": "string",
|
|
14005
|
+
"enum": ["body_text", "body_html"]
|
|
14006
|
+
},
|
|
14007
|
+
"description": "With `include`: the body fields on this row that were cut or left out."
|
|
12973
14008
|
}
|
|
12974
14009
|
},
|
|
12975
14010
|
"required": [
|
|
@@ -13556,7 +14591,33 @@ const openapiDocument = {
|
|
|
13556
14591
|
"description": "Inline attachments for this reply. Use https://api.primitive.dev/v1 for replies with attachments. Combined raw decoded attachment bytes must be at most 31457280.",
|
|
13557
14592
|
"items": { "$ref": "#/components/schemas/SendMailAttachment" }
|
|
13558
14593
|
},
|
|
13559
|
-
"repeat": { "$ref": "#/components/schemas/RepeatInput" }
|
|
14594
|
+
"repeat": { "$ref": "#/components/schemas/RepeatInput" },
|
|
14595
|
+
"headers": {
|
|
14596
|
+
"type": "object",
|
|
14597
|
+
"maxProperties": 25,
|
|
14598
|
+
"propertyNames": {
|
|
14599
|
+
"type": "string",
|
|
14600
|
+
"minLength": 1,
|
|
14601
|
+
"maxLength": 64,
|
|
14602
|
+
"pattern": "^[A-Za-z0-9]+(-[A-Za-z0-9]+)*$"
|
|
14603
|
+
},
|
|
14604
|
+
"additionalProperties": {
|
|
14605
|
+
"type": "string",
|
|
14606
|
+
"minLength": 1,
|
|
14607
|
+
"maxLength": 200,
|
|
14608
|
+
"pattern": "^[\\x20-\\x7E]+$"
|
|
14609
|
+
},
|
|
14610
|
+
"description": "Extra headers, as `{ \"Name\": \"value\" }`, at most 25. A name is one of `Auto-Submitted`, `Precedence`, `X-Auto-Response-Suppress`, `X-Primitive-Interaction`, `X-Primitive-Interaction-Ref`, or a custom `X-` name of letters, digits and hyphens that is not credential-like (a word such as `auth`, `token`, `key`, `secret`, `session` or `bearer`, joined forms included) and not in a reserved family: X-Primitive*, X-Original-*, X-Originating-*, X-Forwarded-*, X-Received*, X-Authenticated*, X-Envelope-*, X-Delivered-To, X-Apparently-To, X-Failed-Recipients, X-Loop, X-Priority, X-MSMail-*, X-MS-*, X-Microsoft-*, X-Forefront-*, X-Exchange-*, X-Google-*, X-GM-*, X-Apple-*, X-Yahoo-*, X-YMail-*, X-AOL-*, X-Spam*, X-Virus-*, X-Proofpoint-*, X-Barracuda-*, X-Rspamd-*, X-Mimecast-*, X-IronPort-*, X-Sophos-*, X-Report-Abuse*, X-CSA-*. Names compare case-insensitively (`_` and `.` count as `-` for families); a repeated or any other name is a `validation_error`. Values: 1 to 200 printable ASCII characters. Custom headers are sent as spelled, are not DKIM-signed, and can be searched with `header` on `GET /sent-emails`.",
|
|
14611
|
+
"example": {
|
|
14612
|
+
"X-Campaign-Id": "spring-sale",
|
|
14613
|
+
"Auto-Submitted": "auto-generated"
|
|
14614
|
+
}
|
|
14615
|
+
},
|
|
14616
|
+
"if_latest_message_id": {
|
|
14617
|
+
"type": "string",
|
|
14618
|
+
"format": "uuid",
|
|
14619
|
+
"description": "Send only if this message id is still the newest message in the thread this send joins, as this credential can see it. Newest means the message that joined the thread last, in the order messages were attached to it, not by timestamp: a message accepted earlier but attached later is newer. Messages stored before join order was recorded count as older than every message that has one, and among themselves sort by timestamp (received_at for mail received, created_at for mail sent), ties going to the larger id. Pass latest_message_id from GET /emails/{id}/conversation: it is read by the same rule, whereas the last message listed there is ordered by timestamp and can differ when mail joined the thread late. An organization credential sees every message in the thread; a mailbox-scoped agent connection key sees only mail received at its address and mail sent from it. Mail awaiting sender authentication never counts. Otherwise nothing is sent and the response is 409 latest_message_mismatch with error.details.latest_message_id, latest_message_at and thread_id. If in_reply_to resolves to no single thread this credential can check, the response is 422 latest_message_thread_unresolved; that does not change on retry. If the thread stays locked by another writer for longer than the send waits, the response is 503 thread_busy with Retry-After; nothing was sent and the same request can be retried. Checked inside the send transaction under a lock on the thread, so two sends conditioned on the same id cannot both go out. The value is part of the request a replay is matched on: a replay of an already accepted request with the same idempotency key, or the identical request within the automatic deduplication window, returns the original result without checking again, and a request that differs only in if_latest_message_id is not a replay. On /send-mail it requires in_reply_to; it cannot be combined with scheduled_at or repeat, because it is checked when the request is accepted."
|
|
14620
|
+
}
|
|
13560
14621
|
}
|
|
13561
14622
|
},
|
|
13562
14623
|
"RepeatInput": {
|
|
@@ -19828,6 +20889,855 @@ const operationManifest = [
|
|
|
19828
20889
|
"tag": "Agent Networks",
|
|
19829
20890
|
"tagCommand": "agent-networks"
|
|
19830
20891
|
},
|
|
20892
|
+
{
|
|
20893
|
+
"binaryResponse": false,
|
|
20894
|
+
"bodyRequired": false,
|
|
20895
|
+
"command": "list-changes",
|
|
20896
|
+
"description": "One feed of small invalidations (ids and kinds) for everything an app keeps a snapshot of: inbound mail, sent mail, the member's conversation read state, the agent directory and presence, and the credential's own access. Non-consumptive and replayable: any number of devices can read the same cursor independently.\n\nStart with `since=start` (or no `since`): it returns an empty page whose `next_cursor` is a baseline. Take it BEFORE the initial list reads; every change that commits after it is delivered, including ones those reads already reflect. Then call with `since=<next_cursor>&wait=20` in a loop. Changes come oldest first in commit order, at least once: a write that commits late is never skipped, because positions are assigned at commit.\n\nEvery change is scoped to what this credential's full reads would show: a connected agent sees only its own mail, its own sends and sends it provably received; mail held for sender authentication is announced only when released; read state reaches only the member it belongs to; agent changes reach members and agents allowed to view the network; organization API keys receive no agent changes. Function credentials cannot call this.\n\nChanges are kept 7 days. A cursor answers 410 `cursor_expired` only when changes after it were pruned (an idle cursor stays valid); rebuild the snapshots from `since=start`.",
|
|
20897
|
+
"hasJsonBody": false,
|
|
20898
|
+
"method": "GET",
|
|
20899
|
+
"operationId": "listChanges",
|
|
20900
|
+
"path": "/changes",
|
|
20901
|
+
"pathParams": [],
|
|
20902
|
+
"queryParams": [
|
|
20903
|
+
{
|
|
20904
|
+
"description": "`start` (the default) for a baseline, otherwise the previous `next_cursor`, unchanged.",
|
|
20905
|
+
"enum": null,
|
|
20906
|
+
"name": "since",
|
|
20907
|
+
"required": false,
|
|
20908
|
+
"type": "string"
|
|
20909
|
+
},
|
|
20910
|
+
{
|
|
20911
|
+
"default": 0,
|
|
20912
|
+
"description": "Seconds to hold a caught-up request open. It returns as soon as a change this credential can see commits, else an empty page at the deadline. Ignored for a baseline request.",
|
|
20913
|
+
"enum": null,
|
|
20914
|
+
"maximum": 25,
|
|
20915
|
+
"minimum": 0,
|
|
20916
|
+
"name": "wait",
|
|
20917
|
+
"required": false,
|
|
20918
|
+
"type": "integer"
|
|
20919
|
+
},
|
|
20920
|
+
{
|
|
20921
|
+
"default": 100,
|
|
20922
|
+
"description": "Maximum changes in one page.",
|
|
20923
|
+
"enum": null,
|
|
20924
|
+
"maximum": 100,
|
|
20925
|
+
"minimum": 1,
|
|
20926
|
+
"name": "limit",
|
|
20927
|
+
"required": false,
|
|
20928
|
+
"type": "integer"
|
|
20929
|
+
},
|
|
20930
|
+
{
|
|
20931
|
+
"description": "`message` adds `message` to every change: the current list row of the message a message change names, so a reader needs no follow-up read. Ignored for a baseline request.",
|
|
20932
|
+
"enum": ["message"],
|
|
20933
|
+
"name": "expand",
|
|
20934
|
+
"required": false,
|
|
20935
|
+
"type": "string"
|
|
20936
|
+
},
|
|
20937
|
+
{
|
|
20938
|
+
"description": "Up to 20 email addresses, comma-separated (or the parameter repeated). Keeps only the message changes whose message is to or from one of them, matched case-insensitively. Inbound mail matches on the address it was delivered to, the envelope sender and the From header address; sent mail on the From address and the To and Cc addresses, and Bcc only for a credential that can read the sender side. Deletions, thread, agent and access changes are never filtered, because a deleted message's addresses can no longer be read: ignore a deletion for an id you do not hold. The cursor still moves past every change the filter drops, so a filtered page can be empty with a new cursor.",
|
|
20939
|
+
"enum": null,
|
|
20940
|
+
"name": "address",
|
|
20941
|
+
"required": false,
|
|
20942
|
+
"type": "string"
|
|
20943
|
+
}
|
|
20944
|
+
],
|
|
20945
|
+
"requestSchema": null,
|
|
20946
|
+
"responseSchema": {
|
|
20947
|
+
"type": "object",
|
|
20948
|
+
"additionalProperties": false,
|
|
20949
|
+
"required": [
|
|
20950
|
+
"changes",
|
|
20951
|
+
"next_cursor",
|
|
20952
|
+
"has_more",
|
|
20953
|
+
"baseline"
|
|
20954
|
+
],
|
|
20955
|
+
"properties": {
|
|
20956
|
+
"changes": {
|
|
20957
|
+
"type": "array",
|
|
20958
|
+
"items": {
|
|
20959
|
+
"type": "object",
|
|
20960
|
+
"additionalProperties": false,
|
|
20961
|
+
"description": "One invalidation: what changed, by id, never its content. Every field is present; fields that do not apply to the kind are null. Apply idempotently: a change can repeat.",
|
|
20962
|
+
"required": [
|
|
20963
|
+
"kind",
|
|
20964
|
+
"email_id",
|
|
20965
|
+
"sent_email_id",
|
|
20966
|
+
"thread_id",
|
|
20967
|
+
"status",
|
|
20968
|
+
"agent_address",
|
|
20969
|
+
"reason",
|
|
20970
|
+
"changed_at"
|
|
20971
|
+
],
|
|
20972
|
+
"properties": {
|
|
20973
|
+
"kind": {
|
|
20974
|
+
"type": "string",
|
|
20975
|
+
"enum": [
|
|
20976
|
+
"email.visible",
|
|
20977
|
+
"email.updated",
|
|
20978
|
+
"email.deleted",
|
|
20979
|
+
"sent_email.created",
|
|
20980
|
+
"sent_email.updated",
|
|
20981
|
+
"sent_email.deleted",
|
|
20982
|
+
"thread.read_state_changed",
|
|
20983
|
+
"thread.mute_changed",
|
|
20984
|
+
"agent.changed",
|
|
20985
|
+
"access.changed"
|
|
20986
|
+
],
|
|
20987
|
+
"description": "`email.visible`: an inbound email became visible to this credential (arrived, or its sender-authentication hold was released). `email.updated`: something a list row or conversation shows changed. `email.deleted`: gone, or withdrawn from view. `sent_email.created` / `sent_email.updated`: a send appeared or changed; `status` is its current status, and several changes between two reads collapse into one entry. Created is a hint (first committed after the cursor): treat both as upsert, including `sent_email.updated` for an id you have not seen. `sent_email.deleted`: the send was deleted. `thread.read_state_changed`: the signed-in member's read cursor for `thread_id` moved, on any device. `thread.mute_changed`: `thread_id` was muted or unmuted for an address this credential reads, which changes the `muted` flag and `exclude_muted` results of its mail. `agent.changed`: an agent in the directory changed (connected, disconnected, renamed, hidden, listed, presence extended, receiver live). `access.changed`: this credential's own access changed; rebuild every snapshot from `since=start`."
|
|
20988
|
+
},
|
|
20989
|
+
"email_id": {
|
|
20990
|
+
"type": ["string", "null"],
|
|
20991
|
+
"format": "uuid",
|
|
20992
|
+
"description": "email.* kinds."
|
|
20993
|
+
},
|
|
20994
|
+
"sent_email_id": {
|
|
20995
|
+
"type": ["string", "null"],
|
|
20996
|
+
"format": "uuid",
|
|
20997
|
+
"description": "sent_email.* kinds."
|
|
20998
|
+
},
|
|
20999
|
+
"thread_id": {
|
|
21000
|
+
"type": ["string", "null"],
|
|
21001
|
+
"format": "uuid",
|
|
21002
|
+
"description": "The conversation, when known. Null for a connected agent reading a send, or its deletion, that it received but did not send, matching what GET /sent-emails withholds. Inbound mail is announced when it becomes visible, which can be before its conversation is resolved, so `email.visible` can carry null; resolving or changing the conversation is always announced afterwards as `email.updated` with the `thread_id`."
|
|
21003
|
+
},
|
|
21004
|
+
"status": {
|
|
21005
|
+
"type": ["string", "null"],
|
|
21006
|
+
"description": "sent_email.created and sent_email.updated: the send's current status."
|
|
21007
|
+
},
|
|
21008
|
+
"agent_address": {
|
|
21009
|
+
"type": ["string", "null"],
|
|
21010
|
+
"description": "agent.changed: the agent's address."
|
|
21011
|
+
},
|
|
21012
|
+
"reason": {
|
|
21013
|
+
"type": ["string", "null"],
|
|
21014
|
+
"enum": [
|
|
21015
|
+
"membership",
|
|
21016
|
+
"agent_connection",
|
|
21017
|
+
null
|
|
21018
|
+
],
|
|
21019
|
+
"description": "access.changed: `membership` (the member's role or membership) or `agent_connection` (this agent connection's status, owner or generation)."
|
|
21020
|
+
},
|
|
21021
|
+
"changed_at": {
|
|
21022
|
+
"type": "string",
|
|
21023
|
+
"format": "date-time",
|
|
21024
|
+
"description": "The commit position in microsecond UTC form. Orders the feed; not a business timestamp."
|
|
21025
|
+
},
|
|
21026
|
+
"message": {
|
|
21027
|
+
"oneOf": [{
|
|
21028
|
+
"description": "A message inlined by `expand=message`. `direction` `inbound` is a GET /emails row; `outbound` is a GET /sent-emails row.",
|
|
21029
|
+
"oneOf": [{
|
|
21030
|
+
"description": "An inbound message inlined by `expand=message`, as GET /emails returns it.",
|
|
21031
|
+
"allOf": [{
|
|
21032
|
+
"type": "object",
|
|
21033
|
+
"properties": {
|
|
21034
|
+
"id": {
|
|
21035
|
+
"type": "string",
|
|
21036
|
+
"format": "uuid"
|
|
21037
|
+
},
|
|
21038
|
+
"message_id": { "type": ["string", "null"] },
|
|
21039
|
+
"domain_id": {
|
|
21040
|
+
"type": ["string", "null"],
|
|
21041
|
+
"format": "uuid"
|
|
21042
|
+
},
|
|
21043
|
+
"org_id": {
|
|
21044
|
+
"type": ["string", "null"],
|
|
21045
|
+
"format": "uuid"
|
|
21046
|
+
},
|
|
21047
|
+
"status": {
|
|
21048
|
+
"type": "string",
|
|
21049
|
+
"description": "Lifecycle status of an INBOUND email (a row in the `emails`\ntable). Distinct from `SentEmailStatus`, which describes\nthe OUTBOUND lifecycle (the `sent_emails` table) and uses\na different vocabulary because the lifecycles differ.\nPossible values:\n\n - `pending`: the row was inserted at ingestion (mx_main)\n and has not yet completed the spam / filter / auth\n pipeline. Body and parsed fields are present; webhook\n delivery is not yet scheduled. Most rows transition out\n of `pending` within seconds.\n - `accepted`: the inbound passed the policy gates and is\n queued for webhook delivery. The `webhook_status` field\n tracks the separate webhook-delivery lifecycle from\n this point.\n - `completed`: terminal success. Webhook delivery\n attempted and acknowledged by every active endpoint, OR\n no endpoints are configured, so the row is durably\n archived.\n - `rejected`: terminal failure at ingestion (spam, blocked\n sender, filter rule, malformed). The body and metadata\n are stored for auditing but no webhook fires and the\n row is not repliable.\n\nSee also `webhook_status` (separate enum tracking the\nwebhook-delivery state machine) and `SentEmailStatus` (the\noutbound vocabulary).\n",
|
|
21050
|
+
"enum": [
|
|
21051
|
+
"pending",
|
|
21052
|
+
"accepted",
|
|
21053
|
+
"completed",
|
|
21054
|
+
"rejected"
|
|
21055
|
+
]
|
|
21056
|
+
},
|
|
21057
|
+
"sender": {
|
|
21058
|
+
"type": "string",
|
|
21059
|
+
"description": "SMTP envelope sender (return-path) the inbound mail server\naccepted. For most legitimate mail this equals the bare\naddress in the From header; for mailing lists, bounce\nhandlers, and forwarders it is typically the bounce address\nrather than the human-visible sender.\n\nFor the parsed From-header value (with display name handling\nand a sender-fallback when the header is unparseable), GET\nthe email by id and use `from_email`.\n"
|
|
21060
|
+
},
|
|
21061
|
+
"recipient": { "type": "string" },
|
|
21062
|
+
"subject": { "type": ["string", "null"] },
|
|
21063
|
+
"domain": { "type": "string" },
|
|
21064
|
+
"spam_score": { "type": ["number", "null"] },
|
|
21065
|
+
"created_at": {
|
|
21066
|
+
"type": "string",
|
|
21067
|
+
"format": "date-time"
|
|
21068
|
+
},
|
|
21069
|
+
"received_at": {
|
|
21070
|
+
"type": "string",
|
|
21071
|
+
"format": "date-time"
|
|
21072
|
+
},
|
|
21073
|
+
"raw_size_bytes": { "type": ["integer", "null"] },
|
|
21074
|
+
"webhook_status": {
|
|
21075
|
+
"type": ["string", "null"],
|
|
21076
|
+
"description": "Webhook-delivery state for an inbound email. Tracks a\nSEPARATE lifecycle from the email's `status` field; the\nsame row carries both. Possible values:\n\n - `pending`: ingestion is past `pending` (the email itself\n is `accepted`) but the webhook fan-out has not yet\n started for this row.\n - `in_flight`: at least one delivery attempt is in flight.\n - `fired`: terminal success. Every active endpoint\n acknowledged the delivery (or accepted it after retries).\n - `failed`: terminal partial-failure. At least one endpoint\n exhausted its retry budget; some endpoints may still\n have succeeded.\n - `exhausted`: terminal failure. Every endpoint exhausted\n its retry budget without success.\n - `null`: no endpoints configured, so no webhook lifecycle\n applies.\n\nNote that the value `pending` here does NOT mean the email\nis `pending`; it means the email is past ingestion but\nwebhook delivery has not yet begun. Two overlapping uses\nof the word `pending` for distinct lifecycle phases.\n",
|
|
21077
|
+
"enum": [
|
|
21078
|
+
"pending",
|
|
21079
|
+
"in_flight",
|
|
21080
|
+
"fired",
|
|
21081
|
+
"failed",
|
|
21082
|
+
"exhausted",
|
|
21083
|
+
null
|
|
21084
|
+
]
|
|
21085
|
+
},
|
|
21086
|
+
"webhook_attempt_count": { "type": "integer" },
|
|
21087
|
+
"thread_id": {
|
|
21088
|
+
"type": ["string", "null"],
|
|
21089
|
+
"format": "uuid",
|
|
21090
|
+
"description": "Conversation thread this message belongs to. Fetch\n`/threads/{thread_id}` for the full ordered thread. NULL on\nmessages received before threading was enabled.\n"
|
|
21091
|
+
},
|
|
21092
|
+
"presence_control": { "anyOf": [{
|
|
21093
|
+
"type": "object",
|
|
21094
|
+
"properties": {
|
|
21095
|
+
"status": {
|
|
21096
|
+
"type": "string",
|
|
21097
|
+
"enum": [
|
|
21098
|
+
"verified",
|
|
21099
|
+
"pending",
|
|
21100
|
+
"rejected"
|
|
21101
|
+
]
|
|
21102
|
+
},
|
|
21103
|
+
"valid_for_ms": {
|
|
21104
|
+
"type": "integer",
|
|
21105
|
+
"minimum": 0,
|
|
21106
|
+
"maximum": 6e5
|
|
21107
|
+
}
|
|
21108
|
+
},
|
|
21109
|
+
"required": ["status", "valid_for_ms"],
|
|
21110
|
+
"additionalProperties": false
|
|
21111
|
+
}, { "type": "null" }] },
|
|
21112
|
+
"automation_headers": {
|
|
21113
|
+
"type": ["object", "null"],
|
|
21114
|
+
"description": "What the message declared about being automated, verbatim:\n`List-Unsubscribe` (RFC 2369/8058), `List-Id` (RFC 2919),\n`Precedence`, and `Auto-Submitted` (RFC 3834). Null or absent when the message\ndeclared none, and on messages received before these headers\nwere captured, so a null value is not evidence that a person\nsent the message.\n",
|
|
21115
|
+
"properties": {
|
|
21116
|
+
"list_unsubscribe": { "type": "string" },
|
|
21117
|
+
"list_id": { "type": "string" },
|
|
21118
|
+
"precedence": { "type": "string" },
|
|
21119
|
+
"auto_submitted": { "type": "string" }
|
|
21120
|
+
},
|
|
21121
|
+
"additionalProperties": true
|
|
21122
|
+
},
|
|
21123
|
+
"reply_count": {
|
|
21124
|
+
"type": "integer",
|
|
21125
|
+
"minimum": 0,
|
|
21126
|
+
"description": "Number of replies recorded against this email: sends whose\n`in_reply_to_email_id` is this email, the same linkage that populates\n`EmailDetail.replies`, excluding sends that never went out (status\n`gate_denied`, `agent_failed`, `canceled`). Queued and scheduled\nreplies count. Under an agent connection, only the replies that agent\ncan see in `EmailDetail.replies`. Unlike `awaiting`, this counts\nreplies to this one email, not to the thread.\n"
|
|
21127
|
+
},
|
|
21128
|
+
"last_replied_at": {
|
|
21129
|
+
"type": ["string", "null"],
|
|
21130
|
+
"format": "date-time",
|
|
21131
|
+
"description": "`created_at` of the latest reply counted in `reply_count`, or\nnull when `reply_count` is 0.\n"
|
|
21132
|
+
},
|
|
21133
|
+
"awaiting": {
|
|
21134
|
+
"type": "string",
|
|
21135
|
+
"enum": ["you", "them"],
|
|
21136
|
+
"description": "Whose turn it is in this email's conversation. A send counts as a\nreply unless its status is `gate_denied`, `agent_failed`, `canceled`\n(queued and scheduled replies count: they are committed to go). For\nan email with a `thread_id`: `them` when the thread's latest counted\noutbound send (by send `created_at`, including any counted reply to\nthis email itself) is at or after the thread's latest inbound message\n(by `received_at`); otherwise `you`. For an email with no\n`thread_id`: `them` when it has at least one counted reply\n(`reply_count > 0` for an API key), otherwise `you`. It is a\nthread-level fact: every email in a thread carries the same value, and\nit reflects sends made by any credential in the organization,\nincluding other agent connections. It changes without a new inbound\nmessage, for example when a reply is sent, a queued reply fails, a\nscheduled reply is canceled, or a message is deleted, and is always\ncurrent when read.\n"
|
|
21137
|
+
},
|
|
21138
|
+
"automated": {
|
|
21139
|
+
"type": "boolean",
|
|
21140
|
+
"description": "Whether the message was sent by a machine rather than a person.\nAn inbound email is `automated` when any of these holds, decided once\nwhen it arrives: `null_envelope_sender` (an empty MAIL FROM, a bounce);\n`no_identifiable_sender` (no address in the envelope or From);\n`own_address` (a sender address is exactly one of the addresses the\nmail was delivered to; sharing a domain with the recipient is not\nenough, so a colleague or another agent in the organization is not\nautomated);\n`mailer_daemon` (mailer-daemon@ or postmaster@ on any domain);\n`auto_submitted` (Auto-Submitted with any keyword but `no`, an\nempty one included);\n`precedence` (Precedence bulk, list, junk or auto_reply);\n`list_unsubscribe` (List-Unsubscribe present); `list_id` (List-Id\npresent); `auto_response_suppress` (X-Auto-Response-Suppress asks\nfor no automatic reply: All, OOF or AutoReply);\n`failed_recipients` (X-Failed-Recipients present, a bounce);\n`report` (a delivery, feedback or disposition report). Mail\nPrimitive's own automatic responders decline to answer is always\n`automated`. The header rules only see headers captured at ingest, so\nolder mail may lack them. This is loop and noise protection, not\nsender authentication.\n"
|
|
21141
|
+
},
|
|
21142
|
+
"automated_reasons": {
|
|
21143
|
+
"type": "array",
|
|
21144
|
+
"items": { "type": "string" },
|
|
21145
|
+
"description": "Why `automated` is true, in rule order; empty when it is false.\nCurrent values: `null_envelope_sender`, `no_identifiable_sender`,\n`own_address`, `mailer_daemon`, `auto_submitted`, `precedence`,\n`list_unsubscribe`, `list_id`, `auto_response_suppress`,\n`failed_recipients`, `report`. Treat an unfamiliar value as a\nreason added after your client was built.\n"
|
|
21146
|
+
},
|
|
21147
|
+
"from_verified": {
|
|
21148
|
+
"type": ["boolean", "null"],
|
|
21149
|
+
"description": "True when the From header address, `from_header_email`, passed DMARC for its domain on arrival (aligned SPF or DKIM); decided once, at receipt. It is about the From header, never the envelope: `sender`, the `from` parameter and `from:` term, and the change feed's `address` match the envelope sender, which can differ. It proves the domain, not the mailbox. False with no From header address, when authentication failed or was not attempted (a delivery between Primitive mailboxes counts only when it passed DMARC too), and for mail stored before the verdict was recorded. Null on sent rows in search results. Not a spam or safety judgement."
|
|
21150
|
+
},
|
|
21151
|
+
"from_header_email": {
|
|
21152
|
+
"type": ["string", "null"],
|
|
21153
|
+
"description": "The From header address, lowercased, that `from_verified` is about; null when the message had none. Unlike `sender` it is never the envelope address."
|
|
21154
|
+
},
|
|
21155
|
+
"repeat": {
|
|
21156
|
+
"type": ["object", "null"],
|
|
21157
|
+
"description": "Set when Primitive sent this message as part of a repeating send. Resolved from Primitive's own records, never from the message content. Null on every other message and on servers that predate repeating sends.",
|
|
21158
|
+
"properties": {
|
|
21159
|
+
"repeat_id": {
|
|
21160
|
+
"type": "string",
|
|
21161
|
+
"format": "uuid"
|
|
21162
|
+
},
|
|
21163
|
+
"sequence": {
|
|
21164
|
+
"type": "integer",
|
|
21165
|
+
"minimum": 1
|
|
21166
|
+
}
|
|
21167
|
+
},
|
|
21168
|
+
"required": ["repeat_id", "sequence"]
|
|
21169
|
+
},
|
|
21170
|
+
"sender_member": {
|
|
21171
|
+
"type": ["object", "null"],
|
|
21172
|
+
"description": "Verified human authorship, projected only within the member organization. Historical attribution is not current sending or owner authority.",
|
|
21173
|
+
"properties": {
|
|
21174
|
+
"address": {
|
|
21175
|
+
"type": "string",
|
|
21176
|
+
"format": "email"
|
|
21177
|
+
},
|
|
21178
|
+
"user_id": { "type": "string" },
|
|
21179
|
+
"name": { "type": ["string", "null"] }
|
|
21180
|
+
},
|
|
21181
|
+
"required": [
|
|
21182
|
+
"address",
|
|
21183
|
+
"user_id",
|
|
21184
|
+
"name"
|
|
21185
|
+
]
|
|
21186
|
+
},
|
|
21187
|
+
"fyi": {
|
|
21188
|
+
"type": "boolean",
|
|
21189
|
+
"description": "True when the email is an informational signal (an acknowledgement, read, working or typing signal) and nothing else, so it needs no answer, not even another informational reply. Never true for mail from the reader's owner. Older servers omit it.\n"
|
|
21190
|
+
},
|
|
21191
|
+
"interaction_hint": {
|
|
21192
|
+
"type": "string",
|
|
21193
|
+
"description": "How to place this email in a timeline without downloading it: `status`, `card`, `none` or `pending`. Not declared as a closed enum so that a value added later does not fail decoding. Decided once by the server from the message's authoritative `interaction.json` part, and only when a passing DKIM signature covering From and Content-Type authenticates the From that wrote it; the `X-Primitive-Interaction` header is never used, so an unsigned or forged message is always `none`. `status`: a pure status signal (`read`, `working`, `typing`, or an `ack` in the strict signal shape), shown as a property of the message it is about rather than as a row. `card`: an interaction that renders as its own timeline item (for example `ack-request`, `x402.payment`, `wake.dispatch`, `primitive.contact`, `repeat.tick`, `repeat.stop`). `none`: ordinary mail, including any interaction kind not listed. `pending`: the email has not finished processing; read it again later. Email processed before the field existed is `none`. Treat an unfamiliar value as `none`. Older servers omit it.\n"
|
|
21194
|
+
},
|
|
21195
|
+
"interaction_kind": {
|
|
21196
|
+
"type": ["string", "null"],
|
|
21197
|
+
"description": "The `<protocol>/<version>` that `interaction_hint` was decided from (for example `repeat.tick/1` or `read/1`) when `interaction_hint` is `status` or `card`; null otherwise.\n"
|
|
21198
|
+
},
|
|
21199
|
+
"interaction_candidate": {
|
|
21200
|
+
"type": "boolean",
|
|
21201
|
+
"description": "UNVERIFIED early signal for the `pending` window: true when the raw message carries an `X-Primitive-Interaction` header or a MIME part named `interaction.json`, recorded before any signature is checked. Use it only to decide, while `interaction_hint` is `pending`, whether to show a placeholder instead of the raw text. It is never evidence of an interaction: anyone can set that header or name a part that way, so it must not grant trust or select an interaction kind. Ignore it once `interaction_hint` is anything other than `pending`.\n"
|
|
21202
|
+
},
|
|
21203
|
+
"relay": {
|
|
21204
|
+
"type": ["object", "null"],
|
|
21205
|
+
"description": "Set when the message reached Primitive through a Primitive mail relay. Null for other mail. The field may be absent; treat a missing value the same as null.",
|
|
21206
|
+
"properties": {
|
|
21207
|
+
"hostname": {
|
|
21208
|
+
"type": "string",
|
|
21209
|
+
"description": "The relay hostname the domain's MX record points at."
|
|
21210
|
+
},
|
|
21211
|
+
"via": {
|
|
21212
|
+
"type": "string",
|
|
21213
|
+
"description": "How the message arrived. Currently always `mail_relay`. Treat an unfamiliar value as one added after your client was built."
|
|
21214
|
+
}
|
|
21215
|
+
},
|
|
21216
|
+
"required": ["hostname", "via"]
|
|
21217
|
+
},
|
|
21218
|
+
"from_name": {
|
|
21219
|
+
"type": ["string", "null"],
|
|
21220
|
+
"description": "Display name from the `From:` header, decoded for display:\nRFC 2047 encoded words are decoded in their declared charset\nand folded lines are joined (e.g. `Dana Rúiz` for\n`=?UTF-8?Q?Dana_R=C3=BAiz?= <dana@example.com>`). Null when\nthe header carries no display name, and may be absent; treat\na missing value the same as null.\n\nFor display only: the sender chooses this name freely, so\nidentify the sender by `from_email`, never by this name.\n"
|
|
21221
|
+
},
|
|
21222
|
+
"body_text": {
|
|
21223
|
+
"type": ["string", "null"],
|
|
21224
|
+
"description": "With `include=body_text`: the plain-text body, cut as `truncated_fields` says."
|
|
21225
|
+
},
|
|
21226
|
+
"body_html": {
|
|
21227
|
+
"type": ["string", "null"],
|
|
21228
|
+
"description": "With `include=body_html`: the HTML body, cut as `truncated_fields` says."
|
|
21229
|
+
},
|
|
21230
|
+
"headers": {
|
|
21231
|
+
"anyOf": [{
|
|
21232
|
+
"type": "object",
|
|
21233
|
+
"description": "Stored addressing and threading headers. A member is null when absent or withheld.",
|
|
21234
|
+
"properties": {
|
|
21235
|
+
"from": { "type": ["string", "null"] },
|
|
21236
|
+
"to": {
|
|
21237
|
+
"type": ["array", "null"],
|
|
21238
|
+
"items": { "type": "string" }
|
|
21239
|
+
},
|
|
21240
|
+
"cc": {
|
|
21241
|
+
"type": ["array", "null"],
|
|
21242
|
+
"items": { "type": "string" }
|
|
21243
|
+
},
|
|
21244
|
+
"bcc": {
|
|
21245
|
+
"type": ["array", "null"],
|
|
21246
|
+
"items": { "type": "string" }
|
|
21247
|
+
},
|
|
21248
|
+
"reply_to": {
|
|
21249
|
+
"type": ["array", "null"],
|
|
21250
|
+
"items": { "type": "string" }
|
|
21251
|
+
},
|
|
21252
|
+
"in_reply_to": {
|
|
21253
|
+
"type": ["array", "null"],
|
|
21254
|
+
"items": { "type": "string" }
|
|
21255
|
+
},
|
|
21256
|
+
"references": {
|
|
21257
|
+
"type": ["array", "null"],
|
|
21258
|
+
"items": { "type": "string" }
|
|
21259
|
+
},
|
|
21260
|
+
"automation": {
|
|
21261
|
+
"type": ["object", "null"],
|
|
21262
|
+
"additionalProperties": { "type": "string" },
|
|
21263
|
+
"description": "Received mail only."
|
|
21264
|
+
}
|
|
21265
|
+
},
|
|
21266
|
+
"required": [
|
|
21267
|
+
"from",
|
|
21268
|
+
"to",
|
|
21269
|
+
"cc",
|
|
21270
|
+
"bcc",
|
|
21271
|
+
"reply_to",
|
|
21272
|
+
"in_reply_to",
|
|
21273
|
+
"references",
|
|
21274
|
+
"automation"
|
|
21275
|
+
]
|
|
21276
|
+
}, { "type": "null" }],
|
|
21277
|
+
"description": "With `include=headers`: stored addressing and threading headers."
|
|
21278
|
+
},
|
|
21279
|
+
"attachments": {
|
|
21280
|
+
"type": "array",
|
|
21281
|
+
"items": {
|
|
21282
|
+
"type": "object",
|
|
21283
|
+
"additionalProperties": true
|
|
21284
|
+
},
|
|
21285
|
+
"description": "With `include=attachments`: attachment metadata as the detail read reports it. Never the bytes."
|
|
21286
|
+
},
|
|
21287
|
+
"truncated": {
|
|
21288
|
+
"type": "boolean",
|
|
21289
|
+
"description": "With `include`: true when a body on this row was cut or left out."
|
|
21290
|
+
},
|
|
21291
|
+
"truncated_fields": {
|
|
21292
|
+
"type": "array",
|
|
21293
|
+
"items": {
|
|
21294
|
+
"type": "string",
|
|
21295
|
+
"enum": ["body_text", "body_html"]
|
|
21296
|
+
},
|
|
21297
|
+
"description": "With `include`: the body fields on this row that were cut or left out."
|
|
21298
|
+
}
|
|
21299
|
+
},
|
|
21300
|
+
"required": [
|
|
21301
|
+
"id",
|
|
21302
|
+
"status",
|
|
21303
|
+
"sender",
|
|
21304
|
+
"recipient",
|
|
21305
|
+
"domain",
|
|
21306
|
+
"created_at",
|
|
21307
|
+
"received_at",
|
|
21308
|
+
"webhook_attempt_count",
|
|
21309
|
+
"reply_count",
|
|
21310
|
+
"last_replied_at",
|
|
21311
|
+
"awaiting",
|
|
21312
|
+
"automated",
|
|
21313
|
+
"automated_reasons"
|
|
21314
|
+
]
|
|
21315
|
+
}, {
|
|
21316
|
+
"type": "object",
|
|
21317
|
+
"required": ["direction"],
|
|
21318
|
+
"properties": { "direction": {
|
|
21319
|
+
"type": "string",
|
|
21320
|
+
"enum": ["inbound"]
|
|
21321
|
+
} }
|
|
21322
|
+
}]
|
|
21323
|
+
}, {
|
|
21324
|
+
"description": "A sent message inlined by `expand=message`, as GET /sent-emails returns it.",
|
|
21325
|
+
"allOf": [{
|
|
21326
|
+
"type": "object",
|
|
21327
|
+
"description": "List-row projection of a sent-email record. Drops\n`body_text` and `body_html` to keep paginated responses\nsmall; fetch /sent-emails/{id} for the full record with\nbodies.\n",
|
|
21328
|
+
"properties": {
|
|
21329
|
+
"id": {
|
|
21330
|
+
"type": "string",
|
|
21331
|
+
"format": "uuid"
|
|
21332
|
+
},
|
|
21333
|
+
"status": {
|
|
21334
|
+
"type": "string",
|
|
21335
|
+
"description": "Lifecycle status of a sent_emails row. Possible values:\n\n - `queued`: pre-call INSERT; the outbound agent has not\n yet replied.\n - `submitted_to_agent`: agent accepted; `queue_id` is set.\n - `agent_failed`: agent rejected; `error_code` and\n `error_message` carry the reason.\n - `gate_denied`: a recipient-scope gate denied the send;\n the agent was never called. The `gates` array carries\n the denial detail. /send-mail returns 403 in this case\n so callers see the denial synchronously; /sent-emails\n additionally records the row for historical lookup,\n which is when this status appears in a listing.\n - `unknown`: terminal indeterminate; the on-box log\n poller couldn't classify the receiver's response.\n - `delivered` / `bounced` / `deferred` / `wait_timeout`:\n terminal delivery outcomes (see DeliveryStatus).\n - `scheduled`: created with a future `scheduled_at` and\n not yet executed; `scheduled_at` carries the pending\n execution time. Reschedulable via PATCH\n /sent-emails/{id} and cancelable via\n /sent-emails/{id}/cancel while in this status.\n - `canceled`: terminal; a scheduled send canceled before\n execution. `canceled_at` carries the cancellation time\n and nothing was dispatched.\n",
|
|
21336
|
+
"enum": [
|
|
21337
|
+
"queued",
|
|
21338
|
+
"submitted_to_agent",
|
|
21339
|
+
"agent_failed",
|
|
21340
|
+
"gate_denied",
|
|
21341
|
+
"unknown",
|
|
21342
|
+
"delivered",
|
|
21343
|
+
"bounced",
|
|
21344
|
+
"deferred",
|
|
21345
|
+
"wait_timeout",
|
|
21346
|
+
"scheduled",
|
|
21347
|
+
"canceled"
|
|
21348
|
+
]
|
|
21349
|
+
},
|
|
21350
|
+
"status_changed_at": {
|
|
21351
|
+
"type": "string",
|
|
21352
|
+
"format": "date-time",
|
|
21353
|
+
"description": "Timestamp of the most recent status transition.\nPolling clients should treat `status='queued'` AND\n`status_changed_at` older than 5 minutes as\n\"stuck-queued\" (the post-tx UPDATE failed and the\nactual delivery state is recoverable from on-box logs\nvia `queue_id` when populated, or `request_id`).\n"
|
|
21354
|
+
},
|
|
21355
|
+
"created_at": {
|
|
21356
|
+
"type": "string",
|
|
21357
|
+
"format": "date-time"
|
|
21358
|
+
},
|
|
21359
|
+
"updated_at": {
|
|
21360
|
+
"type": "string",
|
|
21361
|
+
"format": "date-time"
|
|
21362
|
+
},
|
|
21363
|
+
"client_idempotency_key": {
|
|
21364
|
+
"type": ["string", "null"],
|
|
21365
|
+
"description": "Effective idempotency key used for this send. If the\ncaller passed the `Idempotency-Key` header, this is\nthat value; otherwise it's a server-derived hash of\nthe canonical request payload.\n"
|
|
21366
|
+
},
|
|
21367
|
+
"content_hash": {
|
|
21368
|
+
"type": "string",
|
|
21369
|
+
"description": "Stable hash of the canonical send payload."
|
|
21370
|
+
},
|
|
21371
|
+
"from_header": {
|
|
21372
|
+
"type": "string",
|
|
21373
|
+
"description": "Raw `From:` header as sent on the wire, including any\ndisplay name (e.g. `\"Acme Support\" <agent@acme.test>`).\n"
|
|
21374
|
+
},
|
|
21375
|
+
"from_address": {
|
|
21376
|
+
"type": "string",
|
|
21377
|
+
"description": "Bare email address parsed from `from_header`."
|
|
21378
|
+
},
|
|
21379
|
+
"to_header": {
|
|
21380
|
+
"type": "string",
|
|
21381
|
+
"description": "Raw `To:` header as sent on the wire, including any\ndisplay name.\n"
|
|
21382
|
+
},
|
|
21383
|
+
"to_address": {
|
|
21384
|
+
"type": "string",
|
|
21385
|
+
"description": "Bare email address parsed from `to_header`."
|
|
21386
|
+
},
|
|
21387
|
+
"recipient_count": {
|
|
21388
|
+
"type": ["integer", "null"],
|
|
21389
|
+
"minimum": 0,
|
|
21390
|
+
"description": "Every recipient of the send, To, Cc and Bcc together, each address counted once: the size of `to_addresses`, `cc` and `bcc` combined without duplicates. On a mail relay send `bcc` lists every hidden recipient. Null when the request uses an address-bound agent connection key whose address is not the sender, because the count would reveal Bcc recipients. The `sent_email` object of the `sent_email.*` webhook events carries the same field."
|
|
21391
|
+
},
|
|
21392
|
+
"subject": { "type": "string" },
|
|
21393
|
+
"body_size_bytes": {
|
|
21394
|
+
"type": "integer",
|
|
21395
|
+
"description": "Total UTF-8 byte length of `body_text` + `body_html`.\nSurfaced on the list endpoint so callers can see \"this\nrow has a 4MB body\" without fetching it.\n"
|
|
21396
|
+
},
|
|
21397
|
+
"content_discarded_at": {
|
|
21398
|
+
"type": ["string", "null"],
|
|
21399
|
+
"format": "date-time",
|
|
21400
|
+
"description": "Timestamp at which the bodies were discarded by an\nentitlement-driven retention policy. Null when bodies\nare still present. The detail endpoint returns\nnull-valued `body_text`/`body_html` for discarded rows.\n"
|
|
21401
|
+
},
|
|
21402
|
+
"message_id": {
|
|
21403
|
+
"type": ["string", "null"],
|
|
21404
|
+
"description": "Wire-level Message-ID assigned to the outbound message\n(RFC 5322). Null on rows that never reached signing\n(queued, gate_denied, agent_failed before signing).\n"
|
|
21405
|
+
},
|
|
21406
|
+
"in_reply_to": {
|
|
21407
|
+
"type": ["string", "null"],
|
|
21408
|
+
"description": "Wire-level In-Reply-To header value, when this send\nwas a reply.\n"
|
|
21409
|
+
},
|
|
21410
|
+
"email_references": {
|
|
21411
|
+
"type": ["string", "null"],
|
|
21412
|
+
"description": "Wire-level References header value, when this send\nwas a reply.\n"
|
|
21413
|
+
},
|
|
21414
|
+
"in_reply_to_email_id": {
|
|
21415
|
+
"type": ["string", "null"],
|
|
21416
|
+
"format": "uuid",
|
|
21417
|
+
"description": "Reference to the inbound `emails.id` that this send\nreplied to, when known. Populated when the caller used\n/emails/{id}/reply or when /send-mail's `in_reply_to`\nmatched a stored inbound message_id in the same org.\n"
|
|
21418
|
+
},
|
|
21419
|
+
"thread_id": {
|
|
21420
|
+
"type": ["string", "null"],
|
|
21421
|
+
"format": "uuid",
|
|
21422
|
+
"description": "Conversation thread this send belongs to. A reply inherits\nthe thread of the inbound it answers; a fresh send starts a\nnew thread. Fetch `/threads/{thread_id}` for the full\nordered thread (inbound + outbound interleaved). NULL on\ngate-denied sends and on sends created before threading was\nenabled.\n"
|
|
21423
|
+
},
|
|
21424
|
+
"queue_id": {
|
|
21425
|
+
"type": ["string", "null"],
|
|
21426
|
+
"description": "Message identifier assigned by Primitive's outbound\nrelay once the agent accepts the message. Null on\nqueued, gate_denied, and agent_failed rows.\n"
|
|
21427
|
+
},
|
|
21428
|
+
"smtp_response_code": {
|
|
21429
|
+
"type": ["integer", "null"],
|
|
21430
|
+
"description": "Receiver's 3-digit SMTP code (e.g. 250, 550, 451).\nPopulated on terminal delivery statuses; may be null\non a deferred where the agent never got an SMTP-level\nresponse (TCP refused, DNS failed, TLS handshake\nfailed). `smtp_response_text` still carries Postfix's\ndescriptive text in those cases.\n"
|
|
21431
|
+
},
|
|
21432
|
+
"smtp_response_text": {
|
|
21433
|
+
"type": ["string", "null"],
|
|
21434
|
+
"description": "Free-form text portion of the receiver's SMTP\nresponse. The most useful debugging signal on a\n`bounced` or `deferred` row.\n"
|
|
21435
|
+
},
|
|
21436
|
+
"smtp_enhanced_status_code": {
|
|
21437
|
+
"type": ["string", "null"],
|
|
21438
|
+
"description": "RFC 3463 enhanced status code (e.g. `5.1.1` for \"Bad\ndestination mailbox address\"). Distinct from\n`smtp_response_code`: the basic 3-digit code is coarse\n(550 = \"permanent failure\"), the enhanced code is\nfiner-grained.\n"
|
|
21439
|
+
},
|
|
21440
|
+
"dkim_selector": {
|
|
21441
|
+
"type": ["string", "null"],
|
|
21442
|
+
"description": "DKIM selector used to sign the outbound message.\nPublic DNS data; useful for diagnosing why a downstream\nverifier rejected the signature.\n"
|
|
21443
|
+
},
|
|
21444
|
+
"dkim_domain": {
|
|
21445
|
+
"type": ["string", "null"],
|
|
21446
|
+
"description": "DKIM signing domain."
|
|
21447
|
+
},
|
|
21448
|
+
"error_code": {
|
|
21449
|
+
"type": ["string", "null"],
|
|
21450
|
+
"description": "Stable public error code on `agent_failed` rows. The\nagent's internal codes are remapped to a stable public\ntaxonomy (see `publicAgentError` in the server) so this\nfield is safe to branch on across agent versions.\n"
|
|
21451
|
+
},
|
|
21452
|
+
"error_message": {
|
|
21453
|
+
"type": ["string", "null"],
|
|
21454
|
+
"description": "Free-form error message accompanying `error_code`."
|
|
21455
|
+
},
|
|
21456
|
+
"gates": {
|
|
21457
|
+
"type": ["array", "null"],
|
|
21458
|
+
"items": {
|
|
21459
|
+
"type": "object",
|
|
21460
|
+
"properties": {
|
|
21461
|
+
"name": {
|
|
21462
|
+
"type": "string",
|
|
21463
|
+
"enum": ["send_to_confirmed_domains", "send_to_known_addresses"],
|
|
21464
|
+
"description": "Public recipient-scope gate name that denied the send."
|
|
21465
|
+
},
|
|
21466
|
+
"reason": {
|
|
21467
|
+
"type": "string",
|
|
21468
|
+
"enum": [
|
|
21469
|
+
"domain_not_confirmed",
|
|
21470
|
+
"recipient_unauthenticated",
|
|
21471
|
+
"recipient_not_known"
|
|
21472
|
+
],
|
|
21473
|
+
"description": "Stable machine-readable denial reason."
|
|
21474
|
+
},
|
|
21475
|
+
"message": {
|
|
21476
|
+
"type": "string",
|
|
21477
|
+
"description": "Human-readable explanation of the gate denial."
|
|
21478
|
+
},
|
|
21479
|
+
"subject": {
|
|
21480
|
+
"type": "string",
|
|
21481
|
+
"description": "Domain or address the gate evaluated."
|
|
21482
|
+
},
|
|
21483
|
+
"fix": {
|
|
21484
|
+
"type": "object",
|
|
21485
|
+
"properties": {
|
|
21486
|
+
"action": {
|
|
21487
|
+
"type": "string",
|
|
21488
|
+
"enum": [
|
|
21489
|
+
"confirm_domain",
|
|
21490
|
+
"sender_must_fix_authentication",
|
|
21491
|
+
"wait_for_inbound"
|
|
21492
|
+
],
|
|
21493
|
+
"description": "Suggested next action for the caller."
|
|
21494
|
+
},
|
|
21495
|
+
"subject": {
|
|
21496
|
+
"type": "string",
|
|
21497
|
+
"description": "Entity the action applies to."
|
|
21498
|
+
}
|
|
21499
|
+
},
|
|
21500
|
+
"required": ["action", "subject"]
|
|
21501
|
+
},
|
|
21502
|
+
"docs_url": {
|
|
21503
|
+
"type": "string",
|
|
21504
|
+
"description": "Public docs URL with more context."
|
|
21505
|
+
}
|
|
21506
|
+
},
|
|
21507
|
+
"required": [
|
|
21508
|
+
"name",
|
|
21509
|
+
"reason",
|
|
21510
|
+
"message",
|
|
21511
|
+
"subject"
|
|
21512
|
+
]
|
|
21513
|
+
},
|
|
21514
|
+
"description": "Gate-denial detail on `gate_denied` rows. Mirrors the\nsynchronous /send-mail 403 contract so a caller's\nGateDenial handler is the same across live denies and\nhistorical lookups. Null on every other status.\n"
|
|
21515
|
+
},
|
|
21516
|
+
"request_id": {
|
|
21517
|
+
"type": ["string", "null"],
|
|
21518
|
+
"description": "Server-issued request identifier from the original\n/send-mail call. Surfaced as the `X-Request-Id`\nresponse header on the live send and recorded here\nfor support escalation.\n"
|
|
21519
|
+
},
|
|
21520
|
+
"scheduled_at": {
|
|
21521
|
+
"type": ["string", "null"],
|
|
21522
|
+
"format": "date-time",
|
|
21523
|
+
"description": "Requested execution time for a scheduled send. Kept\nafter execution as the historical schedule; null on\nordinary immediate sends.\n"
|
|
21524
|
+
},
|
|
21525
|
+
"canceled_at": {
|
|
21526
|
+
"type": ["string", "null"],
|
|
21527
|
+
"format": "date-time",
|
|
21528
|
+
"description": "When a scheduled send was canceled. Null unless the row\nreached the `canceled` status.\n"
|
|
21529
|
+
},
|
|
21530
|
+
"presence_control": { "anyOf": [{
|
|
21531
|
+
"type": "object",
|
|
21532
|
+
"properties": {
|
|
21533
|
+
"status": {
|
|
21534
|
+
"type": "string",
|
|
21535
|
+
"enum": [
|
|
21536
|
+
"verified",
|
|
21537
|
+
"pending",
|
|
21538
|
+
"rejected"
|
|
21539
|
+
]
|
|
21540
|
+
},
|
|
21541
|
+
"valid_for_ms": {
|
|
21542
|
+
"type": "integer",
|
|
21543
|
+
"minimum": 0,
|
|
21544
|
+
"maximum": 6e5
|
|
21545
|
+
}
|
|
21546
|
+
},
|
|
21547
|
+
"required": ["status", "valid_for_ms"],
|
|
21548
|
+
"additionalProperties": false
|
|
21549
|
+
}, { "type": "null" }] },
|
|
21550
|
+
"repeat": {
|
|
21551
|
+
"type": ["object", "null"],
|
|
21552
|
+
"description": "Set when Primitive sent this message as part of a repeating send. Resolved from Primitive's own records, never from the message content. Null on every other message and on servers that predate repeating sends.",
|
|
21553
|
+
"properties": {
|
|
21554
|
+
"repeat_id": {
|
|
21555
|
+
"type": "string",
|
|
21556
|
+
"format": "uuid"
|
|
21557
|
+
},
|
|
21558
|
+
"sequence": {
|
|
21559
|
+
"type": "integer",
|
|
21560
|
+
"minimum": 1
|
|
21561
|
+
}
|
|
21562
|
+
},
|
|
21563
|
+
"required": ["repeat_id", "sequence"]
|
|
21564
|
+
},
|
|
21565
|
+
"sender_member": {
|
|
21566
|
+
"type": ["object", "null"],
|
|
21567
|
+
"description": "Verified human authorship, projected only within the member organization. Historical attribution is not current sending or owner authority.",
|
|
21568
|
+
"properties": {
|
|
21569
|
+
"address": {
|
|
21570
|
+
"type": "string",
|
|
21571
|
+
"format": "email"
|
|
21572
|
+
},
|
|
21573
|
+
"user_id": { "type": "string" },
|
|
21574
|
+
"name": { "type": ["string", "null"] }
|
|
21575
|
+
},
|
|
21576
|
+
"required": [
|
|
21577
|
+
"address",
|
|
21578
|
+
"user_id",
|
|
21579
|
+
"name"
|
|
21580
|
+
]
|
|
21581
|
+
},
|
|
21582
|
+
"body_text": {
|
|
21583
|
+
"type": ["string", "null"],
|
|
21584
|
+
"description": "Plain-text body sent on the wire. Null when the\nsend carried only an HTML body, or when bodies have\nbeen discarded post-send (`content_discarded_at`\nset).\n"
|
|
21585
|
+
},
|
|
21586
|
+
"body_html": {
|
|
21587
|
+
"type": ["string", "null"],
|
|
21588
|
+
"description": "HTML body sent on the wire. Null when the send\ncarried only a plain-text body, or when bodies\nhave been discarded post-send.\n"
|
|
21589
|
+
},
|
|
21590
|
+
"headers": {
|
|
21591
|
+
"anyOf": [{
|
|
21592
|
+
"type": "object",
|
|
21593
|
+
"description": "Stored addressing and threading headers. A member is null when absent or withheld.",
|
|
21594
|
+
"properties": {
|
|
21595
|
+
"from": { "type": ["string", "null"] },
|
|
21596
|
+
"to": {
|
|
21597
|
+
"type": ["array", "null"],
|
|
21598
|
+
"items": { "type": "string" }
|
|
21599
|
+
},
|
|
21600
|
+
"cc": {
|
|
21601
|
+
"type": ["array", "null"],
|
|
21602
|
+
"items": { "type": "string" }
|
|
21603
|
+
},
|
|
21604
|
+
"bcc": {
|
|
21605
|
+
"type": ["array", "null"],
|
|
21606
|
+
"items": { "type": "string" }
|
|
21607
|
+
},
|
|
21608
|
+
"reply_to": {
|
|
21609
|
+
"type": ["array", "null"],
|
|
21610
|
+
"items": { "type": "string" }
|
|
21611
|
+
},
|
|
21612
|
+
"in_reply_to": {
|
|
21613
|
+
"type": ["array", "null"],
|
|
21614
|
+
"items": { "type": "string" }
|
|
21615
|
+
},
|
|
21616
|
+
"references": {
|
|
21617
|
+
"type": ["array", "null"],
|
|
21618
|
+
"items": { "type": "string" }
|
|
21619
|
+
},
|
|
21620
|
+
"automation": {
|
|
21621
|
+
"type": ["object", "null"],
|
|
21622
|
+
"additionalProperties": { "type": "string" },
|
|
21623
|
+
"description": "Received mail only."
|
|
21624
|
+
}
|
|
21625
|
+
},
|
|
21626
|
+
"required": [
|
|
21627
|
+
"from",
|
|
21628
|
+
"to",
|
|
21629
|
+
"cc",
|
|
21630
|
+
"bcc",
|
|
21631
|
+
"reply_to",
|
|
21632
|
+
"in_reply_to",
|
|
21633
|
+
"references",
|
|
21634
|
+
"automation"
|
|
21635
|
+
]
|
|
21636
|
+
}, { "type": "null" }],
|
|
21637
|
+
"description": "With `include=headers`: stored addressing and threading headers."
|
|
21638
|
+
},
|
|
21639
|
+
"attachments": {
|
|
21640
|
+
"type": "array",
|
|
21641
|
+
"description": "Metadata for submitted inline attachments; download availability is reported separately. Metadata may exist when the archive is unavailable, and legacy sends may have no metadata. Offloaded payload files are not included in the inline archive.",
|
|
21642
|
+
"items": {
|
|
21643
|
+
"type": "object",
|
|
21644
|
+
"description": "Metadata for one submitted inline attachment.",
|
|
21645
|
+
"additionalProperties": true,
|
|
21646
|
+
"properties": {
|
|
21647
|
+
"filename": { "type": ["string", "null"] },
|
|
21648
|
+
"content_type": { "type": "string" },
|
|
21649
|
+
"size_bytes": {
|
|
21650
|
+
"type": "integer",
|
|
21651
|
+
"minimum": 0,
|
|
21652
|
+
"maximum": 9007199254740991
|
|
21653
|
+
},
|
|
21654
|
+
"sha256": { "type": "string" },
|
|
21655
|
+
"part_index": {
|
|
21656
|
+
"type": "integer",
|
|
21657
|
+
"minimum": 0,
|
|
21658
|
+
"maximum": 9007199254740991
|
|
21659
|
+
},
|
|
21660
|
+
"tar_path": { "type": "string" }
|
|
21661
|
+
},
|
|
21662
|
+
"required": [
|
|
21663
|
+
"filename",
|
|
21664
|
+
"content_type",
|
|
21665
|
+
"size_bytes",
|
|
21666
|
+
"sha256",
|
|
21667
|
+
"part_index",
|
|
21668
|
+
"tar_path"
|
|
21669
|
+
]
|
|
21670
|
+
}
|
|
21671
|
+
},
|
|
21672
|
+
"truncated": {
|
|
21673
|
+
"type": "boolean",
|
|
21674
|
+
"description": "With `include`: true when a body on this row was cut or left out."
|
|
21675
|
+
},
|
|
21676
|
+
"truncated_fields": {
|
|
21677
|
+
"type": "array",
|
|
21678
|
+
"items": {
|
|
21679
|
+
"type": "string",
|
|
21680
|
+
"enum": ["body_text", "body_html"]
|
|
21681
|
+
},
|
|
21682
|
+
"description": "With `include`: the body fields on this row that were cut or left out."
|
|
21683
|
+
}
|
|
21684
|
+
},
|
|
21685
|
+
"required": [
|
|
21686
|
+
"id",
|
|
21687
|
+
"status",
|
|
21688
|
+
"status_changed_at",
|
|
21689
|
+
"created_at",
|
|
21690
|
+
"updated_at",
|
|
21691
|
+
"content_hash",
|
|
21692
|
+
"from_header",
|
|
21693
|
+
"from_address",
|
|
21694
|
+
"to_header",
|
|
21695
|
+
"to_address",
|
|
21696
|
+
"subject",
|
|
21697
|
+
"body_size_bytes"
|
|
21698
|
+
]
|
|
21699
|
+
}, {
|
|
21700
|
+
"type": "object",
|
|
21701
|
+
"required": ["direction"],
|
|
21702
|
+
"properties": { "direction": {
|
|
21703
|
+
"type": "string",
|
|
21704
|
+
"enum": ["outbound"]
|
|
21705
|
+
} }
|
|
21706
|
+
}]
|
|
21707
|
+
}],
|
|
21708
|
+
"discriminator": {
|
|
21709
|
+
"propertyName": "direction",
|
|
21710
|
+
"mapping": {
|
|
21711
|
+
"inbound": "#/components/schemas/ChangeInboundMessage",
|
|
21712
|
+
"outbound": "#/components/schemas/ChangeOutboundMessage"
|
|
21713
|
+
}
|
|
21714
|
+
}
|
|
21715
|
+
}, { "type": "null" }],
|
|
21716
|
+
"description": "Present only when the request sets `expand=message`. For `email.visible`, `email.updated`, `sent_email.created`, `sent_email.updated`: the message as it reads at the time of this response (not at the time of the change), exactly as the list endpoints return it, marked by `direction`. Null for every other kind, and when the message is no longer readable by this credential (deleted, or withdrawn from view after the change)."
|
|
21717
|
+
}
|
|
21718
|
+
}
|
|
21719
|
+
},
|
|
21720
|
+
"description": "Oldest first, in commit order."
|
|
21721
|
+
},
|
|
21722
|
+
"next_cursor": {
|
|
21723
|
+
"type": "string",
|
|
21724
|
+
"description": "Always present. Send it unchanged as `since` next time, and store it only after applying the page. It is exactly the `since` you sent when nothing at all happened after it; it moves past changes this credential cannot see, so the page can be empty with a new cursor."
|
|
21725
|
+
},
|
|
21726
|
+
"has_more": {
|
|
21727
|
+
"type": "boolean",
|
|
21728
|
+
"description": "More changes are already pending: call again at once, without waiting."
|
|
21729
|
+
},
|
|
21730
|
+
"baseline": {
|
|
21731
|
+
"type": "boolean",
|
|
21732
|
+
"description": "True only for a `since=start` request, whose page is always empty."
|
|
21733
|
+
}
|
|
21734
|
+
}
|
|
21735
|
+
},
|
|
21736
|
+
"sdkName": "listChanges",
|
|
21737
|
+
"summary": "Long-poll what changed",
|
|
21738
|
+
"tag": "Changes",
|
|
21739
|
+
"tagCommand": "changes"
|
|
21740
|
+
},
|
|
19831
21741
|
{
|
|
19832
21742
|
"binaryResponse": false,
|
|
19833
21743
|
"bodyRequired": false,
|
|
@@ -22929,6 +24839,76 @@ const operationManifest = [
|
|
|
22929
24839
|
"tag": "Emails",
|
|
22930
24840
|
"tagCommand": "emails"
|
|
22931
24841
|
},
|
|
24842
|
+
{
|
|
24843
|
+
"binaryResponse": true,
|
|
24844
|
+
"bodyRequired": false,
|
|
24845
|
+
"command": "download-email-attachment-archive-member",
|
|
24846
|
+
"description": "Returns the decoded bytes of one regular file inside a zip, tar or tar.gz attachment, by its listed path. The archive's structure, every member path and every limit are checked as for the listing, but only the requested file is decoded and verified (its zip CRC-32, and X-Content-SHA256 of the bytes returned); list the archive to have every file decoded. Authenticate with an organization API key, an authorized organization OAuth token, or an address-bound agent connection key. A connected agent reads only mail received at its own address. Function credentials are not supported. Discarded content is unavailable.",
|
|
24847
|
+
"hasJsonBody": false,
|
|
24848
|
+
"method": "GET",
|
|
24849
|
+
"operationId": "downloadEmailAttachmentArchiveMember",
|
|
24850
|
+
"path": "/emails/{id}/attachments/{attachment}/members/{path}",
|
|
24851
|
+
"pathParams": [
|
|
24852
|
+
{
|
|
24853
|
+
"description": "Resource UUID",
|
|
24854
|
+
"enum": null,
|
|
24855
|
+
"name": "id",
|
|
24856
|
+
"required": true,
|
|
24857
|
+
"type": "string"
|
|
24858
|
+
},
|
|
24859
|
+
{
|
|
24860
|
+
"description": "The attachment, by its exact part_index when the value is digits only, otherwise by its exact filename (case-sensitive, compared after Unicode NFC normalization). A digits-only value always selects that part_index, even when another attachment has that name; read such a name with ?filename=.",
|
|
24861
|
+
"enum": null,
|
|
24862
|
+
"name": "attachment",
|
|
24863
|
+
"required": true,
|
|
24864
|
+
"type": "string"
|
|
24865
|
+
},
|
|
24866
|
+
{
|
|
24867
|
+
"description": "The member path exactly as listed, percent-encoded as one path segment (slashes as %2F).",
|
|
24868
|
+
"enum": null,
|
|
24869
|
+
"name": "path",
|
|
24870
|
+
"required": true,
|
|
24871
|
+
"type": "string"
|
|
24872
|
+
}
|
|
24873
|
+
],
|
|
24874
|
+
"queryParams": [],
|
|
24875
|
+
"requestSchema": null,
|
|
24876
|
+
"responseSchema": null,
|
|
24877
|
+
"sdkName": "downloadEmailAttachmentArchiveMember",
|
|
24878
|
+
"summary": "Download one file from inside a received zip or tar attachment",
|
|
24879
|
+
"tag": "Emails",
|
|
24880
|
+
"tagCommand": "emails"
|
|
24881
|
+
},
|
|
24882
|
+
{
|
|
24883
|
+
"binaryResponse": true,
|
|
24884
|
+
"bodyRequired": false,
|
|
24885
|
+
"command": "download-email-attachment-by-filename",
|
|
24886
|
+
"description": "Returns the same bytes as downloadEmailAttachmentPart, selecting the attachment by its exact filename. The path form `/emails/{id}/attachments/{filename}` also works for any name that is not digits only, which that form reads as a part_index. When several attachments share the filename the response is 409 attachment_name_ambiguous with every matching part_index, and nothing is returned. Authenticate with an organization API key, an authorized organization OAuth token, or an address-bound agent connection key. A connected agent reads only mail received at its own address. Function credentials are not supported. Discarded content is unavailable.",
|
|
24887
|
+
"hasJsonBody": false,
|
|
24888
|
+
"method": "GET",
|
|
24889
|
+
"operationId": "downloadEmailAttachmentByFilename",
|
|
24890
|
+
"path": "/emails/{id}/attachments",
|
|
24891
|
+
"pathParams": [{
|
|
24892
|
+
"description": "Resource UUID",
|
|
24893
|
+
"enum": null,
|
|
24894
|
+
"name": "id",
|
|
24895
|
+
"required": true,
|
|
24896
|
+
"type": "string"
|
|
24897
|
+
}],
|
|
24898
|
+
"queryParams": [{
|
|
24899
|
+
"description": "The attachment's exact filename from its metadata. Any name, digits only included. No other query parameter is accepted.",
|
|
24900
|
+
"enum": null,
|
|
24901
|
+
"name": "filename",
|
|
24902
|
+
"required": true,
|
|
24903
|
+
"type": "string"
|
|
24904
|
+
}],
|
|
24905
|
+
"requestSchema": null,
|
|
24906
|
+
"responseSchema": null,
|
|
24907
|
+
"sdkName": "downloadEmailAttachmentByFilename",
|
|
24908
|
+
"summary": "Download one received email attachment by filename",
|
|
24909
|
+
"tag": "Emails",
|
|
24910
|
+
"tagCommand": "emails"
|
|
24911
|
+
},
|
|
22932
24912
|
{
|
|
22933
24913
|
"binaryResponse": true,
|
|
22934
24914
|
"bodyRequired": false,
|
|
@@ -23158,6 +25138,11 @@ const operationManifest = [
|
|
|
23158
25138
|
"cursor": {
|
|
23159
25139
|
"type": "string",
|
|
23160
25140
|
"description": "Present only on a `since` read: the position to send as\n`since` on the next read. An opaque string: send it back\nverbatim and do not parse it or build one.\n"
|
|
25141
|
+
},
|
|
25142
|
+
"latest_message_id": {
|
|
25143
|
+
"type": ["string", "null"],
|
|
25144
|
+
"format": "uuid",
|
|
25145
|
+
"description": "The id to pass as `if_latest_message_id` when replying in this\nconversation: the message that joined the thread last among\nthose this credential can read, by the rule a conditional send\nchecks. It can differ from the last message in `messages`, which\nis ordered by timestamp, when mail joined the thread after a\nnewer-timestamped message. Present on a full read and a window\nread of a threaded conversation; null when no message in the\nthread is visible; absent on a `since` delta.\n"
|
|
23161
25146
|
}
|
|
23162
25147
|
},
|
|
23163
25148
|
"required": [
|
|
@@ -23626,6 +25611,14 @@ const operationManifest = [
|
|
|
23626
25611
|
"items": { "type": "string" },
|
|
23627
25612
|
"description": "Why `automated` is true, in rule order; empty when it is false.\nCurrent values: `null_envelope_sender`, `no_identifiable_sender`,\n`own_address`, `mailer_daemon`, `auto_submitted`, `precedence`,\n`list_unsubscribe`, `list_id`, `auto_response_suppress`,\n`failed_recipients`, `report`. Treat an unfamiliar value as a\nreason added after your client was built.\n"
|
|
23628
25613
|
},
|
|
25614
|
+
"from_verified": {
|
|
25615
|
+
"type": "boolean",
|
|
25616
|
+
"description": "Whether the From header domain was authenticated when the message arrived. See EmailSummary.from_verified."
|
|
25617
|
+
},
|
|
25618
|
+
"from_header_email": {
|
|
25619
|
+
"type": ["string", "null"],
|
|
25620
|
+
"description": "The From header address, lowercased, that `from_verified` is about; null when the message had none. Unlike `sender` it is never the envelope address."
|
|
25621
|
+
},
|
|
23629
25622
|
"repeat": {
|
|
23630
25623
|
"type": ["object", "null"],
|
|
23631
25624
|
"description": "Set when Primitive sent this message as part of a repeating send. Resolved from Primitive's own records, never from the message content. Null on every other message and on servers that predate repeating sends.",
|
|
@@ -23756,6 +25749,91 @@ const operationManifest = [
|
|
|
23756
25749
|
"tag": "Emails",
|
|
23757
25750
|
"tagCommand": "emails"
|
|
23758
25751
|
},
|
|
25752
|
+
{
|
|
25753
|
+
"binaryResponse": false,
|
|
25754
|
+
"bodyRequired": false,
|
|
25755
|
+
"command": "list-email-attachment-archive-members",
|
|
25756
|
+
"description": "Lists the regular files inside a zip, tar or tar.gz attachment with each one's path, decoded size and SHA-256. The format is detected from the content. Unsafe paths refuse the whole archive, and resource limits bound the work (see the 413 and 422 responses). Authenticate with an organization API key, an authorized organization OAuth token, or an address-bound agent connection key. A connected agent reads only mail received at its own address. Function credentials are not supported. Discarded content is unavailable.",
|
|
25757
|
+
"hasJsonBody": false,
|
|
25758
|
+
"method": "GET",
|
|
25759
|
+
"operationId": "listEmailAttachmentArchiveMembers",
|
|
25760
|
+
"path": "/emails/{id}/attachments/{attachment}/members",
|
|
25761
|
+
"pathParams": [{
|
|
25762
|
+
"description": "Resource UUID",
|
|
25763
|
+
"enum": null,
|
|
25764
|
+
"name": "id",
|
|
25765
|
+
"required": true,
|
|
25766
|
+
"type": "string"
|
|
25767
|
+
}, {
|
|
25768
|
+
"description": "The attachment, by its exact part_index when the value is digits only, otherwise by its exact filename (case-sensitive, compared after Unicode NFC normalization). A digits-only value always selects that part_index, even when another attachment has that name; read such a name with ?filename=.",
|
|
25769
|
+
"enum": null,
|
|
25770
|
+
"name": "attachment",
|
|
25771
|
+
"required": true,
|
|
25772
|
+
"type": "string"
|
|
25773
|
+
}],
|
|
25774
|
+
"queryParams": [],
|
|
25775
|
+
"requestSchema": null,
|
|
25776
|
+
"responseSchema": {
|
|
25777
|
+
"type": "object",
|
|
25778
|
+
"required": [
|
|
25779
|
+
"part_index",
|
|
25780
|
+
"filename",
|
|
25781
|
+
"format",
|
|
25782
|
+
"members"
|
|
25783
|
+
],
|
|
25784
|
+
"properties": {
|
|
25785
|
+
"part_index": {
|
|
25786
|
+
"type": "integer",
|
|
25787
|
+
"minimum": 0,
|
|
25788
|
+
"description": "The attachment the listing is of."
|
|
25789
|
+
},
|
|
25790
|
+
"filename": {
|
|
25791
|
+
"type": ["string", "null"],
|
|
25792
|
+
"description": "That attachment's filename."
|
|
25793
|
+
},
|
|
25794
|
+
"format": {
|
|
25795
|
+
"type": "string",
|
|
25796
|
+
"enum": [
|
|
25797
|
+
"zip",
|
|
25798
|
+
"tar",
|
|
25799
|
+
"tar.gz"
|
|
25800
|
+
]
|
|
25801
|
+
},
|
|
25802
|
+
"members": {
|
|
25803
|
+
"type": "array",
|
|
25804
|
+
"description": "Regular files in archive order.",
|
|
25805
|
+
"items": {
|
|
25806
|
+
"type": "object",
|
|
25807
|
+
"required": [
|
|
25808
|
+
"path",
|
|
25809
|
+
"size_bytes",
|
|
25810
|
+
"sha256"
|
|
25811
|
+
],
|
|
25812
|
+
"properties": {
|
|
25813
|
+
"path": {
|
|
25814
|
+
"type": "string",
|
|
25815
|
+
"description": "Member path inside the archive, relative, with \"/\" separators and no leading \"./\"."
|
|
25816
|
+
},
|
|
25817
|
+
"size_bytes": {
|
|
25818
|
+
"type": "integer",
|
|
25819
|
+
"minimum": 0,
|
|
25820
|
+
"description": "Decoded size in bytes."
|
|
25821
|
+
},
|
|
25822
|
+
"sha256": {
|
|
25823
|
+
"type": "string",
|
|
25824
|
+
"pattern": "^[0-9a-f]{64}$",
|
|
25825
|
+
"description": "SHA-256 of the decoded bytes."
|
|
25826
|
+
}
|
|
25827
|
+
}
|
|
25828
|
+
}
|
|
25829
|
+
}
|
|
25830
|
+
}
|
|
25831
|
+
},
|
|
25832
|
+
"sdkName": "listEmailAttachmentArchiveMembers",
|
|
25833
|
+
"summary": "List the files inside a received zip or tar attachment",
|
|
25834
|
+
"tag": "Emails",
|
|
25835
|
+
"tagCommand": "emails"
|
|
25836
|
+
},
|
|
23759
25837
|
{
|
|
23760
25838
|
"binaryResponse": false,
|
|
23761
25839
|
"bodyRequired": false,
|
|
@@ -23849,6 +25927,13 @@ const operationManifest = [
|
|
|
23849
25927
|
"required": false,
|
|
23850
25928
|
"type": "string"
|
|
23851
25929
|
},
|
|
25930
|
+
{
|
|
25931
|
+
"description": "Only return emails whose `from_verified` (the From domain was authenticated on arrival; see the field) has this value. Combines with every other filter and with both `cursor` and `since`.",
|
|
25932
|
+
"enum": ["true", "false"],
|
|
25933
|
+
"name": "from_verified",
|
|
25934
|
+
"required": false,
|
|
25935
|
+
"type": "string"
|
|
25936
|
+
},
|
|
23852
25937
|
{
|
|
23853
25938
|
"description": "Exact case-insensitive delivered recipient mailbox, applied before pagination. Combines with existing filters and cursors; does not search message text.",
|
|
23854
25939
|
"enum": null,
|
|
@@ -23862,6 +25947,13 @@ const operationManifest = [
|
|
|
23862
25947
|
"name": "exclude_fyi",
|
|
23863
25948
|
"required": false,
|
|
23864
25949
|
"type": "string"
|
|
25950
|
+
},
|
|
25951
|
+
{
|
|
25952
|
+
"description": "Extra parts on every row, comma-separated: `body_text`, `body_html`, `headers`, `attachments`. Reads a page of messages in one request instead of one detail read per row. Parts are withheld where the detail read withholds them. Each body field is cut to 64 KiB per row and a response carries at most 2048 KiB of body text, in page order; a cut or omitted body is named in `truncated_fields`. Unknown values are a `validation_error`.",
|
|
25953
|
+
"enum": null,
|
|
25954
|
+
"name": "include",
|
|
25955
|
+
"required": false,
|
|
25956
|
+
"type": "string"
|
|
23865
25957
|
}
|
|
23866
25958
|
],
|
|
23867
25959
|
"requestSchema": null,
|
|
@@ -23983,6 +26075,14 @@ const operationManifest = [
|
|
|
23983
26075
|
"items": { "type": "string" },
|
|
23984
26076
|
"description": "Why `automated` is true, in rule order; empty when it is false.\nCurrent values: `null_envelope_sender`, `no_identifiable_sender`,\n`own_address`, `mailer_daemon`, `auto_submitted`, `precedence`,\n`list_unsubscribe`, `list_id`, `auto_response_suppress`,\n`failed_recipients`, `report`. Treat an unfamiliar value as a\nreason added after your client was built.\n"
|
|
23985
26077
|
},
|
|
26078
|
+
"from_verified": {
|
|
26079
|
+
"type": ["boolean", "null"],
|
|
26080
|
+
"description": "True when the From header address, `from_header_email`, passed DMARC for its domain on arrival (aligned SPF or DKIM); decided once, at receipt. It is about the From header, never the envelope: `sender`, the `from` parameter and `from:` term, and the change feed's `address` match the envelope sender, which can differ. It proves the domain, not the mailbox. False with no From header address, when authentication failed or was not attempted (a delivery between Primitive mailboxes counts only when it passed DMARC too), and for mail stored before the verdict was recorded. Null on sent rows in search results. Not a spam or safety judgement."
|
|
26081
|
+
},
|
|
26082
|
+
"from_header_email": {
|
|
26083
|
+
"type": ["string", "null"],
|
|
26084
|
+
"description": "The From header address, lowercased, that `from_verified` is about; null when the message had none. Unlike `sender` it is never the envelope address."
|
|
26085
|
+
},
|
|
23986
26086
|
"repeat": {
|
|
23987
26087
|
"type": ["object", "null"],
|
|
23988
26088
|
"description": "Set when Primitive sent this message as part of a repeating send. Resolved from Primitive's own records, never from the message content. Null on every other message and on servers that predate repeating sends.",
|
|
@@ -24049,6 +26149,83 @@ const operationManifest = [
|
|
|
24049
26149
|
"from_name": {
|
|
24050
26150
|
"type": ["string", "null"],
|
|
24051
26151
|
"description": "Display name from the `From:` header, decoded for display:\nRFC 2047 encoded words are decoded in their declared charset\nand folded lines are joined (e.g. `Dana Rúiz` for\n`=?UTF-8?Q?Dana_R=C3=BAiz?= <dana@example.com>`). Null when\nthe header carries no display name, and may be absent; treat\na missing value the same as null.\n\nFor display only: the sender chooses this name freely, so\nidentify the sender by `from_email`, never by this name.\n"
|
|
26152
|
+
},
|
|
26153
|
+
"body_text": {
|
|
26154
|
+
"type": ["string", "null"],
|
|
26155
|
+
"description": "With `include=body_text`: the plain-text body, cut as `truncated_fields` says."
|
|
26156
|
+
},
|
|
26157
|
+
"body_html": {
|
|
26158
|
+
"type": ["string", "null"],
|
|
26159
|
+
"description": "With `include=body_html`: the HTML body, cut as `truncated_fields` says."
|
|
26160
|
+
},
|
|
26161
|
+
"headers": {
|
|
26162
|
+
"anyOf": [{
|
|
26163
|
+
"type": "object",
|
|
26164
|
+
"description": "Stored addressing and threading headers. A member is null when absent or withheld.",
|
|
26165
|
+
"properties": {
|
|
26166
|
+
"from": { "type": ["string", "null"] },
|
|
26167
|
+
"to": {
|
|
26168
|
+
"type": ["array", "null"],
|
|
26169
|
+
"items": { "type": "string" }
|
|
26170
|
+
},
|
|
26171
|
+
"cc": {
|
|
26172
|
+
"type": ["array", "null"],
|
|
26173
|
+
"items": { "type": "string" }
|
|
26174
|
+
},
|
|
26175
|
+
"bcc": {
|
|
26176
|
+
"type": ["array", "null"],
|
|
26177
|
+
"items": { "type": "string" }
|
|
26178
|
+
},
|
|
26179
|
+
"reply_to": {
|
|
26180
|
+
"type": ["array", "null"],
|
|
26181
|
+
"items": { "type": "string" }
|
|
26182
|
+
},
|
|
26183
|
+
"in_reply_to": {
|
|
26184
|
+
"type": ["array", "null"],
|
|
26185
|
+
"items": { "type": "string" }
|
|
26186
|
+
},
|
|
26187
|
+
"references": {
|
|
26188
|
+
"type": ["array", "null"],
|
|
26189
|
+
"items": { "type": "string" }
|
|
26190
|
+
},
|
|
26191
|
+
"automation": {
|
|
26192
|
+
"type": ["object", "null"],
|
|
26193
|
+
"additionalProperties": { "type": "string" },
|
|
26194
|
+
"description": "Received mail only."
|
|
26195
|
+
}
|
|
26196
|
+
},
|
|
26197
|
+
"required": [
|
|
26198
|
+
"from",
|
|
26199
|
+
"to",
|
|
26200
|
+
"cc",
|
|
26201
|
+
"bcc",
|
|
26202
|
+
"reply_to",
|
|
26203
|
+
"in_reply_to",
|
|
26204
|
+
"references",
|
|
26205
|
+
"automation"
|
|
26206
|
+
]
|
|
26207
|
+
}, { "type": "null" }],
|
|
26208
|
+
"description": "With `include=headers`: stored addressing and threading headers."
|
|
26209
|
+
},
|
|
26210
|
+
"attachments": {
|
|
26211
|
+
"type": "array",
|
|
26212
|
+
"items": {
|
|
26213
|
+
"type": "object",
|
|
26214
|
+
"additionalProperties": true
|
|
26215
|
+
},
|
|
26216
|
+
"description": "With `include=attachments`: attachment metadata as the detail read reports it. Never the bytes."
|
|
26217
|
+
},
|
|
26218
|
+
"truncated": {
|
|
26219
|
+
"type": "boolean",
|
|
26220
|
+
"description": "With `include`: true when a body on this row was cut or left out."
|
|
26221
|
+
},
|
|
26222
|
+
"truncated_fields": {
|
|
26223
|
+
"type": "array",
|
|
26224
|
+
"items": {
|
|
26225
|
+
"type": "string",
|
|
26226
|
+
"enum": ["body_text", "body_html"]
|
|
26227
|
+
},
|
|
26228
|
+
"description": "With `include`: the body fields on this row that were cut or left out."
|
|
24052
26229
|
}
|
|
24053
26230
|
},
|
|
24054
26231
|
"required": [
|
|
@@ -24122,7 +26299,7 @@ const operationManifest = [
|
|
|
24122
26299
|
"pathParams": [],
|
|
24123
26300
|
"queryParams": [
|
|
24124
26301
|
{
|
|
24125
|
-
"description": "Full-text search DSL query. Supports `awaiting:you` and `awaiting:them` to filter on reply state (see the `awaiting` field; delivered mail only, never `rejected`). Supports `automated:true` and `automated:false` to filter on the `automated` field. Terms are separated by spaces and every term must match. A term is a word, a `\"quoted phrase\"`, or a `field:value` filter (`from:`, `to:`, `subject:`, `body:`, `has:attachment`, `before:`, `after:`, `domain:`, `status:`, `awaiting:`, `automated:`). An upper-case `OR` between two text terms (words, phrases, `subject:` and `body:` terms) accepts either, and binds tighter than the implicit AND: `acme invoice OR receipt` is `acme` and either `invoice` or `receipt`. `OR` cannot join the other filters, and there is no `NOT` and no parentheses. A query that uses an unsupported operator, an unknown field, an unterminated quote, or only stop words is a `validation_error` that says what to change; it is never silently reinterpreted. Alternatives chain (`invoice OR receipt OR \"past due\"`), and each one counts toward the limit of 32 terms. These are rejected with a 400 `validation_error`: `OR` next to a filter (`from:a@example.com OR from:b@example.com`; run one search per value instead); an `OR` with no term on one side (a leading, trailing or doubled `OR`); an upper-case `NOT` (a search cannot exclude a term); and an alternative with no searchable word once stop words are removed (`the OR invoice`). Only upper case is an operator: to search for the word `OR` or `NOT` itself, write it in lower case or in double quotes (`\"OR\"`). With `prefix=true`, a trailing `OR` or `NOT` is read as a word still being typed. To search for text that contains a colon, such as a URL, put it in double quotes.",
|
|
26302
|
+
"description": "Full-text search DSL query. Supports `awaiting:you` and `awaiting:them` to filter on reply state (see the `awaiting` field; delivered mail only, never `rejected`). Supports `automated:true` and `automated:false` to filter on the `automated` field. Terms are separated by spaces and every term must match. A term is a word, a `\"quoted phrase\"`, or a `field:value` filter (`from:`, `to:`, `subject:`, `body:`, `has:attachment`, `before:`, `after:`, `domain:`, `status:`, `awaiting:`, `automated:`, `header:`, `has-header:`). An upper-case `OR` between two text terms (words, phrases, `subject:` and `body:` terms) accepts either, and binds tighter than the implicit AND: `acme invoice OR receipt` is `acme` and either `invoice` or `receipt`. `OR` cannot join the other filters, and there is no `NOT` and no parentheses. A query that uses an unsupported operator, an unknown field, an unterminated quote, or only stop words is a `validation_error` that says what to change; it is never silently reinterpreted. Alternatives chain (`invoice OR receipt OR \"past due\"`), and each one counts toward the limit of 32 terms. These are rejected with a 400 `validation_error`: `OR` next to a filter (`from:a@example.com OR from:b@example.com`; run one search per value instead); an `OR` with no term on one side (a leading, trailing or doubled `OR`); an upper-case `NOT` (a search cannot exclude a term); and an alternative with no searchable word once stop words are removed (`the OR invoice`). Only upper case is an operator: to search for the word `OR` or `NOT` itself, write it in lower case or in double quotes (`\"OR\"`). With `prefix=true`, a trailing `OR` or `NOT` is read as a word still being typed. To search for text that contains a colon, such as a URL, put it in double quotes. Supports `from_verified:true` and `from_verified:false` to filter on the `from_verified` field. `header:Name=Value` matches mail whose header `Name` has exactly `Value` (name and value case-insensitive, the whole value, quote the term for a value with spaces: `header:\"List-Id=Team news <news.example.com>\"`); `has-header:Name` matches mail that carries header `Name` with any value. Neither can be an `OR` alternative. Header filters read a stored copy of each message's headers: for received mail every header except trace and signature headers (`Received`, `DKIM-Signature`, `ARC-*`, `Authentication-Results` and similar) and credential-like names (a word such as `auth`, `token`, `key`, `secret`, `cookie`, `session`, `bearer`, `jwt` or `hmac`, including joined forms such as `AuthToken` or `SessionId`), at most 100 headers and 16 KiB per message, each value cut to 1024 bytes; for sent mail the `headers` the request set. A filter on a header that is never stored is a `validation_error`. Mail stored before header search was available has no stored headers and is not matched.",
|
|
24126
26303
|
"enum": null,
|
|
24127
26304
|
"name": "q",
|
|
24128
26305
|
"required": false,
|
|
@@ -24226,6 +26403,13 @@ const operationManifest = [
|
|
|
24226
26403
|
"required": false,
|
|
24227
26404
|
"type": "string"
|
|
24228
26405
|
},
|
|
26406
|
+
{
|
|
26407
|
+
"description": "Only return emails whose `from_verified` (the From domain was authenticated on arrival; see the field) has this value. Also available in `q` as `from_verified:true` or `from_verified:false`.",
|
|
26408
|
+
"enum": ["true", "false"],
|
|
26409
|
+
"name": "from_verified",
|
|
26410
|
+
"required": false,
|
|
26411
|
+
"type": "string"
|
|
26412
|
+
},
|
|
24229
26413
|
{
|
|
24230
26414
|
"description": "Sort mode. Defaults to relevance when a text query is present,\notherwise `received_at_desc`.\n",
|
|
24231
26415
|
"enum": [
|
|
@@ -24292,6 +26476,25 @@ const operationManifest = [
|
|
|
24292
26476
|
"name": "count",
|
|
24293
26477
|
"required": false,
|
|
24294
26478
|
"type": "string"
|
|
26479
|
+
},
|
|
26480
|
+
{
|
|
26481
|
+
"default": "inbound",
|
|
26482
|
+
"description": "Which mail to search: `inbound` (mail received by the organization, the default), `outbound` (mail it sent) or `any` (both, merged in date order). Each result carries `direction`. When sent mail is included, results are in date order only (`sort=relevance` is a `validation_error`), filters that describe received mail only (`status`, `spam_score_lt`, `spam_score_gte`, `awaiting`, `automated`, `reply_to_sent_email_id`, `from_verified`, and `status:`, `awaiting:`, `automated:`, `from_verified:` in `q`) are a `validation_error`, as is `include`, and `facets` are not computed. `meta.total` is computed on the first page only and is null on later pages. A `meta.cursor` continues the direction it was issued for: using it with another `direction` is a `validation_error`. Text search over sent mail (`q`, `subject` or `body`) answers 503 `sent_mail_search_unavailable` in an environment where sent-mail search is not enabled. An agent connection searches only the mail its own address sent. On a sent result, `received_at` is when the message was accepted for sending, `recipient` is its first To address, and fields that describe receiving (`spam_score`, `raw_size_bytes`, `webhook_status`) are null, as is `from_verified` (sent mail has no From-authentication verdict). A sent result carries its sending status (`queued`, `delivered`, `bounced` and so on) in `delivery_status`; its `status` stays in the received-mail vocabulary as the nearest reading of it: `pending` (queued, scheduled, deferred), `accepted` (handed off, outcome not confirmed), `completed` (delivered) or `rejected` (not delivered: failed, denied, bounced or canceled).",
|
|
26483
|
+
"enum": [
|
|
26484
|
+
"inbound",
|
|
26485
|
+
"outbound",
|
|
26486
|
+
"any"
|
|
26487
|
+
],
|
|
26488
|
+
"name": "direction",
|
|
26489
|
+
"required": false,
|
|
26490
|
+
"type": "string"
|
|
26491
|
+
},
|
|
26492
|
+
{
|
|
26493
|
+
"description": "Extra parts on every row, comma-separated: `body_text`, `body_html`, `headers`, `attachments`. Reads a page of messages in one request instead of one detail read per row. Parts are withheld where the detail read withholds them. Each body field is cut to 64 KiB per row and a response carries at most 2048 KiB of body text, in page order; a cut or omitted body is named in `truncated_fields`. Unknown values are a `validation_error`.",
|
|
26494
|
+
"enum": null,
|
|
26495
|
+
"name": "include",
|
|
26496
|
+
"required": false,
|
|
26497
|
+
"type": "string"
|
|
24295
26498
|
}
|
|
24296
26499
|
],
|
|
24297
26500
|
"requestSchema": null,
|
|
@@ -24413,6 +26616,14 @@ const operationManifest = [
|
|
|
24413
26616
|
"items": { "type": "string" },
|
|
24414
26617
|
"description": "Why `automated` is true, in rule order; empty when it is false.\nCurrent values: `null_envelope_sender`, `no_identifiable_sender`,\n`own_address`, `mailer_daemon`, `auto_submitted`, `precedence`,\n`list_unsubscribe`, `list_id`, `auto_response_suppress`,\n`failed_recipients`, `report`. Treat an unfamiliar value as a\nreason added after your client was built.\n"
|
|
24415
26618
|
},
|
|
26619
|
+
"from_verified": {
|
|
26620
|
+
"type": ["boolean", "null"],
|
|
26621
|
+
"description": "True when the From header address, `from_header_email`, passed DMARC for its domain on arrival (aligned SPF or DKIM); decided once, at receipt. It is about the From header, never the envelope: `sender`, the `from` parameter and `from:` term, and the change feed's `address` match the envelope sender, which can differ. It proves the domain, not the mailbox. False with no From header address, when authentication failed or was not attempted (a delivery between Primitive mailboxes counts only when it passed DMARC too), and for mail stored before the verdict was recorded. Null on sent rows in search results. Not a spam or safety judgement."
|
|
26622
|
+
},
|
|
26623
|
+
"from_header_email": {
|
|
26624
|
+
"type": ["string", "null"],
|
|
26625
|
+
"description": "The From header address, lowercased, that `from_verified` is about; null when the message had none. Unlike `sender` it is never the envelope address."
|
|
26626
|
+
},
|
|
24416
26627
|
"repeat": {
|
|
24417
26628
|
"type": ["object", "null"],
|
|
24418
26629
|
"description": "Set when Primitive sent this message as part of a repeating send. Resolved from Primitive's own records, never from the message content. Null on every other message and on servers that predate repeating sends.",
|
|
@@ -24479,6 +26690,83 @@ const operationManifest = [
|
|
|
24479
26690
|
"from_name": {
|
|
24480
26691
|
"type": ["string", "null"],
|
|
24481
26692
|
"description": "Display name from the `From:` header, decoded for display:\nRFC 2047 encoded words are decoded in their declared charset\nand folded lines are joined (e.g. `Dana Rúiz` for\n`=?UTF-8?Q?Dana_R=C3=BAiz?= <dana@example.com>`). Null when\nthe header carries no display name, and may be absent; treat\na missing value the same as null.\n\nFor display only: the sender chooses this name freely, so\nidentify the sender by `from_email`, never by this name.\n"
|
|
26693
|
+
},
|
|
26694
|
+
"body_text": {
|
|
26695
|
+
"type": ["string", "null"],
|
|
26696
|
+
"description": "With `include=body_text`: the plain-text body, cut as `truncated_fields` says."
|
|
26697
|
+
},
|
|
26698
|
+
"body_html": {
|
|
26699
|
+
"type": ["string", "null"],
|
|
26700
|
+
"description": "With `include=body_html`: the HTML body, cut as `truncated_fields` says."
|
|
26701
|
+
},
|
|
26702
|
+
"headers": {
|
|
26703
|
+
"anyOf": [{
|
|
26704
|
+
"type": "object",
|
|
26705
|
+
"description": "Stored addressing and threading headers. A member is null when absent or withheld.",
|
|
26706
|
+
"properties": {
|
|
26707
|
+
"from": { "type": ["string", "null"] },
|
|
26708
|
+
"to": {
|
|
26709
|
+
"type": ["array", "null"],
|
|
26710
|
+
"items": { "type": "string" }
|
|
26711
|
+
},
|
|
26712
|
+
"cc": {
|
|
26713
|
+
"type": ["array", "null"],
|
|
26714
|
+
"items": { "type": "string" }
|
|
26715
|
+
},
|
|
26716
|
+
"bcc": {
|
|
26717
|
+
"type": ["array", "null"],
|
|
26718
|
+
"items": { "type": "string" }
|
|
26719
|
+
},
|
|
26720
|
+
"reply_to": {
|
|
26721
|
+
"type": ["array", "null"],
|
|
26722
|
+
"items": { "type": "string" }
|
|
26723
|
+
},
|
|
26724
|
+
"in_reply_to": {
|
|
26725
|
+
"type": ["array", "null"],
|
|
26726
|
+
"items": { "type": "string" }
|
|
26727
|
+
},
|
|
26728
|
+
"references": {
|
|
26729
|
+
"type": ["array", "null"],
|
|
26730
|
+
"items": { "type": "string" }
|
|
26731
|
+
},
|
|
26732
|
+
"automation": {
|
|
26733
|
+
"type": ["object", "null"],
|
|
26734
|
+
"additionalProperties": { "type": "string" },
|
|
26735
|
+
"description": "Received mail only."
|
|
26736
|
+
}
|
|
26737
|
+
},
|
|
26738
|
+
"required": [
|
|
26739
|
+
"from",
|
|
26740
|
+
"to",
|
|
26741
|
+
"cc",
|
|
26742
|
+
"bcc",
|
|
26743
|
+
"reply_to",
|
|
26744
|
+
"in_reply_to",
|
|
26745
|
+
"references",
|
|
26746
|
+
"automation"
|
|
26747
|
+
]
|
|
26748
|
+
}, { "type": "null" }],
|
|
26749
|
+
"description": "With `include=headers`: stored addressing and threading headers."
|
|
26750
|
+
},
|
|
26751
|
+
"attachments": {
|
|
26752
|
+
"type": "array",
|
|
26753
|
+
"items": {
|
|
26754
|
+
"type": "object",
|
|
26755
|
+
"additionalProperties": true
|
|
26756
|
+
},
|
|
26757
|
+
"description": "With `include=attachments`: attachment metadata as the detail read reports it. Never the bytes."
|
|
26758
|
+
},
|
|
26759
|
+
"truncated": {
|
|
26760
|
+
"type": "boolean",
|
|
26761
|
+
"description": "With `include`: true when a body on this row was cut or left out."
|
|
26762
|
+
},
|
|
26763
|
+
"truncated_fields": {
|
|
26764
|
+
"type": "array",
|
|
26765
|
+
"items": {
|
|
26766
|
+
"type": "string",
|
|
26767
|
+
"enum": ["body_text", "body_html"]
|
|
26768
|
+
},
|
|
26769
|
+
"description": "With `include`: the body fields on this row that were cut or left out."
|
|
24482
26770
|
}
|
|
24483
26771
|
},
|
|
24484
26772
|
"required": [
|
|
@@ -24535,7 +26823,24 @@ const operationManifest = [
|
|
|
24535
26823
|
"direction": {
|
|
24536
26824
|
"type": "string",
|
|
24537
26825
|
"enum": ["inbound", "outbound"],
|
|
24538
|
-
"description": "Which side of the conversation the result is on: `inbound` for mail received by the organization, `outbound` for mail it sent.
|
|
26826
|
+
"description": "Which side of the conversation the result is on: `inbound` for mail received by the organization, `outbound` for mail it sent. Only `direction=outbound` or `direction=any` returns `outbound` results. Treat an unfamiliar value as one added after your client was built. An `outbound` result also carries `delivery_status`."
|
|
26827
|
+
},
|
|
26828
|
+
"delivery_status": {
|
|
26829
|
+
"type": "string",
|
|
26830
|
+
"description": "Lifecycle status of a sent_emails row. Possible values:\n\n - `queued`: pre-call INSERT; the outbound agent has not\n yet replied.\n - `submitted_to_agent`: agent accepted; `queue_id` is set.\n - `agent_failed`: agent rejected; `error_code` and\n `error_message` carry the reason.\n - `gate_denied`: a recipient-scope gate denied the send;\n the agent was never called. The `gates` array carries\n the denial detail. /send-mail returns 403 in this case\n so callers see the denial synchronously; /sent-emails\n additionally records the row for historical lookup,\n which is when this status appears in a listing.\n - `unknown`: terminal indeterminate; the on-box log\n poller couldn't classify the receiver's response.\n - `delivered` / `bounced` / `deferred` / `wait_timeout`:\n terminal delivery outcomes (see DeliveryStatus).\n - `scheduled`: created with a future `scheduled_at` and\n not yet executed; `scheduled_at` carries the pending\n execution time. Reschedulable via PATCH\n /sent-emails/{id} and cancelable via\n /sent-emails/{id}/cancel while in this status.\n - `canceled`: terminal; a scheduled send canceled before\n execution. `canceled_at` carries the cancellation time\n and nothing was dispatched.\n",
|
|
26831
|
+
"enum": [
|
|
26832
|
+
"queued",
|
|
26833
|
+
"submitted_to_agent",
|
|
26834
|
+
"agent_failed",
|
|
26835
|
+
"gate_denied",
|
|
26836
|
+
"unknown",
|
|
26837
|
+
"delivered",
|
|
26838
|
+
"bounced",
|
|
26839
|
+
"deferred",
|
|
26840
|
+
"wait_timeout",
|
|
26841
|
+
"scheduled",
|
|
26842
|
+
"canceled"
|
|
26843
|
+
]
|
|
24539
26844
|
}
|
|
24540
26845
|
},
|
|
24541
26846
|
"required": [
|
|
@@ -31695,25 +34000,127 @@ const operationManifest = [
|
|
|
31695
34000
|
"user_id",
|
|
31696
34001
|
"name"
|
|
31697
34002
|
]
|
|
31698
|
-
}
|
|
31699
|
-
|
|
31700
|
-
|
|
31701
|
-
|
|
31702
|
-
|
|
31703
|
-
"
|
|
31704
|
-
|
|
31705
|
-
|
|
31706
|
-
|
|
31707
|
-
"
|
|
31708
|
-
|
|
31709
|
-
|
|
31710
|
-
|
|
31711
|
-
|
|
31712
|
-
|
|
31713
|
-
|
|
31714
|
-
|
|
31715
|
-
|
|
31716
|
-
|
|
34003
|
+
},
|
|
34004
|
+
"body_text": {
|
|
34005
|
+
"type": ["string", "null"],
|
|
34006
|
+
"description": "Plain-text body sent on the wire. Null when the\nsend carried only an HTML body, or when bodies have\nbeen discarded post-send (`content_discarded_at`\nset).\n"
|
|
34007
|
+
},
|
|
34008
|
+
"body_html": {
|
|
34009
|
+
"type": ["string", "null"],
|
|
34010
|
+
"description": "HTML body sent on the wire. Null when the send\ncarried only a plain-text body, or when bodies\nhave been discarded post-send.\n"
|
|
34011
|
+
},
|
|
34012
|
+
"headers": {
|
|
34013
|
+
"anyOf": [{
|
|
34014
|
+
"type": "object",
|
|
34015
|
+
"description": "Stored addressing and threading headers. A member is null when absent or withheld.",
|
|
34016
|
+
"properties": {
|
|
34017
|
+
"from": { "type": ["string", "null"] },
|
|
34018
|
+
"to": {
|
|
34019
|
+
"type": ["array", "null"],
|
|
34020
|
+
"items": { "type": "string" }
|
|
34021
|
+
},
|
|
34022
|
+
"cc": {
|
|
34023
|
+
"type": ["array", "null"],
|
|
34024
|
+
"items": { "type": "string" }
|
|
34025
|
+
},
|
|
34026
|
+
"bcc": {
|
|
34027
|
+
"type": ["array", "null"],
|
|
34028
|
+
"items": { "type": "string" }
|
|
34029
|
+
},
|
|
34030
|
+
"reply_to": {
|
|
34031
|
+
"type": ["array", "null"],
|
|
34032
|
+
"items": { "type": "string" }
|
|
34033
|
+
},
|
|
34034
|
+
"in_reply_to": {
|
|
34035
|
+
"type": ["array", "null"],
|
|
34036
|
+
"items": { "type": "string" }
|
|
34037
|
+
},
|
|
34038
|
+
"references": {
|
|
34039
|
+
"type": ["array", "null"],
|
|
34040
|
+
"items": { "type": "string" }
|
|
34041
|
+
},
|
|
34042
|
+
"automation": {
|
|
34043
|
+
"type": ["object", "null"],
|
|
34044
|
+
"additionalProperties": { "type": "string" },
|
|
34045
|
+
"description": "Received mail only."
|
|
34046
|
+
}
|
|
34047
|
+
},
|
|
34048
|
+
"required": [
|
|
34049
|
+
"from",
|
|
34050
|
+
"to",
|
|
34051
|
+
"cc",
|
|
34052
|
+
"bcc",
|
|
34053
|
+
"reply_to",
|
|
34054
|
+
"in_reply_to",
|
|
34055
|
+
"references",
|
|
34056
|
+
"automation"
|
|
34057
|
+
]
|
|
34058
|
+
}, { "type": "null" }],
|
|
34059
|
+
"description": "With `include=headers`: stored addressing and threading headers."
|
|
34060
|
+
},
|
|
34061
|
+
"attachments": {
|
|
34062
|
+
"type": "array",
|
|
34063
|
+
"description": "Metadata for submitted inline attachments; download availability is reported separately. Metadata may exist when the archive is unavailable, and legacy sends may have no metadata. Offloaded payload files are not included in the inline archive.",
|
|
34064
|
+
"items": {
|
|
34065
|
+
"type": "object",
|
|
34066
|
+
"description": "Metadata for one submitted inline attachment.",
|
|
34067
|
+
"additionalProperties": true,
|
|
34068
|
+
"properties": {
|
|
34069
|
+
"filename": { "type": ["string", "null"] },
|
|
34070
|
+
"content_type": { "type": "string" },
|
|
34071
|
+
"size_bytes": {
|
|
34072
|
+
"type": "integer",
|
|
34073
|
+
"minimum": 0,
|
|
34074
|
+
"maximum": 9007199254740991
|
|
34075
|
+
},
|
|
34076
|
+
"sha256": { "type": "string" },
|
|
34077
|
+
"part_index": {
|
|
34078
|
+
"type": "integer",
|
|
34079
|
+
"minimum": 0,
|
|
34080
|
+
"maximum": 9007199254740991
|
|
34081
|
+
},
|
|
34082
|
+
"tar_path": { "type": "string" }
|
|
34083
|
+
},
|
|
34084
|
+
"required": [
|
|
34085
|
+
"filename",
|
|
34086
|
+
"content_type",
|
|
34087
|
+
"size_bytes",
|
|
34088
|
+
"sha256",
|
|
34089
|
+
"part_index",
|
|
34090
|
+
"tar_path"
|
|
34091
|
+
]
|
|
34092
|
+
}
|
|
34093
|
+
},
|
|
34094
|
+
"truncated": {
|
|
34095
|
+
"type": "boolean",
|
|
34096
|
+
"description": "With `include`: true when a body on this row was cut or left out."
|
|
34097
|
+
},
|
|
34098
|
+
"truncated_fields": {
|
|
34099
|
+
"type": "array",
|
|
34100
|
+
"items": {
|
|
34101
|
+
"type": "string",
|
|
34102
|
+
"enum": ["body_text", "body_html"]
|
|
34103
|
+
},
|
|
34104
|
+
"description": "With `include`: the body fields on this row that were cut or left out."
|
|
34105
|
+
}
|
|
34106
|
+
},
|
|
34107
|
+
"required": [
|
|
34108
|
+
"id",
|
|
34109
|
+
"status",
|
|
34110
|
+
"status_changed_at",
|
|
34111
|
+
"created_at",
|
|
34112
|
+
"updated_at",
|
|
34113
|
+
"content_hash",
|
|
34114
|
+
"from_header",
|
|
34115
|
+
"from_address",
|
|
34116
|
+
"to_header",
|
|
34117
|
+
"to_address",
|
|
34118
|
+
"subject",
|
|
34119
|
+
"body_size_bytes"
|
|
34120
|
+
]
|
|
34121
|
+
}, {
|
|
34122
|
+
"type": "object",
|
|
34123
|
+
"properties": {
|
|
31717
34124
|
"body_text": {
|
|
31718
34125
|
"type": ["string", "null"],
|
|
31719
34126
|
"description": "Plain-text body sent on the wire. Null when the\nsend carried only an HTML body, or when bodies have\nbeen discarded post-send (`content_discarded_at`\nset).\n"
|
|
@@ -31863,6 +34270,564 @@ const operationManifest = [
|
|
|
31863
34270
|
"tag": "Sending",
|
|
31864
34271
|
"tagCommand": "sending"
|
|
31865
34272
|
},
|
|
34273
|
+
{
|
|
34274
|
+
"binaryResponse": false,
|
|
34275
|
+
"bodyRequired": true,
|
|
34276
|
+
"command": "cancel-sent-email-by-idempotency-key",
|
|
34277
|
+
"description": "Cancels the still-scheduled send created with this `Idempotency-Key` (matched exactly), as `POST /sent-emails/{id}/cancel` does, and releases the key. 404 `not_found`: no send this credential can act on was created with the key. 409 `not_scheduled`: the send holding the key is no longer scheduled; `error.details` names its `sent_email_id` and `status` (`canceled` if already canceled). Pass `created_before` on a retry so it cannot reach a newer send that reused the released key. A connected agent can cancel any scheduled send from its own address, whoever created it, and no other.",
|
|
34278
|
+
"hasJsonBody": true,
|
|
34279
|
+
"method": "POST",
|
|
34280
|
+
"operationId": "cancelSentEmailByIdempotencyKey",
|
|
34281
|
+
"path": "/sent-emails/cancel",
|
|
34282
|
+
"pathParams": [],
|
|
34283
|
+
"queryParams": [],
|
|
34284
|
+
"requestSchema": {
|
|
34285
|
+
"type": "object",
|
|
34286
|
+
"additionalProperties": false,
|
|
34287
|
+
"required": ["idempotency_key"],
|
|
34288
|
+
"properties": {
|
|
34289
|
+
"idempotency_key": {
|
|
34290
|
+
"type": "string",
|
|
34291
|
+
"minLength": 1,
|
|
34292
|
+
"maxLength": 255,
|
|
34293
|
+
"pattern": "^[\\x21-\\x7e]+$",
|
|
34294
|
+
"description": "The `Idempotency-Key` the send was created with."
|
|
34295
|
+
},
|
|
34296
|
+
"created_before": {
|
|
34297
|
+
"type": "string",
|
|
34298
|
+
"format": "date-time",
|
|
34299
|
+
"description": "Only consider sends created at or before this instant, such as when you first asked to cancel."
|
|
34300
|
+
}
|
|
34301
|
+
}
|
|
34302
|
+
},
|
|
34303
|
+
"responseSchema": {
|
|
34304
|
+
"description": "Full sent-email record, including `body_text` and\n`body_html`. Returned by /sent-emails/{id}.\n",
|
|
34305
|
+
"allOf": [{
|
|
34306
|
+
"type": "object",
|
|
34307
|
+
"description": "List-row projection of a sent-email record. Drops\n`body_text` and `body_html` to keep paginated responses\nsmall; fetch /sent-emails/{id} for the full record with\nbodies.\n",
|
|
34308
|
+
"properties": {
|
|
34309
|
+
"id": {
|
|
34310
|
+
"type": "string",
|
|
34311
|
+
"format": "uuid"
|
|
34312
|
+
},
|
|
34313
|
+
"status": {
|
|
34314
|
+
"type": "string",
|
|
34315
|
+
"description": "Lifecycle status of a sent_emails row. Possible values:\n\n - `queued`: pre-call INSERT; the outbound agent has not\n yet replied.\n - `submitted_to_agent`: agent accepted; `queue_id` is set.\n - `agent_failed`: agent rejected; `error_code` and\n `error_message` carry the reason.\n - `gate_denied`: a recipient-scope gate denied the send;\n the agent was never called. The `gates` array carries\n the denial detail. /send-mail returns 403 in this case\n so callers see the denial synchronously; /sent-emails\n additionally records the row for historical lookup,\n which is when this status appears in a listing.\n - `unknown`: terminal indeterminate; the on-box log\n poller couldn't classify the receiver's response.\n - `delivered` / `bounced` / `deferred` / `wait_timeout`:\n terminal delivery outcomes (see DeliveryStatus).\n - `scheduled`: created with a future `scheduled_at` and\n not yet executed; `scheduled_at` carries the pending\n execution time. Reschedulable via PATCH\n /sent-emails/{id} and cancelable via\n /sent-emails/{id}/cancel while in this status.\n - `canceled`: terminal; a scheduled send canceled before\n execution. `canceled_at` carries the cancellation time\n and nothing was dispatched.\n",
|
|
34316
|
+
"enum": [
|
|
34317
|
+
"queued",
|
|
34318
|
+
"submitted_to_agent",
|
|
34319
|
+
"agent_failed",
|
|
34320
|
+
"gate_denied",
|
|
34321
|
+
"unknown",
|
|
34322
|
+
"delivered",
|
|
34323
|
+
"bounced",
|
|
34324
|
+
"deferred",
|
|
34325
|
+
"wait_timeout",
|
|
34326
|
+
"scheduled",
|
|
34327
|
+
"canceled"
|
|
34328
|
+
]
|
|
34329
|
+
},
|
|
34330
|
+
"status_changed_at": {
|
|
34331
|
+
"type": "string",
|
|
34332
|
+
"format": "date-time",
|
|
34333
|
+
"description": "Timestamp of the most recent status transition.\nPolling clients should treat `status='queued'` AND\n`status_changed_at` older than 5 minutes as\n\"stuck-queued\" (the post-tx UPDATE failed and the\nactual delivery state is recoverable from on-box logs\nvia `queue_id` when populated, or `request_id`).\n"
|
|
34334
|
+
},
|
|
34335
|
+
"created_at": {
|
|
34336
|
+
"type": "string",
|
|
34337
|
+
"format": "date-time"
|
|
34338
|
+
},
|
|
34339
|
+
"updated_at": {
|
|
34340
|
+
"type": "string",
|
|
34341
|
+
"format": "date-time"
|
|
34342
|
+
},
|
|
34343
|
+
"client_idempotency_key": {
|
|
34344
|
+
"type": ["string", "null"],
|
|
34345
|
+
"description": "Effective idempotency key used for this send. If the\ncaller passed the `Idempotency-Key` header, this is\nthat value; otherwise it's a server-derived hash of\nthe canonical request payload.\n"
|
|
34346
|
+
},
|
|
34347
|
+
"content_hash": {
|
|
34348
|
+
"type": "string",
|
|
34349
|
+
"description": "Stable hash of the canonical send payload."
|
|
34350
|
+
},
|
|
34351
|
+
"from_header": {
|
|
34352
|
+
"type": "string",
|
|
34353
|
+
"description": "Raw `From:` header as sent on the wire, including any\ndisplay name (e.g. `\"Acme Support\" <agent@acme.test>`).\n"
|
|
34354
|
+
},
|
|
34355
|
+
"from_address": {
|
|
34356
|
+
"type": "string",
|
|
34357
|
+
"description": "Bare email address parsed from `from_header`."
|
|
34358
|
+
},
|
|
34359
|
+
"to_header": {
|
|
34360
|
+
"type": "string",
|
|
34361
|
+
"description": "Raw `To:` header as sent on the wire, including any\ndisplay name.\n"
|
|
34362
|
+
},
|
|
34363
|
+
"to_address": {
|
|
34364
|
+
"type": "string",
|
|
34365
|
+
"description": "Bare email address parsed from `to_header`."
|
|
34366
|
+
},
|
|
34367
|
+
"recipient_count": {
|
|
34368
|
+
"type": ["integer", "null"],
|
|
34369
|
+
"minimum": 0,
|
|
34370
|
+
"description": "Every recipient of the send, To, Cc and Bcc together, each address counted once: the size of `to_addresses`, `cc` and `bcc` combined without duplicates. On a mail relay send `bcc` lists every hidden recipient. Null when the request uses an address-bound agent connection key whose address is not the sender, because the count would reveal Bcc recipients. The `sent_email` object of the `sent_email.*` webhook events carries the same field."
|
|
34371
|
+
},
|
|
34372
|
+
"subject": { "type": "string" },
|
|
34373
|
+
"body_size_bytes": {
|
|
34374
|
+
"type": "integer",
|
|
34375
|
+
"description": "Total UTF-8 byte length of `body_text` + `body_html`.\nSurfaced on the list endpoint so callers can see \"this\nrow has a 4MB body\" without fetching it.\n"
|
|
34376
|
+
},
|
|
34377
|
+
"content_discarded_at": {
|
|
34378
|
+
"type": ["string", "null"],
|
|
34379
|
+
"format": "date-time",
|
|
34380
|
+
"description": "Timestamp at which the bodies were discarded by an\nentitlement-driven retention policy. Null when bodies\nare still present. The detail endpoint returns\nnull-valued `body_text`/`body_html` for discarded rows.\n"
|
|
34381
|
+
},
|
|
34382
|
+
"message_id": {
|
|
34383
|
+
"type": ["string", "null"],
|
|
34384
|
+
"description": "Wire-level Message-ID assigned to the outbound message\n(RFC 5322). Null on rows that never reached signing\n(queued, gate_denied, agent_failed before signing).\n"
|
|
34385
|
+
},
|
|
34386
|
+
"in_reply_to": {
|
|
34387
|
+
"type": ["string", "null"],
|
|
34388
|
+
"description": "Wire-level In-Reply-To header value, when this send\nwas a reply.\n"
|
|
34389
|
+
},
|
|
34390
|
+
"email_references": {
|
|
34391
|
+
"type": ["string", "null"],
|
|
34392
|
+
"description": "Wire-level References header value, when this send\nwas a reply.\n"
|
|
34393
|
+
},
|
|
34394
|
+
"in_reply_to_email_id": {
|
|
34395
|
+
"type": ["string", "null"],
|
|
34396
|
+
"format": "uuid",
|
|
34397
|
+
"description": "Reference to the inbound `emails.id` that this send\nreplied to, when known. Populated when the caller used\n/emails/{id}/reply or when /send-mail's `in_reply_to`\nmatched a stored inbound message_id in the same org.\n"
|
|
34398
|
+
},
|
|
34399
|
+
"thread_id": {
|
|
34400
|
+
"type": ["string", "null"],
|
|
34401
|
+
"format": "uuid",
|
|
34402
|
+
"description": "Conversation thread this send belongs to. A reply inherits\nthe thread of the inbound it answers; a fresh send starts a\nnew thread. Fetch `/threads/{thread_id}` for the full\nordered thread (inbound + outbound interleaved). NULL on\ngate-denied sends and on sends created before threading was\nenabled.\n"
|
|
34403
|
+
},
|
|
34404
|
+
"queue_id": {
|
|
34405
|
+
"type": ["string", "null"],
|
|
34406
|
+
"description": "Message identifier assigned by Primitive's outbound\nrelay once the agent accepts the message. Null on\nqueued, gate_denied, and agent_failed rows.\n"
|
|
34407
|
+
},
|
|
34408
|
+
"smtp_response_code": {
|
|
34409
|
+
"type": ["integer", "null"],
|
|
34410
|
+
"description": "Receiver's 3-digit SMTP code (e.g. 250, 550, 451).\nPopulated on terminal delivery statuses; may be null\non a deferred where the agent never got an SMTP-level\nresponse (TCP refused, DNS failed, TLS handshake\nfailed). `smtp_response_text` still carries Postfix's\ndescriptive text in those cases.\n"
|
|
34411
|
+
},
|
|
34412
|
+
"smtp_response_text": {
|
|
34413
|
+
"type": ["string", "null"],
|
|
34414
|
+
"description": "Free-form text portion of the receiver's SMTP\nresponse. The most useful debugging signal on a\n`bounced` or `deferred` row.\n"
|
|
34415
|
+
},
|
|
34416
|
+
"smtp_enhanced_status_code": {
|
|
34417
|
+
"type": ["string", "null"],
|
|
34418
|
+
"description": "RFC 3463 enhanced status code (e.g. `5.1.1` for \"Bad\ndestination mailbox address\"). Distinct from\n`smtp_response_code`: the basic 3-digit code is coarse\n(550 = \"permanent failure\"), the enhanced code is\nfiner-grained.\n"
|
|
34419
|
+
},
|
|
34420
|
+
"dkim_selector": {
|
|
34421
|
+
"type": ["string", "null"],
|
|
34422
|
+
"description": "DKIM selector used to sign the outbound message.\nPublic DNS data; useful for diagnosing why a downstream\nverifier rejected the signature.\n"
|
|
34423
|
+
},
|
|
34424
|
+
"dkim_domain": {
|
|
34425
|
+
"type": ["string", "null"],
|
|
34426
|
+
"description": "DKIM signing domain."
|
|
34427
|
+
},
|
|
34428
|
+
"error_code": {
|
|
34429
|
+
"type": ["string", "null"],
|
|
34430
|
+
"description": "Stable public error code on `agent_failed` rows. The\nagent's internal codes are remapped to a stable public\ntaxonomy (see `publicAgentError` in the server) so this\nfield is safe to branch on across agent versions.\n"
|
|
34431
|
+
},
|
|
34432
|
+
"error_message": {
|
|
34433
|
+
"type": ["string", "null"],
|
|
34434
|
+
"description": "Free-form error message accompanying `error_code`."
|
|
34435
|
+
},
|
|
34436
|
+
"gates": {
|
|
34437
|
+
"type": ["array", "null"],
|
|
34438
|
+
"items": {
|
|
34439
|
+
"type": "object",
|
|
34440
|
+
"properties": {
|
|
34441
|
+
"name": {
|
|
34442
|
+
"type": "string",
|
|
34443
|
+
"enum": ["send_to_confirmed_domains", "send_to_known_addresses"],
|
|
34444
|
+
"description": "Public recipient-scope gate name that denied the send."
|
|
34445
|
+
},
|
|
34446
|
+
"reason": {
|
|
34447
|
+
"type": "string",
|
|
34448
|
+
"enum": [
|
|
34449
|
+
"domain_not_confirmed",
|
|
34450
|
+
"recipient_unauthenticated",
|
|
34451
|
+
"recipient_not_known"
|
|
34452
|
+
],
|
|
34453
|
+
"description": "Stable machine-readable denial reason."
|
|
34454
|
+
},
|
|
34455
|
+
"message": {
|
|
34456
|
+
"type": "string",
|
|
34457
|
+
"description": "Human-readable explanation of the gate denial."
|
|
34458
|
+
},
|
|
34459
|
+
"subject": {
|
|
34460
|
+
"type": "string",
|
|
34461
|
+
"description": "Domain or address the gate evaluated."
|
|
34462
|
+
},
|
|
34463
|
+
"fix": {
|
|
34464
|
+
"type": "object",
|
|
34465
|
+
"properties": {
|
|
34466
|
+
"action": {
|
|
34467
|
+
"type": "string",
|
|
34468
|
+
"enum": [
|
|
34469
|
+
"confirm_domain",
|
|
34470
|
+
"sender_must_fix_authentication",
|
|
34471
|
+
"wait_for_inbound"
|
|
34472
|
+
],
|
|
34473
|
+
"description": "Suggested next action for the caller."
|
|
34474
|
+
},
|
|
34475
|
+
"subject": {
|
|
34476
|
+
"type": "string",
|
|
34477
|
+
"description": "Entity the action applies to."
|
|
34478
|
+
}
|
|
34479
|
+
},
|
|
34480
|
+
"required": ["action", "subject"]
|
|
34481
|
+
},
|
|
34482
|
+
"docs_url": {
|
|
34483
|
+
"type": "string",
|
|
34484
|
+
"description": "Public docs URL with more context."
|
|
34485
|
+
}
|
|
34486
|
+
},
|
|
34487
|
+
"required": [
|
|
34488
|
+
"name",
|
|
34489
|
+
"reason",
|
|
34490
|
+
"message",
|
|
34491
|
+
"subject"
|
|
34492
|
+
]
|
|
34493
|
+
},
|
|
34494
|
+
"description": "Gate-denial detail on `gate_denied` rows. Mirrors the\nsynchronous /send-mail 403 contract so a caller's\nGateDenial handler is the same across live denies and\nhistorical lookups. Null on every other status.\n"
|
|
34495
|
+
},
|
|
34496
|
+
"request_id": {
|
|
34497
|
+
"type": ["string", "null"],
|
|
34498
|
+
"description": "Server-issued request identifier from the original\n/send-mail call. Surfaced as the `X-Request-Id`\nresponse header on the live send and recorded here\nfor support escalation.\n"
|
|
34499
|
+
},
|
|
34500
|
+
"scheduled_at": {
|
|
34501
|
+
"type": ["string", "null"],
|
|
34502
|
+
"format": "date-time",
|
|
34503
|
+
"description": "Requested execution time for a scheduled send. Kept\nafter execution as the historical schedule; null on\nordinary immediate sends.\n"
|
|
34504
|
+
},
|
|
34505
|
+
"canceled_at": {
|
|
34506
|
+
"type": ["string", "null"],
|
|
34507
|
+
"format": "date-time",
|
|
34508
|
+
"description": "When a scheduled send was canceled. Null unless the row\nreached the `canceled` status.\n"
|
|
34509
|
+
},
|
|
34510
|
+
"presence_control": { "anyOf": [{
|
|
34511
|
+
"type": "object",
|
|
34512
|
+
"properties": {
|
|
34513
|
+
"status": {
|
|
34514
|
+
"type": "string",
|
|
34515
|
+
"enum": [
|
|
34516
|
+
"verified",
|
|
34517
|
+
"pending",
|
|
34518
|
+
"rejected"
|
|
34519
|
+
]
|
|
34520
|
+
},
|
|
34521
|
+
"valid_for_ms": {
|
|
34522
|
+
"type": "integer",
|
|
34523
|
+
"minimum": 0,
|
|
34524
|
+
"maximum": 6e5
|
|
34525
|
+
}
|
|
34526
|
+
},
|
|
34527
|
+
"required": ["status", "valid_for_ms"],
|
|
34528
|
+
"additionalProperties": false
|
|
34529
|
+
}, { "type": "null" }] },
|
|
34530
|
+
"repeat": {
|
|
34531
|
+
"type": ["object", "null"],
|
|
34532
|
+
"description": "Set when Primitive sent this message as part of a repeating send. Resolved from Primitive's own records, never from the message content. Null on every other message and on servers that predate repeating sends.",
|
|
34533
|
+
"properties": {
|
|
34534
|
+
"repeat_id": {
|
|
34535
|
+
"type": "string",
|
|
34536
|
+
"format": "uuid"
|
|
34537
|
+
},
|
|
34538
|
+
"sequence": {
|
|
34539
|
+
"type": "integer",
|
|
34540
|
+
"minimum": 1
|
|
34541
|
+
}
|
|
34542
|
+
},
|
|
34543
|
+
"required": ["repeat_id", "sequence"]
|
|
34544
|
+
},
|
|
34545
|
+
"sender_member": {
|
|
34546
|
+
"type": ["object", "null"],
|
|
34547
|
+
"description": "Verified human authorship, projected only within the member organization. Historical attribution is not current sending or owner authority.",
|
|
34548
|
+
"properties": {
|
|
34549
|
+
"address": {
|
|
34550
|
+
"type": "string",
|
|
34551
|
+
"format": "email"
|
|
34552
|
+
},
|
|
34553
|
+
"user_id": { "type": "string" },
|
|
34554
|
+
"name": { "type": ["string", "null"] }
|
|
34555
|
+
},
|
|
34556
|
+
"required": [
|
|
34557
|
+
"address",
|
|
34558
|
+
"user_id",
|
|
34559
|
+
"name"
|
|
34560
|
+
]
|
|
34561
|
+
},
|
|
34562
|
+
"body_text": {
|
|
34563
|
+
"type": ["string", "null"],
|
|
34564
|
+
"description": "Plain-text body sent on the wire. Null when the\nsend carried only an HTML body, or when bodies have\nbeen discarded post-send (`content_discarded_at`\nset).\n"
|
|
34565
|
+
},
|
|
34566
|
+
"body_html": {
|
|
34567
|
+
"type": ["string", "null"],
|
|
34568
|
+
"description": "HTML body sent on the wire. Null when the send\ncarried only a plain-text body, or when bodies\nhave been discarded post-send.\n"
|
|
34569
|
+
},
|
|
34570
|
+
"headers": {
|
|
34571
|
+
"anyOf": [{
|
|
34572
|
+
"type": "object",
|
|
34573
|
+
"description": "Stored addressing and threading headers. A member is null when absent or withheld.",
|
|
34574
|
+
"properties": {
|
|
34575
|
+
"from": { "type": ["string", "null"] },
|
|
34576
|
+
"to": {
|
|
34577
|
+
"type": ["array", "null"],
|
|
34578
|
+
"items": { "type": "string" }
|
|
34579
|
+
},
|
|
34580
|
+
"cc": {
|
|
34581
|
+
"type": ["array", "null"],
|
|
34582
|
+
"items": { "type": "string" }
|
|
34583
|
+
},
|
|
34584
|
+
"bcc": {
|
|
34585
|
+
"type": ["array", "null"],
|
|
34586
|
+
"items": { "type": "string" }
|
|
34587
|
+
},
|
|
34588
|
+
"reply_to": {
|
|
34589
|
+
"type": ["array", "null"],
|
|
34590
|
+
"items": { "type": "string" }
|
|
34591
|
+
},
|
|
34592
|
+
"in_reply_to": {
|
|
34593
|
+
"type": ["array", "null"],
|
|
34594
|
+
"items": { "type": "string" }
|
|
34595
|
+
},
|
|
34596
|
+
"references": {
|
|
34597
|
+
"type": ["array", "null"],
|
|
34598
|
+
"items": { "type": "string" }
|
|
34599
|
+
},
|
|
34600
|
+
"automation": {
|
|
34601
|
+
"type": ["object", "null"],
|
|
34602
|
+
"additionalProperties": { "type": "string" },
|
|
34603
|
+
"description": "Received mail only."
|
|
34604
|
+
}
|
|
34605
|
+
},
|
|
34606
|
+
"required": [
|
|
34607
|
+
"from",
|
|
34608
|
+
"to",
|
|
34609
|
+
"cc",
|
|
34610
|
+
"bcc",
|
|
34611
|
+
"reply_to",
|
|
34612
|
+
"in_reply_to",
|
|
34613
|
+
"references",
|
|
34614
|
+
"automation"
|
|
34615
|
+
]
|
|
34616
|
+
}, { "type": "null" }],
|
|
34617
|
+
"description": "With `include=headers`: stored addressing and threading headers."
|
|
34618
|
+
},
|
|
34619
|
+
"attachments": {
|
|
34620
|
+
"type": "array",
|
|
34621
|
+
"description": "Metadata for submitted inline attachments; download availability is reported separately. Metadata may exist when the archive is unavailable, and legacy sends may have no metadata. Offloaded payload files are not included in the inline archive.",
|
|
34622
|
+
"items": {
|
|
34623
|
+
"type": "object",
|
|
34624
|
+
"description": "Metadata for one submitted inline attachment.",
|
|
34625
|
+
"additionalProperties": true,
|
|
34626
|
+
"properties": {
|
|
34627
|
+
"filename": { "type": ["string", "null"] },
|
|
34628
|
+
"content_type": { "type": "string" },
|
|
34629
|
+
"size_bytes": {
|
|
34630
|
+
"type": "integer",
|
|
34631
|
+
"minimum": 0,
|
|
34632
|
+
"maximum": 9007199254740991
|
|
34633
|
+
},
|
|
34634
|
+
"sha256": { "type": "string" },
|
|
34635
|
+
"part_index": {
|
|
34636
|
+
"type": "integer",
|
|
34637
|
+
"minimum": 0,
|
|
34638
|
+
"maximum": 9007199254740991
|
|
34639
|
+
},
|
|
34640
|
+
"tar_path": { "type": "string" }
|
|
34641
|
+
},
|
|
34642
|
+
"required": [
|
|
34643
|
+
"filename",
|
|
34644
|
+
"content_type",
|
|
34645
|
+
"size_bytes",
|
|
34646
|
+
"sha256",
|
|
34647
|
+
"part_index",
|
|
34648
|
+
"tar_path"
|
|
34649
|
+
]
|
|
34650
|
+
}
|
|
34651
|
+
},
|
|
34652
|
+
"truncated": {
|
|
34653
|
+
"type": "boolean",
|
|
34654
|
+
"description": "With `include`: true when a body on this row was cut or left out."
|
|
34655
|
+
},
|
|
34656
|
+
"truncated_fields": {
|
|
34657
|
+
"type": "array",
|
|
34658
|
+
"items": {
|
|
34659
|
+
"type": "string",
|
|
34660
|
+
"enum": ["body_text", "body_html"]
|
|
34661
|
+
},
|
|
34662
|
+
"description": "With `include`: the body fields on this row that were cut or left out."
|
|
34663
|
+
}
|
|
34664
|
+
},
|
|
34665
|
+
"required": [
|
|
34666
|
+
"id",
|
|
34667
|
+
"status",
|
|
34668
|
+
"status_changed_at",
|
|
34669
|
+
"created_at",
|
|
34670
|
+
"updated_at",
|
|
34671
|
+
"content_hash",
|
|
34672
|
+
"from_header",
|
|
34673
|
+
"from_address",
|
|
34674
|
+
"to_header",
|
|
34675
|
+
"to_address",
|
|
34676
|
+
"subject",
|
|
34677
|
+
"body_size_bytes"
|
|
34678
|
+
]
|
|
34679
|
+
}, {
|
|
34680
|
+
"type": "object",
|
|
34681
|
+
"properties": {
|
|
34682
|
+
"body_text": {
|
|
34683
|
+
"type": ["string", "null"],
|
|
34684
|
+
"description": "Plain-text body sent on the wire. Null when the\nsend carried only an HTML body, or when bodies have\nbeen discarded post-send (`content_discarded_at`\nset).\n"
|
|
34685
|
+
},
|
|
34686
|
+
"body_html": {
|
|
34687
|
+
"type": ["string", "null"],
|
|
34688
|
+
"description": "HTML body sent on the wire. Null when the send\ncarried only a plain-text body, or when bodies\nhave been discarded post-send.\n"
|
|
34689
|
+
},
|
|
34690
|
+
"attachments": {
|
|
34691
|
+
"type": "array",
|
|
34692
|
+
"description": "Metadata for submitted inline attachments; download availability is reported separately. Metadata may exist when the archive is unavailable, and legacy sends may have no metadata. Offloaded payload files are not included in the inline archive.",
|
|
34693
|
+
"items": {
|
|
34694
|
+
"type": "object",
|
|
34695
|
+
"description": "Metadata for one submitted inline attachment.",
|
|
34696
|
+
"additionalProperties": true,
|
|
34697
|
+
"properties": {
|
|
34698
|
+
"filename": { "type": ["string", "null"] },
|
|
34699
|
+
"content_type": { "type": "string" },
|
|
34700
|
+
"size_bytes": {
|
|
34701
|
+
"type": "integer",
|
|
34702
|
+
"minimum": 0,
|
|
34703
|
+
"maximum": 9007199254740991
|
|
34704
|
+
},
|
|
34705
|
+
"sha256": { "type": "string" },
|
|
34706
|
+
"part_index": {
|
|
34707
|
+
"type": "integer",
|
|
34708
|
+
"minimum": 0,
|
|
34709
|
+
"maximum": 9007199254740991
|
|
34710
|
+
},
|
|
34711
|
+
"tar_path": { "type": "string" }
|
|
34712
|
+
},
|
|
34713
|
+
"required": [
|
|
34714
|
+
"filename",
|
|
34715
|
+
"content_type",
|
|
34716
|
+
"size_bytes",
|
|
34717
|
+
"sha256",
|
|
34718
|
+
"part_index",
|
|
34719
|
+
"tar_path"
|
|
34720
|
+
]
|
|
34721
|
+
}
|
|
34722
|
+
},
|
|
34723
|
+
"attachments_size_bytes": {
|
|
34724
|
+
"type": "integer",
|
|
34725
|
+
"minimum": 0,
|
|
34726
|
+
"maximum": 9007199254740991,
|
|
34727
|
+
"description": "Original decoded attachment byte total accepted for this send, not the compressed archive size. It may exceed the retained inline total after offloading."
|
|
34728
|
+
},
|
|
34729
|
+
"attachments_complete": {
|
|
34730
|
+
"type": "boolean",
|
|
34731
|
+
"description": "Whether attachments enumerates the complete original submitted attachment set, including zero-byte and inline CID parts. True requires no submitted payload-reference, offloaded, or visibility-filtered attachment to be omitted, retained content, and a published archive when the attachment inventory is nonempty. False or absent provides no completeness proof, including for legacy or discarded content. This field grants no download access and has no interaction semantics."
|
|
34732
|
+
},
|
|
34733
|
+
"attachments_download_available": {
|
|
34734
|
+
"type": "boolean",
|
|
34735
|
+
"description": "Whether an inline attachment archive has been successfully retained and is available for download. Download authorization is checked separately; address-bound agent connection keys cannot download archives."
|
|
34736
|
+
},
|
|
34737
|
+
"to_addresses": {
|
|
34738
|
+
"type": ["array", "null"],
|
|
34739
|
+
"items": { "type": "string" },
|
|
34740
|
+
"description": "Every normalized To recipient of the send. Always populated on\nnew sends (a single-recipient send carries a one-element array);\nnull only on records that predate this field. `to_address` keeps\ncarrying the first recipient.\n"
|
|
34741
|
+
},
|
|
34742
|
+
"cc": {
|
|
34743
|
+
"type": ["array", "null"],
|
|
34744
|
+
"items": { "type": "string" },
|
|
34745
|
+
"description": "Normalized Cc address list. Null when the send had no Cc\nrecipients.\n"
|
|
34746
|
+
},
|
|
34747
|
+
"bcc": {
|
|
34748
|
+
"type": ["array", "null"],
|
|
34749
|
+
"items": { "type": "string" },
|
|
34750
|
+
"description": "Normalized Bcc address list, surfaced on the sending\norganization's own record. Bcc recipients are never written\ninto the transmitted message. Null when the send had no Bcc\nrecipients. Also null when the request uses an address-bound\nagent connection key whose address is not the sender, because\nBcc is withheld from recipients.\n"
|
|
34751
|
+
},
|
|
34752
|
+
"reply_to": {
|
|
34753
|
+
"type": ["array", "null"],
|
|
34754
|
+
"items": { "type": "string" },
|
|
34755
|
+
"description": "Normalized Reply-To address list. Null when the send had none.\n"
|
|
34756
|
+
},
|
|
34757
|
+
"tags": {
|
|
34758
|
+
"type": ["array", "null"],
|
|
34759
|
+
"description": "Customer metadata tags stored verbatim at send time. Null when\nthe send had none. Also null when the request uses an\naddress-bound agent connection key whose address is not the\nsender, because tags are withheld from recipients.\n",
|
|
34760
|
+
"items": {
|
|
34761
|
+
"type": "object",
|
|
34762
|
+
"properties": {
|
|
34763
|
+
"name": { "type": "string" },
|
|
34764
|
+
"value": { "type": "string" }
|
|
34765
|
+
},
|
|
34766
|
+
"required": ["name", "value"]
|
|
34767
|
+
}
|
|
34768
|
+
}
|
|
34769
|
+
}
|
|
34770
|
+
}],
|
|
34771
|
+
"properties": {
|
|
34772
|
+
"presence_control": { "anyOf": [{
|
|
34773
|
+
"type": "object",
|
|
34774
|
+
"properties": {
|
|
34775
|
+
"status": {
|
|
34776
|
+
"type": "string",
|
|
34777
|
+
"enum": [
|
|
34778
|
+
"verified",
|
|
34779
|
+
"pending",
|
|
34780
|
+
"rejected"
|
|
34781
|
+
]
|
|
34782
|
+
},
|
|
34783
|
+
"valid_for_ms": {
|
|
34784
|
+
"type": "integer",
|
|
34785
|
+
"minimum": 0,
|
|
34786
|
+
"maximum": 6e5
|
|
34787
|
+
}
|
|
34788
|
+
},
|
|
34789
|
+
"required": ["status", "valid_for_ms"],
|
|
34790
|
+
"additionalProperties": false
|
|
34791
|
+
}, { "type": "null" }] },
|
|
34792
|
+
"repeat": {
|
|
34793
|
+
"type": ["object", "null"],
|
|
34794
|
+
"description": "Set when Primitive sent this message as part of a repeating send. Resolved from Primitive's own records, never from the message content. Null on every other message and on servers that predate repeating sends.",
|
|
34795
|
+
"properties": {
|
|
34796
|
+
"repeat_id": {
|
|
34797
|
+
"type": "string",
|
|
34798
|
+
"format": "uuid"
|
|
34799
|
+
},
|
|
34800
|
+
"sequence": {
|
|
34801
|
+
"type": "integer",
|
|
34802
|
+
"minimum": 1
|
|
34803
|
+
}
|
|
34804
|
+
},
|
|
34805
|
+
"required": ["repeat_id", "sequence"]
|
|
34806
|
+
},
|
|
34807
|
+
"sender_member": {
|
|
34808
|
+
"type": ["object", "null"],
|
|
34809
|
+
"description": "Verified human authorship, projected only within the member organization. Historical attribution is not current sending or owner authority.",
|
|
34810
|
+
"properties": {
|
|
34811
|
+
"address": {
|
|
34812
|
+
"type": "string",
|
|
34813
|
+
"format": "email"
|
|
34814
|
+
},
|
|
34815
|
+
"user_id": { "type": "string" },
|
|
34816
|
+
"name": { "type": ["string", "null"] }
|
|
34817
|
+
},
|
|
34818
|
+
"required": [
|
|
34819
|
+
"address",
|
|
34820
|
+
"user_id",
|
|
34821
|
+
"name"
|
|
34822
|
+
]
|
|
34823
|
+
}
|
|
34824
|
+
}
|
|
34825
|
+
},
|
|
34826
|
+
"sdkName": "cancelSentEmailByIdempotencyKey",
|
|
34827
|
+
"summary": "Cancel a scheduled send by idempotency key",
|
|
34828
|
+
"tag": "Sending",
|
|
34829
|
+
"tagCommand": "sending"
|
|
34830
|
+
},
|
|
31866
34831
|
{
|
|
31867
34832
|
"binaryResponse": false,
|
|
31868
34833
|
"bodyRequired": false,
|
|
@@ -31904,6 +34869,76 @@ const operationManifest = [
|
|
|
31904
34869
|
"tag": "Sending",
|
|
31905
34870
|
"tagCommand": "sending"
|
|
31906
34871
|
},
|
|
34872
|
+
{
|
|
34873
|
+
"binaryResponse": true,
|
|
34874
|
+
"bodyRequired": false,
|
|
34875
|
+
"command": "download-sent-attachment-archive-member",
|
|
34876
|
+
"description": "Returns the decoded bytes of one regular file inside a zip, tar or tar.gz attachment, by its listed path. The archive's structure, every member path and every limit are checked as for the listing, but only the requested file is decoded and verified (its zip CRC-32, and X-Content-SHA256 of the bytes returned); list the archive to have every file decoded. Authenticate with an organization API key, an authorized organization OAuth token, or an address-bound agent connection key. A connected agent reads only mail sent from its own address. Function credentials are not supported. Discarded content is unavailable.",
|
|
34877
|
+
"hasJsonBody": false,
|
|
34878
|
+
"method": "GET",
|
|
34879
|
+
"operationId": "downloadSentAttachmentArchiveMember",
|
|
34880
|
+
"path": "/sent-emails/{id}/attachments/{attachment}/members/{path}",
|
|
34881
|
+
"pathParams": [
|
|
34882
|
+
{
|
|
34883
|
+
"description": "Resource UUID",
|
|
34884
|
+
"enum": null,
|
|
34885
|
+
"name": "id",
|
|
34886
|
+
"required": true,
|
|
34887
|
+
"type": "string"
|
|
34888
|
+
},
|
|
34889
|
+
{
|
|
34890
|
+
"description": "The attachment, by its exact part_index when the value is digits only, otherwise by its exact filename (case-sensitive, compared after Unicode NFC normalization). A digits-only value always selects that part_index, even when another attachment has that name; read such a name with ?filename=.",
|
|
34891
|
+
"enum": null,
|
|
34892
|
+
"name": "attachment",
|
|
34893
|
+
"required": true,
|
|
34894
|
+
"type": "string"
|
|
34895
|
+
},
|
|
34896
|
+
{
|
|
34897
|
+
"description": "The member path exactly as listed, percent-encoded as one path segment (slashes as %2F).",
|
|
34898
|
+
"enum": null,
|
|
34899
|
+
"name": "path",
|
|
34900
|
+
"required": true,
|
|
34901
|
+
"type": "string"
|
|
34902
|
+
}
|
|
34903
|
+
],
|
|
34904
|
+
"queryParams": [],
|
|
34905
|
+
"requestSchema": null,
|
|
34906
|
+
"responseSchema": null,
|
|
34907
|
+
"sdkName": "downloadSentAttachmentArchiveMember",
|
|
34908
|
+
"summary": "Download one file from inside a sent zip or tar attachment",
|
|
34909
|
+
"tag": "Sending",
|
|
34910
|
+
"tagCommand": "sending"
|
|
34911
|
+
},
|
|
34912
|
+
{
|
|
34913
|
+
"binaryResponse": true,
|
|
34914
|
+
"bodyRequired": false,
|
|
34915
|
+
"command": "download-sent-attachment-by-filename",
|
|
34916
|
+
"description": "Returns the same bytes as downloadSentAttachmentPart, selecting the attachment by its exact filename. The path form `/sent-emails/{id}/attachments/{filename}` also works for any name that is not digits only, which that form reads as a part_index. When several attachments share the filename the response is 409 attachment_name_ambiguous with every matching part_index, and nothing is returned. Authenticate with an organization API key, an authorized organization OAuth token, or an address-bound agent connection key. A connected agent reads only mail sent from its own address. Function credentials are not supported. Discarded content is unavailable.",
|
|
34917
|
+
"hasJsonBody": false,
|
|
34918
|
+
"method": "GET",
|
|
34919
|
+
"operationId": "downloadSentAttachmentByFilename",
|
|
34920
|
+
"path": "/sent-emails/{id}/attachments",
|
|
34921
|
+
"pathParams": [{
|
|
34922
|
+
"description": "Resource UUID",
|
|
34923
|
+
"enum": null,
|
|
34924
|
+
"name": "id",
|
|
34925
|
+
"required": true,
|
|
34926
|
+
"type": "string"
|
|
34927
|
+
}],
|
|
34928
|
+
"queryParams": [{
|
|
34929
|
+
"description": "The attachment's exact filename from its metadata. Any name, digits only included. No other query parameter is accepted.",
|
|
34930
|
+
"enum": null,
|
|
34931
|
+
"name": "filename",
|
|
34932
|
+
"required": true,
|
|
34933
|
+
"type": "string"
|
|
34934
|
+
}],
|
|
34935
|
+
"requestSchema": null,
|
|
34936
|
+
"responseSchema": null,
|
|
34937
|
+
"sdkName": "downloadSentAttachmentByFilename",
|
|
34938
|
+
"summary": "Download one sent email attachment by filename",
|
|
34939
|
+
"tag": "Sending",
|
|
34940
|
+
"tagCommand": "sending"
|
|
34941
|
+
},
|
|
31907
34942
|
{
|
|
31908
34943
|
"binaryResponse": true,
|
|
31909
34944
|
"bodyRequired": false,
|
|
@@ -32435,6 +35470,108 @@ const operationManifest = [
|
|
|
32435
35470
|
"user_id",
|
|
32436
35471
|
"name"
|
|
32437
35472
|
]
|
|
35473
|
+
},
|
|
35474
|
+
"body_text": {
|
|
35475
|
+
"type": ["string", "null"],
|
|
35476
|
+
"description": "Plain-text body sent on the wire. Null when the\nsend carried only an HTML body, or when bodies have\nbeen discarded post-send (`content_discarded_at`\nset).\n"
|
|
35477
|
+
},
|
|
35478
|
+
"body_html": {
|
|
35479
|
+
"type": ["string", "null"],
|
|
35480
|
+
"description": "HTML body sent on the wire. Null when the send\ncarried only a plain-text body, or when bodies\nhave been discarded post-send.\n"
|
|
35481
|
+
},
|
|
35482
|
+
"headers": {
|
|
35483
|
+
"anyOf": [{
|
|
35484
|
+
"type": "object",
|
|
35485
|
+
"description": "Stored addressing and threading headers. A member is null when absent or withheld.",
|
|
35486
|
+
"properties": {
|
|
35487
|
+
"from": { "type": ["string", "null"] },
|
|
35488
|
+
"to": {
|
|
35489
|
+
"type": ["array", "null"],
|
|
35490
|
+
"items": { "type": "string" }
|
|
35491
|
+
},
|
|
35492
|
+
"cc": {
|
|
35493
|
+
"type": ["array", "null"],
|
|
35494
|
+
"items": { "type": "string" }
|
|
35495
|
+
},
|
|
35496
|
+
"bcc": {
|
|
35497
|
+
"type": ["array", "null"],
|
|
35498
|
+
"items": { "type": "string" }
|
|
35499
|
+
},
|
|
35500
|
+
"reply_to": {
|
|
35501
|
+
"type": ["array", "null"],
|
|
35502
|
+
"items": { "type": "string" }
|
|
35503
|
+
},
|
|
35504
|
+
"in_reply_to": {
|
|
35505
|
+
"type": ["array", "null"],
|
|
35506
|
+
"items": { "type": "string" }
|
|
35507
|
+
},
|
|
35508
|
+
"references": {
|
|
35509
|
+
"type": ["array", "null"],
|
|
35510
|
+
"items": { "type": "string" }
|
|
35511
|
+
},
|
|
35512
|
+
"automation": {
|
|
35513
|
+
"type": ["object", "null"],
|
|
35514
|
+
"additionalProperties": { "type": "string" },
|
|
35515
|
+
"description": "Received mail only."
|
|
35516
|
+
}
|
|
35517
|
+
},
|
|
35518
|
+
"required": [
|
|
35519
|
+
"from",
|
|
35520
|
+
"to",
|
|
35521
|
+
"cc",
|
|
35522
|
+
"bcc",
|
|
35523
|
+
"reply_to",
|
|
35524
|
+
"in_reply_to",
|
|
35525
|
+
"references",
|
|
35526
|
+
"automation"
|
|
35527
|
+
]
|
|
35528
|
+
}, { "type": "null" }],
|
|
35529
|
+
"description": "With `include=headers`: stored addressing and threading headers."
|
|
35530
|
+
},
|
|
35531
|
+
"attachments": {
|
|
35532
|
+
"type": "array",
|
|
35533
|
+
"description": "Metadata for submitted inline attachments; download availability is reported separately. Metadata may exist when the archive is unavailable, and legacy sends may have no metadata. Offloaded payload files are not included in the inline archive.",
|
|
35534
|
+
"items": {
|
|
35535
|
+
"type": "object",
|
|
35536
|
+
"description": "Metadata for one submitted inline attachment.",
|
|
35537
|
+
"additionalProperties": true,
|
|
35538
|
+
"properties": {
|
|
35539
|
+
"filename": { "type": ["string", "null"] },
|
|
35540
|
+
"content_type": { "type": "string" },
|
|
35541
|
+
"size_bytes": {
|
|
35542
|
+
"type": "integer",
|
|
35543
|
+
"minimum": 0,
|
|
35544
|
+
"maximum": 9007199254740991
|
|
35545
|
+
},
|
|
35546
|
+
"sha256": { "type": "string" },
|
|
35547
|
+
"part_index": {
|
|
35548
|
+
"type": "integer",
|
|
35549
|
+
"minimum": 0,
|
|
35550
|
+
"maximum": 9007199254740991
|
|
35551
|
+
},
|
|
35552
|
+
"tar_path": { "type": "string" }
|
|
35553
|
+
},
|
|
35554
|
+
"required": [
|
|
35555
|
+
"filename",
|
|
35556
|
+
"content_type",
|
|
35557
|
+
"size_bytes",
|
|
35558
|
+
"sha256",
|
|
35559
|
+
"part_index",
|
|
35560
|
+
"tar_path"
|
|
35561
|
+
]
|
|
35562
|
+
}
|
|
35563
|
+
},
|
|
35564
|
+
"truncated": {
|
|
35565
|
+
"type": "boolean",
|
|
35566
|
+
"description": "With `include`: true when a body on this row was cut or left out."
|
|
35567
|
+
},
|
|
35568
|
+
"truncated_fields": {
|
|
35569
|
+
"type": "array",
|
|
35570
|
+
"items": {
|
|
35571
|
+
"type": "string",
|
|
35572
|
+
"enum": ["body_text", "body_html"]
|
|
35573
|
+
},
|
|
35574
|
+
"description": "With `include`: the body fields on this row that were cut or left out."
|
|
32438
35575
|
}
|
|
32439
35576
|
},
|
|
32440
35577
|
"required": [
|
|
@@ -32603,6 +35740,91 @@ const operationManifest = [
|
|
|
32603
35740
|
"tag": "Sending",
|
|
32604
35741
|
"tagCommand": "sending"
|
|
32605
35742
|
},
|
|
35743
|
+
{
|
|
35744
|
+
"binaryResponse": false,
|
|
35745
|
+
"bodyRequired": false,
|
|
35746
|
+
"command": "list-sent-attachment-archive-members",
|
|
35747
|
+
"description": "Lists the regular files inside a zip, tar or tar.gz attachment with each one's path, decoded size and SHA-256. The format is detected from the content. Unsafe paths refuse the whole archive, and resource limits bound the work (see the 413 and 422 responses). Authenticate with an organization API key, an authorized organization OAuth token, or an address-bound agent connection key. A connected agent reads only mail sent from its own address. Function credentials are not supported. Discarded content is unavailable.",
|
|
35748
|
+
"hasJsonBody": false,
|
|
35749
|
+
"method": "GET",
|
|
35750
|
+
"operationId": "listSentAttachmentArchiveMembers",
|
|
35751
|
+
"path": "/sent-emails/{id}/attachments/{attachment}/members",
|
|
35752
|
+
"pathParams": [{
|
|
35753
|
+
"description": "Resource UUID",
|
|
35754
|
+
"enum": null,
|
|
35755
|
+
"name": "id",
|
|
35756
|
+
"required": true,
|
|
35757
|
+
"type": "string"
|
|
35758
|
+
}, {
|
|
35759
|
+
"description": "The attachment, by its exact part_index when the value is digits only, otherwise by its exact filename (case-sensitive, compared after Unicode NFC normalization). A digits-only value always selects that part_index, even when another attachment has that name; read such a name with ?filename=.",
|
|
35760
|
+
"enum": null,
|
|
35761
|
+
"name": "attachment",
|
|
35762
|
+
"required": true,
|
|
35763
|
+
"type": "string"
|
|
35764
|
+
}],
|
|
35765
|
+
"queryParams": [],
|
|
35766
|
+
"requestSchema": null,
|
|
35767
|
+
"responseSchema": {
|
|
35768
|
+
"type": "object",
|
|
35769
|
+
"required": [
|
|
35770
|
+
"part_index",
|
|
35771
|
+
"filename",
|
|
35772
|
+
"format",
|
|
35773
|
+
"members"
|
|
35774
|
+
],
|
|
35775
|
+
"properties": {
|
|
35776
|
+
"part_index": {
|
|
35777
|
+
"type": "integer",
|
|
35778
|
+
"minimum": 0,
|
|
35779
|
+
"description": "The attachment the listing is of."
|
|
35780
|
+
},
|
|
35781
|
+
"filename": {
|
|
35782
|
+
"type": ["string", "null"],
|
|
35783
|
+
"description": "That attachment's filename."
|
|
35784
|
+
},
|
|
35785
|
+
"format": {
|
|
35786
|
+
"type": "string",
|
|
35787
|
+
"enum": [
|
|
35788
|
+
"zip",
|
|
35789
|
+
"tar",
|
|
35790
|
+
"tar.gz"
|
|
35791
|
+
]
|
|
35792
|
+
},
|
|
35793
|
+
"members": {
|
|
35794
|
+
"type": "array",
|
|
35795
|
+
"description": "Regular files in archive order.",
|
|
35796
|
+
"items": {
|
|
35797
|
+
"type": "object",
|
|
35798
|
+
"required": [
|
|
35799
|
+
"path",
|
|
35800
|
+
"size_bytes",
|
|
35801
|
+
"sha256"
|
|
35802
|
+
],
|
|
35803
|
+
"properties": {
|
|
35804
|
+
"path": {
|
|
35805
|
+
"type": "string",
|
|
35806
|
+
"description": "Member path inside the archive, relative, with \"/\" separators and no leading \"./\"."
|
|
35807
|
+
},
|
|
35808
|
+
"size_bytes": {
|
|
35809
|
+
"type": "integer",
|
|
35810
|
+
"minimum": 0,
|
|
35811
|
+
"description": "Decoded size in bytes."
|
|
35812
|
+
},
|
|
35813
|
+
"sha256": {
|
|
35814
|
+
"type": "string",
|
|
35815
|
+
"pattern": "^[0-9a-f]{64}$",
|
|
35816
|
+
"description": "SHA-256 of the decoded bytes."
|
|
35817
|
+
}
|
|
35818
|
+
}
|
|
35819
|
+
}
|
|
35820
|
+
}
|
|
35821
|
+
}
|
|
35822
|
+
},
|
|
35823
|
+
"sdkName": "listSentAttachmentArchiveMembers",
|
|
35824
|
+
"summary": "List the files inside a sent zip or tar attachment",
|
|
35825
|
+
"tag": "Sending",
|
|
35826
|
+
"tagCommand": "sending"
|
|
35827
|
+
},
|
|
32606
35828
|
{
|
|
32607
35829
|
"binaryResponse": false,
|
|
32608
35830
|
"bodyRequired": false,
|
|
@@ -32679,6 +35901,20 @@ const operationManifest = [
|
|
|
32679
35901
|
"name": "from",
|
|
32680
35902
|
"required": false,
|
|
32681
35903
|
"type": "string"
|
|
35904
|
+
},
|
|
35905
|
+
{
|
|
35906
|
+
"description": "Return only sends that set this header: `Name=Value` matches that exact value, `Name` alone any value. Name and value compare case-insensitively; the value is trimmed and matched whole, not as a substring, and only the first `=` separates the two. Matches only the `headers` the send request set. History pages only: with `since` or `wait` it is a `validation_error`, as is a name that is never stored (trace, signature or credential-like). Sends made before header search was available are not matched.",
|
|
35907
|
+
"enum": null,
|
|
35908
|
+
"name": "header",
|
|
35909
|
+
"required": false,
|
|
35910
|
+
"type": "string"
|
|
35911
|
+
},
|
|
35912
|
+
{
|
|
35913
|
+
"description": "Extra parts on every row, comma-separated: `body_text`, `body_html`, `headers`, `attachments`. Reads a page of messages in one request instead of one detail read per row. Parts are withheld where the detail read withholds them. Each body field is cut to 64 KiB per row and a response carries at most 2048 KiB of body text, in page order; a cut or omitted body is named in `truncated_fields`. Unknown values are a `validation_error`.",
|
|
35914
|
+
"enum": null,
|
|
35915
|
+
"name": "include",
|
|
35916
|
+
"required": false,
|
|
35917
|
+
"type": "string"
|
|
32682
35918
|
}
|
|
32683
35919
|
],
|
|
32684
35920
|
"requestSchema": null,
|
|
@@ -32940,6 +36176,108 @@ const operationManifest = [
|
|
|
32940
36176
|
"user_id",
|
|
32941
36177
|
"name"
|
|
32942
36178
|
]
|
|
36179
|
+
},
|
|
36180
|
+
"body_text": {
|
|
36181
|
+
"type": ["string", "null"],
|
|
36182
|
+
"description": "Plain-text body sent on the wire. Null when the\nsend carried only an HTML body, or when bodies have\nbeen discarded post-send (`content_discarded_at`\nset).\n"
|
|
36183
|
+
},
|
|
36184
|
+
"body_html": {
|
|
36185
|
+
"type": ["string", "null"],
|
|
36186
|
+
"description": "HTML body sent on the wire. Null when the send\ncarried only a plain-text body, or when bodies\nhave been discarded post-send.\n"
|
|
36187
|
+
},
|
|
36188
|
+
"headers": {
|
|
36189
|
+
"anyOf": [{
|
|
36190
|
+
"type": "object",
|
|
36191
|
+
"description": "Stored addressing and threading headers. A member is null when absent or withheld.",
|
|
36192
|
+
"properties": {
|
|
36193
|
+
"from": { "type": ["string", "null"] },
|
|
36194
|
+
"to": {
|
|
36195
|
+
"type": ["array", "null"],
|
|
36196
|
+
"items": { "type": "string" }
|
|
36197
|
+
},
|
|
36198
|
+
"cc": {
|
|
36199
|
+
"type": ["array", "null"],
|
|
36200
|
+
"items": { "type": "string" }
|
|
36201
|
+
},
|
|
36202
|
+
"bcc": {
|
|
36203
|
+
"type": ["array", "null"],
|
|
36204
|
+
"items": { "type": "string" }
|
|
36205
|
+
},
|
|
36206
|
+
"reply_to": {
|
|
36207
|
+
"type": ["array", "null"],
|
|
36208
|
+
"items": { "type": "string" }
|
|
36209
|
+
},
|
|
36210
|
+
"in_reply_to": {
|
|
36211
|
+
"type": ["array", "null"],
|
|
36212
|
+
"items": { "type": "string" }
|
|
36213
|
+
},
|
|
36214
|
+
"references": {
|
|
36215
|
+
"type": ["array", "null"],
|
|
36216
|
+
"items": { "type": "string" }
|
|
36217
|
+
},
|
|
36218
|
+
"automation": {
|
|
36219
|
+
"type": ["object", "null"],
|
|
36220
|
+
"additionalProperties": { "type": "string" },
|
|
36221
|
+
"description": "Received mail only."
|
|
36222
|
+
}
|
|
36223
|
+
},
|
|
36224
|
+
"required": [
|
|
36225
|
+
"from",
|
|
36226
|
+
"to",
|
|
36227
|
+
"cc",
|
|
36228
|
+
"bcc",
|
|
36229
|
+
"reply_to",
|
|
36230
|
+
"in_reply_to",
|
|
36231
|
+
"references",
|
|
36232
|
+
"automation"
|
|
36233
|
+
]
|
|
36234
|
+
}, { "type": "null" }],
|
|
36235
|
+
"description": "With `include=headers`: stored addressing and threading headers."
|
|
36236
|
+
},
|
|
36237
|
+
"attachments": {
|
|
36238
|
+
"type": "array",
|
|
36239
|
+
"description": "Metadata for submitted inline attachments; download availability is reported separately. Metadata may exist when the archive is unavailable, and legacy sends may have no metadata. Offloaded payload files are not included in the inline archive.",
|
|
36240
|
+
"items": {
|
|
36241
|
+
"type": "object",
|
|
36242
|
+
"description": "Metadata for one submitted inline attachment.",
|
|
36243
|
+
"additionalProperties": true,
|
|
36244
|
+
"properties": {
|
|
36245
|
+
"filename": { "type": ["string", "null"] },
|
|
36246
|
+
"content_type": { "type": "string" },
|
|
36247
|
+
"size_bytes": {
|
|
36248
|
+
"type": "integer",
|
|
36249
|
+
"minimum": 0,
|
|
36250
|
+
"maximum": 9007199254740991
|
|
36251
|
+
},
|
|
36252
|
+
"sha256": { "type": "string" },
|
|
36253
|
+
"part_index": {
|
|
36254
|
+
"type": "integer",
|
|
36255
|
+
"minimum": 0,
|
|
36256
|
+
"maximum": 9007199254740991
|
|
36257
|
+
},
|
|
36258
|
+
"tar_path": { "type": "string" }
|
|
36259
|
+
},
|
|
36260
|
+
"required": [
|
|
36261
|
+
"filename",
|
|
36262
|
+
"content_type",
|
|
36263
|
+
"size_bytes",
|
|
36264
|
+
"sha256",
|
|
36265
|
+
"part_index",
|
|
36266
|
+
"tar_path"
|
|
36267
|
+
]
|
|
36268
|
+
}
|
|
36269
|
+
},
|
|
36270
|
+
"truncated": {
|
|
36271
|
+
"type": "boolean",
|
|
36272
|
+
"description": "With `include`: true when a body on this row was cut or left out."
|
|
36273
|
+
},
|
|
36274
|
+
"truncated_fields": {
|
|
36275
|
+
"type": "array",
|
|
36276
|
+
"items": {
|
|
36277
|
+
"type": "string",
|
|
36278
|
+
"enum": ["body_text", "body_html"]
|
|
36279
|
+
},
|
|
36280
|
+
"description": "With `include`: the body fields on this row that were cut or left out."
|
|
32943
36281
|
}
|
|
32944
36282
|
},
|
|
32945
36283
|
"required": [
|
|
@@ -33071,6 +36409,32 @@ const operationManifest = [
|
|
|
33071
36409
|
}
|
|
33072
36410
|
},
|
|
33073
36411
|
"required": ["every_minutes"]
|
|
36412
|
+
},
|
|
36413
|
+
"headers": {
|
|
36414
|
+
"type": "object",
|
|
36415
|
+
"maxProperties": 25,
|
|
36416
|
+
"propertyNames": {
|
|
36417
|
+
"type": "string",
|
|
36418
|
+
"minLength": 1,
|
|
36419
|
+
"maxLength": 64,
|
|
36420
|
+
"pattern": "^[A-Za-z0-9]+(-[A-Za-z0-9]+)*$"
|
|
36421
|
+
},
|
|
36422
|
+
"additionalProperties": {
|
|
36423
|
+
"type": "string",
|
|
36424
|
+
"minLength": 1,
|
|
36425
|
+
"maxLength": 200,
|
|
36426
|
+
"pattern": "^[\\x20-\\x7E]+$"
|
|
36427
|
+
},
|
|
36428
|
+
"description": "Extra headers, as `{ \"Name\": \"value\" }`, at most 25. A name is one of `Auto-Submitted`, `Precedence`, `X-Auto-Response-Suppress`, `X-Primitive-Interaction`, `X-Primitive-Interaction-Ref`, or a custom `X-` name of letters, digits and hyphens that is not credential-like (a word such as `auth`, `token`, `key`, `secret`, `session` or `bearer`, joined forms included) and not in a reserved family: X-Primitive*, X-Original-*, X-Originating-*, X-Forwarded-*, X-Received*, X-Authenticated*, X-Envelope-*, X-Delivered-To, X-Apparently-To, X-Failed-Recipients, X-Loop, X-Priority, X-MSMail-*, X-MS-*, X-Microsoft-*, X-Forefront-*, X-Exchange-*, X-Google-*, X-GM-*, X-Apple-*, X-Yahoo-*, X-YMail-*, X-AOL-*, X-Spam*, X-Virus-*, X-Proofpoint-*, X-Barracuda-*, X-Rspamd-*, X-Mimecast-*, X-IronPort-*, X-Sophos-*, X-Report-Abuse*, X-CSA-*. Names compare case-insensitively (`_` and `.` count as `-` for families); a repeated or any other name is a `validation_error`. Values: 1 to 200 printable ASCII characters. Custom headers are sent as spelled, are not DKIM-signed, and can be searched with `header` on `GET /sent-emails`.",
|
|
36429
|
+
"example": {
|
|
36430
|
+
"X-Campaign-Id": "spring-sale",
|
|
36431
|
+
"Auto-Submitted": "auto-generated"
|
|
36432
|
+
}
|
|
36433
|
+
},
|
|
36434
|
+
"if_latest_message_id": {
|
|
36435
|
+
"type": "string",
|
|
36436
|
+
"format": "uuid",
|
|
36437
|
+
"description": "Send only if this message id is still the newest message in the thread this send joins, as this credential can see it. Newest means the message that joined the thread last, in the order messages were attached to it, not by timestamp: a message accepted earlier but attached later is newer. Messages stored before join order was recorded count as older than every message that has one, and among themselves sort by timestamp (received_at for mail received, created_at for mail sent), ties going to the larger id. Pass latest_message_id from GET /emails/{id}/conversation: it is read by the same rule, whereas the last message listed there is ordered by timestamp and can differ when mail joined the thread late. An organization credential sees every message in the thread; a mailbox-scoped agent connection key sees only mail received at its address and mail sent from it. Mail awaiting sender authentication never counts. Otherwise nothing is sent and the response is 409 latest_message_mismatch with error.details.latest_message_id, latest_message_at and thread_id. If in_reply_to resolves to no single thread this credential can check, the response is 422 latest_message_thread_unresolved; that does not change on retry. If the thread stays locked by another writer for longer than the send waits, the response is 503 thread_busy with Retry-After; nothing was sent and the same request can be retried. Checked inside the send transaction under a lock on the thread, so two sends conditioned on the same id cannot both go out. The value is part of the request a replay is matched on: a replay of an already accepted request with the same idempotency key, or the identical request within the automatic deduplication window, returns the original result without checking again, and a request that differs only in if_latest_message_id is not a replay. On /send-mail it requires in_reply_to; it cannot be combined with scheduled_at or repeat, because it is checked when the request is accepted."
|
|
33074
36438
|
}
|
|
33075
36439
|
}
|
|
33076
36440
|
},
|
|
@@ -33508,6 +36872,108 @@ const operationManifest = [
|
|
|
33508
36872
|
"user_id",
|
|
33509
36873
|
"name"
|
|
33510
36874
|
]
|
|
36875
|
+
},
|
|
36876
|
+
"body_text": {
|
|
36877
|
+
"type": ["string", "null"],
|
|
36878
|
+
"description": "Plain-text body sent on the wire. Null when the\nsend carried only an HTML body, or when bodies have\nbeen discarded post-send (`content_discarded_at`\nset).\n"
|
|
36879
|
+
},
|
|
36880
|
+
"body_html": {
|
|
36881
|
+
"type": ["string", "null"],
|
|
36882
|
+
"description": "HTML body sent on the wire. Null when the send\ncarried only a plain-text body, or when bodies\nhave been discarded post-send.\n"
|
|
36883
|
+
},
|
|
36884
|
+
"headers": {
|
|
36885
|
+
"anyOf": [{
|
|
36886
|
+
"type": "object",
|
|
36887
|
+
"description": "Stored addressing and threading headers. A member is null when absent or withheld.",
|
|
36888
|
+
"properties": {
|
|
36889
|
+
"from": { "type": ["string", "null"] },
|
|
36890
|
+
"to": {
|
|
36891
|
+
"type": ["array", "null"],
|
|
36892
|
+
"items": { "type": "string" }
|
|
36893
|
+
},
|
|
36894
|
+
"cc": {
|
|
36895
|
+
"type": ["array", "null"],
|
|
36896
|
+
"items": { "type": "string" }
|
|
36897
|
+
},
|
|
36898
|
+
"bcc": {
|
|
36899
|
+
"type": ["array", "null"],
|
|
36900
|
+
"items": { "type": "string" }
|
|
36901
|
+
},
|
|
36902
|
+
"reply_to": {
|
|
36903
|
+
"type": ["array", "null"],
|
|
36904
|
+
"items": { "type": "string" }
|
|
36905
|
+
},
|
|
36906
|
+
"in_reply_to": {
|
|
36907
|
+
"type": ["array", "null"],
|
|
36908
|
+
"items": { "type": "string" }
|
|
36909
|
+
},
|
|
36910
|
+
"references": {
|
|
36911
|
+
"type": ["array", "null"],
|
|
36912
|
+
"items": { "type": "string" }
|
|
36913
|
+
},
|
|
36914
|
+
"automation": {
|
|
36915
|
+
"type": ["object", "null"],
|
|
36916
|
+
"additionalProperties": { "type": "string" },
|
|
36917
|
+
"description": "Received mail only."
|
|
36918
|
+
}
|
|
36919
|
+
},
|
|
36920
|
+
"required": [
|
|
36921
|
+
"from",
|
|
36922
|
+
"to",
|
|
36923
|
+
"cc",
|
|
36924
|
+
"bcc",
|
|
36925
|
+
"reply_to",
|
|
36926
|
+
"in_reply_to",
|
|
36927
|
+
"references",
|
|
36928
|
+
"automation"
|
|
36929
|
+
]
|
|
36930
|
+
}, { "type": "null" }],
|
|
36931
|
+
"description": "With `include=headers`: stored addressing and threading headers."
|
|
36932
|
+
},
|
|
36933
|
+
"attachments": {
|
|
36934
|
+
"type": "array",
|
|
36935
|
+
"description": "Metadata for submitted inline attachments; download availability is reported separately. Metadata may exist when the archive is unavailable, and legacy sends may have no metadata. Offloaded payload files are not included in the inline archive.",
|
|
36936
|
+
"items": {
|
|
36937
|
+
"type": "object",
|
|
36938
|
+
"description": "Metadata for one submitted inline attachment.",
|
|
36939
|
+
"additionalProperties": true,
|
|
36940
|
+
"properties": {
|
|
36941
|
+
"filename": { "type": ["string", "null"] },
|
|
36942
|
+
"content_type": { "type": "string" },
|
|
36943
|
+
"size_bytes": {
|
|
36944
|
+
"type": "integer",
|
|
36945
|
+
"minimum": 0,
|
|
36946
|
+
"maximum": 9007199254740991
|
|
36947
|
+
},
|
|
36948
|
+
"sha256": { "type": "string" },
|
|
36949
|
+
"part_index": {
|
|
36950
|
+
"type": "integer",
|
|
36951
|
+
"minimum": 0,
|
|
36952
|
+
"maximum": 9007199254740991
|
|
36953
|
+
},
|
|
36954
|
+
"tar_path": { "type": "string" }
|
|
36955
|
+
},
|
|
36956
|
+
"required": [
|
|
36957
|
+
"filename",
|
|
36958
|
+
"content_type",
|
|
36959
|
+
"size_bytes",
|
|
36960
|
+
"sha256",
|
|
36961
|
+
"part_index",
|
|
36962
|
+
"tar_path"
|
|
36963
|
+
]
|
|
36964
|
+
}
|
|
36965
|
+
},
|
|
36966
|
+
"truncated": {
|
|
36967
|
+
"type": "boolean",
|
|
36968
|
+
"description": "With `include`: true when a body on this row was cut or left out."
|
|
36969
|
+
},
|
|
36970
|
+
"truncated_fields": {
|
|
36971
|
+
"type": "array",
|
|
36972
|
+
"items": {
|
|
36973
|
+
"type": "string",
|
|
36974
|
+
"enum": ["body_text", "body_html"]
|
|
36975
|
+
},
|
|
36976
|
+
"description": "With `include`: the body fields on this row that were cut or left out."
|
|
33511
36977
|
}
|
|
33512
36978
|
},
|
|
33513
36979
|
"required": [
|
|
@@ -33883,6 +37349,32 @@ const operationManifest = [
|
|
|
33883
37349
|
"type": "string",
|
|
33884
37350
|
"format": "date-time",
|
|
33885
37351
|
"description": "Optional future execution time (ISO 8601). When set, the\nsend is recorded with status `scheduled` and executed at\nthe requested time instead of immediately. Must be in the\nfuture and at most 30 days out. Incompatible with `wait`\n(a scheduled send resolves after this request completes)\nand with `attachments` / `payload_attachments` (not yet\nsupported on scheduled sends). Reschedule via PATCH\n/sent-emails/{id}; cancel via /sent-emails/{id}/cancel.\nAccepts `Z` or a numeric UTC offset\n(`2026-10-09T17:00:00Z`, `2026-10-09T13:00:00-04:00`); an\noffset is converted to UTC, so the stored and echoed value\nis UTC. A time with no zone, or a bare date, is rejected.\n"
|
|
37352
|
+
},
|
|
37353
|
+
"headers": {
|
|
37354
|
+
"type": "object",
|
|
37355
|
+
"maxProperties": 25,
|
|
37356
|
+
"propertyNames": {
|
|
37357
|
+
"type": "string",
|
|
37358
|
+
"minLength": 1,
|
|
37359
|
+
"maxLength": 64,
|
|
37360
|
+
"pattern": "^[A-Za-z0-9]+(-[A-Za-z0-9]+)*$"
|
|
37361
|
+
},
|
|
37362
|
+
"additionalProperties": {
|
|
37363
|
+
"type": "string",
|
|
37364
|
+
"minLength": 1,
|
|
37365
|
+
"maxLength": 200,
|
|
37366
|
+
"pattern": "^[\\x20-\\x7E]+$"
|
|
37367
|
+
},
|
|
37368
|
+
"description": "Extra headers, as `{ \"Name\": \"value\" }`, at most 25. A name is one of `Auto-Submitted`, `Precedence`, `X-Auto-Response-Suppress`, `X-Primitive-Interaction`, `X-Primitive-Interaction-Ref`, or a custom `X-` name of letters, digits and hyphens that is not credential-like (a word such as `auth`, `token`, `key`, `secret`, `session` or `bearer`, joined forms included) and not in a reserved family: X-Primitive*, X-Original-*, X-Originating-*, X-Forwarded-*, X-Received*, X-Authenticated*, X-Envelope-*, X-Delivered-To, X-Apparently-To, X-Failed-Recipients, X-Loop, X-Priority, X-MSMail-*, X-MS-*, X-Microsoft-*, X-Forefront-*, X-Exchange-*, X-Google-*, X-GM-*, X-Apple-*, X-Yahoo-*, X-YMail-*, X-AOL-*, X-Spam*, X-Virus-*, X-Proofpoint-*, X-Barracuda-*, X-Rspamd-*, X-Mimecast-*, X-IronPort-*, X-Sophos-*, X-Report-Abuse*, X-CSA-*. Names compare case-insensitively (`_` and `.` count as `-` for families); a repeated or any other name is a `validation_error`. Values: 1 to 200 printable ASCII characters. Custom headers are sent as spelled, are not DKIM-signed, and can be searched with `header` on `GET /sent-emails`.",
|
|
37369
|
+
"example": {
|
|
37370
|
+
"X-Campaign-Id": "spring-sale",
|
|
37371
|
+
"Auto-Submitted": "auto-generated"
|
|
37372
|
+
}
|
|
37373
|
+
},
|
|
37374
|
+
"if_latest_message_id": {
|
|
37375
|
+
"type": "string",
|
|
37376
|
+
"format": "uuid",
|
|
37377
|
+
"description": "Send only if this message id is still the newest message in the thread this send joins, as this credential can see it. Newest means the message that joined the thread last, in the order messages were attached to it, not by timestamp: a message accepted earlier but attached later is newer. Messages stored before join order was recorded count as older than every message that has one, and among themselves sort by timestamp (received_at for mail received, created_at for mail sent), ties going to the larger id. Pass latest_message_id from GET /emails/{id}/conversation: it is read by the same rule, whereas the last message listed there is ordered by timestamp and can differ when mail joined the thread late. An organization credential sees every message in the thread; a mailbox-scoped agent connection key sees only mail received at its address and mail sent from it. Mail awaiting sender authentication never counts. Otherwise nothing is sent and the response is 409 latest_message_mismatch with error.details.latest_message_id, latest_message_at and thread_id. If in_reply_to resolves to no single thread this credential can check, the response is 422 latest_message_thread_unresolved; that does not change on retry. If the thread stays locked by another writer for longer than the send waits, the response is 503 thread_busy with Retry-After; nothing was sent and the same request can be retried. Checked inside the send transaction under a lock on the thread, so two sends conditioned on the same id cannot both go out. The value is part of the request a replay is matched on: a replay of an already accepted request with the same idempotency key, or the identical request within the automatic deduplication window, returns the original result without checking again, and a request that differs only in if_latest_message_id is not a replay. On /send-mail it requires in_reply_to; it cannot be combined with scheduled_at or repeat, because it is checked when the request is accepted."
|
|
33886
37378
|
}
|
|
33887
37379
|
},
|
|
33888
37380
|
"required": [
|
|
@@ -42534,14 +46026,199 @@ var DomainsZoneFileCommand = class DomainsZoneFileCommand extends Command {
|
|
|
42534
46026
|
}
|
|
42535
46027
|
};
|
|
42536
46028
|
//#endregion
|
|
46029
|
+
//#region src/oclif/commands/emails-attachment.ts
|
|
46030
|
+
const sharedFlags = {
|
|
46031
|
+
sent: Flags.boolean({ description: "Read an email you sent (a sent email id) instead of one you received." }),
|
|
46032
|
+
output: Flags.string({
|
|
46033
|
+
char: "o",
|
|
46034
|
+
description: "Write the bytes to this file instead of stdout. Nothing else is written to stdout."
|
|
46035
|
+
}),
|
|
46036
|
+
wait: Flags.boolean({
|
|
46037
|
+
description: ATTACHMENT_WAIT_FLAG_DESCRIPTION,
|
|
46038
|
+
default: true,
|
|
46039
|
+
allowNo: true
|
|
46040
|
+
}),
|
|
46041
|
+
"api-key": Flags.string({
|
|
46042
|
+
description: "Primitive API key override (defaults to PRIMITIVE_API_KEY or saved OAuth login credentials)",
|
|
46043
|
+
env: "PRIMITIVE_API_KEY"
|
|
46044
|
+
}),
|
|
46045
|
+
"api-base-url": Flags.string({
|
|
46046
|
+
description: API_BASE_URL_FLAG_DESCRIPTION,
|
|
46047
|
+
env: "PRIMITIVE_API_BASE_URL",
|
|
46048
|
+
hidden: true
|
|
46049
|
+
}),
|
|
46050
|
+
time: Flags.boolean({ description: TIME_FLAG_DESCRIPTION })
|
|
46051
|
+
};
|
|
46052
|
+
/** Retries attachment_not_ready unless --no-wait, with the note on stderr. */
|
|
46053
|
+
function waitFor(call, wait) {
|
|
46054
|
+
return withAttachmentWait(call, {
|
|
46055
|
+
wait,
|
|
46056
|
+
errorCode: (error) => extractErrorCode(extractErrorPayload(error)),
|
|
46057
|
+
onFirstWait: () => {
|
|
46058
|
+
process.stderr.write(`Attachment content is not ready yet; waiting up to ${ATTACHMENT_WAIT_MAX_MS / 1e3}s. Pass --no-wait to return immediately.\n`);
|
|
46059
|
+
}
|
|
46060
|
+
});
|
|
46061
|
+
}
|
|
46062
|
+
/** Writes downloaded bytes to --output or stdout. */
|
|
46063
|
+
async function writeBytes(data, output) {
|
|
46064
|
+
const bytes = Buffer.from(await data.arrayBuffer());
|
|
46065
|
+
if (output) writeFileSync(output, bytes);
|
|
46066
|
+
else process.stdout.write(bytes);
|
|
46067
|
+
}
|
|
46068
|
+
function reportFailure(command, context, error) {
|
|
46069
|
+
const payload = extractErrorPayload(error);
|
|
46070
|
+
writeErrorWithHints(payload);
|
|
46071
|
+
surfaceUnauthorizedHint({
|
|
46072
|
+
auth: context.auth,
|
|
46073
|
+
baseUrlOverridden: context.baseUrlOverridden,
|
|
46074
|
+
configDir: command.config.configDir,
|
|
46075
|
+
payload
|
|
46076
|
+
});
|
|
46077
|
+
process.exitCode = 1;
|
|
46078
|
+
}
|
|
46079
|
+
var EmailsAttachmentCommand = class EmailsAttachmentCommand extends Command {
|
|
46080
|
+
static summary = "Download one attachment by its filename";
|
|
46081
|
+
static description = `Download one attachment of an email by its exact filename, without looking up its part_index first.
|
|
46082
|
+
|
|
46083
|
+
The filename is matched exactly (case-sensitive, after Unicode NFC
|
|
46084
|
+
normalization) against the attachment names in the email's metadata.
|
|
46085
|
+
Any name works, including one made only of digits. When two or more
|
|
46086
|
+
attachments share the name, nothing is downloaded and the command
|
|
46087
|
+
exits 1 with error code attachment_name_ambiguous; error.details
|
|
46088
|
+
lists every matching part_index, and
|
|
46089
|
+
\`primitive emails download-email-attachment-part --id <id> --part-index <n>\`
|
|
46090
|
+
fetches one of them.
|
|
46091
|
+
|
|
46092
|
+
stdout carries only the attachment bytes, unless --output names a file.
|
|
46093
|
+
With --sent, <id> is a sent email id and the attachment is one you sent.`;
|
|
46094
|
+
static examples = ["<%= config.bin %> emails attachment <email-id> report.pdf --output report.pdf", "<%= config.bin %> emails attachment <sent-email-id> invoice.csv --sent > invoice.csv"];
|
|
46095
|
+
static args = {
|
|
46096
|
+
id: Args.string({
|
|
46097
|
+
description: "Email id",
|
|
46098
|
+
required: true
|
|
46099
|
+
}),
|
|
46100
|
+
filename: Args.string({
|
|
46101
|
+
description: "Exact attachment filename",
|
|
46102
|
+
required: true
|
|
46103
|
+
})
|
|
46104
|
+
};
|
|
46105
|
+
static flags = sharedFlags;
|
|
46106
|
+
async run() {
|
|
46107
|
+
const { args, flags } = await this.parse(EmailsAttachmentCommand);
|
|
46108
|
+
const context = await createAuthenticatedCliApiClient({
|
|
46109
|
+
configDir: this.config.configDir,
|
|
46110
|
+
apiKey: flags["api-key"],
|
|
46111
|
+
apiBaseUrl: flags["api-base-url"]
|
|
46112
|
+
});
|
|
46113
|
+
await runWithTiming(flags.time, async () => {
|
|
46114
|
+
const options = {
|
|
46115
|
+
client: context.apiClient.client,
|
|
46116
|
+
path: { id: args.id },
|
|
46117
|
+
query: { filename: args.filename },
|
|
46118
|
+
parseAs: "blob"
|
|
46119
|
+
};
|
|
46120
|
+
const result = await waitFor(() => flags.sent ? downloadSentAttachmentByFilename(options) : downloadEmailAttachmentByFilename(options), flags.wait);
|
|
46121
|
+
if (result.error) {
|
|
46122
|
+
reportFailure(this, context, result.error);
|
|
46123
|
+
return;
|
|
46124
|
+
}
|
|
46125
|
+
await writeBytes(result.data, flags.output);
|
|
46126
|
+
});
|
|
46127
|
+
}
|
|
46128
|
+
};
|
|
46129
|
+
var EmailsAttachmentMembersCommand = class EmailsAttachmentMembersCommand extends Command {
|
|
46130
|
+
static summary = "List the files inside a zip or tar attachment, or fetch one";
|
|
46131
|
+
static description = `List the files inside a zip, tar or tar.gz attachment, or download one of them with --member.
|
|
46132
|
+
|
|
46133
|
+
<attachment> is the attachment's part_index when it is only digits,
|
|
46134
|
+
otherwise its exact filename. The listing prints JSON: the attachment's
|
|
46135
|
+
part_index and filename, the archive format, and members, each with
|
|
46136
|
+
path, size_bytes and sha256. Directories and links are not listed.
|
|
46137
|
+
|
|
46138
|
+
With --member <path>, the command downloads that one file instead, by
|
|
46139
|
+
the path the listing reports. stdout then carries only its bytes,
|
|
46140
|
+
unless --output names a file.
|
|
46141
|
+
|
|
46142
|
+
The server refuses archives that are not zip or tar, that hold an
|
|
46143
|
+
unsafe path (absolute, "..", backslashes and similar), or that exceed
|
|
46144
|
+
its size, entry-count or compression-ratio limits. Each refusal exits 1
|
|
46145
|
+
with its error code, for example archive_unsafe_path or
|
|
46146
|
+
archive_limit_exceeded.`;
|
|
46147
|
+
static examples = [
|
|
46148
|
+
"<%= config.bin %> emails attachment-members <email-id> logs.zip",
|
|
46149
|
+
"<%= config.bin %> emails attachment-members <email-id> 0 --member logs/run.txt --output run.txt",
|
|
46150
|
+
"<%= config.bin %> emails attachment-members <sent-email-id> bundle.tar.gz --sent"
|
|
46151
|
+
];
|
|
46152
|
+
static args = {
|
|
46153
|
+
id: Args.string({
|
|
46154
|
+
description: "Email id",
|
|
46155
|
+
required: true
|
|
46156
|
+
}),
|
|
46157
|
+
attachment: Args.string({
|
|
46158
|
+
description: "The attachment: its part_index when only digits, otherwise its exact filename",
|
|
46159
|
+
required: true
|
|
46160
|
+
})
|
|
46161
|
+
};
|
|
46162
|
+
static flags = {
|
|
46163
|
+
...sharedFlags,
|
|
46164
|
+
member: Flags.string({ description: "Download the file at this path inside the archive, as the listing reports it." })
|
|
46165
|
+
};
|
|
46166
|
+
async run() {
|
|
46167
|
+
const { args, flags } = await this.parse(EmailsAttachmentMembersCommand);
|
|
46168
|
+
const context = await createAuthenticatedCliApiClient({
|
|
46169
|
+
configDir: this.config.configDir,
|
|
46170
|
+
apiKey: flags["api-key"],
|
|
46171
|
+
apiBaseUrl: flags["api-base-url"]
|
|
46172
|
+
});
|
|
46173
|
+
await runWithTiming(flags.time, async () => {
|
|
46174
|
+
const client = context.apiClient.client;
|
|
46175
|
+
const member = flags.member;
|
|
46176
|
+
if (member !== void 0) {
|
|
46177
|
+
const options = {
|
|
46178
|
+
client,
|
|
46179
|
+
path: {
|
|
46180
|
+
id: args.id,
|
|
46181
|
+
attachment: args.attachment,
|
|
46182
|
+
path: member
|
|
46183
|
+
},
|
|
46184
|
+
parseAs: "blob"
|
|
46185
|
+
};
|
|
46186
|
+
const result = await waitFor(() => flags.sent ? downloadSentAttachmentArchiveMember(options) : downloadEmailAttachmentArchiveMember(options), flags.wait);
|
|
46187
|
+
if (result.error) {
|
|
46188
|
+
reportFailure(this, context, result.error);
|
|
46189
|
+
return;
|
|
46190
|
+
}
|
|
46191
|
+
await writeBytes(result.data, flags.output);
|
|
46192
|
+
return;
|
|
46193
|
+
}
|
|
46194
|
+
const options = {
|
|
46195
|
+
client,
|
|
46196
|
+
path: {
|
|
46197
|
+
id: args.id,
|
|
46198
|
+
attachment: args.attachment
|
|
46199
|
+
}
|
|
46200
|
+
};
|
|
46201
|
+
const result = await waitFor(() => flags.sent ? listSentAttachmentArchiveMembers(options) : listEmailAttachmentArchiveMembers(options), flags.wait);
|
|
46202
|
+
if (result.error) {
|
|
46203
|
+
reportFailure(this, context, result.error);
|
|
46204
|
+
return;
|
|
46205
|
+
}
|
|
46206
|
+
const listing = result.data?.data;
|
|
46207
|
+
const text = `${JSON.stringify(listing, null, 2)}\n`;
|
|
46208
|
+
if (flags.output) writeFileSync(flags.output, text);
|
|
46209
|
+
else process.stdout.write(text);
|
|
46210
|
+
});
|
|
46211
|
+
}
|
|
46212
|
+
};
|
|
46213
|
+
//#endregion
|
|
42537
46214
|
//#region src/oclif/repeat-message.ts
|
|
42538
|
-
const UUID$
|
|
46215
|
+
const UUID$5 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
42539
46216
|
/** The server-verified repeat marker on an email read, if any. */
|
|
42540
46217
|
function repeatMarker(detail) {
|
|
42541
46218
|
const marker = detail && typeof detail === "object" ? detail.repeat : void 0;
|
|
42542
46219
|
if (!marker || typeof marker !== "object") return null;
|
|
42543
46220
|
const { repeat_id, sequence } = marker;
|
|
42544
|
-
return typeof repeat_id === "string" && UUID$
|
|
46221
|
+
return typeof repeat_id === "string" && UUID$5.test(repeat_id) && typeof sequence === "number" && Number.isSafeInteger(sequence) && sequence >= 1 ? {
|
|
42545
46222
|
repeat_id: repeat_id.toLowerCase(),
|
|
42546
46223
|
sequence
|
|
42547
46224
|
} : null;
|
|
@@ -44956,7 +48633,7 @@ const PRIMITIVE_TEAM_AUTHOR = {
|
|
|
44956
48633
|
url: "https://primitive.dev"
|
|
44957
48634
|
};
|
|
44958
48635
|
const SDK_VERSION_RANGE = "^2.0.0";
|
|
44959
|
-
const CLI_VERSION_RANGE = "^1.
|
|
48636
|
+
const CLI_VERSION_RANGE = "^1.71.0";
|
|
44960
48637
|
const ESBUILD_VERSION_RANGE = "^0.27.0";
|
|
44961
48638
|
function renderHandler() {
|
|
44962
48639
|
return `// env.PRIMITIVE_API_KEY, env.PRIMITIVE_WEBHOOK_SECRET, and
|
|
@@ -44969,8 +48646,9 @@ function renderHandler() {
|
|
|
44969
48646
|
// including Web Crypto signature verification.
|
|
44970
48647
|
import {
|
|
44971
48648
|
createPrimitiveClient,
|
|
44972
|
-
|
|
48649
|
+
getThread,
|
|
44973
48650
|
PRIMITIVE_SIGNATURE_HEADER,
|
|
48651
|
+
replyToEmail,
|
|
44974
48652
|
type EmailReceivedEvent,
|
|
44975
48653
|
verifyWebhookSignature,
|
|
44976
48654
|
WebhookVerificationError,
|
|
@@ -44982,7 +48660,7 @@ interface Env {
|
|
|
44982
48660
|
PRIMITIVE_WEBHOOK_SECRET: string;
|
|
44983
48661
|
}
|
|
44984
48662
|
|
|
44985
|
-
// Optional loop-protection knob.
|
|
48663
|
+
// Optional loop-protection knob. The reply endpoint server-defaults the
|
|
44986
48664
|
// outbound from-address from the inbound recipient, so most handlers
|
|
44987
48665
|
// do not need to fill this in. Add any extra addresses your handler
|
|
44988
48666
|
// sends from if you later switch to client.send or a custom domain.
|
|
@@ -45047,17 +48725,11 @@ function inboundRecipientDomains(event: EmailReceivedEvent): Set<string> {
|
|
|
45047
48725
|
// empty) is treated as a loop terminator: better to drop one ambiguous
|
|
45048
48726
|
// message than to reply blindly and loop on a bounce.
|
|
45049
48727
|
//
|
|
45050
|
-
//
|
|
45051
|
-
//
|
|
45052
|
-
//
|
|
45053
|
-
//
|
|
45054
|
-
//
|
|
45055
|
-
// EmailReceivedEvent.email.headers shape does not currently surface
|
|
45056
|
-
// these, so detection requires either a parsed-headers field on
|
|
45057
|
-
// the event or a parse of the raw RFC 822 body.
|
|
45058
|
-
// - Track Message-ID / In-Reply-To chains to break ping-pong loops
|
|
45059
|
-
// between two cooperating handlers on different domains.
|
|
45060
|
-
// - Rate-limit replies per sender per hour as a safety net.
|
|
48728
|
+
// Mail that declares itself automated (RFC 3834 Auto-Submitted,
|
|
48729
|
+
// Precedence, List-* and similar headers) and ping-pong between two
|
|
48730
|
+
// automated responders are handled separately, by automatedReasons and
|
|
48731
|
+
// replyCapReached below. Extend this helper if you need stricter
|
|
48732
|
+
// detection, for example a per-sender hourly reply limit.
|
|
45061
48733
|
//
|
|
45062
48734
|
// Loop protection is NOT sender authentication. The address matching
|
|
45063
48735
|
// here is deliberately loose (it exists to avoid replying to our own
|
|
@@ -45108,6 +48780,114 @@ export function isLoop(event: EmailReceivedEvent): boolean {
|
|
|
45108
48780
|
return false;
|
|
45109
48781
|
}
|
|
45110
48782
|
|
|
48783
|
+
// Agent-to-agent loop protection. isLoop above stops bounces and
|
|
48784
|
+
// self-mail. These stop two automated responders (two copies of this
|
|
48785
|
+
// handler, or this handler and another agent) from answering each
|
|
48786
|
+
// other forever:
|
|
48787
|
+
// - every reply is sent with Auto-Submitted: auto-replied (RFC 3834)
|
|
48788
|
+
// and X-Auto-Response-Suppress: All, so a responder that honours
|
|
48789
|
+
// them, including another copy of this handler, does not answer;
|
|
48790
|
+
// - mail the platform marks automated is skipped (automatedReasons);
|
|
48791
|
+
// - at most MAX_REPLIES_PER_THREAD replies from this handler's
|
|
48792
|
+
// address per thread, and none once the thread is MAX_THREAD_DEPTH
|
|
48793
|
+
// messages deep (replyCapReached), which holds even against a peer
|
|
48794
|
+
// that strips the headers above;
|
|
48795
|
+
// - one reply per inbound Message-ID (replyIdempotencyKey).
|
|
48796
|
+
// Raise the caps only if you are sure a peer can never answer you back.
|
|
48797
|
+
const MAX_REPLIES_PER_THREAD = 5;
|
|
48798
|
+
const MAX_THREAD_DEPTH = 20;
|
|
48799
|
+
const THREAD_LOOKUP_TIMEOUT_MS = 5000;
|
|
48800
|
+
const AUTO_REPLY_HEADERS = {
|
|
48801
|
+
"Auto-Submitted": "auto-replied",
|
|
48802
|
+
"X-Auto-Response-Suppress": "All",
|
|
48803
|
+
};
|
|
48804
|
+
|
|
48805
|
+
// The platform's verdict on whether a machine sent this email, decided
|
|
48806
|
+
// when it arrived: the same value GET /v1/emails/{id} returns as
|
|
48807
|
+
// \`automated\`. It covers bounces, auto-replies, and mailing list or
|
|
48808
|
+
// newsletter mail; a handler that answers the sender should not answer
|
|
48809
|
+
// a list. Returns the reasons, or null when a person sent it. Read
|
|
48810
|
+
// loosely so this compiles against SDK releases that predate the typed
|
|
48811
|
+
// field; events without it fall through to isLoop alone.
|
|
48812
|
+
export function automatedReasons(event: EmailReceivedEvent): string[] | null {
|
|
48813
|
+
const email = event.email as {
|
|
48814
|
+
automated?: unknown;
|
|
48815
|
+
automated_reasons?: unknown;
|
|
48816
|
+
};
|
|
48817
|
+
if (email.automated !== true) return null;
|
|
48818
|
+
const reasons = Array.isArray(email.automated_reasons)
|
|
48819
|
+
? email.automated_reasons.filter(
|
|
48820
|
+
(reason): reason is string => typeof reason === "string",
|
|
48821
|
+
)
|
|
48822
|
+
: [];
|
|
48823
|
+
return reasons.length > 0 ? reasons : ["automated"];
|
|
48824
|
+
}
|
|
48825
|
+
|
|
48826
|
+
function ownAddresses(event: EmailReceivedEvent): Set<string> {
|
|
48827
|
+
return new Set([
|
|
48828
|
+
...event.email.smtp.rcpt_to.flatMap(extractEmailAddresses),
|
|
48829
|
+
...EXTRA_SELF_ADDRESSES.map((address) => address.toLowerCase()),
|
|
48830
|
+
]);
|
|
48831
|
+
}
|
|
48832
|
+
|
|
48833
|
+
// Returns why this handler must not reply again in this thread, or
|
|
48834
|
+
// null. The thread lookup counts this handler's own earlier replies; if
|
|
48835
|
+
// it fails or times out, the References depth check still bounds the
|
|
48836
|
+
// thread. The thread view lists only the most recent messages, so a
|
|
48837
|
+
// thread longer than what came back counts as capped.
|
|
48838
|
+
export async function replyCapReached(
|
|
48839
|
+
client: ReturnType<typeof createPrimitiveClient>,
|
|
48840
|
+
event: EmailReceivedEvent,
|
|
48841
|
+
): Promise<string | null> {
|
|
48842
|
+
const parsed = event.email.parsed as { references?: unknown };
|
|
48843
|
+
const depth = Array.isArray(parsed.references) ? parsed.references.length : 0;
|
|
48844
|
+
if (depth >= MAX_THREAD_DEPTH) return "thread_depth_cap";
|
|
48845
|
+
|
|
48846
|
+
const threadId = event.email.thread_id;
|
|
48847
|
+
if (!threadId) return null;
|
|
48848
|
+
try {
|
|
48849
|
+
const result = await getThread({
|
|
48850
|
+
client: client.client,
|
|
48851
|
+
path: { id: threadId },
|
|
48852
|
+
signal: AbortSignal.timeout(THREAD_LOOKUP_TIMEOUT_MS),
|
|
48853
|
+
});
|
|
48854
|
+
const thread = result.data?.data;
|
|
48855
|
+
const messages = thread?.messages ?? [];
|
|
48856
|
+
if ((thread?.message_count ?? 0) > messages.length) {
|
|
48857
|
+
return "thread_reply_cap";
|
|
48858
|
+
}
|
|
48859
|
+
const own = ownAddresses(event);
|
|
48860
|
+
const replies = messages.filter(
|
|
48861
|
+
(message) =>
|
|
48862
|
+
message.direction === "outbound" &&
|
|
48863
|
+
extractEmailAddresses(message.from).some((address) => own.has(address)),
|
|
48864
|
+
).length;
|
|
48865
|
+
return replies >= MAX_REPLIES_PER_THREAD ? "thread_reply_cap" : null;
|
|
48866
|
+
} catch (threadError) {
|
|
48867
|
+
console.error("thread lookup failed:", threadError);
|
|
48868
|
+
return null;
|
|
48869
|
+
}
|
|
48870
|
+
}
|
|
48871
|
+
|
|
48872
|
+
// One reply per inbound message per replying address, however many
|
|
48873
|
+
// times or through whichever relay it is delivered.
|
|
48874
|
+
export async function replyIdempotencyKey(
|
|
48875
|
+
event: EmailReceivedEvent,
|
|
48876
|
+
): Promise<string> {
|
|
48877
|
+
const source = [
|
|
48878
|
+
event.email.smtp.rcpt_to[0],
|
|
48879
|
+
event.email.headers.message_id ?? event.email.id,
|
|
48880
|
+
].join("\\n");
|
|
48881
|
+
const digest = await crypto.subtle.digest(
|
|
48882
|
+
"SHA-256",
|
|
48883
|
+
new TextEncoder().encode(source),
|
|
48884
|
+
);
|
|
48885
|
+
const hex = Array.from(new Uint8Array(digest), (byte) =>
|
|
48886
|
+
byte.toString(16).padStart(2, "0"),
|
|
48887
|
+
).join("");
|
|
48888
|
+
return \`reply:\${hex}\`;
|
|
48889
|
+
}
|
|
48890
|
+
|
|
45111
48891
|
export default {
|
|
45112
48892
|
async fetch(
|
|
45113
48893
|
req: Request,
|
|
@@ -45146,11 +48926,19 @@ export default {
|
|
|
45146
48926
|
if (isLoop(event)) {
|
|
45147
48927
|
return Response.json({ ok: true, skipped: "loop" });
|
|
45148
48928
|
}
|
|
48929
|
+
const automated = automatedReasons(event);
|
|
48930
|
+
if (automated) {
|
|
48931
|
+
return Response.json({ ok: true, skipped: "automated", reasons: automated });
|
|
48932
|
+
}
|
|
45149
48933
|
|
|
45150
48934
|
const client = createPrimitiveClient({
|
|
45151
48935
|
apiKey: env.PRIMITIVE_API_KEY,
|
|
45152
48936
|
apiBaseUrl: env.PRIMITIVE_API_BASE_URL,
|
|
45153
48937
|
});
|
|
48938
|
+
const capped = await replyCapReached(client, event);
|
|
48939
|
+
if (capped) {
|
|
48940
|
+
return Response.json({ ok: true, skipped: capped });
|
|
48941
|
+
}
|
|
45154
48942
|
|
|
45155
48943
|
// To add an LLM or another API, store its key as a Function secret.
|
|
45156
48944
|
// Example:
|
|
@@ -45159,7 +48947,7 @@ export default {
|
|
|
45159
48947
|
|
|
45160
48948
|
// Recipient gate
|
|
45161
48949
|
// https://www.primitive.dev/docs/sending#who-you-can-send-to
|
|
45162
|
-
// Even via
|
|
48950
|
+
// Even via the reply endpoint, sends to the original sender are
|
|
45163
48951
|
// subject to the recipient gate. New accounts can send to
|
|
45164
48952
|
// Primitive-managed domains, verified domains, addresses that
|
|
45165
48953
|
// have authenticated to you, and other org-member signup emails.
|
|
@@ -45171,23 +48959,42 @@ export default {
|
|
|
45171
48959
|
// Recipient routing: a single function can handle multiple inbound
|
|
45172
48960
|
// addresses by branching on event.email.headers.to. For example,
|
|
45173
48961
|
// route "support@" to a ticketing flow and "sales@" to a lead
|
|
45174
|
-
// capture flow before
|
|
48962
|
+
// capture flow before replying.
|
|
45175
48963
|
|
|
45176
|
-
//
|
|
45177
|
-
//
|
|
45178
|
-
//
|
|
45179
|
-
//
|
|
45180
|
-
//
|
|
45181
|
-
//
|
|
45182
|
-
//
|
|
45183
|
-
//
|
|
45184
|
-
//
|
|
45185
|
-
// and
|
|
45186
|
-
|
|
45187
|
-
|
|
48964
|
+
// replyToEmail calls POST /v1/emails/{id}/reply (NOT /send-mail)
|
|
48965
|
+
// so the server derives recipients, the \`Re: <parent>\` subject,
|
|
48966
|
+
// threading headers, and the in_reply_to_email_id foreign key
|
|
48967
|
+
// automatically. The FK is what populates the inbound's
|
|
48968
|
+
// \`replies\` array on GET /emails/{id}; without it,
|
|
48969
|
+
// \`primitive functions:test-function --show-sends\` would report
|
|
48970
|
+
// no outbound emails for this handler because the join would
|
|
48971
|
+
// miss. It is used instead of client.reply because the reply
|
|
48972
|
+
// carries AUTO_REPLY_HEADERS (the endpoint accepts Auto-Submitted,
|
|
48973
|
+
// X-Auto-Response-Suppress and Precedence). Use client.send only
|
|
48974
|
+
// when you need to send to a different address than the inbound
|
|
48975
|
+
// sender, and pass \`in_reply_to\` explicitly to preserve
|
|
48976
|
+
// correlation.
|
|
48977
|
+
const replyBody = {
|
|
48978
|
+
body_text: "Got your message.",
|
|
48979
|
+
headers: AUTO_REPLY_HEADERS,
|
|
48980
|
+
};
|
|
48981
|
+
const result = await replyToEmail({
|
|
48982
|
+
client: client.client,
|
|
48983
|
+
path: { id: event.email.id },
|
|
48984
|
+
body: replyBody,
|
|
48985
|
+
headers: { "Idempotency-Key": await replyIdempotencyKey(event) },
|
|
45188
48986
|
});
|
|
48987
|
+
if (result.error) {
|
|
48988
|
+
// A refused send (a rate limit included) is final: answer 2xx
|
|
48989
|
+
// so the delivery queue does not re-invoke this handler.
|
|
48990
|
+
return Response.json({
|
|
48991
|
+
ok: false,
|
|
48992
|
+
status: result.response?.status ?? null,
|
|
48993
|
+
error: result.error,
|
|
48994
|
+
});
|
|
48995
|
+
}
|
|
45189
48996
|
|
|
45190
|
-
return Response.json({ ok: true, reply });
|
|
48997
|
+
return Response.json({ ok: true, reply: result.data?.data ?? null });
|
|
45191
48998
|
} catch (err) {
|
|
45192
48999
|
// Return 2xx so the webhook delivery loop does not retry a bug
|
|
45193
49000
|
// it cannot fix. The function-invocation row still records the
|
|
@@ -48888,7 +52695,7 @@ function failure(result, now) {
|
|
|
48888
52695
|
const retryAfter = retry ? /^\d+$/.test(retry) ? Number(retry) * 1e3 : Date.parse(retry) - now : 0;
|
|
48889
52696
|
return new RequestFailure(result.response?.status ?? 0, code, Number.isFinite(retryAfter) ? Math.max(0, retryAfter) : 0);
|
|
48890
52697
|
}
|
|
48891
|
-
const UUID$
|
|
52698
|
+
const UUID$4 = /^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/i;
|
|
48892
52699
|
function validDelivery(value) {
|
|
48893
52700
|
if (!value || typeof value !== "object") return false;
|
|
48894
52701
|
const d = value;
|
|
@@ -48896,7 +52703,7 @@ function validDelivery(value) {
|
|
|
48896
52703
|
"queue_id",
|
|
48897
52704
|
"event_id",
|
|
48898
52705
|
"delivery_id"
|
|
48899
|
-
].every((key) => typeof d[key] === "string" && UUID$
|
|
52706
|
+
].every((key) => typeof d[key] === "string" && UUID$4.test(d[key])) && [
|
|
48900
52707
|
"event_type",
|
|
48901
52708
|
"lease_token",
|
|
48902
52709
|
"body"
|
|
@@ -50340,7 +54147,7 @@ var ListenInitCommand = class ListenInitCommand extends Command {
|
|
|
50340
54147
|
};
|
|
50341
54148
|
//#endregion
|
|
50342
54149
|
//#region src/oclif/commands/listen-pending.ts
|
|
50343
|
-
const UUID$
|
|
54150
|
+
const UUID$3 = /^[0-9a-f]{8}(?:-[0-9a-f]{4}){3}-[0-9a-f]{12}$/i;
|
|
50344
54151
|
var ListenPendingCommand = class ListenPendingCommand extends Command {
|
|
50345
54152
|
static summary = "List mail a session's wake listener accepted but not yet read";
|
|
50346
54153
|
static description = `Prints the durable pending notices for one Claude Code session of the selected connected profile. A notice is written before its wake event is acknowledged and is removed when the session reads that exact email with \`primitive emails get --id <id>\`. Notices carry only server-derived metadata (sender, thread, whether this profile sent in the thread, newer-mail count), never subject or body. Status notices (kind status) name a peer signal on a message this session sent and have nothing to read; clear them with --clear after delivering them. Unread mail notices are never dropped: at 50 notices the list reports full and newer mail stays queued, unacknowledged, until some are read. Reading never takes the writer lock, so it works while a listener holds it.`;
|
|
@@ -50360,9 +54167,9 @@ var ListenPendingCommand = class ListenPendingCommand extends Command {
|
|
|
50360
54167
|
if (!profileName) this.error("Pending notices belong to a connected agent profile. Set PRIMITIVE_AGENT_PROFILE.");
|
|
50361
54168
|
const runtime = currentMailSessionKey();
|
|
50362
54169
|
const session = flags.session ?? (runtime?.startsWith("claude:") ? runtime.slice(7) : void 0);
|
|
50363
|
-
if (!session || !UUID$
|
|
54170
|
+
if (!session || !UUID$3.test(session)) this.error("Pass --session with the Claude Code session UUID.");
|
|
50364
54171
|
const clear = flags.clear ?? [];
|
|
50365
|
-
if (clear.some((id) => !UUID$
|
|
54172
|
+
if (clear.some((id) => !UUID$3.test(id))) this.error("--clear takes exact email UUIDs.");
|
|
50366
54173
|
const notices = clear.length ? await removePendingMail(this.config.configDir, profileName, session, clear) : readPendingMail(this.config.configDir, profileName, session);
|
|
50367
54174
|
this.log(JSON.stringify({
|
|
50368
54175
|
session_id: session.toLowerCase(),
|
|
@@ -55754,7 +59561,7 @@ const ID_ARG = Args.string({
|
|
|
55754
59561
|
description: "Repeat id (UUID), as returned by send or reply",
|
|
55755
59562
|
required: true
|
|
55756
59563
|
});
|
|
55757
|
-
const UUID$
|
|
59564
|
+
const UUID$2 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
55758
59565
|
const STATUSES = [
|
|
55759
59566
|
"active",
|
|
55760
59567
|
"paused",
|
|
@@ -55828,7 +59635,7 @@ var RepeatStopCommand = class RepeatStopCommand extends Command {
|
|
|
55828
59635
|
};
|
|
55829
59636
|
async run() {
|
|
55830
59637
|
const { flags } = await this.parse(RepeatStopCommand);
|
|
55831
|
-
if (!UUID$
|
|
59638
|
+
if (!UUID$2.test(flags.id)) this.error("--id must be a repeat id or email id (UUID).", { exit: 2 });
|
|
55832
59639
|
let body;
|
|
55833
59640
|
try {
|
|
55834
59641
|
body = buildRepeatStopBody({ reason: flags.reason });
|
|
@@ -56238,6 +60045,7 @@ var ReplyCommand = class ReplyCommand extends Command {
|
|
|
56238
60045
|
description: "Attach a file to the reply. Repeat --attachment to attach multiple files.",
|
|
56239
60046
|
multiple: true
|
|
56240
60047
|
}),
|
|
60048
|
+
"if-latest-message-id": Flags.string({ description: "Send only if this message id is still the newest message in the thread, as your credential sees it (received or sent, in the order messages joined the thread). Otherwise nothing is sent and the command exits 1 with error code latest_message_mismatch and the current newest id in error.details.latest_message_id. Pass latest_message_id from `primitive emails conversation`. Cannot be combined with a repeat." }),
|
|
56241
60049
|
wait: Flags.boolean({ description: "Block until the receiving MTA returns an outcome. Without --wait, the call returns once Primitive has accepted the reply for delivery." }),
|
|
56242
60050
|
"idempotency-key": Flags.string({ description: IDEMPOTENCY_KEY_FLAG_DESCRIPTION }),
|
|
56243
60051
|
json: Flags.boolean({ description: "Emit one outcome envelope { outcome, exit_code, outcome_message, sent_email_id, idempotency_key, sent, http_status, error, follow_up_commands, prior_replies, prior_replies_check } on stdout for every outcome, including failures, with nothing on stderr. Without --json, stdout is the send record as before." }),
|
|
@@ -56353,12 +60161,14 @@ var ReplyCommand = class ReplyCommand extends Command {
|
|
|
56353
60161
|
fyi: true,
|
|
56354
60162
|
...bodies.body !== void 0 ? { body_text: bodies.body.trimEnd() } : {},
|
|
56355
60163
|
...flags.from !== void 0 ? { from: flags.from } : {},
|
|
56356
|
-
in_reply_to_email_id: emailId
|
|
60164
|
+
in_reply_to_email_id: emailId,
|
|
60165
|
+
...flags["if-latest-message-id"] !== void 0 ? { if_latest_message_id: flags["if-latest-message-id"] } : {}
|
|
56357
60166
|
} : {
|
|
56358
60167
|
...plainContent,
|
|
56359
60168
|
...flags.from !== void 0 ? { from: flags.from } : {},
|
|
56360
60169
|
...flags.all ? { reply_all: true } : {},
|
|
56361
60170
|
...repeat !== void 0 ? { repeat } : {},
|
|
60171
|
+
...flags["if-latest-message-id"] !== void 0 ? { if_latest_message_id: flags["if-latest-message-id"] } : {},
|
|
56362
60172
|
in_reply_to_email_id: emailId
|
|
56363
60173
|
});
|
|
56364
60174
|
this.idempotencyKey = idempotencyKey;
|
|
@@ -56385,7 +60195,8 @@ var ReplyCommand = class ReplyCommand extends Command {
|
|
|
56385
60195
|
...flags.from !== void 0 ? { from: flags.from } : {},
|
|
56386
60196
|
...flags.wait !== void 0 ? { wait: flags.wait } : {},
|
|
56387
60197
|
...flags.all ? { reply_all: true } : {},
|
|
56388
|
-
...repeat !== void 0 ? { repeat } : {}
|
|
60198
|
+
...repeat !== void 0 ? { repeat } : {},
|
|
60199
|
+
...flags["if-latest-message-id"] !== void 0 ? { if_latest_message_id: flags["if-latest-message-id"] } : {}
|
|
56389
60200
|
};
|
|
56390
60201
|
const halted = await haltAutoWorking(this.config.configDir, {
|
|
56391
60202
|
emailIds: [emailId],
|
|
@@ -57386,6 +61197,10 @@ var SendCommand = class SendCommand extends Command {
|
|
|
57386
61197
|
multiple: true
|
|
57387
61198
|
}),
|
|
57388
61199
|
"in-reply-to": Flags.string({ description: "Message-Id of the parent email when threading a reply on the wire. For replying to an inbound message you received, prefer `primitive reply --id <inbound-id>`." }),
|
|
61200
|
+
"if-latest-message-id": Flags.string({
|
|
61201
|
+
description: "Send only if this message id is still the newest message in the thread, as your credential sees it (received or sent, in the order messages joined the thread). Otherwise nothing is sent and the command exits 1 with error code latest_message_mismatch and the current newest id in error.details.latest_message_id. Pass latest_message_id from `primitive emails conversation`. Cannot be combined with a repeat. Requires --in-reply-to, which names the thread.",
|
|
61202
|
+
dependsOn: ["in-reply-to"]
|
|
61203
|
+
}),
|
|
57389
61204
|
fyi: Flags.boolean({
|
|
57390
61205
|
description: `${FYI_FLAG_DESCRIPTION} Requires --in-reply-to, the Message-Id being acknowledged.`,
|
|
57391
61206
|
dependsOn: ["in-reply-to"],
|
|
@@ -57488,6 +61303,7 @@ var SendCommand = class SendCommand extends Command {
|
|
|
57488
61303
|
if (this.waitNotice && !flags.json) process.stderr.write(`${this.waitNotice}\n`);
|
|
57489
61304
|
const threading = {
|
|
57490
61305
|
...flags["in-reply-to"] !== void 0 ? { in_reply_to: flags["in-reply-to"] } : {},
|
|
61306
|
+
...flags["if-latest-message-id"] !== void 0 ? { if_latest_message_id: flags["if-latest-message-id"] } : {},
|
|
57491
61307
|
...flags.wait !== void 0 ? { wait: flags.wait } : {},
|
|
57492
61308
|
...waitTimeoutMs !== void 0 ? { wait_timeout_ms: waitTimeoutMs } : {},
|
|
57493
61309
|
...repeat !== void 0 ? { repeat } : {}
|
|
@@ -57564,6 +61380,92 @@ var SendCommand = class SendCommand extends Command {
|
|
|
57564
61380
|
}
|
|
57565
61381
|
};
|
|
57566
61382
|
//#endregion
|
|
61383
|
+
//#region src/oclif/commands/sent-cancel.ts
|
|
61384
|
+
const UUID$1 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
61385
|
+
/** Shown when the send exists but has left `scheduled`. */
|
|
61386
|
+
const NOT_SCHEDULED_HINT = "Only a send that is still scheduled can be canceled. `error.details.status` is where it is now; a send already handed to delivery cannot be recalled.";
|
|
61387
|
+
/** One line for terminal output. */
|
|
61388
|
+
function formatCanceledSend(send) {
|
|
61389
|
+
const parts = [`Canceled ${send.id}`];
|
|
61390
|
+
if (send.scheduled_at) parts.push(`(was scheduled for ${send.scheduled_at})`);
|
|
61391
|
+
if (send.client_idempotency_key) parts.push(`Idempotency-Key ${send.client_idempotency_key} is free to use again.`);
|
|
61392
|
+
return parts.join(" ");
|
|
61393
|
+
}
|
|
61394
|
+
const ISO_INSTANT_WITH_ZONE = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}(:\d{2}(\.\d+)?)?(Z|[+-]\d{2}:\d{2})$/i;
|
|
61395
|
+
var SentCancelCommand = class SentCancelCommand extends Command {
|
|
61396
|
+
static summary = "Cancel a scheduled send";
|
|
61397
|
+
static description = "Cancel a send that is still scheduled, by its id or by the Idempotency-Key you sent it with. Nothing is sent, the record is kept with status `canceled`, and the key is released so it can schedule a new send. A send the scheduler has already picked up, or one already sent, canceled or failed, cannot be canceled and the command exits 1 with its current status. A connected agent can cancel only its own sends.";
|
|
61398
|
+
static examples = ["<%= config.bin %> sent cancel --idempotency-key order-42", "<%= config.bin %> sent cancel --id 7f9c24e8-3b1a-4b6e-9c11-0d5f8a2e6c3d --json"];
|
|
61399
|
+
static flags = {
|
|
61400
|
+
"api-key": Flags.string({
|
|
61401
|
+
description: "Primitive API key override (defaults to PRIMITIVE_API_KEY or saved OAuth login credentials)",
|
|
61402
|
+
env: "PRIMITIVE_API_KEY"
|
|
61403
|
+
}),
|
|
61404
|
+
"api-base-url": Flags.string({
|
|
61405
|
+
description: API_BASE_URL_FLAG_DESCRIPTION,
|
|
61406
|
+
env: "PRIMITIVE_API_BASE_URL",
|
|
61407
|
+
hidden: true
|
|
61408
|
+
}),
|
|
61409
|
+
id: Flags.string({
|
|
61410
|
+
description: "The sent email id (UUID) returned when it was scheduled",
|
|
61411
|
+
exactlyOne: ["id", "idempotency-key"]
|
|
61412
|
+
}),
|
|
61413
|
+
"idempotency-key": Flags.string({
|
|
61414
|
+
description: "The Idempotency-Key the send was created with, matched exactly",
|
|
61415
|
+
exactlyOne: ["id", "idempotency-key"]
|
|
61416
|
+
}),
|
|
61417
|
+
"created-before": Flags.string({
|
|
61418
|
+
description: "With --idempotency-key: only consider sends created at or before this ISO 8601 time, such as when you first tried to cancel, so a retry never reaches a newer send that reused the key",
|
|
61419
|
+
dependsOn: ["idempotency-key"]
|
|
61420
|
+
}),
|
|
61421
|
+
json: Flags.boolean({ description: "Print the API result as JSON" }),
|
|
61422
|
+
time: Flags.boolean({ description: TIME_FLAG_DESCRIPTION })
|
|
61423
|
+
};
|
|
61424
|
+
async run() {
|
|
61425
|
+
const { flags } = await this.parse(SentCancelCommand);
|
|
61426
|
+
if (flags.id !== void 0 && !UUID$1.test(flags.id)) this.error("--id must be a sent email id (UUID).", { exit: 2 });
|
|
61427
|
+
const key = flags["idempotency-key"]?.trim();
|
|
61428
|
+
if (key !== void 0 && key.length === 0) this.error("--idempotency-key must not be empty.", { exit: 2 });
|
|
61429
|
+
const createdBefore = flags["created-before"];
|
|
61430
|
+
if (createdBefore !== void 0 && (!ISO_INSTANT_WITH_ZONE.test(createdBefore) || Number.isNaN(Date.parse(createdBefore)))) this.error("--created-before must be an ISO 8601 time with a time zone, such as 2026-10-20T14:00:00Z.", { exit: 2 });
|
|
61431
|
+
const { apiClient, auth, baseUrlOverridden } = await createAuthenticatedCliApiClient({
|
|
61432
|
+
apiKey: flags["api-key"],
|
|
61433
|
+
apiBaseUrl: flags["api-base-url"],
|
|
61434
|
+
configDir: this.config.configDir
|
|
61435
|
+
});
|
|
61436
|
+
await runWithTiming(flags.time === true, async () => {
|
|
61437
|
+
const client = apiClient.client;
|
|
61438
|
+
const result = key !== void 0 ? await cancelSentEmailByIdempotencyKey({
|
|
61439
|
+
body: {
|
|
61440
|
+
idempotency_key: key,
|
|
61441
|
+
...createdBefore ? { created_before: createdBefore } : {}
|
|
61442
|
+
},
|
|
61443
|
+
client,
|
|
61444
|
+
responseStyle: "fields"
|
|
61445
|
+
}) : await cancelSentEmail({
|
|
61446
|
+
client,
|
|
61447
|
+
path: { id: flags.id },
|
|
61448
|
+
responseStyle: "fields"
|
|
61449
|
+
});
|
|
61450
|
+
const send = result.data?.data;
|
|
61451
|
+
if (result.error || send === void 0) {
|
|
61452
|
+
const payload = extractErrorPayload(result.error);
|
|
61453
|
+
writeErrorWithHints(payload);
|
|
61454
|
+
if (extractErrorCode(payload) === "not_scheduled") process.stderr.write(`${NOT_SCHEDULED_HINT}\n`);
|
|
61455
|
+
surfaceUnauthorizedHint({
|
|
61456
|
+
auth,
|
|
61457
|
+
baseUrlOverridden,
|
|
61458
|
+
configDir: this.config.configDir,
|
|
61459
|
+
payload
|
|
61460
|
+
});
|
|
61461
|
+
process.exitCode = 1;
|
|
61462
|
+
return;
|
|
61463
|
+
}
|
|
61464
|
+
this.log(flags.json ? JSON.stringify(send, null, 2) : formatCanceledSend(send));
|
|
61465
|
+
});
|
|
61466
|
+
}
|
|
61467
|
+
};
|
|
61468
|
+
//#endregion
|
|
57567
61469
|
//#region src/oclif/commands/sent-get.ts
|
|
57568
61470
|
const IDEMPOTENCY_LOOKUP_LIMIT = 10;
|
|
57569
61471
|
function idempotencyKeyNotFoundError(key) {
|
|
@@ -59322,6 +63224,7 @@ const CANONICAL_OPERATION_ALIASES = {
|
|
|
59322
63224
|
"account:show": "account:get-account",
|
|
59323
63225
|
"account:storage": "account:get-storage-stats",
|
|
59324
63226
|
"account:webhook-secret": "account:get-webhook-secret",
|
|
63227
|
+
"changes:list": "changes:list-changes",
|
|
59325
63228
|
"agent:claim": "agent:start-agent-claim",
|
|
59326
63229
|
"agent:claim-link": "agent:create-agent-claim-link",
|
|
59327
63230
|
"agent:claim-verify": "agent:verify-agent-claim",
|
|
@@ -59486,6 +63389,8 @@ const COMMANDS = {
|
|
|
59486
63389
|
doctor: DoctorCommand,
|
|
59487
63390
|
"machine:doctor": MachineDoctorCommand,
|
|
59488
63391
|
"emails:latest": EmailsLatestCommand,
|
|
63392
|
+
"emails:attachment": EmailsAttachmentCommand,
|
|
63393
|
+
"emails:attachment-members": EmailsAttachmentMembersCommand,
|
|
59489
63394
|
"emails:watch": EmailsWatchCommand,
|
|
59490
63395
|
"emails:wait": EmailsWaitCommand,
|
|
59491
63396
|
search: SearchCommand,
|
|
@@ -59533,6 +63438,7 @@ const COMMANDS = {
|
|
|
59533
63438
|
"credits:redeem-credit-code": CreditsRedeemCommand,
|
|
59534
63439
|
"credits:balance": CreditsBalanceCommand,
|
|
59535
63440
|
"sent:get": SentGetCommand,
|
|
63441
|
+
"sent:cancel": SentCancelCommand,
|
|
59536
63442
|
...Object.fromEntries(Object.entries(CANONICAL_OPERATION_ALIASES).map(([alias, target]) => {
|
|
59537
63443
|
const command = generatedCommands[target];
|
|
59538
63444
|
if (!command) throw new Error(`Missing generated command target for alias ${alias}`);
|